Secrets
db_pass = secret(os.env("DB_PASS"))
print(db_pass) # [secret]
print("pw: {db_pass}") # pw: [secret]
sql.connect("pg://u:{reveal(db_pass)}@host/db")
What secret does
It wraps a credential so the wrapper renders as [secret] through every stringifying sink: print, interpolation, std.log, ECKO_TRACE, error messages, templates, and JSON encoding.
A wrapped value cannot leak into a log line by accident. That is the whole design goal - not to protect against an attacker with memory access, but against the ordinary path where a credential ends up in a log because someone printed a config map.
reveal is the only way out
reveal(db_pass) # the inner value
is_secret(v) # test for the wrapper
One function, so grep reveal audits every exposure point in a codebase. That auditability is why there is exactly one accessor and no convenience alternatives.
ecko check reports a credential used where the plain value is required without reveal, at error level - so the mistake stops the program rather than sending a literal [secret] to your database.
The rules
"x" + sis a type error. Usereveal. Concatenation would defeat the
wrapper silently.
- Secrets compare equal only to other secrets with equal contents. `s ==
"raw" is always false`, so a comparison cannot be used to probe the value.
error(secret(x))renders redacted whilecatchstill binds the wrapped
value, so an error path keeps the guard.
What it is not
Redaction is a leak guard for honest code paths, not encryption. The value lives in memory in the clear and reveal is unrestricted. A program that wants the value can have it; the point is that a program that does not want it cannot get it by accident.
Where secrets come from
The environment, and nowhere else:
import std.os
key = secret(os.env("API_KEY"))
Not a committed file - including not the environment block of ecko.json, which is committed. std.config can declare fields as secret so they arrive wrapped.
Tracing and prompts
A revealed value interpolated into an ai prompt lands in the trace file in the clear, because the trace records prompts verbatim. Wrapping protects the value until you reveal it; after that it is data like any other. Treat trace output like any log.