honker: PRD 1.8 draft r4: Mos's rulings on duckworth-0057 (mos-1129, sage-1538): B1 in Duckworth's text, B2 his second option (only an Approve creates a grant, a hand-written grant makes nothing, row 210's hand grants a test path until #436a, the hand path out), N1 the target or its enrolled identity, N2 a revoke during creation leaves nothing usable, N3 old or field-less credential.mint makes nothing, N4 one credential.mint decision that shows the account name, N5 the arbiter route for host.sudo, N6 messaging lists and role contracts, C1 account.create as in 1.7; the B7 residual and the root-command change named for Jason

This commit is contained in:
2026-10-20 17:32:14 -05:00
parent d16ccdb910
commit 781d91624a
+89 -71
View File
@@ -10,12 +10,24 @@ the PRD file, this draft's anchors name 1.7's sections.
Revision 2 makes Mos's edits a to f (mos-1124, through Sage, sage-1534) and fixes Duckworth's B1 and B2
with notes N1 to N6 (duckworth-0057) on r1 at 3c30f1036. Revision 3 fixes Duckworth's C1 and takes
notes N1 and N2 (duckworth-0058) on r2 at 60f5ca8fa, with Gizmoduck's enrollment wording
(gizmoduck-0067).
(gizmoduck-0067). Revision 4 makes Mos's rulings on duckworth-0057 (mos-1129, through Sage, sage-1538)
on r3 at d16ccdb91: only an Approve creates a grant, one decision for a grant that makes an account, and
the arbiter route for `host.sudo`.
For Jason at approval: "The main agent or chief of staff" in Q10 is read as one role under two names,
the chief of staff. So only the chief raises a grant request, and your assistant passes your own request
to the chief. That reading is Mos's (mos-1124). To change it, say so when you approve, and REQ-GRANT-1 changes
before the text goes into the PRD.
to the chief. That reading is Mos's (mos-1124). To change it, say so when you approve, and REQ-GRANT-1
changes before the text goes into the PRD.
Also for Jason at approval:
- Only an Approve creates a grant. A grant written into the business file by hand makes nothing. An agent
that runs as the same OS user as the stack can still write the stack's own store. That is the B7
residual (Q182 on dragon-lin, #423 on .53 and .54), and this version doesn't close it.
- Who decides a root command on another host changes. Once `host.sudo` is routed, a raise for it that
cites the spec and isn't within-role goes to the arbiter for its domain, as version 1.7 set for
`git.push.remote`, not to you. The grant and its allowlist still limit the command. In manual mode the
decision comes to you (REQ-TRUST-2).
## What it changes
@@ -45,29 +57,31 @@ This version adds:
The hard waits stay with the user (Mos, mos-1117): spending money, deleting data, new external accounts,
messages outside the fleet and the rest of REQ-WAIT-1. Approving a grant is the user resolving one of
those hard waits, `credential.mint` and, when the grant makes an account, `account.create`. A seat's own
Gitea user (Q190) is such an account. So no hard wait moves to an agent.
those hard waits, `credential.mint`. A grant that makes an account, such as a seat's own Gitea user
(Q190), is the same one decision: its method says it makes an account, and the inbox shows the account
name before Approve. So no hard wait moves to an agent.
When this version lands, reviewed commits:
1. add the manual-mode list to the organization file schema, written only on the human's path
(REQ-TRUST-2), and make the broker classify a listed action as needing a decision for each use, before
any other routing.
2. add the grant request: a decision the chief of staff raises with action `credential.mint`, or
`account.create` when the grant makes an account, naming the agent, the method and target, and the
exact rights (REQ-GRANT-1). The broker refuses a `credential.mint` raise, and an `account.create`
raise that carries a grant method, from any other role. An `account.create` raise with no grant
method, such as a PM's sign-up for a service, routes as it does in version 1.7.
2. add the grant request: a decision the chief of staff raises with action `credential.mint`, naming the
agent, the method and target, the exact rights and, when the method makes an account, the account
name (REQ-GRANT-1). The broker refuses a `credential.mint` raise from any other role. An
`account.create` raise isn't a grant request and routes as it does in version 1.7, such as a PM's
sign-up for a service.
3. add the grant code that runs on a resolved Approve, one method at a time, and the grant record it
writes (REQ-GRANT-2, Vikunja #435 and #436).
4. add the user's token store, written from the WebUI (or the CLI until the Console knows the signed-in
human) and read only by the grant code (REQ-GRANT-3).
5. move `host.sudo` into both `ROUTED` lists, as row 199 does for `git.push.remote`, and route it under
REQ-CRED-4. Which definitions list it within-role is a separate reviewed role commit in `roles/`.
REQ-CRED-4, with the arbiter route version 1.7 set for `git.push.remote`. Which definitions list it
within-role is a separate reviewed role commit in `roles/`.
6. update the role contracts that list the hard waits: `roles/chief-of-staff.md:75` raises grant
requests with the REQ-GRANT-1 fields, and `roles/pm.md:55` asks the chief of staff for a grant instead
of raising `credential.mint` itself (line numbers on origin/refactor). Other roles that list the hard
waits get the same line as the PM.
waits get the same line as the PM. A role's messaging list carries the chain of command.
## Text for the PRD
@@ -146,15 +160,17 @@ Under "Credentials", after REQ-CRED-3 (version 1.7):
> rotate-by or expiry date. The chief raises one only for an agent whose reviewed role definition
> allows that kind of credential, and the broker refuses any other. A grant can't name a wildcard, a
> shell or an empty command. The request is a decision with the action `credential.mint`, so it goes
> to the user as a hard wait (REQ-WAIT-1). A request that makes an account, such as a seat's own Gitea
> user (Q190), is raised with the action `account.create` and names the credential it makes as well.
> Both actions are hard waits, so the request goes to the user either way, and the user's one
> resolution covers the account and its credential. So the hard-waits list doesn't change. The broker
> checks that the chief of staff raised the request. The chain of command before that is in the role
> contracts, not a broker check.
> to the user as a hard wait (REQ-WAIT-1). A grant whose method makes an account, such as a seat's own
> Gitea user (Q190), is the same one decision: the method in the grant says it makes an account, the
> request names the account, and the inbox shows the account name before Approve. The user's one
> resolution covers the account and its credential, so the hard-waits list doesn't change. An
> `account.create` raise isn't a grant request and routes as version 1.7 has it. The broker checks
> only that the chief of staff raised the request. The chain of command before that is in the
> messaging lists and the role contracts, not a broker check.
> - **REQ-GRANT-2.** The user approves or denies each grant request in the inbox. Approve is the only thing
> that makes the grant code create a grant. No agent creates a grant. The user creates one by Approve,
> or by writing it in their own business file on the human's path, as row 210 does. On Approve,
> that creates a grant, and the code uses a grant only when it has a matching Approve record. No agent
> creates a grant. A grant written into the business file by hand makes nothing. Row 210's hand-written
> grants stay a test path until Vikunja #436a moves `host.run` to grants made by Approve. On Approve,
> protected stack code does every step, not an agent and not the user: it records the grant, makes the
> key or token, installs it on the machine or creates it on the service, and logs each step. On Deny
> nothing is made, and the denial is logged. The grant methods are:
@@ -167,17 +183,14 @@ Under "Credentials", after REQ-CRED-3 (version 1.7):
>
> The Gitea and GitHub rights units come in a later design that Jason approves.
>
> The approval binds the exact rights the user saw and the target's enrolled identity. A change to the
> agent, its definition, the target, the target's identity or the rights after the raise voids it. An
> The approval binds the exact rights the user saw and the target or its enrolled identity. A change to
> the agent, its definition, the target, the target's identity or the rights after the raise voids it. An
> approval is used once. A grant the code couldn't finish is undone at once on the stack's side, so it
> is never used. What it left on the host or service stays visible as pending until it is removed, as
> for a revoke (REQ-GRANT-4). A new grant needs a new approval, never an automatic retry. The grant
> code runs only on a request the user resolved. A request resolved by an arbiter, the tester or any
> agent makes nothing. The grant code makes nothing from a `credential.mint` decision raised before
> version 1.8 or without the fields of REQ-GRANT-1, or from an `account.create` decision with no grant
> method. An `account.create` decision with no grant method isn't a grant request and stays as version
> 1.7 has it. A grant the user writes by hand is held to the same bounds when it is loaded: a wildcard,
> a shell, an empty command or a missing rotate-by date is refused (REQ-CRED-4). Until the Console
> agent makes nothing. A `credential.mint` decision raised before version 1.8, or without the fields of
> REQ-GRANT-1, makes nothing when it is approved. Until the Console
> knows the signed-in human (Vikunja #412), Approve is `mosaic decide` in the CLI (REQ-DEC-3).
> - **REQ-GRANT-3.** The user enters their own personal access tokens and API tokens in the WebUI, or the
> CLI. Only the grant code reads them, to make grants. No agent ever sees a token value or a key's
@@ -188,14 +201,13 @@ Under "Credentials", after REQ-CRED-3 (version 1.7):
> organization, or by the tester on a dev instance (Q173). A revoke needs no decision and no new trust.
> It takes effect at once on the stack's side, without waiting on the network, so the next use is
> refused. Removing the key or token at the host or service follows. Until that removal is done it stays
> visible as pending. A revoke while the grant is being made stops it, and what was made is handled the
> same way. Every revoke is logged with who made it.
> visible as pending. A revoke while the grant is being made stops it, and nothing it made stays usable.
> Every revoke is logged with who made it.
REQ-CRED-1, appended:
> Since version 1.8, a token or key for a grant is made by the grant code on the user's Approve
> (REQ-GRANT-2). A grant the user writes in their own business file, with its key made by hand, stays
> allowed. Tokens that exist stay as they are.
> (REQ-GRANT-2), not by hand. Tokens that exist stay as they are.
REQ-WAIT-3, appended:
@@ -217,8 +229,8 @@ Under "Risks and open questions", a new bullet:
- The hard waits stay with the user (mos-1117). REQ-GRANT-1 makes the grant request a `credential.mint`
decision, already a hard wait under REQ-WAIT-1, so Approve is the user resolving it. REQ-WAIT-2 stands:
no role file grants `credential.mint`. The grant code acts on the user's resolution, not on a role
grant. A grant that makes an account, such as a seat's Gitea user under Q190, is also `account.create`,
already a hard wait. So 1.8 doesn't change the hard-waits list (mos-1124 a).
grant. A grant that makes an account, such as a seat's Gitea user under Q190, is the same one
`credential.mint` decision. So 1.8 doesn't change the hard-waits list (mos-1124 a, mos-1129 N4).
- Manual mode: Sage asked who sets it, where it lives and what it changes. Mos accepted these choices
(mos-1124 b), and Jason can change them: a list of actions in the organization file, empty by
default, changed only by the user, outside the chief's organization writes. A list rather than one
@@ -233,37 +245,44 @@ Under "Risks and open questions", a new bullet:
is at once on the stack's side with the host or service removal visible until done, every grant has a
rotate-by or expiry date, and the host identity comes from the host side. Duckworth hasn't reviewed
their draft yet, so a change there may need a change here.
- B2 (duckworth-0057): REQ-GRANT-2 lets the user create a grant by Approve or by writing it in their own
business file. The other choice, existing grants stay and every new one needs an Approve record, would
leave no way to make a new grant between row 210 landing and the grant code (#435, #436) landing. Q188
calls requiring the user to do the steps a no-fly. This text allows the hand path and requires it
nowhere. Mos or Jason can close it once the grant code is live.
- C1 (duckworth-0058): `account.create` is the hard wait for any new external account, not only a grant
(REQ-WAIT-1, `vocabulary.mjs:43-46`, `roles/pm.md:55`). So landing step 2 and REQ-GRANT-2 apply the
chief-only raise and the "makes nothing" rule only to an `account.create` raise that carries a grant
method. A PM's sign-up for a service routes as in version 1.7.
- N1 (duckworth-0058): a grant the user writes by hand isn't a request, so REQ-GRANT-1's bounds don't
reach it as a request. REQ-GRANT-2 has the loader refuse a hand-written grant with a wildcard, a
shell, an empty command or no rotate-by date. Row 210's key entry already carries one (`rotateBy`,
origin/gizmoduck/row210-on-199-r2 `agents/gizmoduck/work/queue-210/DESIGN.md:144`).
- B1 (duckworth-0057, mos-1129): a grant the code couldn't finish is undone at once on the stack's side.
What it left on the host or service stays visible as pending until it is removed, as for a revoke.
- B2 (duckworth-0057, mos-1129): Mos chose Duckworth's second option. Only an Approve creates a grant,
and the code uses a grant only when it has a matching Approve record. A grant written by hand makes
nothing. Jason's Q188 gives the reason: "Disallowing the agents to create grants autonomously is a
necessary evil for better security" and "Upon approval, the grant is created programmatically." Row
210's hand-written grants stay a test path until #436a moves `host.run` to grants made by Approve. r2
and r3 had a hand path in the business file, and r4 takes it out, with r3's load check on hand grants
(duckworth-0058 N1) and the note that a revoke never rewrites the user's file (duckworth-0058 N2).
The same-uid write to the stack's own store stays the B7 residual, and the top of this draft names it
for Jason.
- C1 (duckworth-0058, sage-1538): `account.create` is the hard wait for any new external account, not
only a grant (REQ-WAIT-1, `vocabulary.mjs:43-46`, `roles/pm.md:55`). It stays as version 1.7 has it.
Only `credential.mint` is chief-only, and a grant that makes an account is a `credential.mint`
decision (N4), so a PM's sign-up for a service routes as before.
- Gizmoduck's machine-grant r2 (eb4c756a1, gizmoduck-0067): enrollment is a request the user approves
and the stack runs (mos-1120), and a host user with sudo is enrolled by name, the user's Approve
covering that user (mos-1125). REQ-GRANT-2's machine key says both. Their design gives revoke to the
user and the tester only, and their Q-S5 asks Jason whether the assistant gets it too. REQ-GRANT-4
follows Mos's ruling (mos-1124 d) until Jason answers Q-S5, and then matches his answer.
- N2 (duckworth-0058), for #436: a revoke by the assistant or the tester can reach a grant the user
wrote by hand. The revoke marks the grant revoked in the stack's own record and never rewrites the
user's business file.
- N4 (duckworth-0057): a decision carries one action (`a.action`, broker.mjs:1678 on origin/refactor). A
request that makes an account is raised as `account.create` and names its credential, so the decision
shape doesn't change.
- N5 (duckworth-0057): once `host.sudo` is routed, a raise for it that cites the spec and isn't
within-role goes to the arbiter for its domain, like any gated raise (broker.mjs:1677-1678 on
origin/refactor), not to the user. REQ-CRED-4's grant and allowlist bound what the arbiter can allow.
Mos to say whether that is wanted or `host.sudo` should go to the user in any case.
- N6 (duckworth-0057): the broker checks only that the chief of staff raised a grant request. Which
role asked the chief, and through whom, is in the role contracts (step 6 of "When this version
lands"), not a broker check. REQ-GRANT-1 says so.
- N1 (duckworth-0057, mos-1129): the approval binds the target or its enrolled identity.
- N2 (duckworth-0057, mos-1129): a revoke during creation stops it, and nothing it made stays usable.
- N3 (duckworth-0057, mos-1129): landing step 6 lists the role contract updates (`roles/pm.md:55`,
`roles/chief-of-staff.md:75`). A `credential.mint` decision raised before 1.8, or one without the
grant fields, makes nothing when it is approved.
- N4 (duckworth-0057, mos-1129): a decision carries one action (`a.action`, broker.mjs:1678 on
origin/refactor). A grant that makes an account is one `credential.mint` decision: the method in the
grant says it makes an account, and the inbox shows the account name before Approve. The user clicks
once, and there is no separate `account.create` action for it. Gizmoduck's machine-grant r2 reads it
the same way.
- N5 (duckworth-0057, mos-1129): `host.sudo` keeps the arbiter route version 1.7 set for
`git.push.remote`. A raise for it that cites the spec and isn't within-role goes to the arbiter for
its domain (broker.mjs:1677-1678 on origin/refactor), and the grant and its allowlist still limit the
command. In manual mode the decision goes to the user. The top of this draft names this for Jason as a
change in who decides a root command on another host.
- N6 (duckworth-0057, mos-1129): the broker enforces only that the chief of staff raised a grant request.
The rest of the chain of command is the messaging lists and the role contracts (step 6 of "When this
version lands"). REQ-GRANT-1 says so.
- Approving a grant doesn't by itself let a command run without a click. That needs REQ-CRED-4 and
`host.sudo` in the routed lists, step 5 of "When this version lands" (gizmoduck-0062 item 4i).
- Which roles may request: Q188 says "Not all agents are capable or authorized to request directly. It
@@ -274,11 +293,11 @@ Under "Risks and open questions", a new bullet:
or chief of staff" as one role under two names. That reading is Mos's, so the top of this draft puts it
to Jason at approval.
- REQ-CRED-4 keeps row 210's conditions (origin/gizmoduck/row210-on-199-r2,
agents/gizmoduck/work/queue-210/DESIGN.md:376-392): the definition declares the kind, the grant names
the agent, an exact argv-prefix allowlist with wildcards refused, the key issued, and only a reviewed role
change adds the action. Row 210 put the grant in a hand-edited business file. Under 1.8 the grant is
what Approve records, so "the business file grants that agent that host" becomes "a grant names that
agent and that host".
agents/gizmoduck/work/queue-210/DESIGN.md:376-392): the definition declares the kind, the grant names the
agent, an exact argv-prefix allowlist with wildcards refused, the key issued, and only a reviewed role
change adds the action. Row 210 put the grant in a hand-edited business file, which stays a test path until
#436a. Under 1.8 the grant is what Approve records, so "the business file grants that agent that host"
becomes "a grant names that agent and that host".
- Revoke: Q188 says "you, Mos or the tester". Mos is Jason's assistant (REQ-ROLE-4), so the text says the
assistant, limited to grants in its own organization, and the tester only on a dev instance (Q173).
Mos accepted this with those limits (mos-1124 d).
@@ -287,13 +306,12 @@ Under "Risks and open questions", a new bullet:
REQ-WEB-2, which stays with the Console decide buttons amendment (Vikunja 19/266). That amendment, or a
later version, moves Approve into the Console.
- Roles already use a scoped Gitea token (`<read|write>:<category>`, all and admin never granted,
`packages/business/src/vocabulary.mjs:105-106` on origin/refactor). It is a likely Gitea unit, not a
settled one. Only the machine key is
designed so far. Gitea and GitHub are a later #436 draft, and minting a Gitea token needs an admin
credential the user enters, which is a design of its own (Gizmoduck's draft section 11). GitHub's
rights unit isn't settled. So the text leaves both units to a later design Jason approves (mos-1124 f),
and keeps only the bounds: an expiry, and never the all or admin scope. The PRD names outcomes and
checks, not the grant code's steps, as REQ-CRED-3 does.
`packages/business/src/vocabulary.mjs:105-106` on origin/refactor). It is a likely Gitea unit, not a settled
one. Only the machine key is designed so far. Gitea and GitHub are a later #436 draft, and minting a Gitea
token needs an admin credential the user enters, which is a design of its own (Gizmoduck's draft section
11). GitHub's rights unit isn't settled. So the text leaves both units to a later design Jason approves
(mos-1124 f), and keeps only the bounds: an expiry, and never the all or admin scope. The PRD names outcomes
and checks, not the grant code's steps, as REQ-CRED-3 does.
- Jason expects the inbox Approve and the Gitea and GitHub grants by 10-27 (Q188 point 6). That is a
delivery date, so it is in the source and not in the requirement text.
- This draft follows the PRD's own style: plain words, no semicolons in new sentences, and the status