feat(secrets): egress-time credential injection with process-local sentinels (#102009)

* feat(secrets): resolve SecretRef model credentials at egress via process-local sentinels

SecretRef-managed model-provider credentials now travel as opaque
oc-sent-v1 sentinels through auth storage, stream options, and SDK
config; the guarded model fetch injects real values into headers and
URLs immediately before the SSRF-guarded send and fails closed on
unknown sentinels. packages/ai adapters converge on the host guarded
fetch where the SDK supports custom fetch and unwrap at construction
where it does not. Resolved values (and their percent-encoded forms)
register for exact-value log redaction. Kill switch:
OPENCLAW_SECRET_SENTINELS=off. Also fixes a pre-existing unhandled
rejection race in capNonOkResponseBodyLazily (pipeThrough writer leak).

* test(plugin-sdk): update public surface budget
This commit is contained in:
Peter Steinberger 2026-07-08 12:56:41 +01:00 committed by GitHub
parent 83ebbcb3ac
commit 4bf70be01a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
67 changed files with 2959 additions and 234 deletions

View file

@ -3710,6 +3710,7 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
- Route: /gateway/secrets
- Headings:
- H2: Runtime model
- H2: Egress-time injection (sentinels)
- H2: Agent-access boundary
- H2: Active-surface filtering
- H2: Gateway auth surface diagnostics

View file

@ -24,10 +24,25 @@ Plaintext credentials remain agent-readable if they sit in files the agent can i
- Startup fails fast when an effectively active SecretRef cannot be resolved.
- Reload is an atomic swap: full success, or keep the last-known-good snapshot.
- Policy violations (for example an OAuth-mode auth profile combined with SecretRef input) fail activation before the runtime swap.
- Runtime requests read only the active in-memory snapshot. Outbound delivery paths (Discord reply/thread delivery, Telegram action sends) also read that snapshot and do not re-resolve refs per send.
- Runtime requests read only the active in-memory snapshot. Model-provider SecretRef credentials pass through auth storage and stream options as process-local sentinels until egress. Outbound delivery paths (Discord reply/thread delivery, Telegram action sends) also read that snapshot and do not re-resolve refs per send.
This keeps secret-provider outages off hot request paths.
## Egress-time injection (sentinels)
For model-provider credentials backed by SecretRefs, OpenClaw mints an opaque, process-local sentinel during model-auth resolution. Auth storage, stream options, SDK configuration, logs, error objects, and most runtime introspection therefore see a value such as `oc-sent-v1-...`, not the provider credential. The guarded model fetch and managed local-provider health probes replace known sentinels in URL and header values immediately before each request leaves the process.
Unknown sentinel-shaped values fail closed before network activity. OpenClaw refuses to send the request rather than forwarding an unresolved sentinel to a provider. Resolved secret values are also registered for exact-value log redaction as a defense in depth measure.
Provider adapters use the latest injection point their SDK supports:
- SDKs with a custom fetch option receive OpenClaw's guarded fetch, so the SDK retains the sentinel.
- SDKs without a custom fetch option unwrap the sentinel immediately before client construction. Plugin-owned provider streams and agent harnesses unwrap at the final core-owned handoff because those transports do not share OpenClaw's guarded fetch.
Sentinels reduce plaintext exposure across the model-call chain, but they are not process isolation. The real value still exists in same-process memory and appears at the final adapter boundary. Plain environment credentials that are not configured through SecretRefs remain plaintext and are outside this mechanism.
Set `OPENCLAW_SECRET_SENTINELS=off` (also accepts `0` or `false`, case-insensitive) to disable sentinel minting during incident response or compatibility troubleshooting. The kill switch does not disable exact-value redaction registration.
## Agent-access boundary
SecretRefs stop credentials from being persisted in config and generated model files, but they are not a process-isolation boundary. A plaintext credential left on disk in a path the agent can read is still readable via file or shell tools, bypassing API-level redaction.