diff --git a/03-DESIGN/01-to-be/58-agent-work-as-the-meshs-own.md b/03-DESIGN/01-to-be/58-agent-work-as-the-meshs-own.md new file mode 100644 index 00000000..84af84f3 --- /dev/null +++ b/03-DESIGN/01-to-be/58-agent-work-as-the-meshs-own.md @@ -0,0 +1,155 @@ +--- +layer: to-be +status: designed +code: [] +updated: 2026-10-16 +decisions: + - "ADR 0383" + - "ADR 0384" + - "ADR 0385" + - "ADR 0386" + - "ADR 0387" + - "ADR 0388" + - "ADR 0389" + - "ADR 0390" +--- + +# 58 — Agent work as the mesh's own + +**An agent's work belongs to the mesh, not to one vendor's program or one machine. A thread is kept whole on the +bus by a thread seat; a thread handler on an always-on node runs the agent's harness headless for it, in a sandbox +of its own, with the thread's own branch on the forge as its workspace; the agent works through a workspace toolkit +and named tools, never a general command runner. Every person, agent and module is an identity held by one identity +seat, with a role made of grants; the bus checks each login by its signature, and the tool runner checks each call +against the role, tool by tool, by the effect every tool carries.** +(ADR 0383 to ADR 0390, from research 053.) + +**Acceptance.** The operator starts a thread from the terminal client, an agent on an always-on node works it in a +sandbox, asks a question the operator answers in place, and the thread moves to another node and carries on with +nothing lost; the agent's calls outside its role are refused, saying which grant is missing. + +## 1. The parts + +``` + any node always-on nodes the control-node + ───────── ─────────────── ──────────────── + front ends nox-thread-handler nox-threads (seat thread) + ├ terminal client (first) ├ takes a thread's lease ├ a restricted stream per thread + ├ web page (then) ├ runs the harness headless ├ the object store for large content + └ Telegram: questions, │ inside a microsandbox └ leases in the bus's key-value store + approvals ├ answers permission requests + │ │ through the mesh's asks identity seat + │ └ commits + pushes the workspace ├ persons, agents, modules, roles + │ branch every turn │ in the bus's key-value store + │ nox-workspace (node seat workspace) ├ signs logins (one key per role) + │ └ read, glob, search, edit, write, └ revokes over the bus, reads back + │ git, named tools; no command runner + │ claude-code + │ ├ mirrors a session into its thread + │ ├ rebuilds a session on pick-up + │ └ writes the login and licence token + │ into the sandbox + └──────────────── every call over the bus, carrying its thread and, + for a module acting for an agent, the agent's token +``` + +## 2. Threads (ADR 0383) + +A **thread** is one piece of agent work, kept as a stream on the bus in the mesh's own format. The stream is the +source of truth: the conversation, the agent's questions and their answers, tool calls and results, usage, and +every change of state. Large content goes to the object store and is named from the stream. The stream is +restricted (ADR 0376): only modules granted it read it. A thread is never deleted; the trail is kept for good. + +A thread is **open**, **assigned** (to a node and a harness), **released** or **stopped**. Its seat's verbs create, +assign, release, stop, move it, and take it over with a reason. The assignment is a **lease** in the bus's +key-value store, changed only by a compare-and-set on its revision. Its holder renews it every 20 s. A lease not +renewed for 60 s is said to the operator; nothing takes it over by itself. + +## 3. Running a thread (ADR 0384) + +The thread handler on an always-on node takes the lease and starts the harness (Claude Code today) headless, one +turn at a time. It answers the harness's permission requests through the mesh's asks, so a front end answers them in +place. At the end of every turn it commits and pushes the workspace, which is the thread's own branch on the forge; +a node that picks the thread up fetches it, so a move loses at most the turn in progress. Before a shutdown it +releases at a turn boundary; on a stop it ends the process group, since a tool's child process outlives the harness. + +The claude-code module mirrors the session's stream into the thread and, on pick-up, rebuilds a session from the +thread. Usage per thread comes from each run's result and rate-limit messages on the stream, not the transcript, and the +licence manager records it with the thread as its session. + +The front ends are clients of a thread doing the same work: the terminal client first, then the web page, and +Telegram for questions and approvals. Their first scope is the conversation as it streams, the agent's questions and +permission requests answered in place, sending a message and stopping a turn. The rest is tracked by issue 850. + +## 4. The workspace toolkit (ADR 0385) + +The node seat `workspace`, held by `nox-workspace` on every thread-handler node and served through the mesh's MCP +server, works in the thread's workspace only, by relative path: read, glob, regex search, edit, write, git, and named +tools such as running the repository's tests and its merge check. It is built early. There is no general command +runner in it, and no module offers one to an agent: a command an agent needs becomes a named tool in the module that +owns it. Claude Code's own built-in tools, its command tool included, are turned off once the toolkit is in. + +## 5. Identities and roles (ADR 0386) + +The identity seat, held once for the mesh, owns every **identity** (a person, an agent or a module) and every +**role**, in the bus's key-value store. An identity has a name, a kind, a role and whether it is switched on. They +change only through the seat's verbs, each recorded with who asked; anything may watch the store live. Only the +operator creates a person or an agent, or changes its role. A module's identity is asked for by the controller when +the module is assigned to a node, named by machine and module, with a role made from what its manifest declares it +calls. + +A role holds **grants**. A grant is an effect everywhere, an effect on one seat, or one named tool, and may be +limited to named machines. Two fences check them: the bus, coarsely, by seat and module, from the role's signing +key; and the tool runner, exactly, tool by tool, in the SDK. Every call carries its thread; a module acting for an +agent carries a token the identity seat signed for that agent, and the tool runner then checks the agent's role. A +refusal names the missing grant; every call is recorded with its identity and thread. + +## 6. Effects, asks and break-glass (ADR 0387) + +Every seat verb and module tool is one permission and carries one **effect**, set in its module's manifest: +**reads**, **acts** or **dangerous**. The catalogue's check refuses an unmarked tool, once every module already in +the catalogue is marked. A dangerous tool is granted only by name, and naming one in a role goes to the operator as +an ask first. Every other dangerous act is an ask (ADR 0259), bound to that act and carried out on the warrant. + +The **break-glass** agent holds every tool and never asks. It is sealed until the operator opens it, or a condition +the operator has named as opening it is raised (none is named at the start); it stays open until the operator closes +it, and every act it takes is said to the operator at once. A tool served without an effect, from a module built +before its mark, counts as dangerous. + +## 7. Signed logins on the bus (ADR 0388) + +The bus checks each login by its signature and reads no list. The root key is sealed in the vault and used only to +replace the identity seat's signing keys, on the operator's Approve. The seat signs with one scoped signing key per +role, so every login stays small and each identity gets its own inbox from the role's template. An agent's login +lives minutes, a module's and a node's hours; each is renewed by a reconnect forced before expiry, by the handler or +the node-engine. + +A switched-off identity is revoked over the bus. The seat reads back every update, sends updates at least a second +apart, and resends the current state to every server id it has not seen on the system account, which every joined +server carries. This changes ADR 0106's rule that the bus is configured only through its settings file. + +## 8. The switch (ADR 0389) + +A new bus with signed logins runs beside today's, joined as a leaf node carrying the system account. Identities move +one at a time, each checked before the next. Streams, key-value buckets and the object store move last, each by a +copy of its whole configuration: copy, wait for equal counts, stop its writers, then one update hands the addresses +over. Today's bus is switched off when nothing is left on it. + +## 9. Sandboxes (ADR 0390) + +Each session runs in a microsandbox of its own, its version pinned and declared in a module. The workspace is +mounted; the claude-code module writes in the agent's login and the licence token without its refresh part, and +replaces the token as the licence manager refreshes it. Connections are allowed only to the vendor's interface, by +address range, and to the bus. A node whose KVM modules are not loaded gets them from the sandbox module. + +## 10. The build's acceptance tests + +The build is not done until each of these has been shown, and the milestones of the work breakdown (playbook 04) +carry them: + +1. A licence token replaced under a running session keeps that session working. +2. The sandbox module loads KVM on the home server, and a sandbox runs there. +3. Every existing module's tools carry an effect, and the catalogue's check refuses an unmarked one. +4. The switch, run on the mesh's own modules in the lab from the bed test kept on the lab's branch + `research/053-signed-bus-logins`, loses nothing and leaves every identity on the new bus. +5. The acceptance sentence at the top, end to end. diff --git a/03-DESIGN/01-to-be/README.md b/03-DESIGN/01-to-be/README.md index 95b3e4da..d8fe9fc8 100644 --- a/03-DESIGN/01-to-be/README.md +++ b/03-DESIGN/01-to-be/README.md @@ -57,6 +57,7 @@ document is written and this one's status becomes `implemented`. | [`55-the-knowledge-base.md`](55-the-knowledge-base.md) | **Designed.** The mesh keeps its decision records and research efforts whole in one module, `mesh-knowledge`, on the control-node: number, status, topic, typed links both ways, each decision's clauses with their own status, and the reasoning, with every version; a record is proposed, checked at the write, reviewed by the knowledge base's own judgement and accepted there, with the operator's Approve when it needs it; agents ask what holds in one bounded call | ADR 0290 | | [`56-the-mail-client-set-up-by-the-mesh.md`](56-the-mail-client-set-up-by-the-mesh.md) | **Designed.** On every node with a display server, the `betterbird` module installs Betterbird from a pinned upstream release mirrored into the artifact store, ships the Owl add-on pinned and installed by Betterbird's enterprise policy, and declares the operator's mail accounts in a trusted setting without credentials; each account signs in once at a person's first use, and one the profile already holds is adopted by its address | ADR 0316, ADR 0317 | | [`57-building-config-and-provisioning.md`](57-building-config-and-provisioning.md) | **Designed.** The work of ADR 0348 and ADR 0349 as 37 packages, each an issue with its proof: foundations, template files, config in its own module, the provisioner and its client, secrets in the vault, bindings from providers, collections, a module's runtime readers, and removing the old forms; at most five worked at once | ADR 0348, ADR 0349 | +| [`58-agent-work-as-the-meshs-own.md`](58-agent-work-as-the-meshs-own.md) | **Designed.** Agent work as the mesh's own: a thread seat keeps every thread whole on the bus; a thread handler runs the harness headless in a sandbox per session, the workspace on the thread's own forge branch; a workspace toolkit and named tools, no general command runner; one identity seat for persons, agents and modules, roles of grants, every tool an effect, signed bus logins, and the switch to them one identity at a time | ADR 0383, ADR 0384, ADR 0385, ADR 0386, ADR 0387, ADR 0388, ADR 0389, ADR 0390 | ## Not yet written