Files
hq/02-DECISIONS/0171-the-mesh-binds-and-delivers-a-licence-and-the-module-writes-the-tools-credential.md
T
jochen 8e27116cd0 The operator's agent is a module: three records and to-be 36 for the claude-code successor
The predecessor's agent module was retired and its six files stayed on both workstations telling
every session to use tools that no longer exist. Before a successor module is written, the design
needs the decisions it rests on and nothing in the record stated them:

- ADR 0169 (reconstructed) records what the controller shipped on 2026-09-27 without a record: the
  operator account is a node fact stated by the operator, the home is derived unless stated, a
  resource may be placed under it owned by the account, and a node with no account refuses one.
- ADR 0170 generalises to-be 29 §3's found-vs-owned boundary to every directory under a home: the
  module owns the directory and the files it places, writes into the tool's own files for its few
  keys, never declares a credential's content, and holds everything else as found — a predecessor's
  leftovers included, which the operator removes once.
- ADR 0171 draws the licence line the operator asked to have drawn rather than assumed: the mesh
  binds and delivers (to-be 14 and 15 stand), the module alone writes the credential file, refresh
  stays central (ADR 0050), a switch is the binding changed through a controller seat verb asked
  for via the console, and the token-carrying shell helper is retired. The controller learns
  nothing about the agent; that is what "no part" means.

To-be 36 is the module's design: the ownership map per path, the fate of the six predecessor
files, what the three instruction documents say, the licence tools and skill, the console as a
node-scoped provision, the package gap stated honestly, and the order of the build. To-be 14, 29
and 34 carry dated notes; the glossary gains "operator account".
2026-10-02 13:30:02 +02:00

143 lines
10 KiB
Markdown

