Governance — Bring Your Own Key (BYOK)¶
AKKO ships with a sovereign AI stack — every model runs locally via the AI Runtime layer (Ollama + Qwen 2.5 family + Nomic Embed). Some workloads still need a hosted provider (long context, vision, advanced tool-calling). The Bring Your Own Key feature lets a platform admin register an external AI provider key (OpenAI, Anthropic, Mistral, Cohere, or any OpenAI-compatible endpoint) on the AKKO AI Gateway without giving up the sovereign default.
Threat model¶
- Plaintext in Git — values files commit-by-accident. Mitigated : the key is uploaded only through the cockpit modal, never typed into a YAML file.
- Plaintext in container logs — accidental
print(key). Mitigated : the Pydantic model wrapsapi_keyinSecretStr, sorepr(),str(), andmodel_dump_json()all return**********. - Plaintext at rest — disk-level encryption does not protect against a
pg_dumpexfil. Mitigated : column-levelpgp_sym_encrypt(plaintext, master_key, 'cipher-algo=aes256'); the master key lives in a separate k8s Secret. - Plaintext in HTTP responses — admin operators see only the masked form (
sk-…XXXX). The plaintext is never returned by any cockpit-backend endpoint.
Architecture (one paragraph)¶
The cockpit page #ai-models carries a dedicated "Bring Your Own Key" section, visible only to the admin realm role. Adding a key POSTs to /api/governance/byok/keys. The cockpit-backend (akko-cockpit-backend) wraps the plaintext in SecretStr, opens one short Postgres transaction, sets akko.master_key via SET LOCAL, and INSERTs pgp_sym_encrypt(plain, master, 'cipher-algo=aes256') into akko_governance.byok_keys. The local plaintext variable goes out of scope immediately. The future LiteLLM sync worker (PR-D, separate sprint) decrypts inside Postgres and mints a virtual key bound to the rbac_label.
How to enable the vault (operator)¶
The feature is OFF by default. Two values + two Secrets to wire :
# 1. Generate a 256-bit master key.
kubectl -n akko create secret generic akko-cockpit-backend-byok \
--from-literal=master-key="$(openssl rand -hex 32)"
# 2. Wire the Postgres DSN to the data instance.
kubectl -n akko create secret generic akko-cockpit-backend-byok-pg \
--from-literal=dsn="postgresql://akko:$AKKO_PG_PASSWORD@akko-postgresql-data:5432/akko"
# 3. Patch the Helm values.
cat >> my-values.yaml <<EOF
akko-cockpit-backend:
byok:
encryptionKey:
secretName: akko-cockpit-backend-byok
dsnSecretRef:
name: akko-cockpit-backend-byok-pg
EOF
# 4. helm upgrade — the akko-init job creates the table idempotently.
helm upgrade akko helm/akko -n akko -f my-values.yaml
How to register a key (admin)¶
- Sign in to the cockpit as
alice(akko-admin). - Open the sidebar entry AI Models.
- Scroll to the "Bring Your Own Key" section. Click Add provider key.
- Pick the provider (OpenAI / Anthropic / Mistral / Cohere / Custom).
- Fill a unique Label (e.g.
corp-prod-key). - Paste the API key. Submit.
The row appears in the table with the mask sk-…XXXX. The plaintext is never echoed again ; if you lose it, regenerate one on the provider side and re-upload.
How to disable a key without deleting¶
Toggle the Enable / Disable button in the row's Actions column. The ciphertext stays in the vault — re-enabling restores routing without re-uploading. This is the supported kill-switch during a provider outage.
How to permanently delete a key¶
Click Delete in the row's Actions column and confirm. The row is removed. There is no recycle bin.
Audit trail¶
Every BYOK action (add / toggle / delete / list) emits an OCSF class_uid 3008 event (LLM Access Mgmt). Sample :
{
"class_uid": 3008,
"type_uid": 300801,
"activity_id": 1,
"activity_name": "byok.key.add",
"actor": { "user": { "uid": "...alice...", "name": "alice", "email_addr": "alice@akko-ai.com", "type_id": 1 } },
"resources": [ { "type_name": "byok-key", "name": "corp-prod-key" } ],
"status": "Success"
}
The plaintext api_key field is NEVER serialised in OCSF events.
Key rotation¶
Annual rotation procedure :
ALTER SYSTEM SET akko.master_key = '<new-key>';
SELECT pg_reload_conf();
UPDATE akko_governance.byok_keys
SET encrypted_key = akko.reencrypt(encrypted_key, '<old-key>', '<new-key>');
Same helpers as the PII column encryption — see Admin / Encryption.
References¶
- ADR-068 — BYOK vault design rationale
feedback_layer_first_abstraction— "AI Gateway" / "AI Provider Key" namingfeedback_lego_zero_hardcoded— keys via Secret, never inlinepostgres/init/12-pgcrypto.sql—akko.encrypt_pii / akko.decrypt_pii / akko.reencrypt