secret
stdlib/core/secret.hk: Secret, material the renderers refuse to print.
A private key or a token leaks through the machinery that exists to be helpful. dbg! and the crash-time actor-state dump render a value structurally, from its layout, and leaving Display off a type does not stop either of them. A derived Encode walks every field, and a serialised config would hold the key with nothing at the call site to say so. And == on bytes returns at the first difference, and the obvious comparison hands back the material one byte at a time.
Wrapping the material in a Secret closes those three. The renderers print <redacted> from the layout alone, a derive refuses the field outright in place of including it unannounced, and there is no Eq to reach for.
Reading the material back is reveal, and it is meant to be the greppable act it looks like: one call site per place the secret has to be used.
Comparing two secrets means revealing both and comparing in constant time, through sha2.constant_time_eq? in extra. It takes bytes, and a Secret<string> needs .to_bytes() on each revealed side.
Three limits are real, and stated here:
- A closure is a hole. The Sendable rule reads a type, and a closure's captures are no part of one, and a handler taking
() -> stringaccepts one that closes over a secret and reveals it. Writing the type does not help either:() -> Secret<T>cannot be told from a factory that mints a fresh secret in the receiver. Resources and futures share this gap. - Nothing wipes the material. A Hanki value has no destructor, and the heap pool clears a cell when it hands the cell out and not when the value dies, and the bytes outlive the last handle by an unbounded window. A
Secretnarrows who reads the material, never how long it exists. - A
Secretis not Sendable. An actor send deep-copies its argument into the receiving heap, which would leave a second copy the sender can no longer account for, and a handler parameter, return orthrowstype carrying one is a compile error (H0635). Keep the secret in an actor'sstate, the way a resource is kept.
Secret
opaque Secret<T>
value: T
end
Material that must not be rendered, serialised, or compared by accident. T is the material's own type: bytes for a private key, string for a token. A Secret has no traits at all, and an aggregate holding one cannot derive Display, Encode, Eq, or Hash either.
impl<T> Secret<T>
hide
def hide(value: T) -> Secret<T>
Wrap value where the renderers and the derives cannot reach it.
Secret.hide("hunter2").reveal() => "hunter2"
reveal
def reveal(self) -> T
The material itself. Every call marks a place the secret exists in the clear, which is the reason it is spelled out and never implicit.
Secret.hide(42).reveal() => 42