Answer round 2 of the domain review of the approval mark
A stuck state recovers as a kept call, a node with no or stale readings is denied, the caller-named subjects are withheld too, and the per-machine key form and the client's prefix are named, so no path serves a marked tool unasked; the forge's loosening is marked by the operator's answer. Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
This commit is contained in:
@@ -339,7 +339,9 @@ approves, for a marked event or a restricted stream.
|
||||
waking it, and answers at once, so the same call made again runs within the warrant's 30 minutes. *Why:* ADR 0286
|
||||
§14 answers within 5 seconds, which a watcher's loop would turn into a call every few seconds for the ask's hour;
|
||||
ending at the choice keeps a long wait from spending the warrant's window. An unmarked wait is heard as §13 to §16
|
||||
say, with no ask.
|
||||
say, with no ask. Every machine's marked waits ask under the one asker `node-tools`, so they share its open asks
|
||||
and the router's holding back of the oldest beyond three (ADR 0666 §12, as G builds it), and the wait reads the
|
||||
router's record through `Client.Where` under `node-tools`.
|
||||
37. **The tests hold it.** Unit, as the budget counts them (`meshtest` runs its bus in the test's process): §19's table
|
||||
of refusals in `console/wait_test.go` gains rows, no test function: a wait on a fixture module's marked event, and
|
||||
one on a fixture stream with a `grant`, each with no warrant, subscribes nothing, asks once and answers design 46
|
||||
|
||||
@@ -53,7 +53,9 @@ them, on a machine whose runner honours the mark. Read on the workstation on 202
|
||||
file is mode 0600, owned by the operator's account, in a directory of mode 0755: every process running as the
|
||||
operator's account, every module on the machine's shared account and every agent session among them, can read it and
|
||||
hear a restricted stream without a warrant. This is the same known limit as design 47 G §385 (modules running as the
|
||||
machine's account, issue 1966 note 193, whose fix carries its check), and this milestone does not change it. The
|
||||
machine's account, issue 1966 note 193, whose fix carries its check), and this milestone does not change it: that a
|
||||
restricted stream is heard only on a warrant holds against every caller once that fix runs modules and sessions on
|
||||
accounts that cannot read the file, and until then against callers that go through the tool runner. The
|
||||
prepare reads the file's mode and owner on every node with `<node>/node-reads.stat` and writes them in the milestone
|
||||
file; a node where the file is readable beyond the operator's account is a finding of the prepare.
|
||||
|
||||
@@ -78,8 +80,10 @@ file; a node where the file is readable beyond the operator's account is a findi
|
||||
no tool runner loads or gates them (a module such as the forge's, whose tools bundle no process runs, is not
|
||||
refused); or names in `needs-approval` one name that is both one of its tools and one of its events. A tool mark
|
||||
is checked against `tools` where the manifest lists its tools; where only its bundle names them, the tool runner
|
||||
checks each marked tool against the tools the bundle registers at load, and a mark naming none is its finding
|
||||
`<node>.<module>.mark-unserved` (said as §16's), cleared when the bundle serves it or the mark goes. Each refusal names the entry and the rule. *Why:* a mark
|
||||
checks each marked tool against the tools the bundle registers at load: a bundle whose marks name a tool it does not
|
||||
register is not loaded, failing closed rather than serving unmarked what the manifest meant to mark, and that is its
|
||||
finding `<node>.<module>.mark-unserved` (said as §16's), cleared when a bundle that serves it, or a manifest without
|
||||
the mark, is loaded. Each refusal names the entry and the rule. *Why:* a mark
|
||||
nothing enforces would be a call that runs unasked while saying it is guarded; ADR 0286 §43 had the controller
|
||||
enforce its own verbs, and no other own-process server has a gate, so such a mark is refused rather than ignored;
|
||||
and an entry that names two things is read one way by one reader and another way by the next (the design's choices
|
||||
@@ -106,18 +110,25 @@ file; a node where the file is readable beyond the operator's account is a findi
|
||||
`approval-mark/1` among its call capabilities (`runtime.CallCapabilities`), which its announcement carries and its
|
||||
node's node-engine takes into the node's record; the assignment half carries it among the capability readings
|
||||
(design 47 G §194). The grants part, composing a runtime's account, reads that runtime's node's readings: where the
|
||||
tool runner does not state `approval-mark/1`, it composes a subscribe deny of every marked subject of every module
|
||||
the runtime carries (`mesh.mod.<module>.tool.<tool>`, the same followed by `.*`, and for a marked verb
|
||||
`mesh.seat.<seat>.tool.<verb>`, the same followed by `.<node>`), which the bus's deny outranks its allow of
|
||||
`mesh.mod.<module>.tool.>` for. `Permissions` gains `SubscribeDeny`, written into the user list as the server's
|
||||
tool runner does not state `approval-mark/1`, where the node has no readings, or where its readings are older than
|
||||
the tool runner's version on that node (failing closed as §19), it composes a subscribe deny of every marked
|
||||
subject of every module the runtime carries: `mesh.mod.<module>.tool.<tool>` and the same followed by `.*`; the
|
||||
caller-named subject mesh-sdk's `process.Call` sends on first, `mesh.mod.<module>.call.<tool>.>`; and for a marked
|
||||
verb `mesh.seat.<seat>.tool.<verb>`, the same followed by `.<node>`, and `mesh.seat.<seat>.call.<verb>.<node>.>`.
|
||||
The bus's deny outranks its allow of `mesh.mod.<module>.tool.>` for each. Going back to an older tool runner on a
|
||||
node leaves the window until its readings update, in which the old deny or none stands; that window is accepted. `Permissions` gains `SubscribeDeny`, written into the user list as the server's
|
||||
subscribe deny, as `PublishDeny` is for publishing. The withheld tools and verbs are named in the grants part's read
|
||||
of that account (`mesh-grants.grants_user_list`, a field `withheld` with each subject and "its tool runner does not
|
||||
state approval-mark/1"), and in the node-engine's `composition` of the module. *Why:* ADR 0286 §7, §8, §9: the bus
|
||||
enforces the mark; the runner's status of ADR 0286 §7 is read from the grants part's account read, since a runner
|
||||
that does not honour the mark is a version that cannot say so. *How it is checked:* unit, the grants part's
|
||||
composition table: with readings lacking the capability the deny of each marked subject is composed and named in
|
||||
`withheld`, with it no deny; integration, the grants part's real-bus test: an account composed with the deny is
|
||||
refused delivery of a call on a marked subject while it holds `mesh.mod.<module>.tool.>`.
|
||||
that does not honour the mark is a version that cannot say so. A call that reaches no responder because its
|
||||
subject is withheld is answered by the mesh MCP server naming `withheld` and the grants part's read. *How it is
|
||||
checked:* unit, the grants part's composition table: readings lacking the capability, no readings, and readings
|
||||
older than the runner each compose the deny of every subject above, the caller-named ones and a marked seat verb's
|
||||
`.<node>` form among them, each named in `withheld`; readings with the capability compose no deny; integration, the
|
||||
grants part's real-bus test: an account composed with the deny is refused delivery of a call on a marked subject
|
||||
while it holds `mesh.mod.<module>.tool.>`, and so is a call sent through `process.Call` on the caller-named
|
||||
subject.
|
||||
7. **The asker's grants are a user's of `operator-channel`.** Because the manifest says `operator-channel` in `uses`
|
||||
(§4), the grants part composes, on the account of the runtime carrying the module, the publish of
|
||||
`mesh.seat.operator-channel.accept.ask.<module>` and `.cancel.<module>`, the hearing of its warrants on
|
||||
@@ -141,7 +152,9 @@ file; a node where the file is readable beyond the operator's account is a findi
|
||||
than 16 KiB. *Why:* ADR 0286 §12; mesh-sdk's `outward` is the content rule ADR 0310 §4 moved there. *How it is
|
||||
checked:* unit, two rows of §22's table.
|
||||
10. **One ask, under the module's name.** Otherwise the runner asks through an `operatorchannelclient.Client` whose
|
||||
`Module` is the marked module and whose `State` is the runner's state of §13, unless an ask for the same act is
|
||||
`Module` is the marked module, whose `State` is the runner's state of §13, and whose new field `KeyPrefix`, which
|
||||
the client in mesh-contracts gains and puts before every key it writes and reads (empty for every asker today), is
|
||||
`<node>.`; unless an ask for the same act is
|
||||
open there (§13's `act.` key), in which case it asks nothing new. The ask: `about` the act's digest; `who`
|
||||
`operator`; `expires` 60 minutes after; no `on-expiry`; two options, `approve` (label Approve) and `decline` (label
|
||||
Decline), both at level `approve`, each binding the act's digest; the headline "Run <tool> on <node>?",
|
||||
@@ -178,12 +191,18 @@ file; a node where the file is readable beyond the operator's account is a findi
|
||||
newest ask for that act. The runner on each machine is the one writer of its machine's keys; a key is deleted by
|
||||
its time to live, never by hand. A per-machine state reaches today only the key named for its machine; mesh-sdk's
|
||||
per-machine form and the grants part's `stateAccess` gain the keys below it, `<node>.>`, for every per-machine
|
||||
state: one more subject per per-machine state on each account, and every module's per-machine keys keep their
|
||||
form. A write the bus refuses (the cap reached, or any other) fails closed under §19 and is said in the answer.
|
||||
state, a bundle's membership (`stateIssuedFor`, ADR 0260 §18) too, in the same form: one more subject per
|
||||
per-machine state on each account, and every module's per-machine keys keep their form; mesh-sdk's rebuild of it
|
||||
rides in G's group. The cap is the bucket's, shared by every machine's keys. A write the bus refuses (the cap
|
||||
reached, or any other) fails closed under §19 and is said in the answer; `mesh_approvals` reports the bucket's fill
|
||||
against its cap. A run still unrecorded (§16) an hour before its key's 48 hours end is written in the runner's log
|
||||
as an error naming the ask.
|
||||
*Why:* ADR 0286 §24, §30 kept the waiting call in the runner's record and claimed a warrant in the node's own file,
|
||||
kept 48 hours, by one writer per node; the client built since keeps asks and marks a warrant acted on by
|
||||
compare-and-set in a module's bus state (ADR 0259 §10, ADR 0627 §1), and a per-machine state (ADR 0595) is one
|
||||
writer per machine, so the file is not built. The cap and the key form are the design's choices. *How it is
|
||||
writer per machine, so the file is not built. The keys below the machine's own depart from ADR 0260 §16 (a
|
||||
per-machine state reaches only its machine's key), the design's choice following the operator's answers of
|
||||
2026-11-11 (the gate built in G); the cap and the key form are the design's choices too. *How it is
|
||||
checked:* unit, the manifest test of `node-tools` holds the entry; the grants part's composition table holds the
|
||||
`<node>.>` reach and no other node's; integration, §22's whole test reads the keys.
|
||||
14. **The same call again is judged on the router's record.** When a call of a marked tool comes and `<node>.act.`
|
||||
@@ -237,15 +256,18 @@ file; a node where the file is readable beyond the operator's account is a findi
|
||||
refused, runs nothing and says why when: no module holds `operator-channel` (read from the assigner's first view
|
||||
`mesh-delivery-assigner.views.seat-holders`, which the tool runner consumes; the seat list names no holders); the
|
||||
router refuses the ask; the ask cannot be published; the router's record cannot be read; §13's state cannot be read
|
||||
or written, in which case no marked call runs on that machine until it can; or the marks are not yet read. Before
|
||||
or written, in which case no marked call runs on that machine until it can, the failed read or write being made
|
||||
again as a kept call (ADR 0668 §1) until it succeeds; or the marks are not yet read. Before
|
||||
the first view of the catalog half is read, a call waits for it within its 5 seconds and is then judged, asking
|
||||
nothing while the marks are unread; still unread, it is refused, naming `mesh_first_views`. When §13's state has
|
||||
been unreadable or unwritable for five minutes, the runner raises its finding `<node>.approvals.approvals-unreadable`
|
||||
(said on `node-tools-findings` as §16's), and clears it at the next read and write that succeed. Each refusal is
|
||||
(said on `node-tools-findings` as §16's), and clears it when the kept call succeeds; its `findings-stated` at a start
|
||||
judges it anew from a read of the state (design 47 G §384). Each refusal is
|
||||
written in the runner's log with the ask's id and fingerprint, never the arguments. *Why:* ADR 0286 §40, §42; a
|
||||
gate that refuses every call for a reason nobody hears is a silent fault, and the finding and its five minutes are
|
||||
the design's choice. *How it is checked:* unit, one row of §22's table per case, the holder read from a fixture
|
||||
`views.seat-holders` with none and with one, and the finding raised after five minutes and cleared.
|
||||
`views.seat-holders` with none and with one; and a row where the state fails, the finding is raised after five
|
||||
minutes, the state recovers with no further call, and the finding clears.
|
||||
20. **The terminal is not exempt.** A marked tool called through `mesh-cli` or any caller asks as every call does: the
|
||||
runner cannot tell who called. *Why:* ADR 0286 §41. *How it is checked:* the code review reads that the gate sits
|
||||
on the call's handler, before the bundle, and that no path to a bundle passes around it.
|
||||
@@ -265,9 +287,9 @@ file; a node where the file is readable beyond the operator's account is a findi
|
||||
gains `operator-channel` in `uses`. No other tool or verb is marked in this milestone. *Why:* the forge's own
|
||||
refusal says these two acts are the operator's and waited for this mark (issue 417, 2026-10-28); a mark is on a
|
||||
whole tool (ADR 0286 §1), so the acts move to a tool of their own, as ADR 0286 §48 splits a tool into an unmarked
|
||||
and a marked form, and tightening a protection stays unasked; marked now pending the operator's answer of
|
||||
2026-11-11. *How it is checked:* the forge module's tool test: `set_full` refuses both acts naming the new
|
||||
tool; the new tool performs each; and live, a loosening asked on the phone runs once on the Approve.
|
||||
and a marked form, and tightening a protection stays unasked; the operator, 2026-11-11: "Mark them in G". *How it
|
||||
is checked:* the forge module's tool test: `set_full` refuses both acts naming the new tool; the new tool
|
||||
performs each; and live, as the acceptance says.
|
||||
|
||||
## Tests, delivery and going back
|
||||
|
||||
@@ -276,7 +298,8 @@ file; a node where the file is readable beyond the operator's account is a findi
|
||||
(`meshtest`), the router's record and the warrant written there as the router writes them; and one integration
|
||||
test that starts the runner against the real bus, with no store, with a fixture marked module: a call, the
|
||||
operator's Approve written as the router's record, the call again, the run, the hand-act, `mesh_approvals`, and a
|
||||
restart between the run and `warranted`'s answer. *Why:* the operator's test budget (one whole test over many small
|
||||
restart between the run and `warranted`'s answer. The client's `KeyPrefix` (§10) has a row in mesh-contracts'
|
||||
client test: every key written and read carries it, and an empty prefix writes today's keys. *Why:* the operator's test budget (one whole test over many small
|
||||
ones). *How it is checked:* the check counts the module's tests against its budget.
|
||||
23. **In G's delivery group, ordered by the module graph.** The members are pushed and checked in design 47 milestone
|
||||
G's group, with design 34's milestone every-event. The orders are the module graph's: the indexer's manifest
|
||||
@@ -298,6 +321,11 @@ file; a node where the file is readable beyond the operator's account is a findi
|
||||
`node-login-shell.execute` on a node other than the control-node answers "Not run", the ask is on
|
||||
`operator-channel.asks`, and after the operator's Approve the same call runs once, a third identical call is refused
|
||||
as already run, and `mesh-hand-acts.hand-acts` names the warranted act (§10, §12, §14 to §16, §21).
|
||||
- **The forge's loosening is asked, and the protection is put back** (live, read by tool call): on a scratch repository
|
||||
of the forge made for this acceptance, `acceptance-approval-mark`, with full protection on its trunk,
|
||||
`gitea_branch_protection_loosen` removing the trunk's protection answers "Not run" and asks on the phone; after the
|
||||
Approve the same call runs once; then `gitea_branch_protection_set_full` sets the full protection again and
|
||||
`gitea_branch_protection_get` reads it back as it was (§21).
|
||||
- **No machine serves `execute` unasked** (live, read by tool call): before this acceptance passes, on every node
|
||||
`execute` is either asked as above or absent, read with each node's `node-engine.composition` and
|
||||
`mesh-grants.grants_user_list`'s `withheld` (§6).
|
||||
|
||||
Reference in New Issue
Block a user