feat(secrets): add SQLite-backed secret store (#121559)

This commit is contained in:
Peter Steinberger
2026-08-10 07:08:40 -07:00
committed by GitHub
parent b121219a44
commit f4bac99a81
81 changed files with 4455 additions and 2611 deletions
+2 -2
View File
@@ -224,7 +224,7 @@ Provider builder targets must use `secrets.providers.<alias>` as the path.
<AccordionGroup>
<Accordion title="Common flags">
- `--provider-source <env|file|exec>`
- `--provider-source <env|file|exec|store>`
- `--provider-timeout-ms <ms>` (`file`, `exec`)
</Accordion>
@@ -443,7 +443,7 @@ openclaw config set channels.discord.token \
<Accordion title="If dry-run fails">
- `config schema validation failed`: your post-change config shape is invalid; fix the path/value or provider/ref object shape.
- `Config policy validation failed: unsupported SecretRef usage`: move that credential back to plaintext/string input; keep SecretRefs on supported surfaces only.
- `SecretRef assignment(s) could not be resolved`: the referenced provider/ref cannot currently resolve (missing env var, invalid file pointer, exec provider failure, or provider/source mismatch).
- `SecretRef assignment(s) could not be resolved`: the referenced provider/ref cannot currently resolve (missing env/store name, invalid file pointer, exec provider failure, or provider/source mismatch).
- `model reference validation failed`: a changed text-model primary or fallback is unknown; run `openclaw models list` and choose an available model.
- `Dry run note: skipped <n> exec SecretRef resolvability check(s)`: rerun with `--allow-exec` if you need exec resolvability validation.
- For batch mode, fix failing entries and rerun `--dry-run` before writing.
+1 -1
View File
@@ -264,7 +264,7 @@ openclaw onboard --non-interactive \
--accept-risk
```
With `--secret-input-mode ref`, onboarding stores new credentials as env-backed refs instead of plaintext: auth profiles use `keyRef: { source: "env", provider: "default", id: <envVar> }`, and custom providers use `models.providers.<id>.apiKey` (for example `{ source: "env", provider: "default", id: "CUSTOM_API_KEY" }`). Set the provider env var when adding a new credential; an inline key flag without its matching env var fails fast. Existing resolvable named auth profiles and their `env`, `file`, or `exec` references are reused unchanged, without a new `apiKey` or `keyRef` write or additional provider env var. Existing plaintext profile credentials are not migrated; run `openclaw secrets configure --apply`, then `openclaw secrets audit --check`. See [Secrets management](/gateway/secrets).
With `--secret-input-mode ref`, onboarding stores new credentials as env-backed refs instead of plaintext: auth profiles use `keyRef: { source: "env", provider: "default", id: <envVar> }`, and custom providers use `models.providers.<id>.apiKey` (for example `{ source: "env", provider: "default", id: "CUSTOM_API_KEY" }`). Set the provider env var when adding a new credential; an inline key flag without its matching env var fails fast. Existing resolvable named auth profiles and their `env`, `file`, `exec`, or `store` references are reused unchanged, without a new `apiKey` or `keyRef` write or additional provider env var. Existing plaintext profile credentials are not migrated; run `openclaw secrets configure --apply`, then `openclaw secrets audit --check`. See [Secrets management](/gateway/secrets).
### Gateway auth (non-interactive)
+1 -1
View File
@@ -43,7 +43,7 @@ status 1. If no recent session matches, it suggests the picker and
configured Gateway is unavailable, start or repair it and rerun the command.
`resume` resolves configured Gateway auth SecretRefs for token/password auth
when possible (`env`/`file`/`exec` providers).
when possible (`env`/`file`/`exec`/`store` providers).
Gateway target precedence is explicit `--url`, then `OPENCLAW_GATEWAY_URL`,
then `gateway.remote.url` when `gateway.mode` is `remote`, then the local
+83 -3
View File
@@ -1,7 +1,8 @@
---
summary: "CLI reference for `openclaw secrets` (reload, audit, configure, apply)"
summary: "CLI reference for `openclaw secrets` (store, reload, audit, configure, apply)"
read_when:
- Re-resolving secret refs at runtime
- Managing team-scoped values in the shared secret store
- Auditing plaintext residues and unresolved refs
- Configuring SecretRefs and applying one-way scrub changes
title: "Secrets"
@@ -14,6 +15,7 @@ Manage SecretRefs and keep the active runtime snapshot healthy.
| Command | Role |
| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `reload` | Gateway RPC (`secrets.reload`): re-resolves refs and atomically publishes the owner-aware runtime snapshot (no config writes); eligible owner failures may publish as cold or stale warnings |
| `store` | Manages team-scoped secret and environment values in the local shared state SQLite database |
| `audit` | Read-only scan of config/auth/generated-model stores and legacy residues for plaintext, unresolved refs, and precedence drift (exec refs skipped unless `--allow-exec`) |
| `configure` | Interactive planner for provider setup, target mapping, and preflight (requires a TTY) |
| `apply` | Executes a saved plan (`--dry-run` validates only and skips exec checks by default; write mode rejects exec-containing plans unless `--allow-exec`), then scrubs targeted plaintext residues |
@@ -35,9 +37,86 @@ Exit codes for CI/gates:
- `audit --check` returns `1` on findings.
- Unresolved refs return `2` (regardless of `--check`).
- Store validation and disclosure-policy failures return `2`; `store get` returns `3` when the name is missing.
Related: [Secrets Management](/gateway/secrets) · [1Password plugin](/plugins/onepassword) · [SecretRef Credential Surface](/reference/secretref-credential-surface) · [Security](/gateway/security)
## Shared secret store
`openclaw secrets store` writes directly to the local shared state database. The store is Gateway-wide and team-scoped; this release accepts only `--scope team`. `--scope me` is rejected because identity scope arrives with the settings UI.
```bash
openclaw secrets store list
openclaw secrets store set <NAME>
openclaw secrets store get <NAME>
openclaw secrets store rm <NAME>...
openclaw secrets store import [--from <file>]
```
Names must match `^[A-Z][A-Z0-9_]{0,127}$`. Values are limited to 64 KiB (65,536 UTF-8 bytes). `--kind secret|env` overrides automatic kind detection; otherwise names ending in common credential suffixes such as `_API_KEY`, `_TOKEN`, `_PASSWORD`, `_PRIVATE_KEY`, or `_SECRET` become `secret`, and other names become `env`.
### Set values safely
`--value` is accepted only when the resolved kind is `env`:
```bash
openclaw secrets store set LOG_LEVEL --kind env --value debug
```
For `secret` values, `--value` is refused with exit code `2` because command-line arguments can leak through shell history and process listings. Use one of the three safe inputs instead:
- Pipe stdin when stdin is not a TTY.
- Pass `--value-file <path>`; `--value-file -` means stdin.
- Run interactively and enter the value in the no-echo prompt.
Examples:
```bash
op read 'op://Engineering/OpenAI/apiKey' | \
openclaw secrets store set OPENAI_API_KEY --kind secret
openclaw secrets store set TLS_PRIVATE_KEY \
--kind secret \
--value-file ./client-key.pem
```
`set` is idempotent and updates an existing name. Add `--dry-run` to validate and preview the operation without writing. A successful write reminds you to run `openclaw secrets reload` before a config-referenced value can take effect.
### Read values
```bash
openclaw secrets store list --json
openclaw secrets store list --plain
openclaw secrets store get LOG_LEVEL
```
Secret values never appear in human, `--json`, or `--plain` output. `store get` refuses a `secret` entry as write-only by design and exits `2`; it exits `3` when the name does not exist. Environment-kind values are readable.
### Remove values
```bash
openclaw secrets store rm OLD_TOKEN
openclaw secrets store rm OLD_TOKEN LEGACY_PASSWORD --yes
openclaw secrets store rm OLD_TOKEN --dry-run
```
Removal is idempotent, so a missing name succeeds quietly. Without `--yes`, the CLI asks for confirmation. Removed rows are soft-deleted and purged after 30 days.
### Import dotenv files
Import dotenv-format assignments from a regular file or stdin:
```bash
openclaw secrets store import --from .env
openclaw secrets store import --from .env --dry-run
openclaw secrets store import --from .env --yes
op read 'op://Engineering/service-account/dotenv' | openclaw secrets store import --yes
```
The importer supports quoted values and multiline quoted values such as PEM keys. Use `--yes` to skip confirmation and `--dry-run` to inspect the import without writing. Kind detection follows the same name-based rule as `store set`.
The store commands do not accept `--url` or `--token`; Gateway RPC methods are not part of this storage layer.
## Reload runtime snapshot
```bash
@@ -57,6 +136,7 @@ Scans OpenClaw state for:
- plaintext secret storage
- unresolved refs
- precedence drift (`auth-profiles.json` credentials shadowing `openclaw.json` refs)
- store residue (a team store value duplicated by plaintext in `openclaw.json`)
- generated `agents/*/agent/models.json` residues (provider `apiKey` values and sensitive provider headers)
- legacy residues (legacy auth store entries, OAuth reminders)
@@ -75,8 +155,8 @@ Report shape:
- `status`: `clean | findings | unresolved`
- `resolution`: `refsChecked`, `skippedExecRefs`, `resolvabilityComplete`
- `summary`: `plaintextCount`, `unresolvedRefCount`, `shadowedRefCount`, `legacyResidueCount`
- finding codes: `PLAINTEXT_FOUND`, `REF_UNRESOLVED`, `REF_SHADOWED`, `LEGACY_RESIDUE`
- `summary`: `plaintextCount`, `unresolvedRefCount`, `shadowedRefCount`, `storeResidueCount`, `legacyResidueCount`
- finding codes: `PLAINTEXT_FOUND`, `REF_UNRESOLVED`, `REF_SHADOWED`, `STORE_PLAINTEXT_RESIDUE`, `LEGACY_RESIDUE`
## Configure (interactive helper)
+1 -1
View File
@@ -67,7 +67,7 @@ Aliases: `openclaw chat` and `openclaw terminal` invoke this command with
ambiguous, the CLI prints candidate names and longer ID prefixes without
attaching to either session.
- With no URL/host target or explicit `--url`, `tui` resolves configured Gateway
auth SecretRefs for token/password auth when possible (`env`/`file`/`exec`
auth SecretRefs for token/password auth when possible (`env`/`file`/`exec`/`store`
providers).
- With no explicit URL or port, `tui` follows the active local Gateway port
recorded by the running Gateway. Explicit `--url`, `OPENCLAW_GATEWAY_URL`,