-- A licence is a named thing, and the name is the operator's. -- -- novox/hq ADR 0024. Not an anonymous credential hanging off a vendor: *the personal account*, -- *the organisation's account* are names a person uses, and the mesh has to use them too, because -- the whole point is saying WHICH ONE a given consumer uses. -- -- **Many to many.** One vendor has several licences; one licence serves several consumers. So it -- is deliberately not a claim — claims are for things only one holder may have, and two machines -- sharing an account is the ordinary case rather than a collision. create table licence ( -- The operator's name for it. The primary key, because that is what a person types and what a -- consumer is pinned to. name text primary key, -- Which company sells it: anthropic, openai, a model the mesh runs itself. Selects the adapter -- that runs this licence's lifecycle (novox/hq ADR 0050). Named `vendor`, not `provider`: the -- inventory already uses "provider" for which node answers a brokered provision. vendor text not null, -- What a consumer needs to know that is not secret -- a base URL, a model name. The key is -- never here. serves jsonb not null default '{}'::jsonb, -- The one node that holds this licence's refresh token readably, and refreshes it (novox/hq -- ADR 0050's carve-out). Null for a static-key licence, which has nothing to refresh and no -- manager -- and the null is the check that a static key never grows a readable-at-rest value. -- A name, not a foreign key: nodes live in another context this one may not join across -- (novox/hq ADR 0008). manager text, -- The module ON the manager node that runs the refresh -- the manager holder. Named alongside -- the manager node because a node may run the manager module AND a consuming module of the same -- licence (the lab co-locates both), and which holder is delivered the refresh token rather than -- an access token turns on the module, not the node alone. Null exactly when `manager` is. manager_module text, added_at timestamptz not null default now() ); -- Who holds it, and the key sealed to them. -- -- **The node is a name, not a foreign key.** It lives in another context and this one may not join -- across that boundary (novox/hq ADR 0008); a name is the published identifier and is what -- crossing a context boundary is allowed to carry. create table licence_holder ( licence text not null references licence(name) on delete cascade, node text not null, module text not null, -- Sealed to that node's key. Null until a key has been supplied while this holder existed -- -- which is a real state and not an error: the mesh discarded the plaintext, so it cannot seal -- to a holder that arrived afterwards, and saying so is better than delivering nothing. sealed text, node_key text, added_at timestamptz not null default now(), primary key (licence, node, module) ); -- The refresh token, sealed to the manager node's key -- the same anonymous box every credential -- the mesh delivers uses. -- -- **novox/hq ADR 0050's carve-out, delivered the way the mesh delivers everything else.** A -- `refreshable-grant` licence cannot be both sealed so the mesh cannot read it and rotated centrally, -- because rotating means a node reads the refresh token back. So exactly one node -- the licence's -- manager -- reads it. But the earlier attempt at a bespoke at-rest envelope, and the module holding -- the node's private key to open it, hit a wall the mesh's own design forbids: a module is never -- given a node's private sealing key. So the refresh token rides the *ordinary* path instead -- it is -- an anonymous sealed box (secrets.Seal, `crypto_box_seal`) to the manager node's public sealing key, -- exactly like a consumer's db password, and the HOST unseals it and mounts the cleartext at the -- manager module's bound path. This database on its own holds a sealed box with no private half to -- open it (novox/hq ADR 0004), the same guarantee as every other sealed value here. -- -- **Separate from the access tokens.** licence_holder.sealed is the ACCESS token, sealed per holder -- and delivered to consumers. This is the REFRESH token, one per licence, delivered to the manager -- holder alone. Keeping them apart is what makes "a consumer is never delivered the refresh token" -- structural: a consumer's delivery reads licence_holder, and the refresh token is not there. create table refresh_grant ( -- One refresh token per licence. On delete cascade: forgetting a licence forgets its refresh -- token with it, the same way it forgets its holders. licence text primary key references licence(name) on delete cascade, -- base64( anonymous-box( manager sealing key, refresh_token ) ) -- the refresh token sealed to -- the manager node, openable only by that node's private half, which the mesh never holds. sealed text not null, -- The manager's public sealing key the refresh token was sealed to. Kept so a manager that -- regenerated its key can be told it can no longer open this, rather than discovering it as a -- refresh that will not decrypt (the same reason licence_holder.node_key is kept). manager_key text not null, updated_at timestamptz not null default now() );