Revise ADR 0259 after the security review: the router and verified channels get bus accounts of their own, verified-sender follows placement, and unlinking is asked of the linked account
This commit is contained in:
+63
-20
@@ -250,8 +250,18 @@ met, and when. It carries no secret.
|
||||
and the name the service shows. The router shows a six-digit code on the desk, with that name, valid
|
||||
for ten minutes. The operator sends the code to the bot; the channel passes it on the `proof` subject;
|
||||
the router links the identity when it matches, and says so on every channel.
|
||||
- **One identity per kind.** A second link of a kind is refused until the first is unlinked. Unlinking
|
||||
(a tool of the router) is said on every channel, the unlinked one included.
|
||||
- **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
|
||||
their own in its place. The notice of an unlink, and of any link for an hour after it, is sent to the
|
||||
account being replaced by name, before anything changes, and to every other channel.
|
||||
- **A link within a day of an unlink waits for the replaced account's approval**, asked there. When that
|
||||
account is lost with its phone, the operator unlinks at a terminal on the control node as root (`messenger
|
||||
unlink --at-the-machine`, refused over the bus), recorded and announced as break-glass, as ADR 0234 §8 does
|
||||
for a lost factor.
|
||||
- **Linking is bounded**: at most three links asked per kind an hour; 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.
|
||||
- **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
|
||||
@@ -261,26 +271,55 @@ met, and when. It carries no secret.
|
||||
an account of its own. It is said loudly on every channel when it happens, the hour gives the operator
|
||||
time to unlink it before it can approve anything, and the factor closes it.
|
||||
|
||||
### 8. The `telegram` module's own account
|
||||
### 8. Who may say what on the bus: the trusted parties' own accounts
|
||||
|
||||
Decided by the operator on 2026-10-08. The bot token and the channel's state are kept from every agent,
|
||||
because every agent runs as the operator.
|
||||
*Revised after the security review of 2026-10-08, and not yet accepted by the operator.* The first version of
|
||||
this section rested on the tool runner running as root with a credential only root reads. **On the control
|
||||
node it runs as the operator's account, its bus credential belongs to that account, and that account may
|
||||
become root without a password.** Every agent runs as that account. So an agent there could read the runner's
|
||||
credential and publish anything any carried module may: a warrant, an answer from Telegram, a link. The
|
||||
service account of the first version kept the bot token from nobody.
|
||||
|
||||
- **Its process runs as the account `telegram`**, which the module makes (a `user` resource: no shell, no
|
||||
login, home in its state directory). The tool runner, which runs as root, starts the module's bundle
|
||||
as that account when the bundle's environment names it (`MESH_RUN_AS`); it refuses to start a bundle as
|
||||
root or as the operator's account.
|
||||
- **Its state directory is owned by `telegram`, mode 0700, and its own secret `telegram-token` by
|
||||
`telegram`, mode 0600** (the manifest's `owner` and `secrets-owner`). The operator's account can read
|
||||
neither, and cannot attach to a process of another account.
|
||||
- **The bus credential stays the tool runner's**, readable by root only. The bundle reaches the bus only
|
||||
through its standard input and output, and the runner publishes for it only the kind it claims.
|
||||
- **An agent reaches the runner only through the mesh MCP server's five tools**, which make tool calls.
|
||||
No tool publishes an intake event or a proof: those come only from the bundle's own channel.
|
||||
- **What remains if an agent has root on the control node:** nothing here holds. Root reads the token,
|
||||
the runner's bus credential and the router's state, and can publish as anything. The boundary rests on
|
||||
agents not holding root there; whether the operator's account may become root without a person is
|
||||
checked by the self-check, which raises a condition when it can.
|
||||
The revision rests on three rules.
|
||||
|
||||
**1. The trusted parties hold bus accounts of their own, never a runner's.**
|
||||
|
||||
- The **router** (`operator-channel`'s holder) and **every channel that declares `verified-sender`** run as
|
||||
their own process, as their own service account (`messenger`, `telegram`), with a bus account of their
|
||||
own: an own secret `broker`, owned by that account, mode 0600. They are not carried by the tool runner.
|
||||
Their tools are served by their own process on their own tool subjects.
|
||||
- **The controller refuses to compose** into any runner's user, or into any principal whose process runs as
|
||||
the node's operator account:
|
||||
- publishing on a by-caller event of `operator-channel` (a warrant);
|
||||
- writing the router's state (asks, identities);
|
||||
- publishing on, or answering, a kinded bench's subjects for a kind declaring `verified-sender`.
|
||||
A manifest asking for it is refused at registration, and a runner carrying such a module is refused at
|
||||
composition, naming the module.
|
||||
- **Proofs are answered by the holder of `operator-channel` alone**, not by any module watching the bench;
|
||||
and **a kind's accepts are published by that holder alone**, not by every user of the bench.
|
||||
- **The tool runner hands a bundle no bus variable** of its own: the credential and the bus's address are
|
||||
taken out of every bundle's environment.
|
||||
|
||||
**2. `verified-sender` is honoured only where the controller can show who holds it.**
|
||||
|
||||
The controller passes a kind's `verified-sender` to the router only when its holder runs as its own
|
||||
account, with its own bus account, on a machine where the self-check finds that **no account an agent runs
|
||||
as can become root without a person** (no passwordless sudo, no membership of a group that grants root).
|
||||
Otherwise it strips the capability from the membership, and the router answers that kind only as far as
|
||||
acknowledgements, saying why. The same check holds the router: while it fails on the router's machine, the
|
||||
router issues no approve warrant, and the condition says what to change.
|
||||
|
||||
**3. What remains, said plainly.**
|
||||
|
||||
- **Root on the router's or the channel's machine defeats all of this.** The self-check raises an urgent
|
||||
condition while an agent's account can become root there without a person; on the control node today it
|
||||
can (the operator's account has passwordless sudo, measured on 2026-10-08). The operator chooses one of:
|
||||
a password for sudo on that machine; agents there under an account of their own without sudo; or the
|
||||
router and the channels placed on a machine where neither holds.
|
||||
- **The controller holds the whole JetStream API** today. Narrowing it to the streams and buckets it owns is
|
||||
planned work of its own; until then the controller is trusted with every record.
|
||||
- **On the X11 desk an agent can read a code as it is shown or typed** (ADR 0234's accepted risk). The hour
|
||||
after a link, the replaced account's approval and the announcements bound what a stolen code can do.
|
||||
|
||||
### 9. What is superseded in ADR 0234
|
||||
|
||||
@@ -347,6 +386,10 @@ Telegram, or from the mesh MCP server." The asker's words never name a channel.
|
||||
| An ask no ready channel can carry is refused at once, in words | router test |
|
||||
| Linking needs the code shown on the desk, and is said on every channel | router tests: a wrong code, a late code, a second link of a kind |
|
||||
| A new link approves only after one hour; it acknowledges at once | router tests: an approve answer at 59 minutes refused and said, at 60 accepted; an acknowledge at once accepted |
|
||||
| No runner's user, and no principal running as the operator's account, may publish a warrant, write the router's state, or speak for a verified-sender kind | controller composition test over every such grant; registration refuses a manifest asking for it |
|
||||
| `verified-sender` reaches the router only from a holder with its own account on a machine no agent can become root on | controller test stripping the capability; self-check probe raising the condition |
|
||||
| Unlinking and a relink within a day are approve asks answered on the linked account; the notice reaches it first | router tests, with a fake channel that honours the addressee |
|
||||
| No bus variable reaches a bundle | tool runner test over a bundle's environment |
|
||||
| The `telegram` bundle runs as its own account, never root or the operator's | tool runner tests: a bundle naming `MESH_RUN_AS` is started as that account; root and the operator's account refused; the manifest's owners checked by the catalogue's manifest test |
|
||||
| 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 |
|
||||
|
||||
@@ -476,6 +476,10 @@ needs the lost factor's code. So:
|
||||
|
||||
### Telegram — kind `telegram`, both seats, the away channel
|
||||
|
||||
- *Revised after the security review of 2026-10-08 (ADR 0259 §8, not yet accepted):* the router and every
|
||||
channel declaring `verified-sender` run as their own process and service account with a bus account of
|
||||
their own, never carried by a tool runner; the controller passes `verified-sender` only for a holder so
|
||||
placed, on a machine where no agent's account can become root without a person.
|
||||
- **Its own module**, `telegram`, holding the bot token as its own secret; the router keeps no Telegram
|
||||
client. *Amended 2026-10-08, ADR 0259:* **it runs on the control node under its own account `telegram`**,
|
||||
never root or the operator's: the tool runner starts its bundle as that account, its state directory and
|
||||
@@ -554,7 +558,7 @@ Each phase ends at its own *done when*. Owning repositories from [`repos.md`](..
|
||||
| **3 — The seats, and the router** | the kinded bench in the seat set; `kind` and `capabilities` on a claim; `channel-capabilities/1` and its contract tests; publishing on a seat's event subjects and its grant; `channel` and `intake` seats; the output seat's holder becomes the router and holds `channel/desktop` and `intake/desktop`; presence as current state; the operator's identity list and identity check in the controller, the router's `trusted` stamping and untrusted input (§4); the content rule allowing machine names, references and `detail`, refusals naming the offending part (§9) | mesh-controller (seat set, claim fields, registration refusals, grants, the identity list and check); mesh-sdk and mesh-tools (seat-event publishing in the shared library and the tool runner); mesh-catalog (the seats' definitions, the router) | the catalogue refuses an unknown capability and a second holder of one kind; a message is routed to the desk by capability and context; an envelope from an identity not on the list is re-emitted untrusted; a path is refused to its sender by name, and travels as a reference the desk opens |
|
||||
| **4 — Asks, and operator messages** | `ask`, `ask cancel`, `asks`, `answer`; the events; kinds, life, limits, batching, history; escalation; addressing, the register of agents with kept messages, the responder; the untrusted-input rule in every agent module's instructions (§5) | mesh-catalog | the router tests of ADR 0234 pass; live: an agent's question answered at the desk, and with the desk locked, on the phone; `@mesh status` answered; a message to a stopped agent kept and said |
|
||||
| **5a — Asks on the bus** (ADR 0259) | the kinded bench, caller-named verbs, `proof` subjects and the records users read (controller); a bundle's seat traffic, compare-and-set state and its own account (tool runner); the asker's and channel's types (shared library); the router's asks, tokens, identities, judging, warrants, expiry and linking with its hour (messenger); `telegram` and `desk-channel`; the controller asking for its conditions' actions and acting on warrants | mesh-controller, mesh-tools, mesh-sdk, mesh-catalog | a held delivery released from the phone; the hand-act reads "the operator, via telegram, user id verified"; a desk Silence goes through an ask |
|
||||
| **5 — Authorise, with TOTP** | `authorises` in the verb table; the router's checks for the code; direct calls refused, break-glass; `retire approve` takes `expect`; the hand-act fields; the TOTP seed as the controller's state, enrolled at a terminal with ten recovery codes; `factor recover`, `factor status`, the local-only break-glass enrolment; no P2 tier before recovery codes exist; optional key enrolment; the self-check probe for the away channel; agents' grants lose the authorising verbs | mesh-controller; mesh-catalog (agents' grants, the desk's code prompt) | one controller test per refusal passes; at the desk, approve needs a code; a recovery drill re-enrols with a recovery code, and is announced |
|
||||
| **5 — Authorise, with TOTP** | `authorises` in the verb table; the router's checks for the code; direct calls refused, break-glass; `retire approve` takes `expect`; the hand-act fields; the TOTP seed as the router's state, enrolled at a terminal with ten recovery codes; `factor recover`, `factor status`, the local-only break-glass enrolment; no P2 tier before recovery codes exist; optional key enrolment; the self-check probe for the away channel; agents' grants lose the authorising verbs | mesh-controller; mesh-catalog (agents' grants, the desk's code prompt) | one controller test per refusal passes; at the desk, approve needs a code; a recovery drill re-enrols with a recovery code, and is announced |
|
||||
| **6 — Telegram live** | the `telegram` module (built in 5a) with replies and forced-reply codes, under its own account on the control node; the operator's two bots configured; the watcher assigned off the control node; the dead-man ping | mesh-catalog | the self-check says the away channel carries every tier; live drills: an approve and a destroy on a test condition answered on the phone, with the hand-acts read |
|
||||
|
||||
**Not touched:** the node engine (`mesh-host` repository). Presence comes from the screen-lock seat's
|
||||
|
||||
Reference in New Issue
Block a user