OpenClaw Secrets Management with 1Password
This guide walks through setting up 1Password as a centralized secret provider for OpenClaw. By the end, every API key, token, and credential in your OpenClaw config will resolve at runtime through 1Password — nothing sensitive stored in plaintext.
Prerequisites
Section titled “Prerequisites”- OpenClaw installed and running
- 1Password account (personal or business)
- 1Password CLI (
op) installed - Basic familiarity with OpenClaw’s
openclaw.jsonconfig
Part 1: Install and Configure 1Password CLI
Section titled “Part 1: Install and Configure 1Password CLI”Install the CLI
Section titled “Install the CLI”# Ubuntu/Debiancurl -sS https://downloads.1password.com/linux/keys/1password.asc | \ sudo gpg --dearmor --output /usr/share/keyrings/1password-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/1password-archive-keyring.gpg] https://downloads.1password.com/linux/debian/$(dpkg --print-architecture) stable main" | \ sudo tee /etc/apt/sources.list.d/1password.list
sudo apt update && sudo apt install 1password-cli
# Verifyop --versionCreate a Service Account
Section titled “Create a Service Account”For non-interactive (agent) access, you need a service account — not a personal sign-in.
- Go to your 1Password admin console
- Navigate to Developer → Service Accounts
- Create a new service account with access to your vault
- Copy the service account token
Set the Token
Section titled “Set the Token”Add the service account token to your OpenClaw environment:
# In ~/.openclaw/.envOP_SERVICE_ACCOUNT_TOKEN=ops_your_service_account_token_hereThis is the one secret that bootstraps access to everything else. Protect this file with appropriate permissions:
chmod 600 ~/.openclaw/.envVerify Access
Section titled “Verify Access”# Test that op can authenticateexport OP_SERVICE_ACCOUNT_TOKEN="ops_your_token"op vault listop item list --vault "Your-Vault-Name"Part 2: Create a Vault and Add Credentials
Section titled “Part 2: Create a Vault and Add Credentials”Create a Dedicated Vault
Section titled “Create a Dedicated Vault”Keep OpenClaw credentials separate from personal items:
# Via 1Password web UI or CLIop vault create "OpenClaw-API-Keys"Add Credentials
Section titled “Add Credentials”For each API key your OpenClaw instance uses:
# Create items with the password fieldop item create \ --category=password \ --title="ANTHROPIC_API_KEY" \ --vault="OpenClaw-API-Keys" \ password="sk-ant-your-key-here"
op item create \ --category=password \ --title="OPENROUTER_API_KEY" \ --vault="OpenClaw-API-Keys" \ password="sk-or-v1-your-key-here"
op item create \ --category=password \ --title="DISCORD_TOKEN" \ --vault="OpenClaw-API-Keys" \ password="your-discord-bot-token"Verify Items
Section titled “Verify Items”# List all items (titles only, never echo values)op item list --vault OpenClaw-API-Keys --format json | jq '.[].title'
# Verify a specific item resolves (check existence, not value)op read "op://OpenClaw-API-Keys/ANTHROPIC_API_KEY/password" > /dev/null && echo "OK" || echo "FAILED"Important: Never echo secret values to terminal, logs, or chat. Always verify with existence checks.
Part 3: Configure OpenClaw Secret Providers
Section titled “Part 3: Configure OpenClaw Secret Providers”Add Exec Providers
Section titled “Add Exec Providers”In openclaw.json, add a secrets.providers entry for each credential:
{ "secrets": { "providers": { "onepassword_anthropic": { "source": "exec", "command": "/usr/bin/op", "args": ["read", "op://OpenClaw-API-Keys/ANTHROPIC_API_KEY/password"], "jsonOnly": false, "passEnv": ["HOME", "OP_SERVICE_ACCOUNT_TOKEN"], "allowInsecurePath": true }, "onepassword_openrouter": { "source": "exec", "command": "/usr/bin/op", "args": ["read", "op://OpenClaw-API-Keys/OPENROUTER_API_KEY/password"], "jsonOnly": false, "passEnv": ["HOME", "OP_SERVICE_ACCOUNT_TOKEN"], "allowInsecurePath": true } } }}Key Configuration Notes
Section titled “Key Configuration Notes”| Field | Purpose | Notes |
|---|---|---|
source |
Always "exec" for CLI-based providers |
|
command |
Full path to op binary |
Use which op to find it |
args |
Arguments for op read |
Format: op://Vault/Item/Field |
jsonOnly |
Set false for raw text output |
op read returns raw text, not JSON |
passEnv |
Environment variables to forward | Must include OP_SERVICE_ACCOUNT_TOKEN |
allowInsecurePath |
Allow non-user-owned binaries | Required because op is owned by root |
Common Field Names
Section titled “Common Field Names”Most 1Password items use password as the field name, but watch for variations:
password— Standard for most itemscredential— Some items created through web UItoken— Custom field namenotesPlain— Notes field
Check your item’s field structure:
op item get "ITEM_NAME" --vault "OpenClaw-API-Keys" --format json | \ jq '[.fields[] | select(.value != null) | .label]'Part 4: Replace Plaintext Values with SecretRefs
Section titled “Part 4: Replace Plaintext Values with SecretRefs”Config-Level Credentials
Section titled “Config-Level Credentials”Replace plaintext apiKey values with SecretRef objects:
Before:
{ "tools": { "web": { "search": { "apiKey": "BSA-actual-key-value" } } }}After:
{ "tools": { "web": { "search": { "apiKey": { "source": "exec", "provider": "onepassword_brave", "id": "value" } } } }}Skill Credentials
Section titled “Skill Credentials”Same pattern for skill configs:
{ "skills": { "entries": { "todoist": { "apiKey": { "source": "exec", "provider": "onepassword_todoist", "id": "value" } } } }}Agent Auth Profiles
Section titled “Agent Auth Profiles”For model provider credentials, use auth-profiles instead of direct apiKey values.
Create auth-profiles.json in each agent’s directory (~/.openclaw/agents/<name>/agent/auth-profiles.json):
{ "version": 1, "profiles": { "openrouter:default": { "type": "api_key", "provider": "openrouter", "keyRef": { "source": "exec", "provider": "onepassword_openrouter", "id": "value" } }, "anthropic:default": { "type": "token", "provider": "anthropic", "tokenRef": { "source": "exec", "provider": "onepassword_anthropic", "id": "value" } }, "cloudflare-ai-gateway:default": { "type": "api_key", "provider": "cloudflare-ai-gateway", "keyRef": { "source": "exec", "provider": "onepassword_cloudflare", "id": "value" } } }}Then set the apiKey in the agent’s models.json to "secretref-managed":
{ "providers": { "openrouter": { "apiKey": "secretref-managed" }, "anthropic": { "apiKey": "secretref-managed" } }}Local Services (No Auth Needed)
Section titled “Local Services (No Auth Needed)”If a provider doesn’t require authentication (local Ollama, local Whisper), remove the apiKey field entirely rather than setting a placeholder:
{ "providers": { "ollama": { "baseUrl": "http://localhost:11434", "api": "ollama" } }}Part 5: Validate and Restart
Section titled “Part 5: Validate and Restart”Pre-Flight Backup
Section titled “Pre-Flight Backup”Always back up before restarting:
cp ~/.openclaw/openclaw.json ~/.openclaw/openclaw.json.backup.$(date +%Y%m%d-%H%M%S)Run the Secrets Audit
Section titled “Run the Secrets Audit”openclaw secrets audit --checkTarget output:
Secrets audit: clean. plaintext=0, unresolved=0, shadowed=0, legacy=0.If you see REF_UNRESOLVED, the SecretRef can’t resolve. Common causes:
| Error | Cause | Fix |
|---|---|---|
| “returned invalid JSON” | Wrong id value (e.g., "google/apiKey" instead of "value") |
Use "id": "value" for op read providers |
| “must be owned by current user” | Missing allowInsecurePath |
Add "allowInsecurePath": true |
| “provider not found” | Typo in provider name | Check secrets.providers key matches |
| “command failed” | OP_SERVICE_ACCOUNT_TOKEN not available |
Add to passEnv array |
Restart Gateway
Section titled “Restart Gateway”openclaw gateway restartIf things break, restore:
cp ~/.openclaw/openclaw.json.backup.TIMESTAMP ~/.openclaw/openclaw.jsonopenclaw gateway restartPart 6: Best Practices
Section titled “Part 6: Best Practices”Migration Strategy
Section titled “Migration Strategy”- Start with low-risk credentials — Skills and non-critical integrations first
- Test one at a time — Validate each SecretRef resolves before moving to the next
- Save critical-path credentials for last — Discord token, primary model provider keys
- Back up before every restart — Revert should always be one command away
Security Hygiene
Section titled “Security Hygiene”- One vault, one purpose — Keep OpenClaw credentials in a dedicated vault
- Service account, not personal — Don’t rely on interactive sign-in for agent access
- Never echo values — Verify credentials exist with
> /dev/null && echo OK, never print them - Audit regularly — Run
openclaw secrets audit --checkafter any config change - Restrict
.envpermissions —chmod 600on the file containingOP_SERVICE_ACCOUNT_TOKEN
Operational Notes
Section titled “Operational Notes”- Boot order matters — The
OP_SERVICE_ACCOUNT_TOKENmust be available in the environment when OpenClaw starts. If using systemd, ensure the.envfile is loaded viaEnvironmentFile= - 1Password CLI caches — The CLI caches auth sessions. If you rotate the service account token, restart the gateway to pick up the new one
- Field name consistency — Standardize on
passwordas the field name when creating vault items. It makesop readpaths predictable
Adding New Credentials
Section titled “Adding New Credentials”When you add a new integration:
- Create the 1Password vault item
- Add a
secrets.providersentry inopenclaw.json - Reference it with a SecretRef in the appropriate config location
- Run
openclaw secrets audit --check - Restart and verify
Quick Reference
Section titled “Quick Reference”SecretRef Format
Section titled “SecretRef Format”{ "source": "exec", "provider": "<provider_name>", "id": "value"}Provider Template
Section titled “Provider Template”"onepassword_<name>": { "source": "exec", "command": "/usr/bin/op", "args": ["read", "op://OpenClaw-API-Keys/<ITEM_TITLE>/password"], "jsonOnly": false, "passEnv": ["HOME", "OP_SERVICE_ACCOUNT_TOKEN"], "allowInsecurePath": true}Useful Commands
Section titled “Useful Commands”# Audit current stateopenclaw secrets audit --check
# List vault itemsop item list --vault OpenClaw-API-Keys --format json | jq '.[].title'
# Verify a specific item (never echo the value)op read "op://OpenClaw-API-Keys/ITEM/password" > /dev/null && echo "resolves: yes"
# Backup configcp ~/.openclaw/openclaw.json ~/.openclaw/openclaw.json.backup.$(date +%Y%m%d-%H%M%S)Written based on a real migration from 127 plaintext credentials to zero on an OpenClaw homelab deployment. March 2026.