User Guide

Credential Vault

The credential vault stores user API keys (GitHub tokens, Slack bot tokens, etc.) encrypted at rest. Credentials are decrypted only at MCP spawn time and immediately zeroed from memory.


Why a credential vault?

Without MCP Ambassador, every developer copies their API keys into local MCP configs — dotfiles, .env files, shell profiles. Keys accumulate on laptops, in CI configs, and in version control.

With MCP Ambassador:

  • Users store their keys once in the encrypted vault
  • Keys are injected at spawn time as environment variables to the MCP process
  • Keys never leave the server unencrypted
  • When a user unsubscribes, their key is deleted
  • When a user's account is deactivated, their keys are inaccessible

Encryption

Each credential is encrypted using AES-256-GCM:

plaintext credential


 HKDF-SHA256(master_key, user_id + credential_name)
       │ derived_key (per-user, per-credential)

 AES-256-GCM encrypt(derived_key, plaintext)


 { ciphertext, iv, auth_tag }  ← stored in database

Key derivation: HKDF (HMAC-based Key Derivation Function) derives a unique encryption key for each credential using the CREDENTIAL_MASTER_KEY and the user+credential identifier as the info parameter. This means:

  • Two users with the same API key produce different ciphertexts
  • Rotating the master key (future feature) invalidates all credentials cleanly
  • An attacker with the database but not the master key cannot decrypt

Authentication: AES-GCM provides authenticated encryption — tampering with ciphertext is detected.


Master key

The CREDENTIAL_MASTER_KEY is a 32-byte (64-character hex) key. In development mode, it is auto-generated on first boot and saved to ./data/credential_master_key (permissions 0600). In production, set it explicitly:

# Generate
openssl rand -hex 32

# Set in docker-compose.yml environment
CREDENTIAL_MASTER_KEY=your_64_char_hex_key

Back up this key. If you lose it, all stored credentials are unrecoverable — they cannot be decrypted without it.


Memory security

After spawning a downstream MCP process:

  1. Credentials are retrieved from the vault (decrypted in memory)
  2. Passed as environment variables to the child process
  3. Immediately zeroed from the server process's memory using Buffer.fill(0)

The window in which plaintext credentials exist in the server process is minimized to the spawn operation.


Credential lifecycle

EventWhat happens
User subscribes to MCPCredentials encrypted and stored
MCP process spawnsCredentials decrypted, injected as env vars, zeroed
User updates credentialsOld ciphertext overwritten
User unsubscribesCredentials deleted from database
User account deactivatedCredentials remain encrypted; master key required to recover
Admin deletes userCredentials permanently deleted

What is stored

Each credential record contains:

  • user_id — owner
  • mcp_id — which MCP this credential is for
  • name — environment variable name (e.g., GITHUB_PERSONAL_ACCESS_TOKEN)
  • ciphertext, iv, auth_tag — the encrypted credential
  • created_at, updated_at — timestamps

Credential names (environment variable names) are stored in plaintext — only the values are encrypted.


User access

Users manage their own credentials via:

  • User Portal at https://your-server:9443My Subscriptions → select subscription → Edit Credentials
  • Client API: PUT /v1/subscriptions/:id with updated credentials

Admins cannot read or view user credentials — only users with access to their own subscriptions can provide or update them. Admins can see that a credential is configured (name + timestamp) but not the value.


Production recommendations

  • Set CREDENTIAL_MASTER_KEY explicitly — do not rely on the auto-generated key
  • Store the master key in a secrets manager (AWS Secrets Manager, HashiCorp Vault, etc.)
  • Back up ./data/ambassador.db regularly — it contains all encrypted credentials
  • Rotate the master key periodically (tooling planned for v2.0)
Previous
Marketplace & MCPs