Skip to content

Data Encryption

OpenViking provides transparent at-rest data encryption to ensure data security and isolation in multi-tenant environments.

Overview

Why Encryption

In a multi-tenant architecture, resources, memories, and skills from different customers (accounts) are stored in a shared AGFS instance. Encryption ensures:

  • Even if an attacker gains AGFS disk access, they cannot read any customer's plaintext data
  • Different accounts' data is encrypted with independent keys for tenant isolation
  • All encryption/decryption operations are centralized at the VikingFS layer; AGFS and external object stores only see ciphertext

Transparency

Encryption is completely transparent to users and developers:

  • No client API changes: Existing code works without modification
  • Application layer unaware: Read/write operations behave exactly like unencrypted
  • Backward compatible: Unencrypted old files can still be read normally

Three-Layer Key Architecture

OpenViking uses an Envelope Encryption architecture with a three-layer key system:

┌─────────────────────────────────────────────────────────┐
│  Layer 1: Root Key                                     │
│  • Global unique per OpenViking instance               │
│  • Storage: KMS service / ~/.openviking/master.key    │
│  • Purpose: Derive all account keys                    │
└────────────────────┬────────────────────────────────────┘
                     │ HKDF derivation

┌─────────────────────────────────────────────────────────┐
│  Layer 2: Account Key (KEK)                           │
│  • One independent key per account                     │
│  • Not stored, derived at runtime                      │
│  • Purpose: Encrypt all file keys for this account     │
└────────────────────┬────────────────────────────────────┘
                     │ AES-256-GCM encryption

┌─────────────────────────────────────────────────────────┐
│  Layer 3: File Key (DEK)                              │
│  • New random key generated per write operation        │
│  • Stored encrypted in file header (envelope)          │
│  • Purpose: Encrypt actual file content                │
└─────────────────────────────────────────────────────────┘

Key Hierarchy Summary

LayerNameDescriptionQuantity
Root KeyRoot KeySystem master key, used to derive all account keys1 per instance
Account KeyAccount KeyIndependent key per account, derived from root key1 per account
File KeyFile KeyOne-time random key per file1 per write

Key Providers

OpenViking supports three key providers for different deployment scenarios:

ProviderUse CaseRoot Key StorageFeatures
LocalDev environments, single-node deploymentsLocal file ~/.openviking/master.keySimple, no external services
VaultProduction, multi-cloudHashiCorp Vault Transit EngineEnterprise-grade key management, version control
Volcengine KMSVolcengine cloud deploymentsVolcengine KMSCloud-native KMS service

Local (File)

Suitable for development and single-node deployments:

json
{
  "encryption": {
    "enabled": true,
    "provider": "local",
    "local": {
      "key_file": "~/.openviking/master.key"
    }
  }
}

Initialization command:

bash
ov system crypto init-key --output ~/.openviking/master.key

Vault (HashiCorp Vault)

Suitable for production and multi-cloud deployments:

json
{
  "encryption": {
    "enabled": true,
    "provider": "vault",
    "vault": {
      "address": "https://vault.example.com:8200",
      "token": "hvs.your-vault-token",
      "mount_point": "transit",
      "kv_mount_point": "secret",
      "kv_version": 1,
      "root_key_name": "openviking-root-key",
      "encrypted_root_key_key": "openviking-encrypted-root-key"
    }
  }
}

Volcengine KMS

Suitable for Volcengine cloud deployments:

json
{
  "encryption": {
    "enabled": true,
    "provider": "volcengine_kms",
    "volcengine_kms": {
      "key_id": "d926aa0d-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
      "region": "cn-beijing",
      "access_key": "AKLTxxxxxxxxxxxxxxxxxx",
      "secret_key": "Tmpxxxxxxxxxxxxxxxxxxxxxx",
      "endpoint": null,
      "key_file": "~/.openviking/openviking-volcengine-root-key.enc"
    }
  }
}

How It Works

Write Flow

Client              VikingFS             FileEncryptor         KeyManager        AGFS
  │                   │                       │                     │             │
  │  write(uri, data) │                       │                     │             │
  │──────────────────>│                       │                     │             │
  │                   │  encrypt(account_id,  │                     │             │
  │                   │           plaintext)  │                     │             │
  │                   │──────────────────────>│                     │             │
  │                   │                       │ derive_account_key()│             │
  │                   │                       │────────────────────>│             │
  │                   │                       │<────────────────────│             │
  │                   │                       │  account_key        │             │
  │                   │  1. Generate random File Key                              │
  │                   │  2. Encrypt content with File Key                         │
  │                   │  3. Encrypt File Key with Account Key                     │
  │                   │  4. Build envelope format                                 │
  │                   │<──────────────────────│                     │             │
  │                   │  ciphertext           │                     │             │
  │                   │──────────────────────────────────────────────────────────>│
  │                   │                       │                     │  Write      │
  │<──────────────────│                       │                     │             │
  │   success         │                       │                     │             │

Read Flow

Client              VikingFS             FileEncryptor         KeyManager        AGFS
  │                   │                       │                     │             │
  │  read(uri)        │                       │                     │             │
  │──────────────────>│                       │                     │             │
  │                   │──────────────────────────────────────────────────────────>│
  │                   │                       │                     │  Read       │
  │                   │<──────────────────────────────────────────────────────────│
  │                   │ raw_bytes             │                     │             │
  │                   │ Check magic == "OVE1"?│                     │             │
  │                   │ Yes → decrypt()       │                     │             │
  │                   │──────────────────────>│                     │             │
  │                   │                       │ derive_account_key()│             │
  │                   │                       │────────────────────>│             │
  │                   │                       │<────────────────────│             │
  │                   │                       │  account_key        │             │
  │                   │  1. Parse envelope format                                 │
  │                   │  2. Decrypt File Key with Account Key                     │
  │                   │  3. Decrypt content with File Key                         │
  │                   │<──────────────────────│                     │             │
  │                   │  plaintext            │                     │             │
  │<──────────────────│                       │                     │             │
  │   content         │                       │                     │             │

Envelope Format

Encrypted files use a unified envelope format starting with the magic number OVE1 (OpenViking Encryption v1):

┌─────────────────────────────────────────────────────────────┐
│  Magic   │ Version │ Provider  │ Encrypted File Key │  ...  │
│  4 bytes │ 1 byte  │  1 byte   │   Variable length  │  ...  │
│  "OVE1"  │  0x01   │ 0x01=local│                    │  ...  │
└─────────────────────────────────────────────────────────────┘
  • If a file doesn't start with OVE1, it's treated as unencrypted and plaintext is returned directly
  • Backward compatible, old files don't need migration

Multi-Tenant Isolation

Different accounts' data is encrypted with independent Account Keys:

  • Account A's key cannot decrypt Account B's files
  • Even with full AGFS access, data can't be read without the corresponding key
  • Tenant isolation is implemented at the key layer, not relying on storage permissions

Configuration Example

See Configuration Guide for detailed configuration.

Released under the Apache-2.0 License.