Compare commits

...
1 Commits
Author SHA1 Message Date
jochen 6e306fcb4c To-be 40: building the operator's agent and its licence manager as work packages
Designs 36 and 39 say what is built; this says in which order and what proves each step, in the
shape to-be 38 gave the operator's machine. Seven packages: the operator states the facts (accounts,
roles, licences); the console provides its endpoint; the licence manager and the agent module are
built and unit-tested in parallel; the manager goes live on the control node; the agent on one
workstation, with the switch and the predecessor's files removed as the proof of the whole; then the
rest of the nodes and the retirement of the two catalogue modules built on the old placement. The
live proofs wait for to-be 38's WP3, because both modules' tools run in the node's tool runtime
(ADR 0175) and a per-module tool container would rebuild what that record retires.
2026-10-02 17:26:48 +02:00
@@ -0,0 +1,226 @@
---
layer: to-be
status: designed
code: []
updated: 2026-10-02
decisions:
- 02-DECISIONS/0183-the-anthropic-licence-manager-is-a-module-and-hands-tokens-to-the-agent-over-the-bus.md
- 02-DECISIONS/0181-the-operator-account-is-a-node-fact-and-a-home-is-a-placement-root.md
- 02-DECISIONS/0182-inside-a-home-the-mesh-owns-what-it-places-and-holds-the-rest-as-found.md
- 02-DECISIONS/0175-one-tool-runtime-per-node-serves-every-modules-tools-on-the-host-side.md
- 02-DECISIONS/0149-the-live-mesh-is-the-test-bed.md
- 02-DECISIONS/0027-a-provision-names-what-the-consumer-is-coupled-to.md
---
# 40. Building the operator's agent and its licence manager
**The work of [design 36](36-the-operators-agent-on-a-machine.md) and [design 39](39-the-anthropic-licence-manager.md),
broken into packages that each end at something a person can see run, in the order their
dependencies allow.** The two designs are the authority on *what* is built; this document holds the
packages, their order, their sizes and their proofs, and is wrong the moment it disagrees with them.
It is the shape [design 38](38-building-the-operators-machine.md) gave the operator's machine, applied
to the two modules that make its agent work.
## How this is built, and where it is run
**On the live mesh, by the operator's decision** ([ADR 0149](../../02-DECISIONS/0149-the-live-mesh-is-the-test-bed.md),
and design 38's words on the same day). Every package is written with unit tests, committed on one
branch per repository ([playbook 07](../../00-META/process/07-feature-branches.md)), and proven on the
machines: the control node first for the manager, one workstation first for the agent, then the rest.
The cost accepted: a broken agent module leaves a workstation's agent without the mesh's instructions
or with a stale token until the next push; the person's own files under the home are never in reach of
the failure, by [ADR 0182](../../02-DECISIONS/0182-inside-a-home-the-mesh-owns-what-it-places-and-holds-the-rest-as-found.md).
Each package names what proves it. A package that cannot name its proof is divided until it can.
## What exists already, measured
Counted 2026-10-02 in the repositories and on the machines. Nothing here is new ground; every package
reshapes something standing.
| Piece | Today | Becomes |
|---|---|---|
| the vendor's refresh, the sealed box, the grant file | `anthropic-manager` in the catalogue: a token-endpoint client, a sealed-box primitive, adoption of a grant sealed to a node's key; assigned to nothing | the manager's refresh and adoption, with the lease, the floor and the cadence the predecessor's manager had |
| the credentials write, the refresh-token strip, the identity read | `anthropic-consumer` in the catalogue: tested; assigned to nothing | the agent module's write, unchanged in shape |
| the predecessor's manager and consumer | two modules in the retired system: the lease per licence, the expiry floor, the lineage comparison, the identity guard, three touchpoints with fallbacks, cooldowns on alarms | ported as logic with its tests; nothing of its registry or its bus |
| the host's `process` and `archive` shapes | fetch a bundle by digest and run it supervised; fetch and unpack an artifact | **unchanged** — the manager's daemon is one process; both modules' tools are bundles |
| the manifest's `uses`, `claims`, `invokes seat:<seat>.<verb>`, node-scoped `provides` with `serves: {port}` | all four exist and are used by other modules | **unchanged** — the agent uses the seat and invokes its verbs; the console provides its endpoint |
| the console | a container per node, MCP on loopback, no provision | gains one provision; becomes the node-tools runtime's serving mode under design 38's WP3 |
| the agent's package | present on both workstations from a build the predecessor's helper made; the distribution's repositories do not carry it | declared; satisfied where present, refused where not, until a package repository seat exists |
| the operator account | a column on every node record, **empty on all four** | stated by the operator, before anything home-scoped lands |
**One dependency decides the order.** Both modules serve tools and the agent module's tools write
under `/etc` and, as the operator, under the home. Under [ADR 0175](../../02-DECISIONS/0175-one-tool-runtime-per-node-serves-every-modules-tools-on-the-host-side.md)
tools run in the node's tool runtime, host-side, which design 38 builds in its WP1–WP3. Writing a
per-module tool container for these two modules would be building the pattern that record retires, so
**the live proofs of WP3 to WP5 below wait for design 38's WP3.** Everything before a live proof —
manifests, code, tests — does not, and is written now.
## The order the work allows
```
WP0 the operator states the facts (the live mesh) ── accounts, roles, licences to adopt
WP1 the console provides its endpoint (mesh-catalog) ── small, independent
WP2 the licence manager, built and tested (mesh-catalog) ──┐ independent of each other;
WP3 the agent module, built and tested (mesh-catalog) ──┘ both wait on design 38 WP3 to run
│
WP4 the manager live on the control node (the live mesh) ── three licences adopted, a refresh seen
WP5 the agent live on one workstation (the live mesh) ── the hand-over, the switch, the instructions
WP6 the rest of the nodes, and the predecessor's remains ── adoption from a login, the retirements
```
WP1, WP2 and WP3 touch different directories of one repository and meet only at the seat's name and
the provision's name; they are built in parallel. WP4 is the first time anything on a machine changes.
WP5 is the proof of the whole.
## WP0 — The operator states the facts
*The live mesh. An hour, and it is the operator's.*
The account on each node record, through the controller's node command — none is stated today, and
[ADR 0181](../../02-DECISIONS/0181-the-operator-account-is-a-node-fact-and-a-home-is-a-placement-root.md)
refuses a home-scoped module without one. The role of each node, as the agent module's setting on the
node layer, once the module exists. Which three licences exist and what each is called.
**Proof.** The controller's node command lists an account for every node.
## WP1 — The console provides its endpoint
*mesh-catalog. Half a day.*
**What changes.** The console's manifest gains a node-scoped provision — working name
`console-endpoint`, fixed when the manifest is written — serving the port the machine gave it, as the
local model server already does for its API. Co-location resolves it
([ADR 0027](../../02-DECISIONS/0027-a-provision-names-what-the-consumer-is-coupled-to.md),
[to-be 34](34-the-console.md) as amended). When design 38's WP3 moves the console into the node-tools
module, the provision moves with it; it is a line in a manifest either way.
**Proof.** The controller's plan for a workstation shows a consumer of the provision bound to the
console's port; the same consumer on a machine without the console is refused naming the provision;
the catalogue's tests pass.
## WP2 — The licence manager, built and tested
*mesh-catalog. Two to three days; the largest package.*
**What is written**, as design 39 says:
1. **The manifest.** Claims the mesh-scoped seat `anthropic-licence-manager` with its verbs; requires a
database, a `secret` for the key its grants are encrypted with, and the bus; a `bundle` of tools; a
`process` for the daemon that refreshes, collects usage and notifies, on a schedule; declared
settings for the cadence, the usage threshold and the cooldown, each with a default; `invokes` the
agent module's `apply`.
2. **The store.** Migrations for licences, bindings, usage and audit, with the lease and the
notification slot as columns, numbered and idempotent.
3. **The refresh.** The token-endpoint client and the sealed box from `anthropic-manager`; the plan
(floor, cadence, forced, cannot) and the lease from the predecessor, as pure functions with their
tests; the vendor's reason logged on failure; counted failures, one notification per cooldown.
4. **Adoption.** From a file on the manager's node for the API key; from a sealed grant a node offers;
the identity guard that refuses a mismatch and notifies.
5. **Usage.** The vendor's reading per licence on a schedule, stored raw and normalised
([ADR 0054](../../02-DECISIONS/0054-model-usage-is-recorded-at-two-grains.md)), one notification
per threshold crossing.
6. **The verbs**: `licences`, `bindings`, `bind`, `switch`, `release`, `refresh`, `usage`, `adopt`,
`register`, `current` — the last answering a consumer's token sealed to the key that consumer
registered.
7. **The hand-over**: on rotation or switch, one call to `claude-code.apply@<node>` per bound node,
the token sealed to that node's key, the answer recorded.
**Proof, before anything runs live.** Unit tests: two refresh runs started together rotate one grant
once; a grant with a mismatching identity is refused; a worker bound to a dead licence is refused and
never lent another; every event the daemon emits is free of a token; the hand-over payload opens only
with the registered key. The catalogue's checks: no installation named, no secret in a declared file.
## WP3 — The agent module, built and tested
*mesh-catalog. Two days.*
**What is written**, as design 36 says:
1. **The manifest.** The agent's package; the state directory; the facts file carrying the node's
name, the operator account and its home, the console's bound port, the role and the extra tool
servers from settings; requires the console's endpoint and the bus; `uses` the seat and `invokes`
its `register`, `current` and `adopt`; a `bundle` of tools. **No file resource under a home or
under `/etc`.**
2. **The renderer.** From the facts file and the current binding, the managed settings file (the tool
servers under the entry `mesh`, the attribution trailers, and the key-helper for an API-key binding)
and the managed instruction file (§3 of design 36), written under the agent's managed directory
with the escalation the tool performs for itself; idempotent; re-run when the facts file changes.
3. **The keypair**, made once in the state directory, the public half registered with the seat at
start and at every start.
4. **The consumer side**: `apply` (a rotation applied only if newer within one lineage, a switch applied
regardless, the answer naming the outcome and never a token); the pull at start and near expiry;
the credentials write as the operator, access-token-only, from `anthropic-consumer` with its tests;
the key-helper program for the API key; the offer of a login to the seat, sealed, after reading the
account's identity.
5. **The tools**: `apply`, `licence_status`, `mcp_configure` (validates a server, sets the module's
setting through the controller's settings verb), `render` (re-render now, for a person).
6. **The documentation**: the six predecessor files and the hand-made console entry a person removes
on a workstation that carried the predecessor.
**Proof, before anything runs live.** Unit tests: the renderer writes only the mesh's keys and leaves
every other key of a seeded settings file; the credentials write strips a refresh token and is atomic;
the lineage comparison from the predecessor, with its cases; a login offer carries the identity it read.
The catalogue's checks pass.
## WP4 — The manager live on the control node
*The live mesh. Half a day, after design 38's WP3.*
**Order.** Assign the manager on the control node; push. Adopt the API key from a file there. Adopt
the two subscription grants: a login in a throwaway home on the control node, offered to the seat the
way a node's agent module will. Bind each node's agent to a licence.
**Proof.** Through the console: `anthropic-licence-manager.licences` lists three licences with
identity and expiry; within the cadence, the audit shows a rotation and `licences` shows a later
expiry; a forced `refresh` on one licence is logged with the vendor's answer; the two retired catalogue
modules are still assigned to nothing.
## WP5 — The agent live on one workstation
*The live mesh. Half a day. The proof of the whole.*
**Order.** Set the workstation's role in the module's settings. Record the checksums of everything
under the person's agent directory. Assign the module; push. Remove the six predecessor files and the
hand-made console entry. Start a new session.
**Proof.** The managed directory holds the settings and instruction files, owned by root. Everything
under the person's agent directory is byte-identical to before except the credentials file, which is
owned by the operator, readable by nobody else, and names no refresh token. The new session lists the
mesh's tools under `mesh` once, answers *which node am I* from the instruction file, and makes a model
request. `anthropic-licence-manager.switch` to the second subscription licence changes the token on
the workstation within a minute, and neither the verb's answer nor either module's log holds a token.
Switched to the API-key licence, the credentials file is left as it was and the agent authenticates
through the key-helper. Switched back.
## WP6 — The rest of the nodes, and the predecessor's remains
*The live mesh and mesh-catalog. One day.*
**Order.** Assign the module on the second workstation and on the servers whose account is stated;
remove the predecessor's files on the second workstation. Log in on a workstation under a licence's
account and watch the offer be adopted — and under the wrong account, and watch it refused and
notified. Retire `anthropic-manager` and `anthropic-consumer` from the catalogue. Set designs 36 and
39 to `implemented` for what runs, with the as-is written
([playbook 02](../../00-META/process/02-graduation.md)).
**Proof.** Every node with an account runs the module and `licence_status` answers on each; the
refused login's notification arrived; the catalogue has no module built on the old placement.
## What is deliberately not here
- **The package repository seat** for a distribution that does not carry the agent's package
(design 36 §7). A fresh node refuses the module in the package manager's words until it exists.
- **An automated switch on exhaustion.** The readings are kept from WP4; the policy is a later record.
- **Workers and the mesh's own sessions as consumers.** The manager's bindings and fallbacks know them
from WP2; the consumers themselves do not exist yet ([to-be 15](15-the-agent-session.md),
[ADR 0003](../../02-DECISIONS/0003-agents-are-persistent-employees.md)).
- **Whether a refresh token is single-use.** WP4 may measure it on a licence deliberately refreshed
twice; the design holds either way.
## How this list is kept true
Each package's proof is run when the package is finished and its line here gains the date and the
commit, as design 38 does. A package whose proof fails is not reworded; the failure is recorded under
it and the package stays open. When WP5 is proven, designs 36 and 39 move to `in-progress` with their
owning repository, and when WP6 is proven to `implemented`, with the as-is written.