mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
feat(secrets): expand SecretRef coverage across user-supplied credentials (#29580)
* feat(secrets): expand secret target coverage and gateway tooling * docs(secrets): align gateway and CLI secret docs * chore(protocol): regenerate swift gateway models for secrets methods * fix(config): restore talk apiKey fallback and stabilize runner test * ci(windows): reduce test worker count for shard stability * ci(windows): raise node heap for test shard stability * test(feishu): make proxy env precedence assertion windows-safe * fix(gateway): resolve auth password SecretInput refs for clients * fix(gateway): resolve remote SecretInput credentials for clients * fix(secrets): skip inactive refs in command snapshot assignments * fix(secrets): scope gateway.remote refs to effective auth surfaces * fix(secrets): ignore memory defaults when enabled agents disable search * fix(secrets): honor Google Chat serviceAccountRef inheritance * fix(secrets): address tsgo errors in command and gateway collectors * fix(secrets): avoid auth-store load in providers-only configure * fix(gateway): defer local password ref resolution by precedence * fix(secrets): gate telegram webhook secret refs by webhook mode * fix(secrets): gate slack signing secret refs to http mode * fix(secrets): skip telegram botToken refs when tokenFile is set * fix(secrets): gate discord pluralkit refs by enabled flag * fix(secrets): gate discord voice tts refs by voice enabled * test(secrets): make runtime fixture modes explicit * fix(cli): resolve local qr password secret refs * fix(cli): fail when gateway leaves command refs unresolved * fix(gateway): fail when local password SecretRef is unresolved * fix(gateway): fail when required remote SecretRefs are unresolved * fix(gateway): resolve local password refs only when password can win * fix(cli): skip local password SecretRef resolution on qr token override * test(gateway): cast SecretRef fixtures to OpenClawConfig * test(secrets): activate mode-gated targets in runtime coverage fixture * fix(cron): support SecretInput webhook tokens safely * fix(bluebubbles): support SecretInput passwords across config paths * fix(msteams): make appPassword SecretInput-safe in onboarding/token paths * fix(bluebubbles): align SecretInput schema helper typing * fix(cli): clarify secrets.resolve version-skew errors * refactor(secrets): return structured inactive paths from secrets.resolve * refactor(gateway): type onboarding secret writes as SecretInput * chore(protocol): regenerate swift models for secrets.resolve * feat(secrets): expand extension credential secretref support * fix(secrets): gate web-search refs by active provider * fix(onboarding): detect SecretRef credentials in extension status * fix(onboarding): allow keeping existing ref in secret prompt * fix(onboarding): resolve gateway password SecretRefs for probe and tui * fix(onboarding): honor secret-input-mode for local gateway auth * fix(acp): resolve gateway SecretInput credentials * fix(secrets): gate gateway.remote refs to remote surfaces * test(secrets): cover pattern matching and inactive array refs * docs(secrets): clarify secrets.resolve and remote active surfaces * fix(bluebubbles): keep existing SecretRef during onboarding * fix(tests): resolve CI type errors in new SecretRef coverage * fix(extensions): replace raw fetch with SSRF-guarded fetch * test(secrets): mark gateway remote targets active in runtime coverage * test(infra): normalize home-prefix expectation across platforms * fix(cli): only resolve local qr password refs in password mode * test(cli): cover local qr token mode with unresolved password ref * docs(cli): clarify local qr password ref resolution behavior * refactor(extensions): reuse sdk SecretInput helpers * fix(wizard): resolve onboarding env-template secrets before plaintext * fix(cli): surface secrets.resolve diagnostics in memory and qr * test(secrets): repair post-rebase runtime and fixtures * fix(gateway): skip remote password ref resolution when token wins * fix(secrets): treat tailscale remote gateway refs as active * fix(gateway): allow remote password fallback when token ref is unresolved * fix(gateway): ignore stale local password refs for none and trusted-proxy * fix(gateway): skip remote secret ref resolution on local call paths * test(cli): cover qr remote tailscale secret ref resolution * fix(secrets): align gateway password active-surface with auth inference * fix(cli): resolve inferred local gateway password refs in qr * fix(gateway): prefer resolvable remote password over token ref pre-resolution * test(gateway): cover none and trusted-proxy stale password refs * docs(secrets): sync qr and gateway active-surface behavior * fix: restore stability blockers from pre-release audit * Secrets: fix collector/runtime precedence contradictions * docs: align secrets and web credential docs * fix(rebase): resolve integration regressions after main rebase * fix(node-host): resolve gateway secret refs for auth * fix(secrets): harden secretinput runtime readers * gateway: skip inactive auth secretref resolution * cli: avoid gateway preflight for inactive secret refs * extensions: allow unresolved refs in onboarding status * tests: fix qr-cli module mock hoist ordering * Security: align audit checks with SecretInput resolution * Gateway: resolve local-mode remote fallback secret refs * Node host: avoid resolving inactive password secret refs * Secrets runtime: mark Slack appToken inactive for HTTP mode * secrets: keep inactive gateway remote refs non-blocking * cli: include agent memory secret targets in runtime resolution * docs(secrets): sync docs with active-surface and web search behavior * fix(secrets): keep telegram top-level token refs active for blank account tokens * fix(daemon): resolve gateway password secret refs for probe auth * fix(secrets): skip IRC NickServ ref resolution when NickServ is disabled * fix(secrets): align token inheritance and exec timeout defaults * docs(secrets): clarify active-surface notes in cli docs * cli: require secrets.resolve gateway capability * gateway: log auth secret surface diagnostics * secrets: remove dead provider resolver module * fix(secrets): restore gateway auth precedence and fallback resolution * fix(tests): align plugin runtime mock typings --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
@@ -50,3 +50,5 @@ Notes:
|
||||
- `memory status --deep --index` runs a reindex if the store is dirty.
|
||||
- `memory index --verbose` prints per-phase details (provider, model, sources, batch activity).
|
||||
- `memory status` includes any extra paths configured via `memorySearch.extraPaths`.
|
||||
- If effectively active memory remote API key fields are configured as SecretRefs, the command resolves those values from the active gateway snapshot. If gateway is unavailable, the command fails fast.
|
||||
- Gateway version skew note: this command path requires a gateway that supports `secrets.resolve`; older gateways return an unknown-method error.
|
||||
|
||||
@@ -34,6 +34,9 @@ openclaw qr --url wss://gateway.example/ws --token '<token>'
|
||||
## Notes
|
||||
|
||||
- `--token` and `--password` are mutually exclusive.
|
||||
- With `--remote`, if effectively active remote credentials are configured as SecretRefs and you do not pass `--token` or `--password`, the command resolves them from the active gateway snapshot. If gateway is unavailable, the command fails fast.
|
||||
- Without `--remote`, local `gateway.auth.password` SecretRefs are resolved when password auth can win (explicit `gateway.auth.mode="password"` or inferred password mode with no winning token from auth/env), and no CLI auth override is passed.
|
||||
- Gateway version skew note: this command path requires a gateway that supports `secrets.resolve`; older gateways return an unknown-method error.
|
||||
- After scanning, approve device pairing with:
|
||||
- `openclaw devices list`
|
||||
- `openclaw devices approve <requestId>`
|
||||
|
||||
@@ -9,14 +9,14 @@ title: "secrets"
|
||||
|
||||
# `openclaw secrets`
|
||||
|
||||
Use `openclaw secrets` to migrate credentials from plaintext to SecretRefs and keep the active secrets runtime healthy.
|
||||
Use `openclaw secrets` to manage SecretRefs and keep the active runtime snapshot healthy.
|
||||
|
||||
Command roles:
|
||||
|
||||
- `reload`: gateway RPC (`secrets.reload`) that re-resolves refs and swaps runtime snapshot only on full success (no config writes).
|
||||
- `audit`: read-only scan of config + auth stores + legacy residues (`.env`, `auth.json`) for plaintext, unresolved refs, and precedence drift.
|
||||
- `configure`: interactive planner for provider setup + target mapping + preflight (TTY required).
|
||||
- `apply`: execute a saved plan (`--dry-run` for validation only), then scrub migrated plaintext residues.
|
||||
- `audit`: read-only scan of configuration/auth stores and legacy residues for plaintext, unresolved refs, and precedence drift.
|
||||
- `configure`: interactive planner for provider setup, target mapping, and preflight (TTY required).
|
||||
- `apply`: execute a saved plan (`--dry-run` for validation only), then scrub targeted plaintext residues.
|
||||
|
||||
Recommended operator loop:
|
||||
|
||||
@@ -31,11 +31,13 @@ openclaw secrets reload
|
||||
|
||||
Exit code note for CI/gates:
|
||||
|
||||
- `audit --check` returns `1` on findings, `2` when refs are unresolved.
|
||||
- `audit --check` returns `1` on findings.
|
||||
- unresolved refs return `2`.
|
||||
|
||||
Related:
|
||||
|
||||
- Secrets guide: [Secrets Management](/gateway/secrets)
|
||||
- Credential surface: [SecretRef Credential Surface](/reference/secretref-credential-surface)
|
||||
- Security guide: [Security](/gateway/security)
|
||||
|
||||
## Reload runtime snapshot
|
||||
@@ -59,8 +61,8 @@ Scan OpenClaw state for:
|
||||
|
||||
- plaintext secret storage
|
||||
- unresolved refs
|
||||
- precedence drift (`auth-profiles` shadowing config refs)
|
||||
- legacy residues (`auth.json`, OAuth out-of-scope notes)
|
||||
- precedence drift (`auth-profiles.json` credentials shadowing `openclaw.json` refs)
|
||||
- legacy residues (legacy auth store entries, OAuth reminders)
|
||||
|
||||
```bash
|
||||
openclaw secrets audit
|
||||
@@ -71,7 +73,7 @@ openclaw secrets audit --json
|
||||
Exit behavior:
|
||||
|
||||
- `--check` exits non-zero on findings.
|
||||
- unresolved refs exit with a higher-priority non-zero code.
|
||||
- unresolved refs exit with higher-priority non-zero code.
|
||||
|
||||
Report shape highlights:
|
||||
|
||||
@@ -85,7 +87,7 @@ Report shape highlights:
|
||||
|
||||
## Configure (interactive helper)
|
||||
|
||||
Build provider + SecretRef changes interactively, run preflight, and optionally apply:
|
||||
Build provider and SecretRef changes interactively, run preflight, and optionally apply:
|
||||
|
||||
```bash
|
||||
openclaw secrets configure
|
||||
@@ -93,6 +95,7 @@ openclaw secrets configure --plan-out /tmp/openclaw-secrets-plan.json
|
||||
openclaw secrets configure --apply --yes
|
||||
openclaw secrets configure --providers-only
|
||||
openclaw secrets configure --skip-provider-setup
|
||||
openclaw secrets configure --agent ops
|
||||
openclaw secrets configure --json
|
||||
```
|
||||
|
||||
@@ -106,23 +109,26 @@ Flags:
|
||||
|
||||
- `--providers-only`: configure `secrets.providers` only, skip credential mapping.
|
||||
- `--skip-provider-setup`: skip provider setup and map credentials to existing providers.
|
||||
- `--agent <id>`: scope `auth-profiles.json` target discovery and writes to one agent store.
|
||||
|
||||
Notes:
|
||||
|
||||
- Requires an interactive TTY.
|
||||
- You cannot combine `--providers-only` with `--skip-provider-setup`.
|
||||
- `configure` targets secret-bearing fields in `openclaw.json`.
|
||||
- Include all secret-bearing fields you intend to migrate (for example both `models.providers.*.apiKey` and `skills.entries.*.apiKey`) so audit can reach a clean state.
|
||||
- `configure` targets secret-bearing fields in `openclaw.json` plus `auth-profiles.json` for the selected agent scope.
|
||||
- `configure` supports creating new `auth-profiles.json` mappings directly in the picker flow.
|
||||
- Canonical supported surface: [SecretRef Credential Surface](/reference/secretref-credential-surface).
|
||||
- It performs preflight resolution before apply.
|
||||
- Generated plans default to scrub options (`scrubEnv`, `scrubAuthProfilesForProviderTargets`, `scrubLegacyAuthJson` all enabled).
|
||||
- Apply path is one-way for migrated plaintext values.
|
||||
- Apply path is one-way for scrubbed plaintext values.
|
||||
- Without `--apply`, CLI still prompts `Apply this plan now?` after preflight.
|
||||
- With `--apply` (and no `--yes`), CLI prompts an extra irreversible-migration confirmation.
|
||||
- With `--apply` (and no `--yes`), CLI prompts an extra irreversible confirmation.
|
||||
|
||||
Exec provider safety note:
|
||||
|
||||
- Homebrew installs often expose symlinked binaries under `/opt/homebrew/bin/*`.
|
||||
- Set `allowSymlinkCommand: true` only when needed for trusted package-manager paths, and pair it with `trustedDirs` (for example `["/opt/homebrew"]`).
|
||||
- On Windows, if ACL verification is unavailable for a provider path, OpenClaw fails closed. For trusted paths only, set `allowInsecurePath: true` on that provider to bypass path security checks.
|
||||
|
||||
## Apply a saved plan
|
||||
|
||||
@@ -154,10 +160,9 @@ Safety comes from strict preflight + atomic-ish apply with best-effort in-memory
|
||||
## Example
|
||||
|
||||
```bash
|
||||
# Audit first, then configure, then confirm clean:
|
||||
openclaw secrets audit --check
|
||||
openclaw secrets configure
|
||||
openclaw secrets audit --check
|
||||
```
|
||||
|
||||
If `audit --check` still reports plaintext findings after a partial migration, verify you also migrated skill keys (`skills.entries.*.apiKey`) and any other reported target paths.
|
||||
If `audit --check` still reports plaintext findings, update the remaining reported target paths and rerun audit.
|
||||
|
||||
@@ -1321,6 +1321,7 @@
|
||||
"pages": [
|
||||
"reference/wizard",
|
||||
"reference/token-use",
|
||||
"reference/secretref-credential-surface",
|
||||
"reference/prompt-caching",
|
||||
"reference/api-usage-costs",
|
||||
"reference/transcript-hygiene",
|
||||
|
||||
@@ -1170,8 +1170,8 @@ Optional **Docker sandboxing** for the embedded agent. See [Sandboxing](/gateway
|
||||
|
||||
**`docker.binds`** mounts additional host directories; global and per-agent binds are merged.
|
||||
|
||||
**Sandboxed browser** (`sandbox.browser.enabled`): Chromium + CDP in a container. noVNC URL injected into system prompt. Does not require `browser.enabled` in main config.
|
||||
noVNC observer access uses VNC auth by default and OpenClaw emits a short-lived token URL that serves a local bootstrap page; noVNC password is passed via URL fragment (instead of URL query).
|
||||
**Sandboxed browser** (`sandbox.browser.enabled`): Chromium + CDP in a container. noVNC URL injected into system prompt. Does not require `browser.enabled` in `openclaw.json`.
|
||||
noVNC observer access uses VNC auth by default and OpenClaw emits a short-lived token URL (instead of exposing the password in the shared URL).
|
||||
|
||||
- `allowHostControl: false` (default) blocks sandboxed sessions from targeting the host browser.
|
||||
- `network` defaults to `openclaw-sandbox-browser` (dedicated bridge network). Set to `bridge` only when you explicitly want global bridge connectivity.
|
||||
@@ -1605,7 +1605,8 @@ Defaults for Talk mode (macOS/iOS/Android).
|
||||
```
|
||||
|
||||
- Voice IDs fall back to `ELEVENLABS_VOICE_ID` or `SAG_VOICE_ID`.
|
||||
- `apiKey` falls back to `ELEVENLABS_API_KEY`.
|
||||
- `apiKey` and `providers.*.apiKey` accept plaintext strings or SecretRef objects.
|
||||
- `ELEVENLABS_API_KEY` fallback applies only when no Talk API key is configured.
|
||||
- `voiceAliases` lets Talk directives use friendly names.
|
||||
|
||||
---
|
||||
@@ -1804,7 +1805,7 @@ Configures inbound media understanding (image/audio/video):
|
||||
|
||||
- `provider`: API provider id (`openai`, `anthropic`, `google`/`gemini`, `groq`, etc.)
|
||||
- `model`: model id override
|
||||
- `profile` / `preferredProfile`: auth profile selection
|
||||
- `profile` / `preferredProfile`: `auth-profiles.json` profile selection
|
||||
|
||||
**CLI entry** (`type: "cli"`):
|
||||
|
||||
@@ -1817,7 +1818,7 @@ Configures inbound media understanding (image/audio/video):
|
||||
- `prompt`, `maxChars`, `maxBytes`, `timeoutSeconds`, `language`: per-entry overrides.
|
||||
- Failures fall back to the next entry.
|
||||
|
||||
Provider auth follows standard order: auth profiles → env vars → `models.providers.*.apiKey`.
|
||||
Provider auth follows standard order: `auth-profiles.json` → env vars → `models.providers.*.apiKey`.
|
||||
|
||||
</Accordion>
|
||||
|
||||
@@ -2638,14 +2639,11 @@ Validation:
|
||||
- `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}$`
|
||||
|
||||
### Supported fields in config
|
||||
### Supported credential surface
|
||||
|
||||
- `models.providers.<provider>.apiKey`
|
||||
- `skills.entries.<skillKey>.apiKey`
|
||||
- `channels.googlechat.serviceAccount`
|
||||
- `channels.googlechat.serviceAccountRef`
|
||||
- `channels.googlechat.accounts.<accountId>.serviceAccount`
|
||||
- `channels.googlechat.accounts.<accountId>.serviceAccountRef`
|
||||
- Canonical matrix: [SecretRef Credential Surface](/reference/secretref-credential-surface)
|
||||
- `secrets apply` targets supported `openclaw.json` credential paths.
|
||||
- `auth-profiles.json` refs are included in runtime resolution and audit coverage.
|
||||
|
||||
### Secret providers config
|
||||
|
||||
@@ -2683,6 +2681,7 @@ Notes:
|
||||
- If `trustedDirs` is configured, the trusted-dir check applies to the resolved target path.
|
||||
- `exec` child environment is minimal by default; pass required variables explicitly with `passEnv`.
|
||||
- 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.
|
||||
|
||||
---
|
||||
|
||||
@@ -2702,8 +2701,8 @@ Notes:
|
||||
}
|
||||
```
|
||||
|
||||
- Per-agent auth profiles stored at `<agentDir>/auth-profiles.json`.
|
||||
- Auth profiles support value-level refs (`keyRef` for `api_key`, `tokenRef` for `token`).
|
||||
- Per-agent profiles are stored at `<agentDir>/auth-profiles.json`.
|
||||
- `auth-profiles.json` supports value-level refs (`keyRef` for `api_key`, `tokenRef` for `token`).
|
||||
- Static runtime credentials come from in-memory resolved snapshots; legacy static `auth.json` entries are scrubbed when discovered.
|
||||
- Legacy OAuth imports from `~/.openclaw/credentials/oauth.json`.
|
||||
- See [OAuth](/concepts/oauth).
|
||||
@@ -2900,7 +2899,7 @@ Split config into multiple files:
|
||||
- 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 (`dirname` of the main config file). Absolute/`../` forms are allowed only when they still resolve inside that boundary.
|
||||
- Paths: resolved relative to the including file, but must stay inside the top-level config directory (`dirname` of `openclaw.json`). Absolute/`../` forms are allowed only when they still resolve inside that boundary.
|
||||
- Errors: clear messages for missing files, parse errors, and circular includes.
|
||||
|
||||
---
|
||||
|
||||
@@ -532,6 +532,7 @@ Rules:
|
||||
```
|
||||
|
||||
SecretRef details (including `secrets.providers` for `env`/`file`/`exec`) are in [Secrets Management](/gateway/secrets).
|
||||
Supported credential paths are listed in [SecretRef Credential Surface](/reference/secretref-credential-surface).
|
||||
</Accordion>
|
||||
|
||||
See [Environment](/help/environment) for full precedence and sources.
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
summary: "Contract for `secrets apply` plans: allowed target paths, validation, and ref-only auth-profile behavior"
|
||||
summary: "Contract for `secrets apply` plans: target validation, path matching, and `auth-profiles.json` target scope"
|
||||
read_when:
|
||||
- Generating or reviewing `openclaw secrets apply` plan files
|
||||
- Generating or reviewing `openclaw secrets apply` plans
|
||||
- Debugging `Invalid plan target path` errors
|
||||
- Understanding how `keyRef` and `tokenRef` influence implicit provider discovery
|
||||
- Understanding target type and path validation behavior
|
||||
title: "Secrets Apply Plan Contract"
|
||||
---
|
||||
|
||||
@@ -11,7 +11,7 @@ title: "Secrets Apply Plan Contract"
|
||||
|
||||
This page defines the strict contract enforced by `openclaw secrets apply`.
|
||||
|
||||
If a target does not match these rules, apply fails before mutating config.
|
||||
If a target does not match these rules, apply fails before mutating configuration.
|
||||
|
||||
## Plan file shape
|
||||
|
||||
@@ -29,29 +29,47 @@ If a target does not match these rules, apply fails before mutating config.
|
||||
providerId: "openai",
|
||||
ref: { source: "env", provider: "default", id: "OPENAI_API_KEY" },
|
||||
},
|
||||
{
|
||||
type: "auth-profiles.api_key.key",
|
||||
path: "profiles.openai:default.key",
|
||||
pathSegments: ["profiles", "openai:default", "key"],
|
||||
agentId: "main",
|
||||
ref: { source: "env", provider: "default", id: "OPENAI_API_KEY" },
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
## Allowed target types and paths
|
||||
## Supported target scope
|
||||
|
||||
| `target.type` | Allowed `target.path` shape | Optional id match rule |
|
||||
| ------------------------------------ | --------------------------------------------------------- | --------------------------------------------------- |
|
||||
| `models.providers.apiKey` | `models.providers.<providerId>.apiKey` | `providerId` must match `<providerId>` when present |
|
||||
| `skills.entries.apiKey` | `skills.entries.<skillKey>.apiKey` | n/a |
|
||||
| `channels.googlechat.serviceAccount` | `channels.googlechat.serviceAccount` | `accountId` must be empty/omitted |
|
||||
| `channels.googlechat.serviceAccount` | `channels.googlechat.accounts.<accountId>.serviceAccount` | `accountId` must match `<accountId>` when present |
|
||||
Plan targets are accepted for supported credential paths in:
|
||||
|
||||
- [SecretRef Credential Surface](/reference/secretref-credential-surface)
|
||||
|
||||
## Target type behavior
|
||||
|
||||
General rule:
|
||||
|
||||
- `target.type` must be recognized and must match the normalized `target.path` shape.
|
||||
|
||||
Compatibility aliases remain accepted for existing plans:
|
||||
|
||||
- `models.providers.apiKey`
|
||||
- `skills.entries.apiKey`
|
||||
- `channels.googlechat.serviceAccount`
|
||||
|
||||
## Path validation rules
|
||||
|
||||
Each target is validated with all of the following:
|
||||
|
||||
- `type` must be one of the allowed target types above.
|
||||
- `type` must be a recognized target type.
|
||||
- `path` must be a non-empty dot path.
|
||||
- `pathSegments` can be omitted. If provided, it must normalize to exactly the same path as `path`.
|
||||
- Forbidden segments are rejected: `__proto__`, `prototype`, `constructor`.
|
||||
- The normalized path must match one of the allowed path shapes for the target type.
|
||||
- If `providerId` / `accountId` is set, it must match the id encoded in the path.
|
||||
- The normalized path must match the registered path shape for the target type.
|
||||
- If `providerId` or `accountId` is set, it must match the id encoded in the path.
|
||||
- `auth-profiles.json` targets require `agentId`.
|
||||
- When creating a new `auth-profiles.json` mapping, include `authProfileProvider`.
|
||||
|
||||
## Failure behavior
|
||||
|
||||
@@ -61,19 +79,12 @@ If a target fails validation, apply exits with an error like:
|
||||
Invalid plan target path for models.providers.apiKey: models.providers.openai.baseUrl
|
||||
```
|
||||
|
||||
No partial mutation is committed for that invalid target path.
|
||||
No writes are committed for an invalid plan.
|
||||
|
||||
## Ref-only auth profiles and implicit providers
|
||||
## Runtime and audit scope notes
|
||||
|
||||
Implicit provider discovery also considers auth profiles that store refs instead of plaintext credentials:
|
||||
|
||||
- `type: "api_key"` profiles can use `keyRef` (for example env-backed refs).
|
||||
- `type: "token"` profiles can use `tokenRef`.
|
||||
|
||||
Behavior:
|
||||
|
||||
- For API-key providers (for example `volcengine`, `byteplus`), ref-only profiles can still activate implicit provider entries.
|
||||
- For `github-copilot`, if the profile has no plaintext token, discovery will try `tokenRef` env resolution before token exchange.
|
||||
- Ref-only `auth-profiles.json` entries (`keyRef`/`tokenRef`) are included in runtime resolution and audit coverage.
|
||||
- `secrets apply` writes supported `openclaw.json` targets, supported `auth-profiles.json` targets, and optional scrub targets.
|
||||
|
||||
## Operator checks
|
||||
|
||||
@@ -85,10 +96,11 @@ openclaw secrets apply --from /tmp/openclaw-secrets-plan.json --dry-run
|
||||
openclaw secrets apply --from /tmp/openclaw-secrets-plan.json
|
||||
```
|
||||
|
||||
If apply fails with an invalid target path message, regenerate the plan with `openclaw secrets configure` or fix the target path to one of the allowed shapes above.
|
||||
If apply fails with an invalid target path message, regenerate the plan with `openclaw secrets configure` or fix the target path to a supported shape above.
|
||||
|
||||
## Related docs
|
||||
|
||||
- [Secrets Management](/gateway/secrets)
|
||||
- [CLI `secrets`](/cli/secrets)
|
||||
- [SecretRef Credential Surface](/reference/secretref-credential-surface)
|
||||
- [Configuration Reference](/gateway/configuration-reference)
|
||||
|
||||
@@ -1,35 +1,70 @@
|
||||
---
|
||||
summary: "Secrets management: SecretRef contract, runtime snapshot behavior, and safe one-way scrubbing"
|
||||
read_when:
|
||||
- Configuring SecretRefs for providers, auth profiles, skills, or Google Chat
|
||||
- Operating secrets reload/audit/configure/apply safely in production
|
||||
- Understanding fail-fast and last-known-good behavior
|
||||
- Configuring SecretRefs for provider credentials and `auth-profiles.json` refs
|
||||
- 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"
|
||||
---
|
||||
|
||||
# Secrets management
|
||||
|
||||
OpenClaw supports additive secret references so credentials do not need to be stored as plaintext in config files.
|
||||
OpenClaw supports additive SecretRefs so supported credentials do not need to be stored as plaintext in configuration.
|
||||
|
||||
Plaintext still works. Secret refs are optional.
|
||||
Plaintext still works. SecretRefs are opt-in per credential.
|
||||
|
||||
## Goals and runtime model
|
||||
|
||||
Secrets are resolved into an in-memory runtime snapshot.
|
||||
|
||||
- Resolution is eager during activation, not lazy on request paths.
|
||||
- Startup fails fast if any referenced credential cannot be resolved.
|
||||
- Reload uses atomic swap: full success or keep last-known-good.
|
||||
- Runtime requests read from the active in-memory snapshot.
|
||||
- Startup fails fast when an effectively active SecretRef cannot be resolved.
|
||||
- Reload uses atomic swap: full success, or keep the last-known-good snapshot.
|
||||
- Runtime requests read from the active in-memory snapshot only.
|
||||
|
||||
This keeps secret-provider outages off the hot request path.
|
||||
This keeps secret-provider outages off hot request paths.
|
||||
|
||||
## Active-surface filtering
|
||||
|
||||
SecretRefs are validated only on effectively active surfaces.
|
||||
|
||||
- Enabled surfaces: unresolved refs block startup/reload.
|
||||
- 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), provider-specific keys are also active for provider auto-detection.
|
||||
- `gateway.remote.token` / `gateway.remote.password` SecretRefs are active (when `gateway.remote.enabled` is not `false`) 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 surface diagnostics
|
||||
|
||||
When a SecretRef is configured on `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.
|
||||
|
||||
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
|
||||
|
||||
When onboarding runs in interactive mode and you choose secret reference storage, OpenClaw performs a fast preflight check before saving:
|
||||
When onboarding runs in interactive mode and you choose SecretRef storage, OpenClaw runs preflight validation before saving:
|
||||
|
||||
- Env refs: validates env var name and confirms a non-empty value is visible during onboarding.
|
||||
- Provider refs (`file` or `exec`): validates the selected provider, resolves the provided `id`, and checks value type.
|
||||
- Provider refs (`file` or `exec`): validates provider selection, resolves `id`, and checks resolved value type.
|
||||
|
||||
If validation fails, onboarding shows the error and lets you retry.
|
||||
|
||||
@@ -122,22 +157,24 @@ Define providers under `secrets.providers`:
|
||||
- `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.
|
||||
|
||||
### Exec provider
|
||||
|
||||
- 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.
|
||||
- Enable `allowSymlinkCommand` only when required for trusted package-manager paths, and pair it with `trustedDirs` (for example `["/opt/homebrew"]`).
|
||||
- When `trustedDirs` is set, checks apply to 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.
|
||||
- Request payload (stdin):
|
||||
- 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.
|
||||
|
||||
Request payload (stdin):
|
||||
|
||||
```json
|
||||
{ "protocolVersion": 1, "provider": "vault", "ids": ["providers/openai/apiKey"] }
|
||||
```
|
||||
|
||||
- Response payload (stdout):
|
||||
Response payload (stdout):
|
||||
|
||||
```json
|
||||
{ "protocolVersion": 1, "values": { "providers/openai/apiKey": "sk-..." } }
|
||||
@@ -242,37 +279,33 @@ Optional per-id errors:
|
||||
}
|
||||
```
|
||||
|
||||
## In-scope fields (v1)
|
||||
## Supported credential surface
|
||||
|
||||
### `~/.openclaw/openclaw.json`
|
||||
Canonical supported and unsupported credentials are listed in:
|
||||
|
||||
- `models.providers.<provider>.apiKey`
|
||||
- `skills.entries.<skillKey>.apiKey`
|
||||
- `channels.googlechat.serviceAccount`
|
||||
- `channels.googlechat.serviceAccountRef`
|
||||
- `channels.googlechat.accounts.<accountId>.serviceAccount`
|
||||
- `channels.googlechat.accounts.<accountId>.serviceAccountRef`
|
||||
- [SecretRef Credential Surface](/reference/secretref-credential-surface)
|
||||
|
||||
### `~/.openclaw/agents/<agentId>/agent/auth-profiles.json`
|
||||
|
||||
- `profiles.<profileId>.keyRef` for `type: "api_key"`
|
||||
- `profiles.<profileId>.tokenRef` for `type: "token"`
|
||||
|
||||
OAuth credential storage changes are out of scope.
|
||||
Runtime-minted or rotating credentials and OAuth refresh material are intentionally excluded from read-only SecretRef resolution.
|
||||
|
||||
## Required behavior and precedence
|
||||
|
||||
- Field without ref: unchanged.
|
||||
- Field with ref: required at activation time.
|
||||
- If plaintext and ref both exist, ref wins at runtime and plaintext is ignored.
|
||||
- Field without a ref: unchanged.
|
||||
- Field with a ref: required on active surfaces during activation.
|
||||
- If both plaintext and ref are present, ref takes precedence on supported precedence paths.
|
||||
|
||||
Warning code:
|
||||
Warning and audit signals:
|
||||
|
||||
- `SECRETS_REF_OVERRIDES_PLAINTEXT`
|
||||
- `SECRETS_REF_OVERRIDES_PLAINTEXT` (runtime warning)
|
||||
- `REF_SHADOWED` (audit finding when `auth-profiles.json` credentials take precedence over `openclaw.json` refs)
|
||||
|
||||
Google Chat compatibility behavior:
|
||||
|
||||
- `serviceAccountRef` takes precedence over plaintext `serviceAccount`.
|
||||
- Plaintext value is ignored when sibling ref is set.
|
||||
|
||||
## Activation triggers
|
||||
|
||||
Secret activation is attempted on:
|
||||
Secret activation runs on:
|
||||
|
||||
- Startup (preflight plus final activation)
|
||||
- Config reload hot-apply path
|
||||
@@ -283,9 +316,9 @@ Activation contract:
|
||||
|
||||
- Success swaps the snapshot atomically.
|
||||
- Startup failure aborts gateway startup.
|
||||
- Runtime reload failure keeps last-known-good snapshot.
|
||||
- Runtime reload failure keeps the last-known-good snapshot.
|
||||
|
||||
## Degraded and recovered operator signals
|
||||
## Degraded and recovered signals
|
||||
|
||||
When reload-time activation fails after a healthy state, OpenClaw enters degraded secrets state.
|
||||
|
||||
@@ -297,13 +330,22 @@ One-shot system event and log codes:
|
||||
Behavior:
|
||||
|
||||
- Degraded: runtime keeps last-known-good snapshot.
|
||||
- Recovered: emitted once after a successful activation.
|
||||
- Recovered: emitted once after the next successful activation.
|
||||
- Repeated failures while already degraded log warnings but do not spam events.
|
||||
- Startup fail-fast does not emit degraded events because no runtime snapshot exists yet.
|
||||
- Startup fail-fast does not emit degraded events because runtime never became active.
|
||||
|
||||
## Command-path resolution
|
||||
|
||||
Credential-sensitive command paths that opt in (for example `openclaw memory` remote-memory paths and `openclaw qr --remote`) can resolve supported SecretRefs via gateway snapshot RPC.
|
||||
|
||||
- When gateway is running, those command paths read from the active snapshot.
|
||||
- If a configured SecretRef is required and gateway is unavailable, command resolution fails fast with actionable diagnostics.
|
||||
- Snapshot refresh after backend secret rotation is handled by `openclaw secrets reload`.
|
||||
- Gateway RPC method used by these command paths: `secrets.resolve`.
|
||||
|
||||
## Audit and configure workflow
|
||||
|
||||
Use this default operator flow:
|
||||
Default operator flow:
|
||||
|
||||
```bash
|
||||
openclaw secrets audit --check
|
||||
@@ -311,26 +353,22 @@ openclaw secrets configure
|
||||
openclaw secrets audit --check
|
||||
```
|
||||
|
||||
Migration completeness:
|
||||
|
||||
- Include `skills.entries.<skillKey>.apiKey` targets when those skills use API keys.
|
||||
- If `audit --check` still reports plaintext findings after a partial migration, migrate the remaining reported paths and rerun audit.
|
||||
|
||||
### `secrets audit`
|
||||
|
||||
Findings include:
|
||||
|
||||
- plaintext values at rest (`openclaw.json`, `auth-profiles.json`, `.env`)
|
||||
- unresolved refs
|
||||
- precedence shadowing (`auth-profiles` taking priority over config refs)
|
||||
- legacy residues (`auth.json`, OAuth out-of-scope reminders)
|
||||
- precedence shadowing (`auth-profiles.json` taking priority over `openclaw.json` refs)
|
||||
- legacy residues (`auth.json`, OAuth reminders)
|
||||
|
||||
### `secrets configure`
|
||||
|
||||
Interactive helper that:
|
||||
|
||||
- configures `secrets.providers` first (`env`/`file`/`exec`, add/edit/remove)
|
||||
- lets you select secret-bearing fields in `openclaw.json`
|
||||
- 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
|
||||
@@ -339,10 +377,11 @@ Helpful modes:
|
||||
|
||||
- `openclaw secrets configure --providers-only`
|
||||
- `openclaw secrets configure --skip-provider-setup`
|
||||
- `openclaw secrets configure --agent <id>`
|
||||
|
||||
`configure` apply defaults to:
|
||||
`configure` apply defaults:
|
||||
|
||||
- scrub matching static creds from `auth-profiles.json` for targeted providers
|
||||
- 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 `<config-dir>/.env`
|
||||
|
||||
@@ -361,26 +400,31 @@ For strict target/path contract details and exact rejection rules, see:
|
||||
|
||||
## One-way safety policy
|
||||
|
||||
OpenClaw intentionally does **not** write rollback backups that contain pre-migration plaintext secret values.
|
||||
OpenClaw intentionally does not write rollback backups containing historical plaintext secret values.
|
||||
|
||||
Safety model:
|
||||
|
||||
- preflight must succeed before write mode
|
||||
- runtime activation is validated before commit
|
||||
- apply updates files using atomic file replacement and best-effort in-memory restore on failure
|
||||
- apply updates files using atomic file replacement and best-effort restore on failure
|
||||
|
||||
## `auth.json` compatibility notes
|
||||
## Legacy auth compatibility notes
|
||||
|
||||
For static credentials, OpenClaw runtime no longer depends on plaintext `auth.json`.
|
||||
For static credentials, runtime no longer depends on plaintext legacy auth storage.
|
||||
|
||||
- Runtime credential source is the resolved in-memory snapshot.
|
||||
- Legacy `auth.json` static `api_key` entries are scrubbed when discovered.
|
||||
- OAuth-related legacy compatibility behavior remains separate.
|
||||
- Legacy static `api_key` entries are scrubbed when discovered.
|
||||
- OAuth-related compatibility behavior remains separate.
|
||||
|
||||
## Web UI note
|
||||
|
||||
Some SecretInput unions are easier to configure in raw editor mode than in form mode.
|
||||
|
||||
## Related docs
|
||||
|
||||
- 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)
|
||||
|
||||
123
docs/reference/secretref-credential-surface.md
Normal file
123
docs/reference/secretref-credential-surface.md
Normal file
@@ -0,0 +1,123 @@
|
||||
---
|
||||
summary: "Canonical supported vs unsupported SecretRef credential surface"
|
||||
read_when:
|
||||
- Verifying SecretRef credential coverage
|
||||
- Auditing whether a credential is eligible for `secrets configure` or `secrets apply`
|
||||
- Verifying why a credential is outside the supported surface
|
||||
title: "SecretRef Credential Surface"
|
||||
---
|
||||
|
||||
# SecretRef credential surface
|
||||
|
||||
This page defines the canonical SecretRef credential surface.
|
||||
|
||||
Scope intent:
|
||||
|
||||
- In scope: strictly user-supplied credentials that OpenClaw does not mint or rotate.
|
||||
- Out of scope: runtime-minted or rotating credentials, OAuth refresh material, and session-like artifacts.
|
||||
|
||||
## Supported credentials
|
||||
|
||||
### `openclaw.json` targets (`secrets configure` + `secrets apply` + `secrets audit`)
|
||||
|
||||
<!-- secretref-supported-list-start -->
|
||||
|
||||
- `models.providers.*.apiKey`
|
||||
- `skills.entries.*.apiKey`
|
||||
- `agents.defaults.memorySearch.remote.apiKey`
|
||||
- `agents.list[].memorySearch.remote.apiKey`
|
||||
- `talk.apiKey`
|
||||
- `talk.providers.*.apiKey`
|
||||
- `messages.tts.elevenlabs.apiKey`
|
||||
- `messages.tts.openai.apiKey`
|
||||
- `tools.web.search.apiKey`
|
||||
- `tools.web.search.gemini.apiKey`
|
||||
- `tools.web.search.grok.apiKey`
|
||||
- `tools.web.search.kimi.apiKey`
|
||||
- `tools.web.search.perplexity.apiKey`
|
||||
- `gateway.auth.password`
|
||||
- `gateway.remote.token`
|
||||
- `gateway.remote.password`
|
||||
- `cron.webhookToken`
|
||||
- `channels.telegram.botToken`
|
||||
- `channels.telegram.webhookSecret`
|
||||
- `channels.telegram.accounts.*.botToken`
|
||||
- `channels.telegram.accounts.*.webhookSecret`
|
||||
- `channels.slack.botToken`
|
||||
- `channels.slack.appToken`
|
||||
- `channels.slack.userToken`
|
||||
- `channels.slack.signingSecret`
|
||||
- `channels.slack.accounts.*.botToken`
|
||||
- `channels.slack.accounts.*.appToken`
|
||||
- `channels.slack.accounts.*.userToken`
|
||||
- `channels.slack.accounts.*.signingSecret`
|
||||
- `channels.discord.token`
|
||||
- `channels.discord.pluralkit.token`
|
||||
- `channels.discord.voice.tts.elevenlabs.apiKey`
|
||||
- `channels.discord.voice.tts.openai.apiKey`
|
||||
- `channels.discord.accounts.*.token`
|
||||
- `channels.discord.accounts.*.pluralkit.token`
|
||||
- `channels.discord.accounts.*.voice.tts.elevenlabs.apiKey`
|
||||
- `channels.discord.accounts.*.voice.tts.openai.apiKey`
|
||||
- `channels.irc.password`
|
||||
- `channels.irc.nickserv.password`
|
||||
- `channels.irc.accounts.*.password`
|
||||
- `channels.irc.accounts.*.nickserv.password`
|
||||
- `channels.bluebubbles.password`
|
||||
- `channels.bluebubbles.accounts.*.password`
|
||||
- `channels.feishu.appSecret`
|
||||
- `channels.feishu.verificationToken`
|
||||
- `channels.feishu.accounts.*.appSecret`
|
||||
- `channels.feishu.accounts.*.verificationToken`
|
||||
- `channels.msteams.appPassword`
|
||||
- `channels.mattermost.botToken`
|
||||
- `channels.mattermost.accounts.*.botToken`
|
||||
- `channels.matrix.password`
|
||||
- `channels.matrix.accounts.*.password`
|
||||
- `channels.nextcloud-talk.botSecret`
|
||||
- `channels.nextcloud-talk.apiPassword`
|
||||
- `channels.nextcloud-talk.accounts.*.botSecret`
|
||||
- `channels.nextcloud-talk.accounts.*.apiPassword`
|
||||
- `channels.zalo.botToken`
|
||||
- `channels.zalo.webhookSecret`
|
||||
- `channels.zalo.accounts.*.botToken`
|
||||
- `channels.zalo.accounts.*.webhookSecret`
|
||||
- `channels.googlechat.serviceAccount` via sibling `serviceAccountRef` (compatibility exception)
|
||||
- `channels.googlechat.accounts.*.serviceAccount` via sibling `serviceAccountRef` (compatibility exception)
|
||||
|
||||
### `auth-profiles.json` targets (`secrets configure` + `secrets apply` + `secrets audit`)
|
||||
|
||||
- `profiles.*.keyRef` (`type: "api_key"`)
|
||||
- `profiles.*.tokenRef` (`type: "token"`)
|
||||
<!-- secretref-supported-list-end -->
|
||||
|
||||
Notes:
|
||||
|
||||
- Auth-profile plan targets require `agentId`.
|
||||
- Plan entries target `profiles.*.key` / `profiles.*.token` and write sibling refs (`keyRef` / `tokenRef`).
|
||||
- Auth-profile refs are included in runtime resolution and audit coverage.
|
||||
- For web search:
|
||||
- In explicit provider mode (`tools.web.search.provider` set), only the selected provider key is active.
|
||||
- In auto mode (`tools.web.search.provider` unset), `tools.web.search.apiKey` and provider-specific keys are active.
|
||||
|
||||
## Unsupported credentials
|
||||
|
||||
Out-of-scope credentials include:
|
||||
|
||||
<!-- secretref-unsupported-list-start -->
|
||||
|
||||
- `gateway.auth.token`
|
||||
- `commands.ownerDisplaySecret`
|
||||
- `channels.matrix.accessToken`
|
||||
- `channels.matrix.accounts.*.accessToken`
|
||||
- `hooks.token`
|
||||
- `hooks.gmail.pushToken`
|
||||
- `hooks.mappings[].sessionKey`
|
||||
- `auth-profiles.oauth.*`
|
||||
- `discord.threadBindings.*.webhookToken`
|
||||
- `whatsapp.creds.json`
|
||||
<!-- secretref-unsupported-list-end -->
|
||||
|
||||
Rationale:
|
||||
|
||||
- These credentials are minted, rotated, session-bearing, or OAuth-durable classes that do not fit read-only external SecretRef resolution.
|
||||
480
docs/reference/secretref-user-supplied-credentials-matrix.json
Normal file
480
docs/reference/secretref-user-supplied-credentials-matrix.json
Normal file
@@ -0,0 +1,480 @@
|
||||
{
|
||||
"version": 1,
|
||||
"matrixId": "strictly-user-supplied-credentials",
|
||||
"pathSyntax": "Dot path with \"*\" for map keys and \"[]\" for arrays.",
|
||||
"scope": "Credentials that are strictly user-supplied and not minted/rotated by OpenClaw runtime.",
|
||||
"excludedMutableOrRuntimeManaged": [
|
||||
"commands.ownerDisplaySecret",
|
||||
"channels.matrix.accessToken",
|
||||
"channels.matrix.accounts.*.accessToken",
|
||||
"gateway.auth.token",
|
||||
"hooks.token",
|
||||
"hooks.gmail.pushToken",
|
||||
"hooks.mappings[].sessionKey",
|
||||
"auth-profiles.oauth.*",
|
||||
"discord.threadBindings.*.webhookToken",
|
||||
"whatsapp.creds.json"
|
||||
],
|
||||
"entries": [
|
||||
{
|
||||
"id": "agents.defaults.memorySearch.remote.apiKey",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "agents.defaults.memorySearch.remote.apiKey",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "agents.list[].memorySearch.remote.apiKey",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "agents.list[].memorySearch.remote.apiKey",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "auth-profiles.api_key.key",
|
||||
"configFile": "auth-profiles.json",
|
||||
"path": "profiles.*.key",
|
||||
"refPath": "profiles.*.keyRef",
|
||||
"when": {
|
||||
"type": "api_key"
|
||||
},
|
||||
"secretShape": "sibling_ref",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "auth-profiles.token.token",
|
||||
"configFile": "auth-profiles.json",
|
||||
"path": "profiles.*.token",
|
||||
"refPath": "profiles.*.tokenRef",
|
||||
"when": {
|
||||
"type": "token"
|
||||
},
|
||||
"secretShape": "sibling_ref",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.bluebubbles.accounts.*.password",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.bluebubbles.accounts.*.password",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.bluebubbles.password",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.bluebubbles.password",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.discord.accounts.*.pluralkit.token",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.discord.accounts.*.pluralkit.token",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.discord.accounts.*.token",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.discord.accounts.*.token",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.discord.accounts.*.voice.tts.elevenlabs.apiKey",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.discord.accounts.*.voice.tts.elevenlabs.apiKey",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.discord.accounts.*.voice.tts.openai.apiKey",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.discord.accounts.*.voice.tts.openai.apiKey",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.discord.pluralkit.token",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.discord.pluralkit.token",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.discord.token",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.discord.token",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.discord.voice.tts.elevenlabs.apiKey",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.discord.voice.tts.elevenlabs.apiKey",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.discord.voice.tts.openai.apiKey",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.discord.voice.tts.openai.apiKey",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.feishu.accounts.*.appSecret",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.feishu.accounts.*.appSecret",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.feishu.accounts.*.verificationToken",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.feishu.accounts.*.verificationToken",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.feishu.appSecret",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.feishu.appSecret",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.feishu.verificationToken",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.feishu.verificationToken",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.googlechat.accounts.*.serviceAccount",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.googlechat.accounts.*.serviceAccount",
|
||||
"refPath": "channels.googlechat.accounts.*.serviceAccountRef",
|
||||
"secretShape": "sibling_ref",
|
||||
"optIn": true,
|
||||
"notes": "Google Chat compatibility exception: sibling ref field remains canonical."
|
||||
},
|
||||
{
|
||||
"id": "channels.googlechat.serviceAccount",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.googlechat.serviceAccount",
|
||||
"refPath": "channels.googlechat.serviceAccountRef",
|
||||
"secretShape": "sibling_ref",
|
||||
"optIn": true,
|
||||
"notes": "Google Chat compatibility exception: sibling ref field remains canonical."
|
||||
},
|
||||
{
|
||||
"id": "channels.irc.accounts.*.nickserv.password",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.irc.accounts.*.nickserv.password",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.irc.accounts.*.password",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.irc.accounts.*.password",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.irc.nickserv.password",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.irc.nickserv.password",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.irc.password",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.irc.password",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.matrix.accounts.*.password",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.matrix.accounts.*.password",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.matrix.password",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.matrix.password",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.mattermost.accounts.*.botToken",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.mattermost.accounts.*.botToken",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.mattermost.botToken",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.mattermost.botToken",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.msteams.appPassword",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.msteams.appPassword",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.nextcloud-talk.accounts.*.apiPassword",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.nextcloud-talk.accounts.*.apiPassword",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.nextcloud-talk.accounts.*.botSecret",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.nextcloud-talk.accounts.*.botSecret",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.nextcloud-talk.apiPassword",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.nextcloud-talk.apiPassword",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.nextcloud-talk.botSecret",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.nextcloud-talk.botSecret",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.slack.accounts.*.appToken",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.slack.accounts.*.appToken",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.slack.accounts.*.botToken",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.slack.accounts.*.botToken",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.slack.accounts.*.signingSecret",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.slack.accounts.*.signingSecret",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.slack.accounts.*.userToken",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.slack.accounts.*.userToken",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.slack.appToken",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.slack.appToken",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.slack.botToken",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.slack.botToken",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.slack.signingSecret",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.slack.signingSecret",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.slack.userToken",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.slack.userToken",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.telegram.accounts.*.botToken",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.telegram.accounts.*.botToken",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.telegram.accounts.*.webhookSecret",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.telegram.accounts.*.webhookSecret",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.telegram.botToken",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.telegram.botToken",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.telegram.webhookSecret",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.telegram.webhookSecret",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.zalo.accounts.*.botToken",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.zalo.accounts.*.botToken",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.zalo.accounts.*.webhookSecret",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.zalo.accounts.*.webhookSecret",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.zalo.botToken",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.zalo.botToken",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "channels.zalo.webhookSecret",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "channels.zalo.webhookSecret",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "cron.webhookToken",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "cron.webhookToken",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "gateway.auth.password",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "gateway.auth.password",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "gateway.remote.password",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "gateway.remote.password",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "gateway.remote.token",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "gateway.remote.token",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "messages.tts.elevenlabs.apiKey",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "messages.tts.elevenlabs.apiKey",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "messages.tts.openai.apiKey",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "messages.tts.openai.apiKey",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "models.providers.*.apiKey",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "models.providers.*.apiKey",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "skills.entries.*.apiKey",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "skills.entries.*.apiKey",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "talk.apiKey",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "talk.apiKey",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "talk.providers.*.apiKey",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "talk.providers.*.apiKey",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "tools.web.search.apiKey",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "tools.web.search.apiKey",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "tools.web.search.gemini.apiKey",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "tools.web.search.gemini.apiKey",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "tools.web.search.grok.apiKey",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "tools.web.search.grok.apiKey",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "tools.web.search.kimi.apiKey",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "tools.web.search.kimi.apiKey",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
},
|
||||
{
|
||||
"id": "tools.web.search.perplexity.apiKey",
|
||||
"configFile": "openclaw.json",
|
||||
"path": "tools.web.search.perplexity.apiKey",
|
||||
"secretShape": "secret_input",
|
||||
"optIn": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
summary: "Web search + fetch tools (Brave Search API, Perplexity direct/OpenRouter, Gemini Google Search grounding)"
|
||||
summary: "Web search + fetch tools (Brave, Perplexity, Gemini, Grok, and Kimi providers)"
|
||||
read_when:
|
||||
- You want to enable web_search or web_fetch
|
||||
- You need Brave Search API key setup
|
||||
@@ -12,7 +12,7 @@ title: "Web Tools"
|
||||
|
||||
OpenClaw ships two lightweight web tools:
|
||||
|
||||
- `web_search` — Search the web via Brave Search API (default), Perplexity Sonar, or Gemini with Google Search grounding.
|
||||
- `web_search` — Search the web via Brave Search API (default), Perplexity Sonar, Gemini with Google Search grounding, Grok, or Kimi.
|
||||
- `web_fetch` — HTTP fetch + readable extraction (HTML → markdown/text).
|
||||
|
||||
These are **not** browser automation. For JS-heavy sites or logins, use the
|
||||
@@ -36,6 +36,8 @@ These are **not** browser automation. For JS-heavy sites or logins, use the
|
||||
| **Brave** (default) | Fast, structured results, free tier | Traditional search results | `BRAVE_API_KEY` |
|
||||
| **Perplexity** | AI-synthesized answers, citations, real-time | Requires Perplexity or OpenRouter access | `OPENROUTER_API_KEY` or `PERPLEXITY_API_KEY` |
|
||||
| **Gemini** | Google Search grounding, AI-synthesized | Requires Gemini API key | `GEMINI_API_KEY` |
|
||||
| **Grok** | xAI web-grounded responses | Requires xAI API key | `XAI_API_KEY` |
|
||||
| **Kimi** | Moonshot web search capability | Requires Moonshot API key | `KIMI_API_KEY` / `MOONSHOT_API_KEY` |
|
||||
|
||||
See [Brave Search setup](/brave-search) and [Perplexity Sonar](/perplexity) for provider-specific details.
|
||||
|
||||
@@ -43,10 +45,11 @@ See [Brave Search setup](/brave-search) and [Perplexity Sonar](/perplexity) for
|
||||
|
||||
If no `provider` is explicitly set, OpenClaw auto-detects which provider to use based on available API keys, checking in this order:
|
||||
|
||||
1. **Brave** — `BRAVE_API_KEY` env var or `search.apiKey` config
|
||||
2. **Gemini** — `GEMINI_API_KEY` env var or `search.gemini.apiKey` config
|
||||
3. **Perplexity** — `PERPLEXITY_API_KEY` / `OPENROUTER_API_KEY` env var or `search.perplexity.apiKey` config
|
||||
4. **Grok** — `XAI_API_KEY` env var or `search.grok.apiKey` config
|
||||
1. **Brave** — `BRAVE_API_KEY` env var or `tools.web.search.apiKey` config
|
||||
2. **Gemini** — `GEMINI_API_KEY` env var or `tools.web.search.gemini.apiKey` config
|
||||
3. **Kimi** — `KIMI_API_KEY` / `MOONSHOT_API_KEY` env var or `tools.web.search.kimi.apiKey` config
|
||||
4. **Perplexity** — `PERPLEXITY_API_KEY` / `OPENROUTER_API_KEY` env var or `tools.web.search.perplexity.apiKey` config
|
||||
5. **Grok** — `XAI_API_KEY` env var or `tools.web.search.grok.apiKey` config
|
||||
|
||||
If no keys are found, it falls back to Brave (you'll get a missing-key error prompting you to configure one).
|
||||
|
||||
@@ -59,7 +62,7 @@ Set the provider in config:
|
||||
tools: {
|
||||
web: {
|
||||
search: {
|
||||
provider: "brave", // or "perplexity" or "gemini"
|
||||
provider: "brave", // or "perplexity" or "gemini" or "grok" or "kimi"
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -208,6 +211,9 @@ Search the web using your configured provider.
|
||||
- API key for your chosen provider:
|
||||
- **Brave**: `BRAVE_API_KEY` or `tools.web.search.apiKey`
|
||||
- **Perplexity**: `OPENROUTER_API_KEY`, `PERPLEXITY_API_KEY`, or `tools.web.search.perplexity.apiKey`
|
||||
- **Gemini**: `GEMINI_API_KEY` or `tools.web.search.gemini.apiKey`
|
||||
- **Grok**: `XAI_API_KEY` or `tools.web.search.grok.apiKey`
|
||||
- **Kimi**: `KIMI_API_KEY`, `MOONSHOT_API_KEY`, or `tools.web.search.kimi.apiKey`
|
||||
|
||||
### Config
|
||||
|
||||
|
||||
Reference in New Issue
Block a user