Secrets

Secure credentials without environment variables

MUXI uses encrypted files instead of environment variables. Your API keys stay secure, portable, and never leak in logs.

Why Not Environment Variables?

We decided against environment variables because they're not portable and not secure to share.

Problem Environment Variables MUXI Secrets
Visible in process lists ✗ Yes ✓ No
Appear in crash dumps ✗ Yes ✓ No
Leak in CI/CD logs ✗ Often ✓ Never
Portable with code ✗ No ✓ Yes
Safe to commit ✗ Never ✓ Encrypted file

The sharing problem: You can share a formation with someone, but how do they know what secrets it needs? With MUXI's approach:

  • secrets is like .env.example - shows required keys
  • secrets.enc contains encrypted values - safe to commit
  • The recipient creates their own values for the same keys

The GitHub Actions-style notation (${{ secrets.KEY }}) makes it clear exactly where each secret is used.

Learn more about the design decision in Why Encrypted Secrets.

API Reference:

How It Works

formation/
├── formation.afs     # References secrets like ${{ secrets.KEY }}
├── secrets.enc       # Encrypted secrets (safe to commit)
├── secrets   # Template showing required secrets
└── .key              # Encryption key (NEVER commit!)

Reference secrets in YAML:

# In formation.afs
llm:
  api_keys:
    openai: "${{ secrets.OPENAI_API_KEY }}"
# In mcp/github.afs
schema: "1.0.0"
id: github
type: command
command: npx
args: ["-y", "@modelcontextprotocol/server-github"]
auth:
  type: env
  GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"

Set Up Secrets

  1. Run the setup wizard

    muxi secrets setup

    The wizard scans your formation and prompts for each required secret:

    Setting up secrets for my-assistant...
    
    Required secrets:
      OPENAI_API_KEY (from llm.api_keys)
      BRAVE_API_KEY (from mcps.web-search)
    
    Enter OPENAI_API_KEY: ****
    Enter BRAVE_API_KEY: ****
    
    ✓ Secrets encrypted and saved
  2. Verify secrets are saved

    muxi secrets list

    Output (keys only, not values):

    OPENAI_API_KEY
    BRAVE_API_KEY

Manage Individual Secrets

Set a Secret

muxi secrets set GITHUB_TOKEN
# Enter value when prompted

Or from a file/pipe:

echo $GITHUB_TOKEN | muxi secrets set GITHUB_TOKEN --stdin

Get a Secret

muxi secrets get OPENAI_API_KEY
# Displays the value (use carefully!)

Delete a Secret

muxi secrets delete OLD_API_KEY

File Structure

my-formation/
├── secrets.enc        # Encrypted - SAFE to commit
├── secrets    # Template - SAFE to commit
└── .key               # Encryption key - NEVER commit!

.gitignore

Always include:

# MUXI secrets
.key

Never commit .key to version control. Without the key, secrets.enc is unreadable - this is a security feature.

secrets

Document required secrets for your team:

# Required secrets for this formation
OPENAI_API_KEY=
BRAVE_API_KEY=
DATABASE_URL=

Using Secrets in Formations

In LLM Config

llm:
  api_keys:
    openai: ${{ secrets.OPENAI_API_KEY }}
    anthropic: ${{ secrets.ANTHROPIC_API_KEY }}

In MCP Config Files

# mcp/database.afs
schema: "1.0.0"
id: database
type: command
command: npx
args: ["-y", "@modelcontextprotocol/server-postgres"]
auth:
  type: env
  DATABASE_URL: "${{ secrets.DATABASE_URL }}"
# mcp/github.afs
schema: "1.0.0"
id: github
type: command
command: npx
args: ["-y", "@modelcontextprotocol/server-github"]
auth:
  type: env
  GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"

Team Sharing

To share a formation with teammates:

  1. Commit secrets.enc and secrets to git
  2. Share .key securely (password manager, secure channel)
  3. Or have teammates run muxi secrets setup with their own keys

Backup and Recovery

Backup

Store .key in a secure location:

  • Password manager (1Password, Bitwarden)
  • Secret vault (HashiCorp Vault, AWS Secrets Manager)
  • Secure backup separate from the repository

Recovery

If you lose .key:

# No recovery possible - must recreate
rm secrets.enc
muxi secrets setup
# Re-enter all secrets

There's no backdoor. Lost key = re-enter all secrets. This is intentional security.

Troubleshooting

"Missing secret" error

The secret isn't set. Add it:

muxi secrets set MISSING_KEY
"Cannot decrypt" error

Wrong .key file or corrupted secrets.enc:

rm secrets.enc
muxi secrets setup
Secrets not loading

Check files exist and permissions:

ls -la secrets.enc .key
# Both should exist and be readable

Best Practices

  1. Never commit .key - Add to .gitignore
  2. Keep secrets updated - Document all required secrets
  3. Backup .key securely - Can't recover without it
  4. Use descriptive names - OPENAI_API_KEY not KEY1
  5. Rotate periodically - Update secrets regularly
  6. Least privilege - Each tool gets only the secrets it needs

Next Steps

Why Encrypted Secrets - Design rationale
Tools - Use secrets with MCP servers
Security - Full security model