ADR 0209: a login on a node moves that node to its account; an API key is added from any node, sealed

Traced live: a login to a second account was adopted and left its node
bound to the first, holding a spent refresh token. Designs 36 and 39
amended.
This commit is contained in:
jochen
2026-10-04 15:09:56 +02:00
parent 438162b5a5
commit 2e5f40c9fa
4 changed files with 98 additions and 5 deletions
@@ -0,0 +1,80 @@
---
topic: what runs on it
status: accepted
date: 2026-10-04
deciders: jochen
reconstructed: false
extends: 02-DECISIONS/0206-a-node-reports-the-anthropic-grant-it-holds-and-the-licence-manager-adopts-a-licence-by-refreshing-it.md
---
# 209. A login on a node moves that node to the account it logged in to; an API key is added from any node, sealed
## Context
[ADR 0206](0206-a-node-reports-the-anthropic-grant-it-holds-and-the-licence-manager-adopts-a-licence-by-refreshing-it.md)
made a licence an account the manager learns from what the nodes report, adopted by refreshing it, and
bound a node to a licence automatically only when it was bound to nothing (§7); every later change was a
person's act through `bind` and `switch`. It went live on 2026-10-04 with one account, bound to all four
nodes.
**The operator then asked what happens on a login to a second account on one node, and traced, the
answer was wrong.** The manager adopts the second account as a new licence — and leaves the node bound to
the first. The node is left holding the second account's access token, a refresh token the adoption just
spent, and a binding to the first; at the first account's next rotation it is handed a token its own state
file does not name. A login is the most direct thing a person does on a machine about which account it
uses, and the mesh read it as a contribution of a grant only.
**An API key could enter only from a file on the manager's node** (ADR 0206, design 39 §6), so adding one
meant reaching that machine. The operator asked for a streamlined process for both.
## Considered Options
1. **A login on a node switches that node to the account logged in to.** Chosen.
2. **Keep ADR 0206 §7, and have the person `switch` after logging in.** Rejected: the step is easy to
forget and the state between the login and the switch is the broken one described above.
3. **Refuse to adopt a login for an account other than the node's binding.** Rejected: it discards what
the person plainly meant, and a second account could then enter only by a separate act.
## Decision
**1. A login on a node is that node's choice of account.** When the manager adopts a node's login (ADR
0206 §4) — a new account, or a newer login of one it holds — it binds that node to the licence the login
belongs to. If the node was bound to another licence, this is a switch: the node is handed the new
licence's access token and its agent's account is pointed at it, as `switch` does. Every other node stays
where it is. `bind`, `switch` and `release` remain for moving a node without a login.
**2. A login that does not refresh moves nothing.** The candidate is recorded dead (ADR 0206 §4) and the
node keeps its binding; the person logs in again.
**3. An API key is added from any node, sealed, never as an argument.** The agent module serves a tool
that reads a key from a file on its own node, seals it to the manager's public key — which the seat now
serves as a verb — and hands it to the seat's `adopt` on request/reply; the file is removed once the
manager has taken it. Optionally the same call binds that node to the new licence. The seat's `adopt`
still also takes a file on the manager's node. An API key is a licence of its own, never an account's:
nothing is learned about it from a report, and it moves a node only when a person says so.
## Consequences
- Logging in on a node is the whole of moving that node to an account, new or known. The mesh's state
stays consistent: the binding, the token on the node and the account its agent names agree.
- A second account enters the mesh by one login, and only the node it was logged in on uses it.
- **What got harder:** a person who logs in on a node to try an account moves that node; moving it back is
`switch`. Said in the seat's own description of `switch`, and in the agent module's instruction file.
- The key file on a node exists only until the manager has taken it; the key then lives encrypted in the
manager's store alone (ADR 0183).
## How it is checked
| Rule | Checked by |
|---|---|
| A login for another account moves its node and no other | the manager's test: two accounts, the login on one node adopted, that node switched, the others unchanged |
| A newer login of a known account on a node bound elsewhere moves that node | the manager's test |
| A login that does not refresh moves nothing | the manager's test: the binding unchanged, the candidate dead |
| An API key never crosses the bus in the clear and its file is gone afterwards | the agent module's test: the request carries a sealed box only; the file is removed after the seat answered |
| Live | a login to a second account on one workstation: a second licence appears, that workstation is bound to it and its agent names it, the other nodes keep the first |
## References
- [ADR 0206](0206-a-node-reports-the-anthropic-grant-it-holds-and-the-licence-manager-adopts-a-licence-by-refreshing-it.md) — the flow this extends; §7 is changed by decision 1
- [ADR 0183](0183-the-anthropic-licence-manager-is-a-module-and-hands-tokens-to-the-agent-over-the-bus.md) — the manager, its seat and its channel
- [to-be 36](../03-DESIGN/01-to-be/36-the-operators-agent-on-a-machine.md), [to-be 39](../03-DESIGN/01-to-be/39-the-anthropic-licence-manager.md)
+1
View File
@@ -307,6 +307,7 @@ python3 00-META/checks/index.py fail if stale
- **0205** — [Software the distribution does not package ships as a pinned archive of the module's own](0205-software-the-distribution-does-not-package-ships-as-a-pinned-archive-of-the-module.md)
- **0206** — [A node reports the Anthropic grant it holds; the licence manager adopts a licence by refreshing it, and what each node should hold is the manager's state](0206-a-node-reports-the-anthropic-grant-it-holds-and-the-licence-manager-adopts-a-licence-by-refreshing-it.md)
- **0208** — [The graphical session is one module per piece, on the mesh's seats](0208-the-graphical-session-is-one-module-per-piece-on-the-meshs-seats.md)
- **0209** — [A login on a node moves that node to the account it logged in to; an API key is added from any node, sealed](0209-a-login-on-a-node-moves-that-node-to-its-account-and-an-api-key-is-added-from-any-node-sealed.md)
### How it is built