-
Notifications
You must be signed in to change notification settings - Fork 0
Secrets
A secret is a named set of keys — nextcloud with username and password,
say — that an operator put on the server. A caller can use one and can never
see one. No tool, resource or report returns a value.
That is the whole point. A password an agent holds is in its transcript, its tool call, and whatever its host logs. A password it merely names is in none of them.
Set SECRETS_DIRS to a directory — or several, separated like PATH. Inside,
one directory per secret and one file per key, which is exactly the shape
Kubernetes mounts a Secret in:
/secrets/
└── nextcloud/
├── username
├── password
└── .meta.yaml # optional
The file name is the key and the file's contents are the value. .meta.yaml
describes the secret without changing it:
description: Admin login for the homelab Nextcloud
allowed_urls:
- https://nextcloud.example.comUnset SECRETS_DIRS means the feature is off, and list_secrets says so rather
than returning an empty list that looks like an empty catalogue.
list_secrets — or the secret://secrets resource — returns names, keys and
sites. Never values.
| Field | Means |
|---|---|
keys |
what may be named as key
|
allowed_urls |
the only sites it may be typed on, as exact origins |
restricted: false |
no sites declared: usable anywhere |
allowed_urls_rejected |
its site list is broken, so it cannot be used at all until an operator fixes it |
Give write a secret instead of text:
write(css="#password", secret={"name": "nextcloud", "key": "password"})
The result carries "text_from": "secret" and no value. The same argument goes
in a flow step's args — see Flows — where the step's line in the
report reads text=<hidden>.
Five rules, each refused rather than guessed at:
-
Only
writetakes a secret, and the rule is structural rather than written down: no other action has asecretargument, so there is nowhere to put one. It is the action that types a value into a field and does nothing else with it. -
A secret is never part of a string. Flow parameters are written
${name}into any argument; a secret is not, and could not be. That is not a restriction to work around — it is why a secret cannot end up in a URL, where it would be in the browser history, in the referer of every request the page then makes, and in the Grid's logs, none of which this server can scrub. -
A secret's name may not come from a parameter.
secret: {name: ${which}}is refused: choosing which credential gets typed is not a decision a caller's parameters may make. -
Never
textas well. Two values, one field, and picking one would be a guess that is wrong silently. -
Navigate first, in its own step. A
writebinding a secret may not also take aurl, because the site check reads the page the browser is on at the moment of typing — awritethat navigated would be checked against the page it was leaving.
A restricted secret is typed only when the browser is on one of its
allowed_urls, compared as an exact origin: scheme, host and port.
https://nextcloud.example.com.evil.test is not a match, and neither is plain
http://. A refusal names the page the browser was on and the sites it would
have accepted.
A parameter is for what varies between runs; a secret is for what must not be seen. An email address is a parameter. Its password is a secret.
Flows · write · Deployment
The action pages are generated from openapi.yaml, which is itself generated from the live MCP tool schemas — so they describe the server that shipped, not the one someone remembered. Prose belongs in wiki-notes/<tool>.md in the repo.
selenium-flow · MIT
Start here
Guides
Lifecycle
Going places
Doing things
Getting things out