Secrets Management¶
How to handle API keys, CMMS credentials, and MCP tokens across deployment methods.
Decision Matrix¶
| Environment | Method | Complexity |
|---|---|---|
| Local dev / evaluation | .env file |
Low |
| Single-host production (systemd) | /etc/machina/machina.env (chmod 600) |
Low |
| Docker / Docker Compose | .env file (not committed) |
Low |
| Enterprise (compliance requirements) | Vault / Azure Key Vault + wrapper script | Medium |
| GitOps pipeline | SOPS-encrypted .env + decrypt at deploy |
Medium |
Secrets Inventory¶
| Secret | Used by | Rotation |
|---|---|---|
MACHINA_MCP_TOKENS_JSON |
MCP server auth (each token ≥ 32 characters) | Restart; coordinate with clients |
CMMS credentials (e.g. MACHINA_CMMS_API_KEY, or a username/password pair) |
CMMS connector | Restart required |
| OPC-UA certificates | IoT connector | Restart; re-create subscriptions |
| Telegram bot token | Telegram connector | Restart required |
| SMTP/IMAP credentials | Email connector | Restart required |
OPENAI_API_KEY / ANTHROPIC_API_KEY |
LLM calls of agents you run (read by LiteLLM; the MCP server needs none) | Restart required |
Machina reads the MCP token variables and the LLM keys (through LiteLLM)
directly. Every other secret reaches a connector through a ${VAR}
placeholder in your YAML config, so the variable names are yours to choose —
the deploy configs use MACHINA_CMMS_URL and MACHINA_CMMS_API_KEY. A
placeholder without a default fails the start when the variable is missing.
Baseline: Environment Files¶
systemd¶
sudo cp deploy/systemd/machina.env.example /etc/machina/machina.env
sudo chmod 600 /etc/machina/machina.env
sudo chown root:root /etc/machina/machina.env
systemd reads the EnvironmentFile before dropping privileges to the machina user.
The file is not readable by the running process — only by root at service start.
Docker¶
cp deploy/docker/.env.example deploy/docker/.env
# Edit .env with your secrets
# NEVER commit .env to version control
Docker Compose loads .env automatically; deploy/docker/.gitignore already
excludes it.
Advanced: External Secret Stores¶
HashiCorp Vault¶
Create a wrapper script that fetches secrets and execs the server:
#!/bin/bash
export MACHINA_MCP_TOKENS_JSON=$(vault kv get -field=tokens secret/machina/mcp)
export MACHINA_CMMS_API_KEY=$(vault kv get -field=api_key secret/machina/cmms)
exec /opt/machina-venv/bin/machina mcp serve \
--transport streamable-http \
--host 127.0.0.1 \
--config /etc/machina/config.yaml
Update the systemd unit's ExecStart to call this script.
Azure Key Vault¶
Same pattern — use az keyvault secret show instead of vault kv get.
SOPS for GitOps¶
Encrypt your env file with SOPS:
# Encrypt
sops --encrypt machina.env > machina.env.enc
git add machina.env.enc # safe to commit
# Decrypt at deploy time
sops --decrypt machina.env.enc > /etc/machina/machina.env
chmod 600 /etc/machina/machina.env
Custom Token Verifiers¶
For MCP auth specifically, a custom TokenVerifier can query an external
identity provider at runtime — no restart needed for token rotation. The
config accepts only verifier classes under the machina. package, so in v0.4
this means adding a module to the package. See
MCP Auth for details.
What NOT to Do¶
- Do not commit
.envfiles to version control - Do not log secrets — on JSONL export, Machina redacts trace metadata whose
key contains
token,password,secret,api_key,client_secretorauthorization, but only that metadata - Do not pass secrets via CLI arguments (visible in
psoutput) - Do not use the legacy
MACHINA_MCP_TOKENSformat — it lacks client identity tracking