Tutorials 08

Lesson 08 — Deep Dive into the Configuration System: The Right Way to Store API Keys

Goal: Understand OpenClaw's three-layer configuration system, master the three Secret Provider modes, and keep your keys both secure and flexible.


What You'll Learn

  • The structure and loading order of OpenClaw's configuration files
  • Three ways to write API Keys: plain string / environment variable reference / external command
  • The security design of the Secret Provider system
  • How to integrate with 1Password, Vault, and other secrets management tools

I. Where Is the Configuration File?

OpenClaw's configuration lives at ~/.openclaw/openclaw.json (or .yaml) and is loaded automatically when the gateway starts.

# View the current configuration
openclaw config show
 
# Set a field via the command line
openclaw config set gateway.mode local

The overall structure is:

{
  "gateway": { ... },        // Gateway mode
  "agents": { ... },         // AI agent default behavior
  "models": { ... },         // Model provider list
  "channels": { ... },       // Message channels (Telegram, Discord, etc.)
  "env": { ... },            // Environment variables injected at runtime
  "secrets": { ... },        // Secret Provider configuration (advanced)
  "hooks": { ... }           // Lifecycle hooks
}

II. Three Ways to Write an API Key

Method A: Plain String (Quick-start only)

The simplest approach, but not recommended for production or shared environments because the key is stored in plaintext in the config file.

{
  "models": {
    "providers": {
      "openai": {
        "apiKey": "sk-proj-xxxxxxxxxxxxxxxx"
      }
    }
  }
}

Method B: Environment Variable Reference (Recommended for Daily Use)

Use the ${VAR_NAME} placeholder to reference system environment variables — no real keys stored in the config file.

Step 1: Export in your shell or add to ~/.profile:

export OPENAI_API_KEY="sk-proj-xxxxxxxxxxxxxxxx"
export MINIMAX_API_KEY="eyJhbGci..."

Step 2: Reference the placeholder in your config:

{
  "models": {
    "providers": {
      "openai": {
        "apiKey": "${OPENAI_API_KEY}"
      },
      "minimax": {
        "apiKey": "${MINIMAX_API_KEY}"
      }
    }
  }
}

You can also use the structured form (more explicit):

{
  "apiKey": { "source": "env", "provider": "default", "id": "OPENAI_API_KEY" }
}

How it works: When the gateway starts, OpenClaw matches the ${VAR_NAME} pattern, reads the corresponding value from process.env, resolves it in memory only, and never writes it to disk.


Method C: Secret Provider (Team / Server Deployments)

This is the most secure and flexible approach, ideal for:

  • Servers where you don't want to set environment variables
  • Teams sharing a gateway with centrally managed credentials
  • Integrating with 1Password CLI, HashiCorp Vault, or similar tools

Secret Provider has three source types:

source: "file" — Read from a File

Suitable for storing keys in a file with strict permissions (OpenClaw verifies file permissions and rejects group-writable files):

{
  "secrets": {
    "providers": {
      "my-keys": {
        "source": "file",
        "path": "~/.openclaw/secrets.json",
        "mode": "json"
      }
    }
  },
  "models": {
    "providers": {
      "openai": {
        "apiKey": { "source": "file", "provider": "my-keys", "id": "/openai/apiKey" }
      }
    }
  }
}

The corresponding ~/.openclaw/secrets.json:

{
  "openai": { "apiKey": "sk-proj-xxxxxxxx" },
  "minimax": { "apiKey": "eyJhbGci..." }
}

Security requirement: The file must be owned by the current user with permissions no broader than 600 (chmod 600 ~/.openclaw/secrets.json).

source: "exec" — Call an External Program

Ideal for integrating with 1Password CLI, Vault, or the system keychain:

{
  "secrets": {
    "providers": {
      "op-vault": {
        "source": "exec",
        "command": "/usr/local/bin/op-resolver",
        "timeoutMs": 5000
      }
    }
  },
  "models": {
    "providers": {
      "openai": {
        "apiKey": { "source": "exec", "provider": "op-vault", "id": "openai/api-key" }
      }
    }
  }
}

The external program receives requests via stdin and returns results via stdout, using this protocol:

# stdin receives (JSON):
{ "protocolVersion": 1, "provider": "op-vault", "ids": ["openai/api-key"] }

# stdout returns (JSON):
{ "protocolVersion": 1, "values": { "openai/api-key": "sk-proj-xxx" } }

III. 1Password Integration Example

If you store API Keys in 1Password, write a simple resolver script:

#!/usr/bin/env bash
# ~/.openclaw/op-resolver.sh
# Set permissions: chmod 700 ~/.openclaw/op-resolver.sh
 
