Skip to content

Secrets Vault

The Shield Secrets Vault provides encrypted secret storage with tenant isolation, versioning, and automatic rotation. It serves as the centralized secret management layer for the ContextUnity service mesh.

Architecture

Project SDK Shield Service
│ │
├── GetSecret(path) ──────────▶│ → SecretBackend.get()
├── PutSecret(path, value) ───▶│ → SecretBackend.put() + encrypt
├── ListSecrets(prefix) ──────▶│ → SecretBackend.list()
└── RotateSecret(path) ───────▶│ → SecretBackend.rotate()
┌────┴────┐
│ Backend │
├─────────┤
│ EnvStore │ ← Default (env vars)
│ PgStore │ ← Planned (PostgreSQL)
│ Vault │ ← Planned (HashiCorp)
└─────────┘

Secret Model

Every secret carries metadata alongside its value:

FieldTypeDescription
pathstrHierarchical path (e.g. project/api-keys/openai)
versionintMonotonically increasing version number
created_atdatetimeWhen this version was created
updated_atdatetimeLast modification timestamp
expires_atdatetime?Optional TTL for auto-expiry
created_bystrIdentity that created this secret
tenant_idstrTenant isolation boundary
tagsdictArbitrary key-value metadata
encryption_backendstrWhich encryption was used (fernet, none)

Backend Factory

The backend is selected via configuration:

from contextunity.shield.secrets.factory import create_secret_backend
# Environment-variable backed (default)
backend = create_secret_backend("env")
# PostgreSQL backed (planned)
backend = create_secret_backend("postgres", dsn="postgresql://...")
# HashiCorp Vault (planned)
backend = create_secret_backend("vault", url="https://vault.example.com")

EnvSecretStore (Default)

The default backend reads secrets from environment variables. Secret paths map to env var names:

Secret PathEnvironment Variable
project/api-keys/openaiPROJECT_API_KEYS_OPENAI
shield/master-keySHIELD_MASTER_KEY

Suitable for development and single-node deployments where secrets are managed via .env files or systemd-creds.

gRPC RPCs

RPCPermissionDescription
GetSecretshield:secrets:readRetrieve a secret by path
PutSecretshield:secrets:writeStore or update a secret
ListSecretsshield:secrets:readList secrets by prefix
RotateSecretshield:secrets:writeCreate a new version

All RPCs require a valid ContextToken with the appropriate permission scope. Tenant isolation is enforced at the backend level.

Encryption at Rest

Secrets are encrypted using Fernet (AES-128-CBC + HMAC-SHA256) before storage:

  • SHIELD_ENCRYPTION_KEY — Fernet key for secret value encryption
  • SHIELD_MASTER_KEY — Fernet key for PKI private key encryption

In production, these keys should be provisioned via systemd-creds rather than .env files. See Systemd Deployment for hardening details.

SDK Integration

During Project Bootstrap, the SDK automatically fetches required secrets from Shield:

  1. SDK presents HMAC-signed bootstrap token
  2. Shield validates project identity
  3. Shield returns LLM API keys, database credentials, etc.
  4. SDK caches secrets in-process (no disk writes)

This eliminates the need for projects to manage their own .env files in production.