# claude-licence-manager Holds the `anthropic-licence-manager` seat: every Anthropic licence the mesh has, kept alive by one rotation source, and handed to each consumer sealed (novox/hq ADR 0183, ADR 0206, design 39). ## How a licence comes to exist Nothing is configured. Every node running `claude-code` reports what it holds as that module's `holdings` state — the account, fingerprints and expiries, never a token. This module reads every report when it starts and watches them: 1. A report with a refresh token it does not hold is a **candidate**. 2. It asks that node's `claude_code_grant`, giving its public key, and receives the grant sealed to it. 3. **It refreshes it.** If the vendor exchanges the token, the grant is this module's — encrypted at rest with the key the vault made for it — and from then on it is the only refresher. If not, the candidate is recorded dead and nothing is adopted. 4. Several nodes logged in to one account: newest login first; the rest are never exchanged. 5. A node reporting that account and bound to nothing is bound to it. Each node is then handed an access token only, so the agent there never refreshes, and a refresh token appearing on a node later can only be a person's login — which wins if it refreshes. An API key enters through `adopt`, from a file on this module's node. ## What each consumer holds This module's `bindings` state: one key per consumer (a node's name) with the licence, its kind and a generation that grows with every rotation and switch. `claude-code` watches its own key and, on a newer generation, asks `current` with its public key. ## The seat's verbs `licences`, `bindings`, `bind`, `switch`, `release`, `refresh`, `usage`, `adopt`, `current` — through the console as `anthropic-licence-manager.`. No answer carries a token. ## Settings `settings.json` in the state directory: `cadence_minutes` (240), `floor_minutes` (60), `failures_to_notify` (3), `cooldown_hours` (24), `refresh_warn_days` (3). ## Events `licence.adopted`, `licence.refused`, `licence.failing`, `usage.read` — none carries a secret. ## Code and tests Go, one binary (`cmd/claude-licence-manager`): the seat's verbs and the daemon in one launched bundle, `prepare` as the run-once preparation step. The sealed box is `claude-code`'s own format, byte for byte — the two modules carry the same `seal.go` — and a test opens one sealed by the TypeScript agent module the Go one replaced, so the format is the one already on the machines. go test ./... # the store against a real postgres: docker run -d --rm --name licmgr-pg -e POSTGRES_PASSWORD=t -p 15498:5432 postgres:16-alpine MESH_TEST_POSTGRES=postgres://postgres:t@127.0.0.1:15498/postgres go test ./...