---
topic: what runs on it
status: accepted
date: 2026-10-02
deciders: jochen
reconstructed: false
extends: 02-DECISIONS/0024-model-access-is-a-provision.md
---
# 171. The mesh binds and delivers a licence; the module alone writes the tool's credential; a switch is the binding changed, asked for through the console
## Context
**The operator's stance, set on 2026-10-02:** the controller has no part in the agent module. The
module owns the agent's directory under the operator's home and every related file, handles them
itself, and carries a licence-switching function as the predecessor's did.
**What the predecessor's switching actually was.** A registry of accounts held server-side; a tool,
callable from a session, that decrypted the chosen account's token on the server, refreshed it if near
expiry, and wrote the agent's credentials file on the target node — never returning the token. Beside
it, a shell helper that ran the agent with a token read from a plaintext file in the operator's own
configuration directory, one token per account, on every workstation; and an enrolment helper that
logged in once in a throwaway home and registered what came out. So the central half did the
refreshing and the writing; the node held nothing it could refresh with; and the convenience path kept
every account's token readable on disk wherever it was wanted.
**What the mesh has.** [To-be 14](../03-DESIGN/01-to-be/14-model-access.md) is built as far as it goes:
a licence is a named record with a vendor; a consumer is a module on a node and is put on one licence;
the access token is sealed per holder and delivered to the holder's machine; for a refreshable grant
the manager node alone holds the refresh token, encrypted, and refreshes centrally — the one stated
carve-out of [ADR 0050](0050-model-access-is-vendor-agnostic.md). The controller has commands to add a
licence, put a consumer on it, release it, accept a key, set a manager, set and refresh a grant. **None
of them is a verb on the `mesh-controller` seat**, so none can be asked for through the console
([ADR 0154](0154-the-meshs-own-verbs-are-the-controller-seats-tools.md) exposes eighteen commands and
not these). The catalogue has the manager module and a consumer module that already writes an
access-token-only credentials file at a path it is told; both are assigned to nothing.
**Why refresh is central and must stay so.** A refreshable grant rotates its refresh token on use. Two
machines each refreshing one account's grant race: the second refresh presents a token the first
retired. The predecessor refreshed centrally for this reason, and ADR 0024 kept that half on purpose
(*the hard half of this already — and it works*). ADR 0050 narrowed the consequence to one node.
**The two designs are not in conflict, and the line has to be drawn in a record.** The controller
resolving *whose* home a file lands in ([ADR 0169](0169-the-operator-account-is-a-node-fact-and-a-home-is-a-placement-root.md))
and *which* licence a consumer holds (to-be 14) is what the controller does for every module. "No
part" cannot mean that, or the module could not be assigned. It can mean — and this record says it
means — that **the controller learns nothing about the agent**: no file shape, no path, no key, no
word beyond the vendor adapter it already has.
## Considered Options
1. **The module keeps its own registry of accounts and tokens**, the stance read literally. Rejected: a
second secret store outside the vault ([ADR 0113](0113-the-vault-makes-every-secret.md)); a refresh
token on every workstation, widening ADR 0050's one-node carve-out to every machine a person sits
at; and two records of one licence, which drift.
2. **The agent refreshes itself**: the mesh delivers a full grant once at a switch and the agent's own
refresh keeps it alive. Rejected: the refresh race above, between the agent and the manager and
between two machines on one account; and every node then holds a refresh token, which ADR 0050
decided no node does.
3. **The mesh binds and delivers; the module writes; a switch is the binding changed, asked for through
the console.** Chosen.
## Decision
**The consumer is the module on the machine: the operator's interactive sessions on that node, under
that account, hold one licence at a time.** That is to-be 15's `(node, module)` identity, with the
agent module as the module. Two machines may hold different licences, the ordinary case. The mesh's own
sessions on a machine are other modules and hold theirs in their own right.
**The mesh delivers; the module writes.** The module requires `model-access`. The mesh resolves the
licence the consumer is on, delivers the access token sealed to the machine as a secret in the module's
own state, and delivers the non-secret facts — the licence's name, what it serves — beside it. **The
module's own process writes the agent's credentials file** from the delivered secret: under the
account's home, owned by the account, readable by nobody else, written atomically, and access-token-only
— a refresh token found there is removed, because a node never holds one (ADR 0050). The file's content
is never a declared file's content ([ADR 0170](0170-inside-a-home-the-mesh-owns-what-it-places-and-holds-the-rest-as-found.md)).
The step runs when the delivered secret changes and on a schedule as a backstop, so a refreshed token
reaches the file without anyone asking.
**The controller learns nothing about the agent.** Where the file is, what shape it has, what the
agent calls its keys, how it is told about the console — all of that is the module's definition and
code. The controller contributes the facts it contributes to every module: the account, the home, the
licence, the delivery.
**A switch is the binding changed.** Putting the consumer on another licence is the mesh's existing
act — *use this licence, for this consumer* — and it becomes a verb on the `mesh-controller` seat the
way the other verbs did (ADR 0154): the command it already has, served on the bus, listed by the
console. The module serves two tools of its own: one that reports which licence the machine holds and
when its token expires, and one that invokes the seat's verb for a named licence and then waits until
the credentials file carries the new licence's token, answering with the licence's name — **never the
token, in any answer, log or event**. A skill in the agent's directory wraps the second so a person
asks in a sentence. Switching remains a reaction, not a declaration (ADR 0024): a person asks for it,
and nothing in the declaration language grows a conditional.
**Enrolling an account is the mesh's act on the manager node.** A new licence is added by name, its
grant obtained by a login in a throwaway home on the manager node and adopted sealed to that node's
key, as the manager module already does. No token is pasted into a prompt, printed, or passed as an
argument (to-be 14's rule for keys).
**The shell helper that read tokens from a file is retired, not replaced.** A second concurrent
session on the same machine under a different licence would need a second consumer identity — the
unbuilt half of to-be 14's gap — and is not provided here. Stated so it is not rediscovered as a bug.
**A licence the mesh no longer grants is withdrawn** at the binding (ADR 0024). The credentials file
the module wrote is the module's own output: unassigning the module leaves it, like the agent's other
files, and the access token in it expires within hours. Releasing the consumer from the licence is the
act that ends its access.
## Consequences
- The agent on a workstation authenticates with a token the mesh delivered and refreshes centrally,
and no workstation holds a refresh token or any other account's token.
- **The manager must run.** The refresh path exists in the catalogue and is assigned to nothing; it is
a prerequisite of this record, on the control node, and the first thing the build proves.
- **The controller gains a verb, not knowledge.** The licence commands become seat verbs, each
running the command it names, as ADR 0154 did for the others; nothing in them is about the agent.
- A switch is a round trip — binding, composition, push, apply — rather than the predecessor's direct
write: seconds to a minute, and reported when done rather than assumed.
- **What got harder:** running two sessions on one machine under two accounts at once, which the
retired helper allowed by keeping tokens readable. The price of not keeping them so.
## How it is checked
| Rule | Checked by |
|---|---|
| The module's definition declares no secret in a file's content, and requires `model-access` | a catalogue test on the module's definition |
| The credentials file is access-token-only, owned by the account, atomic | the consumer module's existing unit tests on the strip and the write, carried into this module; a live check that the file names no refresh token |
| A switch through the console changes the licence and the token, and no answer carries a token | a live check: the bound facts name the new licence, the file's fingerprint changes, the tool's answer and the module's log contain neither token |
| The controller's licence verbs run the commands they name and carry no agent vocabulary | the seat verb's test, as for the eighteen before it |
| The refresh path is live before the module is | the manager assigned on the control node and a refresh observed in the licence's record, before the module's first assignment |
| No workstation holds a refresh token | the live check above, on every machine the module is assigned to |
## References
- [ADR 0024](0024-model-access-is-a-provision.md), [ADR 0050](0050-model-access-is-vendor-agnostic.md),
[ADR 0055](0055-model-access-is-answered-by-a-licence-or-a-node.md) — what a licence is, who refreshes, what answers
- [to-be 14](../03-DESIGN/01-to-be/14-model-access.md), [to-be 15](../03-DESIGN/01-to-be/15-the-agent-session.md) — the consumer identity and the gap this leaves where it is
- [ADR 0154](0154-the-meshs-own-verbs-are-the-controller-seats-tools.md), [ADR 0152](0152-the-operators-surface-is-a-module-the-console.md) — how a verb reaches a person
- [ADR 0113](0113-the-vault-makes-every-secret.md) — why there is no second registry
- [ADR 0170](0170-inside-a-home-the-mesh-owns-what-it-places-and-holds-the-rest-as-found.md) — the class the credentials file is in
- the predecessor's `claude-code` module: its switch tool, its shell helpers and the rules of its skill
- mesh-catalog `modules/anthropic-manager`, `modules/anthropic-consumer` — the refresh and the write, as built