Guarding Secrets & Credentials
When you deployed to the target sites, one line carried a promise we hadn't yet kept:
by Mansour Ayouni · in the repository since 2026-07-22 · series · the security series · area Security & governance
How Softanza holds a key without ever letting your program see it in the clear
oProdApi.SetAuthRefQ("env/DEPLOY_KEY")illustration
We said this was a reference, not a key — a pointer the config could carry safely,
with the real credential resolved "only at the moment of transfer." This narration
makes that real. It introduces stzSecret — a value that hides itself, resolves
lazily, and reveals its plaintext only to an actor entitled to cause effects.
Every code block is real, and every output block is its actual output.
A secret is a pointer, not a value
You don't put a key into a stzSecret. You tell the secret where the key lives:
oKey = new stzSecret("deploy-key")
oKey.FromEnvQ("DEPLOY_KEY") # the value lives in an environment variable
? oKey.Descriptor()run on 2026-10-08 20:41 at commit 4a184e6f0: kept its promise
<secret 'deploy-key' (secret) from env:DEPLOY_KEY>
That descriptor is the secret's only public face. It names the secret and points
at its source — env:DEPLOY_KEY — but the value is nowhere in it. This is deliberate:
the object's own string form is the redaction, so there is no code path — no log
line, no dump, no Show() — that can print the key by accident. Even a literal
secret (FromLiteralQ, for dev) reads from literal, never the value; the inline
marker lives only in SourceLocator().
The sources are a small, honest set:
| Builder | The value comes from |
|---|---|
FromLiteralQ(v) |
an inline value — dev/test only |
FromEnvQ(name) |
the environment variable name |
FromFileQ(path) |
the file at path, trimmed |
FromVaultQ(ref) |
a vault (reserved — raises until wired) |
Revealing is governed — and that is the whole point
To get the plaintext you must ask, and who asks decides the answer. The gate is the same one the delivery plane uses to decide whether a plan may commit: an actor may reveal a secret only if it can cause effects.
oHuman = HumanActor("ops") # effectful, trusted
oLLM = LLMActor("planner") # inference-only, sandboxed
? oKey.Reveal(oHuman) # allowedillustration
ssh-ed25519-AAAAC3NzaC1lZDI1
? oKey.Reveal(oLLM) # refused
run on 2026-10-08 20:41 at commit 4a184e6f0: kept its promise
Refused: actor 'planner' (posture sandboxed) may not reveal secret 'deploy-key'. Only an effectful, non-sandboxed actor can read a secret.
Read that second block again, because it is the thesis. An LLMActor holds only the
inference capability — IsEffectful() is false. So an automated planner can
reference this secret in an entire deployment it designs and rehearses, and the
moment it tries to pull the actual key, the type system refuses. Expression is
free; admission is governed — extended from "which actor may commit a plan" to
"which actor may read a key." The agent you most want to keep credentials away from
is exactly the one the model refuses.
The specialisations name what the credential is
A bare secret is fine, but most credentials have a shape and a use. Softanza ships four specialisations, each adding the one presentation its kind needs:
oApi = new stzApiKey("stripe")
oApi.FromLiteralQ("sk_live_abc")
? oApi.AuthorizationHeader(oHuman) # Bearer sk_live_abc
oPw = new stzPassword("db")
oPw.FromLiteralQ("hunter2")
? oPw.Matches("hunter2", oHuman) # 1
? oPw.HashedBy(oHuman) # a salted PBKDF2 hash (salt:hash), never the plaintext
oTok = new stzToken("session")
oTok.FromLiteralQ("t0k").SetExpiryQ(1000)
? oTok.IsExpiredAt(1500) # 1 (you pass the clock; the token holds none)illustration
stzApiKey presents as a bearer header; stzPassword hashes and matches (with the
engine's PBKDF2, so the hash is safe to store); stzDeployKey yields key material for
a backend to write to a private file; stzToken carries an optional expiry. Each is a
stzSecret, so each redacts and reveals under the same governance.
The payoff: a site that cannot leak its key
Now the promise from the deployment narration comes due. A site's auth can be a secret — and the config the site serialises stays clean:
oKey = new stzDeployKey("prod-deploy")
oKey.FromEnvQ("DEPLOY_KEY")
oSite = new stzDeploymentSite("prod-api")
oSite.SetKindQ(:Server)
oSite.SetEndpointQ("deploy@api.restolean.app:/srv/restolean")
oSite.SetProtocolQ(:ssh)
oSite.SetAuthRefQ(oKey) # a secret, not a string
oSite.SetStoreAtQ("/srv/restolean/api")
? oSite.ConfigJson()illustration
{
"name": "prod-api",
"kind": "server",
"connection": { "endpoint": "deploy@api.restolean.app:/srv/restolean", "protocol": "ssh", "auth": "<secret 'prod-deploy' (deploykey) from env:DEPLOY_KEY>" },
"storage": "/srv/restolean/api",
"control": { "launch": "", "status": "" }
}The auth field is the descriptor. This is the file you commit, version, and
share to describe how the site is reached — and the key is simply not in it. When a
backend actually needs to transfer, it calls one governed method:
oSite.ResolveAuth(oHuman) # "ssh-ed25519-AAAAC3NzaC1lZDI1" -- the live key, for an effectful actor oSite.ResolveAuth(oLLM) # RAISES -- the rehearsing agent cannot pull the key
illustration
The key is resolved only at store/launch time, only through
ResolveAuth(actor), only for an actor that may cause effects. And because
SetAuthRefQ still accepts a plain string, every site config you already wrote keeps
working — the plain "env/DEPLOY_KEY" reference is simply an un-typed secret.
The other side of the door: authenticating people
stzSecret guards the credentials your program holds. The people using an app
raise a different question — are you who you say you are? — answered by stzAuth,
in the stzApp domain:
oAuth = new stzAuth()
oAuth.Register("mansour", "s3cr3t!") # stores a salted hash, never the password
? oAuth.Authenticate("mansour", "s3cr3t!") # 1
cTok = oAuth.Login("mansour", "s3cr3t!") # an opaque 256-bit session token
? oAuth.UserOfSession(cTok) # mansour
oAuth.Logout(cTok)illustration
Register hashes with the engine's PBKDF2; Authenticate verifies in constant time;
sessions are random hex from the engine's CSPRNG. The store never holds a plaintext
password, and no method prints a hash. It is the same crypto stzSecret uses — one
engine, one story, two sides of the same door.
The industry keeps secrets out of code by keeping them in another tool. Softanza keeps them in the program — as a value with no plaintext form until an actor entitled to cause effects asks for it. The key is present enough to deploy with, and absent enough that nothing, and no one un-entitled, can read it.
Runnable guard: libraries/stzlib/base/test/system/secret_narrated.ring (47/47).
Design: SOFTANZA_SECRETS.md(opens in a new tab).
Proved by
The article names these scenario guards of the library:
Related reading
Cite
Mansour Ayouni. "Guarding Secrets & Credentials". Softanza narrations, 2026. https://mayouni.github.io/stzsite/en/narrations/stz-guarding-secrets-and-credentials-narration.html
Run in Haro's name on 2026-10-08 20:41 inside the library at commit 4a184e6f0, block after block in one process: 2 of 8 blocks kept their promise; the others are illustrations. The code of the articles is Haro code. Where an article was written before the language took its present name, the name was changed and nothing else. Genre, area and series read from the file's name, the year from its first commit: derived, until the article carries its own header. The file on GitHub(opens in a new tab)