set -euo pipefail
INPUT=$(cat)
ID=$(echo "$INPUT" | jq -r '.ids[0]')
 
# Read from 1Password
VALUE=$(op read "op://Private/$ID" 2>/dev/null)
 
echo "{\"protocolVersion\":1,\"values\":{\"$ID\":\"$VALUE\"}}"

Then configure:

{
  "secrets": {
    "providers": {
      "1password": {
        "source": "exec",
        "command": "/Users/your-username/.openclaw/op-resolver.sh",
        "timeoutMs": 8000
      }
    }
  }
}

Note: command must be an absolute path, and the file must not be writable by other users.


IV. The env Block in Configuration

Besides Secret Provider, the env block provides another convenient way — declare runtime environment variables directly in the config file:

{
  "env": {
    "vars": {
      "HTTP_PROXY": "http://127.0.0.1:7890",
      "OPENAI_BASE_URL": "https://api.openai.com"
    }
  }
}

Rules:

  • Only values from env.vars in the config file are injected — they won't override existing system variables with the same name
  • Dangerous variable names like PATH and LD_PRELOAD are automatically blocked and cannot be injected this way

V. Automatic Redaction of Sensitive Fields

OpenClaw uses Zod's sensitive registry to mark all secret fields. When configuration is exposed via the Dashboard or logs, these fields are automatically replaced with [REDACTED]:

# What you see in logs or API responses:
{ "apiKey": "[REDACTED]" }

# The real value never appears in logs

VI. Complete Configuration Example (Multi-Model + Secure Keys)

{
  "gateway": { "mode": "local" },
 
  "secrets": {
    "providers": {
      "env-keys": { "source": "env" }
    },
    "defaults": { "env": "env-keys" }
  },
 
  "agents": {
    "defaults": {
      "model": { "primary": "openai/gpt-4o" }
    }
  },
 
  "models": {
    "mode": "merge",
    "providers": {
      "openai": {
        "apiKey": "${OPENAI_API_KEY}",
        "models": [
          { "id": "gpt-4o", "name": "GPT-4o" }
        ]
      },
      "minimax": {
        "baseUrl": "https://api.minimax.io/anthropic",
        "apiKey": "${MINIMAX_API_KEY}",
        "api": "anthropic-messages",
        "models": [
          {
            "id": "MiniMax-M2.1",
            "name": "MiniMax M2.1",
            "contextWindow": 200000,
            "maxTokens": 8192
          }
        ]
      }
    }
  },
 
  "channels": {
    "telegram": {
      "token": "${TELEGRAM_BOT_TOKEN}"
    }
  }
}

Corresponding environment variables (add to ~/.profile or .env):

export OPENAI_API_KEY="sk-proj-..."
export MINIMAX_API_KEY="eyJhbGci..."
export TELEGRAM_BOT_TOKEN="8543054163:AAH..."

VII. FAQ

I changed the config but it didn't take effect — why?

The gateway needs to restart to reload the configuration (Skills / SKILL.md files are the exception):

openclaw gateway restart

How do I verify that a key was read correctly?

openclaw models list
# If you see models for the corresponding provider, the apiKey resolved successfully

If the model list is empty, run openclaw config show to see if the config was parsed correctly, and check whether the environment variable referenced by ${VAR_NAME} has been exported.

I'm getting a permission error with file source — how do I fix it?

chmod 600 ~/.openclaw/secrets.json
# Make sure the file is owned by the current user
ls -la ~/.openclaw/secrets.json

OpenClaw requires the secrets file to have permissions no broader than 600, and it must be owned by the current user (it cannot be root-owned but user-readable).

The exec source script timed out — how do I handle it?

Increase timeoutMs (maximum 120000ms). The most common cause is the external command needing a session — for example, the 1Password CLI requires op signin first. Consider adding automatic re-login logic to the script, or caching the session token to a file.

What's the difference between the .env file and the env block in openclaw.json?

The .env file sets system-level environment variables that apply to all processes. The env.vars block in openclaw.json only takes effect within the OpenClaw gateway process and doesn't affect other programs. Both support ${VAR_NAME} references in fields like apiKey. The recommended approach: store sensitive data in .env, use env.vars for OpenClaw-specific non-sensitive config (like proxy addresses).

Can multiple configuration files coexist?

OpenClaw supports configuration merging ("mode": "merge"), but the primary config file path is fixed at ~/.openclaw/openclaw.json. You can use the import field in openclaw.json to include other config file fragments, enabling modular configuration management.


Next Steps

  • Lesson 05 — Configure multiple model providers with automatic switching
  • Lesson 03 — Add custom Skills to AI

Stay up to date with OpenClaw

Follow @lanmiaoai on X for tips, updates and new tutorials.

Follow