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 |
|
||||
|
||||
Reference in New Issue
Block a user