OpenClaw Centralized Secrets Setup¶
Architecture¶
M1 Max (openclaw host)
├── /Users/agent01/openclaw-docker/
│ ├── docker-compose.yml ← mount secrets volume
│ ├── secrets/
│ │ └── .env ← SINGLE secrets file (gitignored)
│ ├── volumes/
│ │ ├── config/ ← openclaw config
│ │ ├── workspace/ ← agent workspace + receipt_tools
│ │ └── storage/ ← vell-docs, receipts
│ └── .gitignore ← includes secrets/
AWS EC2 (mcp.vell.ai)
├── /opt/qbo-mcp-server/.env ← QBO OAuth tokens (managed separately)
├── /opt/kajabi-mcp-server/.env ← Kajabi creds (managed separately)
└── nginx ← reverse proxy: /mcp → QBO, /kajabi → Kajabi
Why centralize on the M1 Max?¶
The OpenClaw container runs scripts that need credentials for multiple services.
Currently secrets are scattered across:
- openclaw.json (MCP tokens)
- Container env vars in docker-compose.yml
- Hardcoded in individual scripts
A single secrets/.env file:
- One place to audit, rotate, and back up
- Mounted read-only into the container
- Referenced by all scripts via OPENCLAW_SECRETS env var
- Template (.env.example) is version-controlled; actual .env is not
Setup Steps¶
1. Create secrets directory on M1 Max¶
ssh openclaw-admin
mkdir -p /Users/agent01/openclaw-docker/secrets
cp openclaw-secrets.env.example /Users/agent01/openclaw-docker/secrets/.env
chmod 600 /Users/agent01/openclaw-docker/secrets/.env
echo "secrets/" >> /Users/agent01/openclaw-docker/.gitignore
2. Add docker-compose volume mount¶
Add to docker-compose.yml under the gateway service volumes:
services:
gateway:
# ... existing config ...
volumes:
# ... existing mounts ...
- ./secrets/.env:/home/node/.secrets/.env:ro
environment:
- OPENCLAW_SECRETS=/home/node/.secrets/.env
3. Create AWS IAM user¶
# From your admin machine (M4 Pro) with AWS CLI configured
aws iam create-user --user-name openclaw-billing-reader
aws iam put-user-policy \
--user-name openclaw-billing-reader \
--policy-name BillingReadOnly \
--policy-document file://aws-billing-readonly-policy.json
aws iam create-access-key --user-name openclaw-billing-reader
# Save AccessKeyId and SecretAccessKey → secrets/.env
4. Deploy the invoice downloader¶
# Copy script to OpenClaw workspace
scp aws-invoice-downloader.js openclaw:~/openclaw-docker/volumes/workspace/receipt_tools/
# Install AWS SDK deps in the container
ssh openclaw "DOCKER_HOST=unix:///Users/agent01/.colima/default/docker.sock \
/opt/homebrew/bin/docker exec openclaw-docker-gateway-1 \
npm install --prefix /home/node/openclaw/receipt_tools \
@aws-sdk/client-invoicing @aws-sdk/client-cost-explorer dotenv"
5. Test¶
# Dry run from inside the container
ssh openclaw "DOCKER_HOST=unix:///Users/agent01/.colima/default/docker.sock \
/opt/homebrew/bin/docker exec openclaw-docker-gateway-1 \
node /home/node/openclaw/receipt_tools/aws-invoice-downloader.js --months 7 --dry-run"
6. Add to cron (via OpenClaw)¶
Tell the agent to add a monthly cron job:
"Add a monthly cron job on the 3rd at 9 AM CT to run aws-invoice-downloader.js for the previous month, then run match-and-attach.js --vendor AWS"
How scripts access secrets¶
// In any receipt_tools script:
import { config } from 'dotenv';
config({ path: process.env.OPENCLAW_SECRETS || '.env' });
// Now process.env.AWS_ACCESS_KEY_ID etc. are available
Adding new integrations¶
- Add the env vars to
secrets/.envon the M1 Max - Add the template vars to
openclaw-secrets.env.example(committed) - Restart the container:
docker restart openclaw-docker-gateway-1 - Scripts pick up new vars automatically via
OPENCLAW_SECRETS
Security notes¶
secrets/.envischmod 600(owner-only read/write)- Mounted
:ro(read-only) in the container — agent can't modify it - Never commit
secrets/.env— only the.env.exampletemplate - AWS IAM user has read-only billing access — no spend/modify permissions
- Rotate keys quarterly: update
secrets/.env, restart container