Security
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:VOICEGW_SECRETenvironment variable — highest priority, useful for containerized deployments~/.config/voicegateway/.secretfile — persisted on disk withchmod 600permissions- Auto-generated — on first run, a new Fernet key is generated and saved to the secret file
os.replace) to prevent partial writes. The file is created with 0600 permissions (owner read/write only) from the start — it never exists in a world-readable state.
Encryption API
encrypt() and decrypt() unchanged.
Key Rotation
IfVOICEGW_SECRET changes (or the .secret file is deleted), existing encrypted values will fail to decrypt. The decrypt() function raises a clear ValueError:
API Key Masking
All API responses that include provider information mask the API key using themask() function:
"sk-proj-abc123xyz789"becomes"sk-p...z789""short"becomes"*****"""becomes""
Plaintext Key Migration
When VoiceGateway opens a database that was created before encryption was added, it automatically detects and migrates plaintext API keys:Audit Log
Table:config_audit_log
Every create, update, or delete operation on managed resources is recorded in the audit log.
| Field | Description |
|---|---|
timestamp | When the change was made |
entity_type | "provider", "model", or "project" |
entity_id | ID of the affected resource |
action | "create", "update", or "delete" |
changes_json | JSON describing what changed |
source | "api", "mcp", or "dashboard" |
Querying the Audit Log
Security Checklist
| Concern | Mitigation |
|---|---|
| API keys at rest | Fernet encryption (AES-128-CBC + HMAC-SHA256) |
| Secret key storage | chmod 600 file or env var |
| API key exposure in responses | mask() applied to all API/MCP output |
| Configuration changes | Audit log with timestamp, actor, and changes |
| Plaintext key migration | Auto-detected and encrypted on startup |
| Atomic secret file creation | os.replace() prevents partial writes |
| Secret key change detection | Clear error message with recovery instructions |