The problem
You have API keys scattered around your OpenClaw config. Anthropic key, Discord bot token, maybe a Stripe key or a proxy credential. They're sitting in plaintext JSON files on disk. If someone gets access to the machine — or you accidentally push a config file to GitHub — everything is exposed.
SOPS (Secrets OPerationS) encrypts your secrets file so the values are unreadable without the decryption key. Your agent decrypts them at runtime. Everything else works the same.
Setting it up
age handles key management — it encrypts the data key that SOPS uses to protect your secrets file.
# Install age (key management) # Ubuntu 22.04+ / Debian 12+: sudo apt install age # Older Ubuntu/Debian — install from GitHub releases: # https://github.com/FiloSottile/age/releases/latest # macOS: brew install age # Install SOPS # Ubuntu/Debian (check https://github.com/getsops/sops/releases for the latest version): curl -LO https://github.com/getsops/sops/releases/latest/download/sops_amd64.deb sudo dpkg -i sops_amd64.deb # macOS: brew install sops
This creates a private key (for decryption) and a public key (for encryption). The private key stays on the machine that runs your agent. The public key goes in your SOPS config.
# Create the key directory mkdir -p ~/.config/sops/age # Generate the key pair age-keygen -o ~/.config/sops/age/keys.txt # Check the output — you'll see something like: # Public key: age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx cat ~/.config/sops/age/keys.txt
Copy the public key (starts with age1...). You'll need it in the next step.
In your OpenClaw workspace directory, create .sops.yaml. This tells SOPS which key to use when encrypting files:
# ~/.openclaw/workspace/.sops.yaml
creation_rules:
- path_regex: secrets\.json$
age: age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Replace with YOUR public key from step 2
Start with a plaintext JSON file containing your secrets, then encrypt it in place:
# Create the plaintext secrets file
cat > ~/.openclaw/workspace/secrets.json << 'EOF'
{
"anthropic_api_key": "sk-ant-XXXXX",
"discord_bot_token": "XXXXX",
"openai_api_key": "sk-XXXXX"
}
EOF
# Encrypt it in place
export SOPS_AGE_KEY_FILE=~/.config/sops/age/keys.txt
sops --encrypt --in-place ~/.openclaw/workspace/secrets.json
# Verify — values should now show as ENC[AES256_GCM,...]
cat ~/.openclaw/workspace/secrets.json
The file now contains encrypted values. The keys (like anthropic_api_key) are still visible — only the values are encrypted. This is by design: you can see what's in the file without being able to read the actual secrets.
Register the SOPS secrets provider in your OpenClaw config. This tells OpenClaw how to decrypt the file at startup. The source field must be "exec", and command must be the absolute path to the sops binary:
// openclaw.json
{
"secrets": {
"providers": {
"sops": {
"source": "exec",
"command": "/usr/local/bin/sops",
"args": ["-d", "--extract", '["KEY_NAME"]', "~/.openclaw/workspace/secrets.json"],
"passEnv": ["SOPS_AGE_KEY_FILE"],
"jsonOnly": false
}
}
}
}
Replace /usr/local/bin/sops with the actual path on your system (which sops will tell you). The --extract flag lets you pull individual keys — replace KEY_NAME with the key you want, e.g. anthropic_api_key.
Make sure the SOPS_AGE_KEY_FILE environment variable is set wherever OpenClaw runs — in your shell, your systemd service file, or your Docker compose:
# Shell: export SOPS_AGE_KEY_FILE=~/.config/sops/age/keys.txt # Systemd service: [Service] Environment="SOPS_AGE_KEY_FILE=/home/you/.config/sops/age/keys.txt" # Docker Compose: environment: - SOPS_AGE_KEY_FILE=/secrets/age-key.txt
Now instead of pasting plaintext keys into your config, reference them by name using a SecretRef:
// openclaw.json
{
"channels": {
"discord": {
"token": {
"source": "exec",
"provider": "sops",
"id": "discord_bot_token"
}
}
}
}
The id field is the key name to extract from your secrets JSON. provider matches the name you gave the provider in the previous step. OpenClaw resolves these at startup and injects the values — your config file never contains plaintext credentials.
Editing secrets later
SOPS can open the encrypted file in your editor, decrypt it temporarily for editing, and re-encrypt when you save:
export SOPS_AGE_KEY_FILE=~/.config/sops/age/keys.txt sops ~/.openclaw/workspace/secrets.json # Opens in $EDITOR — edit values, save, and it re-encrypts automatically
Back up your age private key
If you lose ~/.config/sops/age/keys.txt, you cannot decrypt your secrets. Ever. Back it up:
- Store it in a password manager (1Password, Bitwarden)
- Print it and store it physically
- Do not store it in the same repo as your encrypted secrets