Skip to content

Encryption & KMS

Purple8 Graph encrypts every node property value and edge property value at rest using AES-256-GCM with a unique IV per value. Encryption is transparent — your Cypher queries, REST API calls, and CLI commands all work unchanged.

Architecture: envelope encryption

┌─────────────────────────────────────────────────────────────┐
│  Node / Edge property value (plaintext)                      │
│      ↓  encrypt with AES-256-GCM (unique IV per value)       │
│  Ciphertext envelope  [P8KM v1 | key_id | iv | tag | data]  │
│      ↓  key wrapping                                         │
│  Data Encryption Key (DEK) — stored encrypted by KEK         │
│      ↓  Key Encryption Key (KEK) lives in your KMS           │
└─────────────────────────────────────────────────────────────┘

The storage layer only ever sees ciphertext. The engine decrypts on read, re-encrypts on write. Your KMS holds the KEK — Purple8 never stores the master key.

Enabling encryption

python
from purple8_graph import GraphEngine

engine = GraphEngine(
    "./data",
    kms_provider="local",                    # or vault, aws, gcp, azure
    kms_key_id="my-key",
    kms_local_keystore="./keystore.json",    # local only
)

Or via environment variables (recommended for production):

bash
KMS_PROVIDER=aws
KMS_KEY_ID=arn:aws:kms:us-east-1:123456789012:key/mrk-abc123
AWS_REGION=us-east-1

Provider setup

Local file (development)

The simplest provider — key material is stored in a JSON file on disk. Do not use in production.

python
engine = GraphEngine(
    "./data",
    kms_provider="local",
    kms_key_id="dev-key",
    kms_local_keystore="./dev-keystore.json",
)
bash
KMS_PROVIDER=local
KMS_KEY_ID=dev-key
KMS_LOCAL_KEYSTORE=./keystore.json

HashiCorp Vault

python
engine = GraphEngine(
    "./data",
    kms_provider="vault",
    kms_key_id="purple8/graph/data-key",
)
bash
KMS_PROVIDER=vault
KMS_KEY_ID=purple8/graph/data-key
VAULT_ADDR=https://vault.internal:8200
VAULT_TOKEN=hvs.xxxxxxxxxxxx
# Optional mTLS:
VAULT_CACERT=/certs/ca.pem
VAULT_CLIENT_CERT=/certs/client.pem
VAULT_CLIENT_KEY=/certs/client.key

Vault policy required (Transit secrets engine):

hcl
path "transit/encrypt/purple8-graph-data-key" {
  capabilities = ["update"]
}
path "transit/decrypt/purple8-graph-data-key" {
  capabilities = ["update"]
}
path "transit/rotate/purple8-graph-data-key" {
  capabilities = ["update"]
}

AWS KMS

python
engine = GraphEngine(
    "./data",
    kms_provider="aws",
    kms_key_id="arn:aws:kms:us-east-1:123456789012:key/mrk-abc123",
)
bash
KMS_PROVIDER=aws
KMS_KEY_ID=arn:aws:kms:us-east-1:123456789012:key/mrk-abc123
AWS_REGION=us-east-1
# Credentials via IAM role (recommended) or:
AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

Minimum IAM policy:

json
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": ["kms:GenerateDataKey", "kms:Decrypt", "kms:DescribeKey"],
    "Resource": "arn:aws:kms:us-east-1:123456789012:key/mrk-abc123"
  }]
}

Google Cloud KMS

python
engine = GraphEngine(
    "./data",
    kms_provider="gcp",
    kms_key_id="projects/my-project/locations/us-east1/keyRings/graph/cryptoKeys/data-key",
)
bash
KMS_PROVIDER=gcp
KMS_KEY_ID=projects/my-project/locations/us-east1/keyRings/graph/cryptoKeys/data-key
GOOGLE_APPLICATION_CREDENTIALS=/secrets/sa-key.json
# Or use Workload Identity (recommended for GKE)

Required IAM role: roles/cloudkms.cryptoKeyEncrypterDecrypter


Azure Key Vault

python
engine = GraphEngine(
    "./data",
    kms_provider="azure",
    kms_key_id="https://my-vault.vault.azure.net/keys/graph-key/abc123",
)
bash
KMS_PROVIDER=azure
KMS_KEY_ID=https://my-vault.vault.azure.net/keys/graph-key/abc123
AZURE_TENANT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
AZURE_CLIENT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
AZURE_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Or use Managed Identity (recommended for Azure VMs/AKS):
# AZURE_USE_MSI=true

Required Key Vault permission: Key Vault Crypto User (encrypt + decrypt, no key management)


Zero-downtime key rotation

Rotation creates a new KEK version. Existing ciphertext is re-wrapped lazily on next write — reads still work because the engine stores the key_id in every ciphertext envelope.

python
from purple8_graph.kms import rotate_key

# Returns immediately. Re-wrapping happens in background.
rotate_key(engine, new_kms_key_id="arn:aws:kms:us-east-1:...key/mrk-NEW")

Or via CLI:

bash
purple8-graph kms rotate \
  --new-key-id "arn:aws:kms:us-east-1:123456789012:key/mrk-NEW"

WARNING

Key rotation is a background operation. Until re-wrapping completes, both the old and new KEK versions must be active in your KMS. Check progress with purple8-graph kms status.

Verify encryption status

bash
# Check which provider is active and how many values are encrypted
purple8-graph kms status --db ./data

# Example output:
# Provider:       aws
# Key ID:         arn:aws:kms:us-east-1:...key/mrk-abc123
# Nodes encrypted: 48,291 / 48,291 (100%)
# Edges encrypted: 201,847 / 201,847 (100%)
# Pending re-wrap: 0

Selecting fields for encryption

By default, all property values are encrypted. You can opt specific labels or properties out of encryption for performance-sensitive, non-sensitive fields:

python
engine = GraphEngine(
    "./data",
    kms_provider="aws",
    kms_key_id="...",
    encryption_exclude=[
        "Node.timestamp",     # never sensitive, high-cardinality
        "Edge.weight",        # numeric metric
    ],
)

Environment variable reference

VariableDescriptionRequired
KMS_PROVIDERlocal / vault / aws / gcp / azureYes
KMS_KEY_IDProvider-specific key identifierYes
KMS_LOCAL_KEYSTOREPath to JSON keystore (local provider only)Local only
VAULT_ADDRHashiCorp Vault server URLVault only
VAULT_TOKENVault tokenVault only
VAULT_CACERTVault CA certificate pathVault (mTLS)
AWS_REGIONAWS region for KMSAWS only
AWS_ACCESS_KEY_IDAWS access key (prefer IAM role)AWS (key auth)
AWS_SECRET_ACCESS_KEYAWS secret key (prefer IAM role)AWS (key auth)
GOOGLE_APPLICATION_CREDENTIALSPath to GCP service account JSONGCP (SA auth)
AZURE_TENANT_IDAzure Active Directory tenant IDAzure
AZURE_CLIENT_IDAzure app registration client IDAzure
AZURE_CLIENT_SECRETAzure app registration client secretAzure
AZURE_USE_MSIUse Managed Identity instead of client secretAzure (MSI)

Purple8 Graph is proprietary software. All rights reserved.