ADR 0259: name the confirmation review's N1 findings and their fixes — an early desk unlink bars nothing, a trusted party's secret is never given at a desk — and root-free answered by the serving controller alone, a silence never ending an approval, the link bound's residual risk

This commit is contained in:
2026-10-09 15:36:36 +02:00
parent 69c6ff73e6
commit ec9bab5ee8
@@ -239,7 +239,13 @@ operator, via telegram, user id verified"), the channel module, the proofs prese
met, and when. It carries no secret.
- **The asker acts once per ask id**, keeping that in its own record, and only on a warrant from its own
`decided` subject.
`decided` subject. A warrant that says no time it was given, or for an ask that says no expiry, is refused:
neither can be shown to be in time (the confirmation review of 2026-10-09).
- **A silence never takes an approval back** (the confirmation review of 2026-10-09, M1). The controller
asks a condition's acknowledgements (Silence) apart from its approvals (Release, Stop, Restart), and choosing
Silence silences the condition; the first build then took the approval ask back as no longer wanted, so an
acknowledgement, which a desk click may give, ended an approval. An approval asked about a condition that is
silenced since stays open, unchanged, until it is answered on a channel that proves who answered, or expires.
- **A warrant authorises one act, as the person was shown it** (added on 2026-10-09, before the code was
merged). Every option above `acknowledge` carries the digest of exactly what the asker performs when it is
chosen — the verb, the machine and every argument (`binds`); an ask without it is refused at the router.
@@ -286,7 +292,8 @@ met, and when. It carries no secret.
channel and no router record keeps a copy, the desk channel stores none and hands it to the node's
notifier as transient, and the notifier keeps it out of its history, where an agent could read it later.
- **The desk channel runs as an account of its own**, like the router and Telegram (§8 rule 1): the codes
pass through it.
pass through it. Registration holds every holder of a `private` kind to it, as it holds a `verified-sender`
holder (the confirmation review of 2026-10-09).
- **One identity per kind.** A second link of a kind is refused until the first is unlinked.
- **Unlinking is an approve ask, answered on the account that is linked** (revised after the security
review of 2026-10-08). It is never a plain tool: whoever unlinks the operator's account could link one of
@@ -301,7 +308,11 @@ met, and when. It carries no secret.
- **Linking is bounded**: at most three links asked per account an hour and ten per kind, counted in the
router's own state so a restart forgets nothing (a count that cannot be read or written refuses the link); the code's notice on the desk names the account by
the name and the number the service gives, and only a channel declaring `private` (the desk) may show a
code.
code. **A residual risk of the bound, accepted** (the confirmation review of 2026-10-09): anyone may make
throwaway accounts and send the bot `/start` ten times in an hour, so the operator's own link is refused
for the rest of that hour. It denies a link, never makes one, and every refused link is said; the operator
links when the hour has passed. A bound per sender alone would let the same throwaway accounts flood the
desk with codes, which is worse.
- **The ask to unlink or relink binds the account**, not only the kind (added on 2026-10-09): a warrant for
unlinking one account never unlinks the one linked after it. A link re-checks that its kind has no account
when it is made, and an account just unlinked cannot link again at once from a code shown before.
@@ -310,6 +321,14 @@ met, and when. It carries no secret.
while one is in its first hour, the router asks at the desk, as an acknowledgement bound to that account,
whether to unlink it, and tells the sender so. If the hour has passed when the answer arrives, the unlink
is refused. After the hour, unlinking needs the linked account's approval, or break-glass.
- **An unlink at the desk in the first hour bars nothing** (the confirmation review of 2026-10-09, finding
N1-router). A desk click is anyone's at the desk machine — a program there can forge one — and the first
build barred the unlinked account for a day and made the next link wait for it. Another account's `/start`
offered the desk the unlink, a forged click took it, and the operator's own account stayed barred, again
and again, so the operator could never approve. Now such an unlink lets any account, the operator's too,
link again at once with a code shown on the desk. **The day's bar is set only by an unlink the account
itself approved, or root's**; root's `allow-link` at the router's machine lifts any bar. What is said on
every channel names which it was.
- **A new link answers `acknowledge` at once and `approve` only after one hour.** The hour runs from the
moment the link is made, is kept in the identity's record, and is said in the announcement ("it can
approve from 15:40"). An approve answer inside the hour is refused and said on the channel. Decided by
@@ -380,8 +399,20 @@ with the router not knowing its own machine, it approved. Now:
approve option and again when an approve answer arrives; an error, a missing machine, or a router that does
not know its own machine is not a pass. **The controller asks it too when it composes**, and strips
`verified-sender` from a kind without a pass; the router's live question covers a pass that ends between
compositions. The `agent-root` condition is raised on the same judgement, so the condition and the verb
never disagree.
compositions. The `root-not-free` condition is raised on the same judgement, so the condition and the
verb never disagree.
- *Added after the confirmation review of 2026-10-09.* **Only the serving controller answers `root-free`.**
Composed from the controller's own manifest, the module principal of the machine running the controller
and that machine's runtime were granted the controller seat's verbs too, and that runtime's credential is
one an agent there may hold. The controller's own seat is now served by the controller's principal alone,
in grants and in memberships, and a grant test composes every principal and finds exactly one that may
answer `root-free`. **A machine waiting for its node-engine's first setuid search is not root-free**
(ADR 0266 keeps the self-check quiet for that bound after a restart; `root-not-free` keeps the same quiet,
and the verb still answers no). The condition has its own key, apart from ADR 0266's
`agent-can-become-root`, which a second probe wrote under the same key with other words. **The router asks
`root-free` before it takes its work**, never while holding it — a controller slow to answer held every
ask, answer and link behind it — and judges an answer with the word it was given then; inside the work it
reads only a word under half a minute old, and an ask that failed drops the last word.
**3. What remains, said plainly.**
@@ -459,6 +490,18 @@ with the router not knowing its own machine, it approved. Now:
from words a caller chose, and says the controller asks; the bus lets only the controller publish the
prompt's verb; no secret value is accepted through a verb otherwise; and every value given raises the
urgent condition `secret-given` on every channel until the operator silences it.
**A trusted party's secret is never given at a desk** (the confirmation review of 2026-10-09, finding
N1-give). The desk prompt's answer comes back over the desk machine's bus, on the subject that machine's
runtime answers, and on a desk machine agents run as the operator, who holds that runtime's credential:
an agent called `give` for the bot token and answered the prompt first with a token of its own, sealed to
the call's key, and the channel the operator's answers are proven on became the agent's. Now the secret of
a module running as an account of its own (the router, Telegram, the desk channel) is refused at the desk
before anybody is asked, naming the line to type at the controller's terminal instead —
`mesh-controller secret accept <node> telegram telegram-token`, read without echo. There it is announced
on every channel, the one being replaced among them (it still runs on its old token until the next push),
**before** it is kept, and not kept when that announcement fails. A grant test holds that only the desk
machine's launcher, carried by that machine's runtime, may answer its prompt; for any other module's
secret that remains the accepted risk of a desk where agents run as the operator.
**Where an ask can be answered is said by the router**, from the channels that qualify: "Answer on
Telegram, or from the mesh MCP server." The asker's words never name a channel.
@@ -511,6 +554,11 @@ Telegram, or from the mesh MCP server." The asker's words never name a channel.
| A tool call never publishes an intake event or a proof | tool runner test: only a bundle's own channel reaches the seat publish, for its own kind |
| The asker acts once per warrant and records the warrant | shared library test; controller test reading the hand-act after a warrant |
| A warrant authorises only the act its option bound, for the ask as it was shown | shared library tests (`TestAWarrantAuthorisesOnlyTheActItsOptionBound`); controller test (`TestAWarrantPerformsOnlyTheActItsOptionBound`); router test (the warrant names the ask's digest) |
| Only the serving controller answers `root-free`; a machine waiting for its first setuid search is not root-free | controller grant test `TestOnlyTheServingControllerMayAnswerRootFree` (one answerer); `TestAMachineWaitingForItsFirstSetuidSearchIsNotRootFree`; `TestRootNotFreeIsQuietWhileTheFirstSearchRunsAndKeyedApartFromDA` |
| A desk unlink in the first hour bars nothing; root lifts any bar (N1-router) | router tests `TestAnUnlinkFromTheDeskInTheFirstHourBarsNothing`, `TestBreakGlassLiftsABarOnRelinking` |
| A trusted party's secret is never given at a desk; only the desk machine's launcher answers its prompt (N1-give) | controller tests `TestATrustedPartysSecretIsNeverTakenAtADesk`, `TestOnlyTheDeskMachinesLauncherMayAnswerItsPrompt` |
| A silence never takes an approval back (M1) | controller test `TestAnAcknowledgementNeverSharesAnAskWithAnApproval`, with the condition silenced as the controller silences it |
| The router never waits on the controller while holding its work | router test `TestTheRootFreeWordIsNotAskedWhileTheWorkIsHeld` |
| The self-check counts the login shell only where `execute` is served, and what it cannot measure fails | controller probe tests (`TestTheLoginShellCountsOnlyWhereExecuteIsServed`, the unmeasured case) |
| The whole flow, before anyone uses it | the lab's proof `asks` (mesh-lab): a real bus with the controller's composed grants, the router and the Telegram channel in runtimes of their own accounts, a fake Telegram API — approve acted once, decline, expiry, a replayed tap, a tap from another account, a forged warrant, an ask under another name, an ask from a module that may not ask |
| The controller holds no channel code | review; the controller's module imports no channel package |