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".
143 lines
10 KiB
Markdown
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
|