mcplexer

Secrets Management

MCPlexer encrypts all sensitive credentials at rest using age encryption. Secrets are never stored in plaintext in the database or config files. Each auth scope has its own encrypted secret storage, and decryption only happens in memory when credentials are needed.

How It Works

MCPlexer uses age (filippo.io/age), a modern encryption tool designed to be simple and composable. Each secret is encrypted with a per-instance age key before being written to the database.

The encryption flow:

  1. You provide a secret value via the CLI or web UI
  2. MCPlexer encrypts it with your age identity key
  3. The encrypted bytes are stored in the auth scope's encrypted_data field
  4. On tool call, MCPlexer decrypts in memory and injects the credential
  5. Plaintext secrets never touch disk or logs

Age Key File

On first run, MCPlexer auto-generates an age identity key at:

~/.mcplexer/age.key

This file contains the private key used to encrypt and decrypt all secrets. It is created with 0600 permissions (owner read/write only).

Protect your key file

The age key file is the master key for all your secrets. If it's lost, encrypted secrets cannot be recovered. If it's compromised, all secrets are exposed.

Custom Key Location

Set the MCPLEXER_AGE_KEY environment variable to use a different key file path:

terminal
export MCPLEXER_AGE_KEY=/secure/path/mcplexer.key mcplexer serve

This is useful for:

  • Storing the key on an encrypted volume
  • Sharing the key across a team via a secrets manager
  • Using different keys for different environments

CLI Commands

Manage secrets with the mcplexer secret subcommand:

Put a Secret

terminal
mcplexer secret put <scope-id> <key> <value>
mcplexer secret put github-token GITHUB_PERSONAL_ACCESS_TOKEN ghp_abc123...

Stores an encrypted key-value pair in the specified auth scope. If the key already exists, it is overwritten.

Get a Secret

terminal
mcplexer secret get <scope-id> <key>
mcplexer secret get github-token GITHUB_PERSONAL_ACCESS_TOKEN

Decrypts and prints the value. Use with caution — the plaintext value is written to stdout.

List Secrets

terminal
mcplexer secret list <scope-id>
mcplexer secret list github-token

Lists all secret keys for an auth scope. Only key names are shown — values are not decrypted.

Delete a Secret

terminal
mcplexer secret delete <scope-id> <key>
mcplexer secret delete github-token GITHUB_PERSONAL_ACCESS_TOKEN

Permanently removes the encrypted key-value pair from the auth scope.

Per-Auth-Scope Storage

Secrets are stored per auth scope — each scope has its own independent encrypted blob. This design means:

  • Isolation — deleting an auth scope removes its secrets automatically
  • Granularity — each scope can hold multiple key-value pairs (e.g., both a token and a secret key)
  • Auditability — secret operations are scoped and logged per auth scope

Storage Model

Auth Scope: "github-personal" ├── GITHUB_PERSONAL_ACCESS_TOKEN = [encrypted] └── GITHUB_WEBHOOK_SECRET = [encrypted] Auth Scope: "slack-bot" └── SLACK_BOT_TOKEN = [encrypted]

Key Backup

Back up your age key

If you lose ~/.mcplexer/age.key (or the file at MCPLEXER_AGE_KEY), all encrypted secrets become permanently unrecoverable. There is no recovery mechanism — age encryption is one-way without the private key.

Recommended backup practices:

  • Copy the key file to a secure, offline location (USB drive, password manager)
  • Never commit the key file to version control
  • Rotate keys periodically by creating a new key and re-encrypting secrets
  • Document the key location so team members know where to find it

Key Rotation

To rotate your age key:

  1. Export existing secrets with mcplexer secret get
  2. Generate a new key (delete the old age.key and restart MCPlexer)
  3. Re-import secrets with mcplexer secret put

MCPlexer does not yet support automated key rotation. This is a manual process that requires re-encrypting all secrets.

Secrets in Configuration

Never put secrets in YAML

Secret values must never appear in your YAML configuration file. Always use the CLI or web UI to manage secrets. The config file defines auth scope structure, but credentials are stored separately in the encrypted database.

yaml
# ✓ Correct — define the scope, set secrets via CLI
auth_scopes:
  - name: "GitHub Token"
    id: github-token
    type: env

# ✗ Wrong — never do this
auth_scopes:
  - name: "GitHub Token"
    type: env
    token: "ghp_abc123..."  # NEVER