Status: executing. One tool reads environment-backed secrets through
secrets::SecretRuntime; one RuntimeChecked guarantee (secrets.trace_never_carries_value) governs what reaches traces.
import "./std/secrets" use secret_read
agent main() -> Result<String, String>:
key = secret_read("ANTHROPIC_API_KEY")?
if not key.present:
return Err("set ANTHROPIC_API_KEY")
return Ok(key.value)
Reads the environment variable name. A MISSING secret is Ok with
present: false — absence is a modeled state, not an error. Err is
reserved for recoverable failures (empty name, unreadable variable).
Three properties hold together, and each is test-pinned:
- The program receives the real value —
envelope.valueis the live secret, because programs need it to work. - The trace never does — the recorded ToolResult carries a
redacted copy:
valuereplaced by the<redacted:XY>marker (final two characters — enough to correlate, never enough to recover) andvalue_redacted: true. - Replay re-reads the live environment — Substitute-mode replay
re-executes the env read instead of substituting (there is
nothing usable in the trace to substitute). The same
read-passthrough rule
db_queryfollows: env reads are process-internal inputs and cannot escape. If the environment differs at replay time, the run diverges HONESTLY instead of replaying a value the trace never stored.
A secret the program forwards into ANOTHER tool's arguments (an HTTP
header, a request body) is recorded by that tool's own trace events.
The structural fix — an opaque SecretHandle value that never
serializes (the DbHandle pattern), accepted by consuming surfaces —
is the tracked post-v1.0 deepening. Until then: pass secrets to as
few tools as possible, and prefer host-side injection for transport
credentials.
core-semantics.md— thesecrets.trace_never_carries_valueguarantee row.corvid tour --topic replay-safe-secrets— runnable demo.crates/corvid-runtime/src/secrets.rs—SecretRuntime+ the guarantee anchor.std/secrets.cor— the tool declaration + envelope.