Secrets, encryption keys and IT vault
This page describes, from the security point of view, where the secrets of the Navin Platform live, who may write, read or reveal them, how keys rotate and what the audit trail keeps. The OpenBao bootstrap and the list of third party service entries are in ../../operations/en/secrets.md; they are not repeated here.
Version française : ../secrets-vault.md
Overview
| Store | Contents | Who writes | Who reads in clear | Code |
|---|---|---|---|---|
| Tenant secrets | Provider keys, tool and connector tokens | tenant_admin, security_officer, developer (own secrets) | The server only, never the API | eai.domains.secrets |
| Tenant encryption keys | DEK per key and per version, wrapped | tenant_admin, security_officer | The server (envelope encryption) | eai.domains.privacy |
| IT vault | Credentials of the services installed for the platform | platform_admin | platform_admin, through an audited reveal | eai.domains.platform_manager.vault |
| Platform keys | Context Authority key, audit seal key, audit payload KEK, bundle signing key | The operator, in OpenBao or the HSM | Never returned | eai.domains.context, eai.domains.evidence, eai.crypto.kms |
Tenant secrets
- Write only API:
POST /v1/secrets(path and value),GET /v1/secrets(metadata: path, backend, version, author, dates),DELETE /v1/secrets/{path}. The value is never returned. Adeveloperonly overwrites or deletes the secrets it wrote (403 otherwise). - Backend: OpenBao KV v2 when
EAI_OPENBAO_URLis set (mountEAI_OPENBAO_KV_MOUNT), else local AES-GCM encryption with a per tenant key derived by HKDF from the master key (EAI_MASTER_KEY, or generated once and kept ineai.platform_keys), the path being the associated data. A backend outage answers 502secret_backend_error. - Other domains read a secret server side (
read_secret); tools, MCP and connectors receive a reference, never the value in the prompt. - References of the form
openbao:<kv path>#<field>name a platform key without exposing it (EAI_CONTEXT_SIGNING_KEY_REF,EAI_AUDIT_SEAL_SIGNING_KEY_REF,EAI_LITELLM_MASTER_KEY_REF). - Dynamic database credentials:
eai.integrations.openbao.database_credsasks the OpenBao database secrets engine for a temporary credential, andrevoke_leaserevokes it at once.
Encryption keys (transit)
POST /v1/encryption/keyscreates a tenant key (name, purpose);GET /v1/encryption/keyslists them;POST /v1/encryption/keys/{key_id}/rotateadds a version. Write roles:tenant_admin,security_officer.- Each version is a random DEK wrapped (
eai.encryption_key_versions) by OpenBao Transit (openbao-transit, mountEAI_OPENBAO_TRANSIT_MOUNT) or by the local tenant key. The ciphertext (eaienc1) names the key and the version: older data stays readable after a rotation. POST /v1/encryption/encryptandPOST /v1/encryption/decryptencrypt and decrypt on demand. Creation, rotation, encryption and decryption are audited (encryption.key.create,encryption.key.rotate,encryption.encrypt,encryption.decrypt).EAI_KMS_PROVIDERselects the key interface ofeai.crypto.kms:openbao(Transit),pkcs11(client HSM, withEAI_KMS_PKCS11_MODULE,EAI_KMS_PKCS11_TOKEN_LABEL,EAI_KMS_PKCS11_PIN_FILE) ornone. Any HSM error raiseskms_unavailableand the caller fails closed. What the client must provide: identity-policy-plane.md.
Platform keys
| Key | Setting | Production requirement |
|---|---|---|
| Context Authority signature (Ed25519 or ECDSA P-256) | EAI_CONTEXT_SIGNING_KEY_REF | OpenBao reference mandatory; clear EAI_CONTEXT_SIGNING_KEY refused |
| Audit seals (Ed25519) | EAI_AUDIT_SEAL_SIGNING_KEY_REF | OpenBao reference mandatory |
| Audit payload KEK | EAI_AUDIT_PAYLOAD_KEK | openbao-transit mandatory |
| WORM export signature | EAI_AUDIT_SIGNING, EAI_OPENBAO_AUDIT_SIGNING_KEY | Transit, never hmac |
| OPA bundle signature (RS256) | EAI_POLICY_BUNDLE_SIGNING_KEY_FILE, EAI_POLICY_BUNDLE_KEY_ID | Mandatory with EAI_OPA_DELIVERY=bundle |
The startup guard refuses every deviation in production (configuration.md). Context key rotation: changing the reference retires the old key, which still verifies until its last signature plus EAI_CONTEXT_MAX_TTL_SECONDS; POST /v1/context/keys/{kid}/revoke stops it at once.
IT vault of the Control Plane
The IT vault of the Control Plane keeps the credentials of the services installed for the platform (PostgreSQL, Redis, Keycloak, OpenBao, OpenFGA, OPA, LiteLLM, Qdrant, NATS, Temporal, LiveKit, Opik, ClickHouse, S3, Platform Manager, installer account of the portal). It has no screen in the portal: the platform administrators read and edit the credentials in the Navin Manager vault, and the routes below serve the SDKs. Table: eai.it_vault_entries (sql/postgres/V196).
Encryption
- Only the service name is in clear (listing, uniqueness). URL, login, password and API key form one JSON document sealed with AES-256-GCM, in the
v1:<base64 nonce|ciphertext>format. - Key: 256 bits derived by HKDF-SHA256 from the platform master key (info
it-vault), dedicated to the vault. Random 96 bit nonce at each write. Associated data: the entry id, so a document copied onto another row does not open (500it_vault_payload_invalid). - Platform table, no tenant data: forced RLS,
eai.is_platform()policy.
Changing the master key (EAI_MASTER_KEY or the key in eai.platform_keys) makes every vault entry and every locally encrypted secret unreadable. Export and re-enter the entries before any change, and back up the master key with the database.
Who may read, reveal, modify
| Operation | Route | Audit |
|---|---|---|
| List | GET /v1/platform-manager/vault/entries | no (the list carries neither password nor key: has_password, has_api_key) |
| Read a sheet | GET /v1/platform-manager/vault/entries/{entry_id} | no (same masked fields) |
| Create | POST /v1/platform-manager/vault/entries | it_vault.create |
| Update | PATCH /v1/platform-manager/vault/entries/{entry_id} | it_vault.update |
| Delete | DELETE /v1/platform-manager/vault/entries/{entry_id} | it_vault.delete |
| Reveal password and key | POST /v1/platform-manager/vault/entries/{entry_id}/reveal | it_vault.reveal |
| Import installed services | POST /v1/platform-manager/vault/import | it_vault.import |
Every route is reserved to platform_admin. The audit trail keeps the service name, never a value.
Rotation
The vault does not rotate service credentials: it keeps them. To rotate, change the secret at its source (OpenBao, then External Secrets, as in ../../operations/en/secrets.md), then update the entry with PATCH. The import (at API startup and through POST /v1/platform-manager/vault/import) only adds missing services: an entry an administrator edited is never overwritten, a deleted entry comes back at the next import.
Audit trail
| Event | When |
|---|---|
api_key.create, api_key.rotate, api_key.revoke | API key lifecycle |
encryption.key.create, encryption.key.rotate, encryption.encrypt, encryption.decrypt | Tenant encryption keys |
it_vault.create, it_vault.update, it_vault.delete, it_vault.reveal, it_vault.import | IT vault |
The secret.* and api_key.* actions are part of the default selection sent to the SIEM (EAI_SIEM_FORWARD_ACTIONS). Search: GET /v1/audit/events.
Related procedures
- Leaked API key: runbooks.md.
- Key rotation in OpenBao: runbooks.md.
- Hardening: hardening.md.