Skip to main content
VoiceGateway encrypts all API keys stored in its database, masks secrets in API responses, and maintains an audit log of configuration changes.

Fernet encryption

File: src/voicegateway/core/crypto.py All API keys stored in the managed_providers table are encrypted with Fernet (AES-128-CBC with HMAC-SHA256 authentication) from the cryptography library.

How it works

Secret key resolution

The Fernet key is resolved in this order:
  1. VOICEGW_SECRET environment variable. Highest priority, recommended for containerized deployments.
  2. ~/.config/voicegateway/.secret file. Persisted on disk with chmod 600 permissions.
  3. Auto-generated on first run. A new Fernet key is generated and saved to the secret file.
A VOICEGW_SECRET_FALLBACK variable is also supported for zero-downtime key rotation: the gateway attempts decryption with the primary key first, then falls back to the secondary key.
The auto-generation uses os.replace() for atomic file creation. The file is created with 0600 permissions from the start and never exists in a world-readable state.

Encryption API

Empty strings pass through encrypt() and decrypt() unchanged.

Key rotation

Rotating the Fernet key does not mean re-adding every provider by hand. Set the new key, keep the old one reachable as a fallback, and run voicegw rotate-secret:
voicegw rotate-secret refuses to run unless both variables are set: VOICEGW_SECRET is the new primary key, VOICEGW_SECRET_FALLBACK is the previous one. It re-encrypts every row in managed_providers under the new primary and reports how many rows were rotated, skipped (empty), or failed (no configured key could decrypt them). A nonzero failure count exits with status 2 and lists the affected provider IDs. Until rotation runs, VOICEGW_SECRET_FALLBACK also keeps decrypt() working on rows still encrypted under the old key: the gateway builds a MultiFernet from the primary key plus every comma-separated key in VOICEGW_SECRET_FALLBACK and tries them in order. If none of them decrypt a value, decrypt() raises:
Remove VOICEGW_SECRET_FALLBACK once rotation succeeds. Leaving it set keeps the old key acceptable for decryption, which defeats the point of rotating.
Losing every key that can decrypt a row (no primary, no fallback) is unrecoverable for that row: rotate-secret reports it under failed, and it must be re-added via the dashboard or vg_add_provider (MCP).

API key masking

All API responses that include provider information mask the API key using mask():
Examples:
  • "sk-proj-abc123xyz789" becomes "sk-p...z789"
  • "short" becomes "*****"
  • "" becomes ""
Masking is applied in the HTTP API and MCP server responses. Plaintext keys never appear in API output.

MCP token authentication

The MCP server authenticates callers with a bearer token. Set VOICEGW_MCP_TOKEN to a strong random string. Any request without a matching Authorization: Bearer <token> header is rejected with 401. If VOICEGW_MCP_TOKEN is not set, the MCP server starts without authentication. This is acceptable for local development but should not be used in shared or networked environments.

Tenant isolation

In multi-tenant cloud deployments, tenant identity is derived server-side from the ingest API key (vk_ prefix). The key is presented as a bearer token on POST /v1/ingest and POST /v1/agents/heartbeat. The tenant ID from the key is stamped on every record at ingest time. Key rules:
  • A worker or record can only be written under the key’s tenant. The tenant_id field in request bodies is advisory only and cannot override the key-derived tenant.
  • VOICEGW_API_KEY is the agent-side variable that holds the vk_ key. Set it in the agent process environment.
  • The cloud verifies the key and resolves the tenant before any write.
See fleet worker heartbeat for the full ingestion contract.

Audit log

Table: config_audit_log Every create, update, or delete on managed resources is recorded.

Querying the audit log

The audit log write is best-effort: it never raises exceptions, to avoid blocking the actual operation if logging fails.

Security checklist

Storage

The SQLite tables where encrypted keys are stored.

Fleet worker heartbeat

Tenant isolation rules for the heartbeat ingest contract.

Configuration layers

How managed provider credentials flow into the resolved config.

Environment variables

All VOICEGW_SECRET, VOICEGW_MCP_TOKEN, and related env vars.