Skip to main content

Secret Vault

The Secret Vault stores encrypted API credentials and injects them into configured outbound requests through the Proxy Gateway. Supported agent callers receive the provider response, not the stored credential. Steward does not restrict arbitrary egress outside configured routes.

How It Works

Instead of giving agents plaintext API keys:
You store credentials in Steward’s Secret Vault and configure routes that map request patterns to credentials:
Now when an agent calls OpenAI through the proxy, Steward decrypts the key and injects it automatically.

Encryption

SecretVault uses the secret-vault domain of the same KeyStore primitive as the Wallet Vault. The implemented derivation and encryption path is:
The random record salt, IV, authentication tag, and ciphertext are stored with each secret version. Tenant, secret name, and version are authenticated as AES-GCM additional authenticated data (AAD), so copying ciphertext into another tenant/name/version context fails authentication. Wallet and credential records use the same primitive with their applicable tenant, agent, chain, venue, name, and version context. There is no stored, wrapped, or independently rotatable per-tenant key. Changing STEWARD_MASTER_PASSWORD or STEWARD_KDF_SALT changes the derivation root, so operators must stop writers and use the root secret key rotation runbook to authenticate and re-encrypt the complete supported ciphertext inventory before cutting every consumer over. Rotation cost follows the encrypted record inventory, not the tenant count. Two legacy compatibility paths are deliberately separate:
  • Secrets written before KDF domain separation may require the old undomained root. Production enables that temporary path only with STEWARD_SECRET_VAULT_LEGACY_ROOT_FALLBACK=true; migrate the full secret inventory and remove the flag as described in the rotation runbook.
  • Ciphertext written before context AAD may require a no-AAD decrypt retry. STEWARD_ALLOW_LEGACY_KEYSTORE_DECRYPT_FALLBACK=true enables that retry only outside production; production ignores the flag and fails closed. Re-encrypt legacy records with context rather than treating this as a tenant-key mechanism.

Secret Lifecycle

Response (value is never returned):

Credential Injection

When the proxy forwards a request, it:
  1. Decrypts the credential for the bounded outbound operation
  2. Injects it according to the route config (header, query param, or body)
  3. Forwards the request to the real API
  4. Drops the plaintext reference after the outbound operation (JavaScript strings cannot be reliably zeroed in place)
Injection methods:

Security Properties

  • Secrets never leave the vault in API responses — list/get endpoints return metadata only
  • Plaintext lifetime is bounded to the use callback or pinned injection path — references are dropped afterward, but JavaScript strings cannot be reliably zeroed in place
  • Auditing is enforced by governed callers, not by the cipher primitive — new consumers use the pre-decrypt audit chokepoint, and the remaining direct-decrypt caller inventory is CI-pinned
  • Tenant isolation — secrets are scoped to tenants; agents can only trigger decryption of their tenant’s secrets