diff --git a/docs/gateway/secrets.md b/docs/gateway/secrets.md
index 7b4d3e5d0bc..cff013086f0 100644
--- a/docs/gateway/secrets.md
+++ b/docs/gateway/secrets.md
@@ -5,11 +5,14 @@ read_when:
- Operating secrets reload, audit, configure, and apply safely in production
- Understanding startup fail-fast, inactive-surface filtering, and last-known-good behavior
title: "Secrets management"
+sidebarTitle: "Secrets management"
---
OpenClaw supports additive SecretRefs so supported credentials do not need to be stored as plaintext in configuration.
+
Plaintext still works. SecretRefs are opt-in per credential.
+
## Goals and runtime model
@@ -33,38 +36,32 @@ SecretRefs are validated only on effectively active surfaces.
- Inactive surfaces: unresolved refs do not block startup/reload.
- Inactive refs emit non-fatal diagnostics with code `SECRETS_REF_IGNORED_INACTIVE_SURFACE`.
-Examples of inactive surfaces:
-
-- Disabled channel/account entries.
-- Top-level channel credentials that no enabled account inherits.
-- Disabled tool/feature surfaces.
-- Web search provider-specific keys that are not selected by `tools.web.search.provider`.
- In auto mode (provider unset), keys are consulted by precedence for provider auto-detection until one resolves.
- After selection, non-selected provider keys are treated as inactive until selected.
-- Sandbox SSH auth material (`agents.defaults.sandbox.ssh.identityData`,
- `certificateData`, `knownHostsData`, plus per-agent overrides) is active only
- when the effective sandbox backend is `ssh` for the default agent or an enabled agent.
-- `gateway.remote.token` / `gateway.remote.password` SecretRefs are active if one of these is true:
- - `gateway.mode=remote`
- - `gateway.remote.url` is configured
- - `gateway.tailscale.mode` is `serve` or `funnel`
- - In local mode without those remote surfaces:
- - `gateway.remote.token` is active when token auth can win and no env/auth token is configured.
- - `gateway.remote.password` is active only when password auth can win and no env/auth password is configured.
-- `gateway.auth.token` SecretRef is inactive for startup auth resolution when `OPENCLAW_GATEWAY_TOKEN` is set, because env token input wins for that runtime.
+
+
+ - Disabled channel/account entries.
+ - Top-level channel credentials that no enabled account inherits.
+ - Disabled tool/feature surfaces.
+ - Web search provider-specific keys that are not selected by `tools.web.search.provider`. In auto mode (provider unset), keys are consulted by precedence for provider auto-detection until one resolves. After selection, non-selected provider keys are treated as inactive until selected.
+ - Sandbox SSH auth material (`agents.defaults.sandbox.ssh.identityData`, `certificateData`, `knownHostsData`, plus per-agent overrides) is active only when the effective sandbox backend is `ssh` for the default agent or an enabled agent.
+ - `gateway.remote.token` / `gateway.remote.password` SecretRefs are active if one of these is true:
+ - `gateway.mode=remote`
+ - `gateway.remote.url` is configured
+ - `gateway.tailscale.mode` is `serve` or `funnel`
+ - In local mode without those remote surfaces:
+ - `gateway.remote.token` is active when token auth can win and no env/auth token is configured.
+ - `gateway.remote.password` is active only when password auth can win and no env/auth password is configured.
+ - `gateway.auth.token` SecretRef is inactive for startup auth resolution when `OPENCLAW_GATEWAY_TOKEN` is set, because env token input wins for that runtime.
+
+
## Gateway auth surface diagnostics
-When a SecretRef is configured on `gateway.auth.token`, `gateway.auth.password`,
-`gateway.remote.token`, or `gateway.remote.password`, gateway startup/reload logs the
-surface state explicitly:
+When a SecretRef is configured on `gateway.auth.token`, `gateway.auth.password`, `gateway.remote.token`, or `gateway.remote.password`, gateway startup/reload logs the surface state explicitly:
- `active`: the SecretRef is part of the effective auth surface and must resolve.
-- `inactive`: the SecretRef is ignored for this runtime because another auth surface wins, or
- because remote auth is disabled/not active.
+- `inactive`: the SecretRef is ignored for this runtime because another auth surface wins, or because remote auth is disabled/not active.
-These entries are logged with `SECRETS_GATEWAY_AUTH_SURFACE` and include the reason used by the
-active-surface policy, so you can see why a credential was treated as active or inactive.
+These entries are logged with `SECRETS_GATEWAY_AUTH_SURFACE` and include the reason used by the active-surface policy, so you can see why a credential was treated as active or inactive.
## Onboarding reference preflight
@@ -84,40 +81,43 @@ Use one object shape everywhere:
{ source: "env" | "file" | "exec", provider: "default", id: "..." }
```
-### `source: "env"`
+
+
+ ```json5
+ { source: "env", provider: "default", id: "OPENAI_API_KEY" }
+ ```
-```json5
-{ source: "env", provider: "default", id: "OPENAI_API_KEY" }
-```
+ Validation:
-Validation:
+ - `provider` must match `^[a-z][a-z0-9_-]{0,63}$`
+ - `id` must match `^[A-Z][A-Z0-9_]{0,127}$`
-- `provider` must match `^[a-z][a-z0-9_-]{0,63}$`
-- `id` must match `^[A-Z][A-Z0-9_]{0,127}$`
+
+
+ ```json5
+ { source: "file", provider: "filemain", id: "/providers/openai/apiKey" }
+ ```
-### `source: "file"`
+ Validation:
-```json5
-{ source: "file", provider: "filemain", id: "/providers/openai/apiKey" }
-```
+ - `provider` must match `^[a-z][a-z0-9_-]{0,63}$`
+ - `id` must be an absolute JSON pointer (`/...`)
+ - RFC6901 escaping in segments: `~` => `~0`, `/` => `~1`
-Validation:
+
+
+ ```json5
+ { source: "exec", provider: "vault", id: "providers/openai/apiKey" }
+ ```
-- `provider` must match `^[a-z][a-z0-9_-]{0,63}$`
-- `id` must be an absolute JSON pointer (`/...`)
-- RFC6901 escaping in segments: `~` => `~0`, `/` => `~1`
+ Validation:
-### `source: "exec"`
+ - `provider` must match `^[a-z][a-z0-9_-]{0,63}$`
+ - `id` must match `^[A-Za-z0-9][A-Za-z0-9._:/-]{0,255}$`
+ - `id` must not contain `.` or `..` as slash-delimited path segments (for example `a/../b` is rejected)
-```json5
-{ source: "exec", provider: "vault", id: "providers/openai/apiKey" }
-```
-
-Validation:
-
-- `provider` must match `^[a-z][a-z0-9_-]{0,63}$`
-- `id` must match `^[A-Za-z0-9][A-Za-z0-9._:/-]{0,255}$`
-- `id` must not contain `.` or `..` as slash-delimited path segments (for example `a/../b` is rejected)
+
+
## Provider config
@@ -155,138 +155,139 @@ Define providers under `secrets.providers`:
}
```
-### Env provider
+
+
+ - Optional allowlist via `allowlist`.
+ - Missing/empty env values fail resolution.
+
+
+ - Reads local file from `path`.
+ - `mode: "json"` expects JSON object payload and resolves `id` as pointer.
+ - `mode: "singleValue"` expects ref id `"value"` and returns file contents.
+ - Path must pass ownership/permission checks.
+ - Windows fail-closed note: if ACL verification is unavailable for a path, resolution fails. For trusted paths only, set `allowInsecurePath: true` on that provider to bypass path security checks.
+
+
+ - Runs configured absolute binary path, no shell.
+ - By default, `command` must point to a regular file (not a symlink).
+ - Set `allowSymlinkCommand: true` to allow symlink command paths (for example Homebrew shims). OpenClaw validates the resolved target path.
+ - Pair `allowSymlinkCommand` with `trustedDirs` for package-manager paths (for example `["/opt/homebrew"]`).
+ - Supports timeout, no-output timeout, output byte limits, env allowlist, and trusted dirs.
+ - Windows fail-closed note: if ACL verification is unavailable for the command path, resolution fails. For trusted paths only, set `allowInsecurePath: true` on that provider to bypass path security checks.
-- Optional allowlist via `allowlist`.
-- Missing/empty env values fail resolution.
+ Request payload (stdin):
-### File provider
+ ```json
+ { "protocolVersion": 1, "provider": "vault", "ids": ["providers/openai/apiKey"] }
+ ```
-- Reads local file from `path`.
-- `mode: "json"` expects JSON object payload and resolves `id` as pointer.
-- `mode: "singleValue"` expects ref id `"value"` and returns file contents.
-- Path must pass ownership/permission checks.
-- Windows fail-closed note: if ACL verification is unavailable for a path, resolution fails. For trusted paths only, set `allowInsecurePath: true` on that provider to bypass path security checks.
+ Response payload (stdout):
-### Exec provider
+ ```jsonc
+ { "protocolVersion": 1, "values": { "providers/openai/apiKey": "" } } // pragma: allowlist secret
+ ```
-- Runs configured absolute binary path, no shell.
-- By default, `command` must point to a regular file (not a symlink).
-- Set `allowSymlinkCommand: true` to allow symlink command paths (for example Homebrew shims). OpenClaw validates the resolved target path.
-- Pair `allowSymlinkCommand` with `trustedDirs` for package-manager paths (for example `["/opt/homebrew"]`).
-- Supports timeout, no-output timeout, output byte limits, env allowlist, and trusted dirs.
-- Windows fail-closed note: if ACL verification is unavailable for the command path, resolution fails. For trusted paths only, set `allowInsecurePath: true` on that provider to bypass path security checks.
+ Optional per-id errors:
-Request payload (stdin):
+ ```json
+ {
+ "protocolVersion": 1,
+ "values": {},
+ "errors": { "providers/openai/apiKey": { "message": "not found" } }
+ }
+ ```
-```json
-{ "protocolVersion": 1, "provider": "vault", "ids": ["providers/openai/apiKey"] }
-```
-
-Response payload (stdout):
-
-```jsonc
-{ "protocolVersion": 1, "values": { "providers/openai/apiKey": "" } } // pragma: allowlist secret
-```
-
-Optional per-id errors:
-
-```json
-{
- "protocolVersion": 1,
- "values": {},
- "errors": { "providers/openai/apiKey": { "message": "not found" } }
-}
-```
+
+
## Exec integration examples
-### 1Password CLI
-
-```json5
-{
- secrets: {
- providers: {
- onepassword_openai: {
- source: "exec",
- command: "/opt/homebrew/bin/op",
- allowSymlinkCommand: true, // required for Homebrew symlinked binaries
- trustedDirs: ["/opt/homebrew"],
- args: ["read", "op://Personal/OpenClaw QA API Key/password"],
- passEnv: ["HOME"],
- jsonOnly: false,
+
+
+ ```json5
+ {
+ secrets: {
+ providers: {
+ onepassword_openai: {
+ source: "exec",
+ command: "/opt/homebrew/bin/op",
+ allowSymlinkCommand: true, // required for Homebrew symlinked binaries
+ trustedDirs: ["/opt/homebrew"],
+ args: ["read", "op://Personal/OpenClaw QA API Key/password"],
+ passEnv: ["HOME"],
+ jsonOnly: false,
+ },
+ },
},
- },
- },
- models: {
- providers: {
- openai: {
- baseUrl: "https://api.openai.com/v1",
- models: [{ id: "gpt-5", name: "gpt-5" }],
- apiKey: { source: "exec", provider: "onepassword_openai", id: "value" },
+ models: {
+ providers: {
+ openai: {
+ baseUrl: "https://api.openai.com/v1",
+ models: [{ id: "gpt-5", name: "gpt-5" }],
+ apiKey: { source: "exec", provider: "onepassword_openai", id: "value" },
+ },
+ },
},
- },
- },
-}
-```
-
-### HashiCorp Vault CLI
-
-```json5
-{
- secrets: {
- providers: {
- vault_openai: {
- source: "exec",
- command: "/opt/homebrew/bin/vault",
- allowSymlinkCommand: true, // required for Homebrew symlinked binaries
- trustedDirs: ["/opt/homebrew"],
- args: ["kv", "get", "-field=OPENAI_API_KEY", "secret/openclaw"],
- passEnv: ["VAULT_ADDR", "VAULT_TOKEN"],
- jsonOnly: false,
+ }
+ ```
+
+
+ ```json5
+ {
+ secrets: {
+ providers: {
+ vault_openai: {
+ source: "exec",
+ command: "/opt/homebrew/bin/vault",
+ allowSymlinkCommand: true, // required for Homebrew symlinked binaries
+ trustedDirs: ["/opt/homebrew"],
+ args: ["kv", "get", "-field=OPENAI_API_KEY", "secret/openclaw"],
+ passEnv: ["VAULT_ADDR", "VAULT_TOKEN"],
+ jsonOnly: false,
+ },
+ },
},
- },
- },
- models: {
- providers: {
- openai: {
- baseUrl: "https://api.openai.com/v1",
- models: [{ id: "gpt-5", name: "gpt-5" }],
- apiKey: { source: "exec", provider: "vault_openai", id: "value" },
+ models: {
+ providers: {
+ openai: {
+ baseUrl: "https://api.openai.com/v1",
+ models: [{ id: "gpt-5", name: "gpt-5" }],
+ apiKey: { source: "exec", provider: "vault_openai", id: "value" },
+ },
+ },
},
- },
- },
-}
-```
-
-### `sops`
-
-```json5
-{
- secrets: {
- providers: {
- sops_openai: {
- source: "exec",
- command: "/opt/homebrew/bin/sops",
- allowSymlinkCommand: true, // required for Homebrew symlinked binaries
- trustedDirs: ["/opt/homebrew"],
- args: ["-d", "--extract", '["providers"]["openai"]["apiKey"]', "/path/to/secrets.enc.json"],
- passEnv: ["SOPS_AGE_KEY_FILE"],
- jsonOnly: false,
+ }
+ ```
+
+
+ ```json5
+ {
+ secrets: {
+ providers: {
+ sops_openai: {
+ source: "exec",
+ command: "/opt/homebrew/bin/sops",
+ allowSymlinkCommand: true, // required for Homebrew symlinked binaries
+ trustedDirs: ["/opt/homebrew"],
+ args: ["-d", "--extract", '["providers"]["openai"]["apiKey"]', "/path/to/secrets.enc.json"],
+ passEnv: ["SOPS_AGE_KEY_FILE"],
+ jsonOnly: false,
+ },
+ },
},
- },
- },
- models: {
- providers: {
- openai: {
- baseUrl: "https://api.openai.com/v1",
- models: [{ id: "gpt-5", name: "gpt-5" }],
- apiKey: { source: "exec", provider: "sops_openai", id: "value" },
+ models: {
+ providers: {
+ openai: {
+ baseUrl: "https://api.openai.com/v1",
+ models: [{ id: "gpt-5", name: "gpt-5" }],
+ apiKey: { source: "exec", provider: "sops_openai", id: "value" },
+ },
+ },
},
- },
- },
-}
-```
+ }
+ ```
+
+
## MCP server environment variables
@@ -356,7 +357,9 @@ Canonical supported and unsupported credentials are listed in:
- [SecretRef Credential Surface](/reference/secretref-credential-surface)
+
Runtime-minted or rotating credentials and OAuth refresh material are intentionally excluded from read-only SecretRef resolution.
+
## Required behavior and precedence
@@ -415,15 +418,22 @@ Command paths can opt into supported SecretRef resolution via gateway snapshot R
There are two broad behaviors:
-- Strict command paths (for example `openclaw memory` remote-memory paths and `openclaw qr --remote` when it needs remote shared-secret refs) read from the active snapshot and fail fast when a required SecretRef is unavailable.
-- Read-only command paths (for example `openclaw status`, `openclaw status --all`, `openclaw channels status`, `openclaw channels resolve`, `openclaw security audit`, and read-only doctor/config repair flows) also prefer the active snapshot, but degrade instead of aborting when a targeted SecretRef is unavailable in that command path.
+
+
+ For example `openclaw memory` remote-memory paths and `openclaw qr --remote` when it needs remote shared-secret refs. They read from the active snapshot and fail fast when a required SecretRef is unavailable.
+
+
+ For example `openclaw status`, `openclaw status --all`, `openclaw channels status`, `openclaw channels resolve`, `openclaw security audit`, and read-only doctor/config repair flows. They also prefer the active snapshot, but degrade instead of aborting when a targeted SecretRef is unavailable in that command path.
-Read-only behavior:
+ Read-only behavior:
-- When the gateway is running, these commands read from the active snapshot first.
-- If gateway resolution is incomplete or the gateway is unavailable, they attempt targeted local fallback for the specific command surface.
-- If a targeted SecretRef is still unavailable, the command continues with degraded read-only output and explicit diagnostics such as “configured but unavailable in this command path”.
-- This degraded behavior is command-local only. It does not weaken runtime startup, reload, or send/auth paths.
+ - When the gateway is running, these commands read from the active snapshot first.
+ - If gateway resolution is incomplete or the gateway is unavailable, they attempt targeted local fallback for the specific command surface.
+ - If a targeted SecretRef is still unavailable, the command continues with degraded read-only output and explicit diagnostics such as "configured but unavailable in this command path".
+ - This degraded behavior is command-local only. It does not weaken runtime startup, reload, or send/auth paths.
+
+
+
Other notes:
@@ -434,82 +444,97 @@ Other notes:
Default operator flow:
-```bash
-openclaw secrets audit --check
-openclaw secrets configure
-openclaw secrets audit --check
-```
+
+
+ ```bash
+ openclaw secrets audit --check
+ ```
+
+
+ ```bash
+ openclaw secrets configure
+ ```
+
+
+ ```bash
+ openclaw secrets audit --check
+ ```
+
+
-### `secrets audit`
+
+
+ Findings include:
-Findings include:
+ - plaintext values at rest (`openclaw.json`, `auth-profiles.json`, `.env`, and generated `agents/*/agent/models.json`)
+ - plaintext sensitive provider header residues in generated `models.json` entries
+ - unresolved refs
+ - precedence shadowing (`auth-profiles.json` taking priority over `openclaw.json` refs)
+ - legacy residues (`auth.json`, OAuth reminders)
-- plaintext values at rest (`openclaw.json`, `auth-profiles.json`, `.env`, and generated `agents/*/agent/models.json`)
-- plaintext sensitive provider header residues in generated `models.json` entries
-- unresolved refs
-- precedence shadowing (`auth-profiles.json` taking priority over `openclaw.json` refs)
-- legacy residues (`auth.json`, OAuth reminders)
+ Exec note:
-Exec note:
+ - By default, audit skips exec SecretRef resolvability checks to avoid command side effects.
+ - Use `openclaw secrets audit --allow-exec` to execute exec providers during audit.
-- By default, audit skips exec SecretRef resolvability checks to avoid command side effects.
-- Use `openclaw secrets audit --allow-exec` to execute exec providers during audit.
+ Header residue note:
-Header residue note:
+ - Sensitive provider header detection is name-heuristic based (common auth/credential header names and fragments such as `authorization`, `x-api-key`, `token`, `secret`, `password`, and `credential`).
-- Sensitive provider header detection is name-heuristic based (common auth/credential header names and fragments such as `authorization`, `x-api-key`, `token`, `secret`, `password`, and `credential`).
+
+
+ Interactive helper that:
-### `secrets configure`
+ - configures `secrets.providers` first (`env`/`file`/`exec`, add/edit/remove)
+ - lets you select supported secret-bearing fields in `openclaw.json` plus `auth-profiles.json` for one agent scope
+ - can create a new `auth-profiles.json` mapping directly in the target picker
+ - captures SecretRef details (`source`, `provider`, `id`)
+ - runs preflight resolution
+ - can apply immediately
-Interactive helper that:
+ Exec note:
-- configures `secrets.providers` first (`env`/`file`/`exec`, add/edit/remove)
-- lets you select supported secret-bearing fields in `openclaw.json` plus `auth-profiles.json` for one agent scope
-- can create a new `auth-profiles.json` mapping directly in the target picker
-- captures SecretRef details (`source`, `provider`, `id`)
-- runs preflight resolution
-- can apply immediately
+ - Preflight skips exec SecretRef checks unless `--allow-exec` is set.
+ - If you apply directly from `configure --apply` and the plan includes exec refs/providers, keep `--allow-exec` set for the apply step too.
-Exec note:
+ Helpful modes:
-- Preflight skips exec SecretRef checks unless `--allow-exec` is set.
-- If you apply directly from `configure --apply` and the plan includes exec refs/providers, keep `--allow-exec` set for the apply step too.
+ - `openclaw secrets configure --providers-only`
+ - `openclaw secrets configure --skip-provider-setup`
+ - `openclaw secrets configure --agent `
-Helpful modes:
+ `configure` apply defaults:
-- `openclaw secrets configure --providers-only`
-- `openclaw secrets configure --skip-provider-setup`
-- `openclaw secrets configure --agent `
+ - scrub matching static credentials from `auth-profiles.json` for targeted providers
+ - scrub legacy static `api_key` entries from `auth.json`
+ - scrub matching known secret lines from `/.env`
-`configure` apply defaults:
+
+
+ Apply a saved plan:
-- scrub matching static credentials from `auth-profiles.json` for targeted providers
-- scrub legacy static `api_key` entries from `auth.json`
-- scrub matching known secret lines from `/.env`
+ ```bash
+ openclaw secrets apply --from /tmp/openclaw-secrets-plan.json
+ openclaw secrets apply --from /tmp/openclaw-secrets-plan.json --allow-exec
+ openclaw secrets apply --from /tmp/openclaw-secrets-plan.json --dry-run
+ openclaw secrets apply --from /tmp/openclaw-secrets-plan.json --dry-run --allow-exec
+ ```
-### `secrets apply`
+ Exec note:
-Apply a saved plan:
+ - dry-run skips exec checks unless `--allow-exec` is set.
+ - write mode rejects plans containing exec SecretRefs/providers unless `--allow-exec` is set.
-```bash
-openclaw secrets apply --from /tmp/openclaw-secrets-plan.json
-openclaw secrets apply --from /tmp/openclaw-secrets-plan.json --allow-exec
-openclaw secrets apply --from /tmp/openclaw-secrets-plan.json --dry-run
-openclaw secrets apply --from /tmp/openclaw-secrets-plan.json --dry-run --allow-exec
-```
+ For strict target/path contract details and exact rejection rules, see [Secrets Apply Plan Contract](/gateway/secrets-plan-contract).
-Exec note:
-
-- dry-run skips exec checks unless `--allow-exec` is set.
-- write mode rejects plans containing exec SecretRefs/providers unless `--allow-exec` is set.
-
-For strict target/path contract details and exact rejection rules, see:
-
-- [Secrets Apply Plan Contract](/gateway/secrets-plan-contract)
+
+
## One-way safety policy
+
OpenClaw intentionally does not write rollback backups containing historical plaintext secret values.
+
Safety model:
@@ -529,11 +554,11 @@ For static credentials, runtime no longer depends on plaintext legacy auth stora
Some SecretInput unions are easier to configure in raw editor mode than in form mode.
-## Related docs
+## Related
-- CLI commands: [secrets](/cli/secrets)
-- Plan contract details: [Secrets Apply Plan Contract](/gateway/secrets-plan-contract)
-- Credential surface: [SecretRef Credential Surface](/reference/secretref-credential-surface)
-- Auth setup: [Authentication](/gateway/authentication)
-- Security posture: [Security](/gateway/security)
-- Environment precedence: [Environment Variables](/help/environment)
+- [Authentication](/gateway/authentication) — auth setup
+- [CLI: secrets](/cli/secrets) — CLI commands
+- [Environment Variables](/help/environment) — environment precedence
+- [SecretRef Credential Surface](/reference/secretref-credential-surface) — credential surface
+- [Secrets Apply Plan Contract](/gateway/secrets-plan-contract) — plan contract details
+- [Security](/gateway/security) — security posture