All docs
Docs · Reference
The grant inlet
your rules decide each request
Work you dispatched opens a sandbox, and each secret the sandbox names is a request. Before the chassis answers one, it presents the request to your tenant's grant stack, with the answer it proposes. Your rules may change that answer either way._
Grants says what a grant and a sandbox are and how a command uses them. This page is about the stack that decides.
One request is one run
OPS/_grant/
0/journal.txcl keep a record
10/policy.txcl decide A request enters _grant/0 as an envelope with @src == "grant", the way
a model call enters _llm. It is an ordinary run: it is admitted, traced
and metered like any other. A sandbox that names three secrets is three
runs.
- A tenant with no
_grantstack runs no rule, and the chassis’s proposal stands. - A request that fails a hard check never gets here. A forged token, an expired one, a revoked grant, or a sandbox the grant does not name is refused before any rule runs.
- One refusal refuses the sandbox. The program is handed nothing, and what was already allowed stays charged.
What a rule sees
Everything is under @grant, stamped by the chassis from the run grant’s
own row. It is read-only, except @grant.res.
| fact | holds |
|---|---|
@grant.phase | request |
@grant.kind, @grant.name, @grant.verb | What was asked for: secret, its name, release — or, for a capability call, capability, its name, invoke |
@grant.sandbox, @grant.env | The sandbox being opened, and the variable this secret fills |
@grant.principal.id, .kind | Whom the work acts for |
@grant.run, @grant.generation | The run, and which minting of it |
@grant.grant, @grant.depth | The run grant’s id, and how many times it was narrowed |
@grant.stack, @grant.workspace | The stack that minted it, and the workspace it was minted for |
@grant.node.class | reviewed or unreviewed |
@grant.via | Who opened the sandbox: exec (the rule that started the command) or launcher (txco sandbox, the program itself). http for a capability call. |
@grant.input | A capability call’s input, as the caller sent it: data for a rule to decide on. Absent for a secret. |
@grant.secret.pull | The secret’s pull policy |
@grant.secret.version, .scope | Its version, and tenant or stack |
@grant.checks.exists | Whether there is such a secret. Always true for a capability: whether anything answers it is found when it runs. |
@grant.checks.allowlist, .standing, .pull, .budget | Each check of the proposal, true or false |
@grant.budget.calls, .spent | The run’s budget, and what it has spent |
@grant.proposed.allow | The chassis’s answer |
@grant.proposed.reason | Why not, when it is false: not_found, allowlist, standing, pull or budget |
The envelope never holds the secret’s value or the grant’s token.
The verdict
# allow one principal a secret its pull policy closed, through one sandbox
WHEN @src == "grant"
&& @grant.sandbox == "deploy"
&& @grant.name == "DEPLOY_KEY"
&& @grant.principal.id == "service:release"
&& @grant.checks.allowlist == true
&& @grant.checks.standing == true
EMIT @grant.res.allow = true,
@grant.res.reason = "release may deploy" | a rule writes | the request is |
|---|---|
| nothing | Decided by the proposal |
@grant.res.allow = true | Allowed |
@grant.res.allow = false | Refused |
@grant.res.hold = true | Refused, as held. A capability’s caller is told so distinctly (409 held, txco_cap_held on the node). The chassis stores nothing: a rule that holds records what a person will need, and your stack runs the call later if they approve. |
@grant.res.reason = "…" | Unchanged. The reason is kept in the log. |
allowis a boolean."true"or1is a rule that meant to decide and did not, and the request is refused.- A hold beats an allow.
- A rule cannot release what is not there. A request for a secret that does not exist is presented, so it is traced, and then refused whatever a rule says.
- A rule that allows is trusted with the budget too. If the chassis proposed to refuse for want of budget and a rule allowed the request, the call is charged past the budget.
- Check what you mean to keep. A rule that writes
allow = trueon the name alone also allows a run whose grant does not name the secret. Include@grant.checks.allowlistand@grant.checks.standingunless you mean to overrule them.
What refuses, whatever was proposed
| the run | the request is |
|---|---|
| Errors | Refused |
Takes longer than --grant-decide-timeout | Refused |
| Is refused admission: rate, concurrency, a suspended tenant | Refused |
| Streams a response | Refused |
Keeping a record
The trace of the _grant run is the record of a request: txco trace.
The chassis keeps no journal of its own. To keep one you can read back,
write it:
# OPS/_grant/0/journal.txcl
WHEN @src == "grant" && @grant.phase == "request"
EXEC "txco://notebook/append"
WITH notebook = &concat("grants/", @grant.run),
type = "grant.request",
data = &object("sandbox", @grant.sandbox, "env", @grant.env,
"secret", @grant.name, "version", @grant.secret.version,
"principal", @grant.principal.id, "proposed", @grant.proposed) After an incident, that record answers what rotation needs: which versions of which secrets reached which runs.
Gate every rule on @grant.phase == "request". Later phases will enter the
same stack.