Gateway
Configuration — environment, secrets, and includes
Process environment, secret resolution, auth storage, and config composition: env, secrets.*, auth.*, and $include.
For the full key index and the other top-level config domains, see Configuration reference.
Environment
env (inline env vars)
{ env: { vars: { OPENROUTER_API_KEY: "sk-or-...", GROQ_API_KEY: "gsk-...", }, shellEnv: { enabled: true, timeoutMs: 15000, }, },}- Inline env vars are only applied if the process env is missing the key.
.envfiles: CWD.env+~/.openclaw/.env(neither overrides existing vars).shellEnv: imports missing expected keys from your login shell profile.- See Environment for full precedence.
Env var substitution
Reference env vars in any config string with ${VAR_NAME}:
{ gateway: { auth: { token: "${OPENCLAW_GATEWAY_TOKEN}" }, },}- Only uppercase names matched:
[A-Z_][A-Z0-9_]*. - Missing/empty vars stay visibly unresolved, emit a warning, and are unavailable to consumers that require the value.
- Escape with
$${VAR}to produce a literal${VAR}value. - Works with
$include.
Secrets
Secret refs are additive: plaintext values still work.
secrets.egressProxy
Default-off Gateway-owned substitution for shared-store secret entries used by agent exec subprocesses:
{ secrets: { egressProxy: { enabled: false, allowedHosts: ["api.example.com"], bypassHosts: ["pinned-api.example.com"], }, },}enabled: starts the loopback proxy and ephemeral CA at Gateway startup. Default:false. Changing it requires a Gateway restart.allowedHosts: optional exact-hostname traffic allowlist for proxy requests and CONNECT tunnels. When present, only listed hosts, hosts bound to a registered secret, andbypassHostsare reachable. An empty array permits only bound or bypassed hosts. Changing it requires a Gateway restart.bypassHosts: optional exact-hostname list for authenticated blind CONNECT tunnels used by certificate-pinned clients. Sentinels are not substituted on bypassed hosts and fail vendor authentication without exposing plaintext.
See Secret egress proxy for subprocess environment wiring, authentication, fail-closed behavior, and limitations.
SecretRef
Use one object shape:
{ source: "env" | "file" | "exec" | "store", provider: "default", id: "..." }Validation:
providerpattern:^[a-z][a-z0-9_-]{0,63}$source: "env"id pattern:^[A-Z][A-Z0-9_]{0,127}$source: "file"id: absolute JSON pointer (for example"/providers/openai/apiKey")source: "exec"id pattern:^[A-Za-z0-9][A-Za-z0-9._:/#-]{0,255}$(supports AWS-stylesecret#json_keyselectors)source: "exec"ids must not contain.or..slash-delimited path segments (for examplea/../bis rejected)
Supported credential surface
- Canonical matrix: SecretRef Credential Surface
secrets applytargets supportedopenclaw.jsoncredential paths.- Per-agent auth-profile refs are included in runtime resolution and audit coverage.
Secret providers config
{ secrets: { providers: { default: { source: "env" }, // optional explicit env provider filemain: { source: "file", path: "~/.openclaw/secrets.json", mode: "json", timeoutMs: 5000, }, vault: { source: "exec", command: "/usr/local/bin/openclaw-vault-resolver", passEnv: ["PATH", "VAULT_ADDR"], }, }, defaults: { env: "default", file: "filemain", exec: "vault", }, },}Notes:
fileprovider supportsmode: "json"andmode: "singleValue"(idmust be"value"in singleValue mode).- File and exec provider paths fail closed when Windows ACL verification is unavailable. Use paths whose ACLs OpenClaw can verify; there is no provider-level bypass.
execprovider requires an absolutecommandpath and uses protocol payloads on stdin/stdout.- Symlink command paths are rejected. Configure the resolved absolute binary path instead; it must not be group- or world-writable and, on POSIX, must be owned by the current user.
- If
trustedDirsis configured, the command path (after~expansion) must be inside an approved directory; symlinked commands are rejected before this check, so the configured path itself is whattrustedDirsconstrains. execchild environment is minimal by default; pass required variables explicitly withpassEnv.- Secret refs are resolved at activation time into an in-memory snapshot, then request paths read the snapshot only.
- Active-surface filtering applies during activation: unresolved refs on enabled surfaces fail startup/reload, while inactive surfaces are skipped with diagnostics.
Auth storage
{ auth: { profiles: { "anthropic:default": { provider: "anthropic", mode: "api_key" }, "anthropic:work": { provider: "anthropic", mode: "api_key" }, "openai:personal": { provider: "openai", mode: "oauth" }, }, order: { anthropic: ["anthropic:default", "anthropic:work"], openai: ["openai:personal"], }, },}- Per-agent profiles are stored in
<agentDir>/openclaw-agent.sqlite(auth_profile_store). - Stored auth profiles support value-level refs (
keyRefforapi_key,tokenReffortoken) for static credential modes. - Legacy flat
auth-profiles.jsonmaps such as{ "provider": { "apiKey": "..." } }are not a runtime format;openclaw doctor --fixrewrites them to canonicalprovider:defaultAPI-key profiles with a.legacy-flat.*.bakbackup. - OAuth-mode profiles (
auth.profiles.<id>.mode = "oauth") do not support SecretRef-backed auth-profile credentials. - Static runtime credentials come from in-memory resolved snapshots; legacy static
auth.jsonentries are scrubbed when discovered. - Legacy OAuth imports from
~/.openclaw/credentials/oauth.json. - See OAuth.
- Secrets runtime behavior and
audit/configure/applytooling: Secrets Management.
Config includes ($include)
Split config into multiple files:
// ~/.openclaw/openclaw.json{ gateway: { port: 18789 }, agents: { $include: "./agents.json5" }, broadcast: { $include: ["./clients/mueller.json5", "./clients/schmidt.json5"], },}Merge behavior:
- Single file: replaces the containing object.
- Array of files: deep-merged in order (later overrides earlier).
- Sibling keys: merged after includes (override included values).
- Nested includes: up to 10 levels deep.
- Paths: resolved relative to the including file, but must stay inside the top-level config directory (
dirnameofopenclaw.json). Absolute/../forms are allowed only when they still resolve inside that boundary. SetOPENCLAW_INCLUDE_ROOTS(absolute paths) to allow additional roots outside the config directory. - Limits: paths must not contain null bytes and must be strictly shorter than 4096 characters before and after resolution; each included file is capped at 2 MB.
- OpenClaw-owned writes that change only one top-level section backed by a single-file include write through to that included file. For example,
plugins installupdatesplugins: { $include: "./plugins.json5" }inplugins.json5and leavesopenclaw.jsonintact. - Root includes, include arrays, and includes with sibling overrides are read-only for OpenClaw-owned writes; those writes fail closed instead of flattening the config.
- Errors: clear messages for missing files, parse errors, circular includes, invalid path format, and excessive length.
Was this useful?