Files
hq/02-DECISIONS/0024-model-access-is-a-provision.md
jschoubben cb1954e7b5 Accept 0024, and rewrite the work breakdown around what is actually being done
**0024 accepted.** Model access was decided, built, and proven in the
lab, and two design documents rest on it; only the status had never
moved. The gate is green again.

**The work breakdown rewritten.** It planned a decomposition of the
existing system in place — extract contexts, declared features, shrink
the shared library. That is not the work. A replacement is being built
beside it, and only the old Phase 0 survived contact with reality, so
the one document meant to say what happens next was describing a system
being retired.

Now ordered by what "modules move across one at a time until the old
registry is off" actually requires:

- Phase 0 is marked done against the twenty-two lab assertions, **and
  carries its own limitation**: every module exercised was written to
  test the mechanism, so the vocabulary was shaped by its own fixtures.
- Phase 1 is the vocabulary gaps found by asking what real modules
  need — an object-store provision, a session as a licence consumer, a
  network shape with ordering, public certificate issuance.
- Phase 2 is one module, then a week of running it, because the point of
  going first is to find what Phase 1 missed.
- Phase 3 picks modules that each prove something the first did not; the
  mail system is last because it is the one that may send work back into
  the declaration language.
- Phase 4 is switching the registry off, named as a phase so it is not
  mistaken for the goal.

Keeps the rules of engagement unchanged — they were about how work is
done, not what it is — with one addition: stop and ask before anything
that touches a machine outside the lab.

Adds a section on keeping the list true, since the document it replaces
was wrong for weeks and nothing said so. A claim here is counted, not
reasoned, and a phase is done when the lab says so.
2026-08-31 17:25:27 +02:00

96 lines
5.1 KiB
Markdown

---
topic: what runs on it
status: accepted
date: 2026-08-30
deciders: jochen
reconstructed: false
---
# 24. Model access is a provision, and a licence is a thing with a name
## Context
Everything in this mesh that thinks needs a model, and there is more than one way to reach one:
| | |
|---|---|
| **hosted services** | several vendors, each with its own account, quota and key |
| **models the mesh runs itself** | open-weight models on a node with the hardware for them |
And the choice is **per consumer, deliberately**: a workstation's own session on one account, a
laptop on the organisation's, two hired workers on the mesh's local model because their work does
not justify paid tokens. Those are three different answers to one requirement, held at once, in
one mesh.
**The existing system has the hard half of this already** — automatic licence refresh and
switching between accounts when one is exhausted — and it works. It is not being replaced because
it was wrong; it is being rebuilt because it lives in a place that cannot express the rest.
## Decision
**Model access is a provision.** A module that needs to think declares `requires: model-access`;
anthropic, openai, grok and a locally-run model are four modules that provide it. That is
[ADR 0009](0009-modules-and-the-graph.md)'s mechanism unchanged, and it buys the things that
mechanism already buys: several implementations of one job, a refusal when more than one could
answer, and choosing by assigning the one you want.
**A locally-run model needs nothing new at all.** It is a mesh-scoped provision on the node with
the hardware — the same shape as a database, including the credential.
### A licence is a named thing, and the name is the operator's
Not an anonymous credential hanging off a provider. *The personal account*, *the organisation's
account* — those 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 provider has several licences; one licence serves several consumers. So it
is **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.
### Four things this needs that the mesh does not have
Written as gaps rather than as design, because each is a real piece of work and pretending
otherwise is how a plan becomes a surprise.
**1. A provider that is not on a node.** A mesh-scoped provision today is answered by *the machine
running it*, and the reachability rule refuses two ends that share no private network. A hosted
service is on nobody's machine and is reached over the public internet. That is a third scope —
answered by a record rather than by a node — and the reachability rule must not apply to it.
**2. A secret the mesh is given rather than one it mints.** Every credential the mesh handles
today it generated itself, sealed to both ends, and discarded. An API key arrives from a person.
The missing verb is *accept*: take a value, seal it to each holder, and **discard the plaintext**
— because a mesh that keeps operator-supplied keys readably is the arrangement this project
[measured and rejected](0009-modules-and-the-graph.md).
**3. A consumer that is not a machine.** *This worker uses that licence* is a binding to an agent,
not to a node. [ADR 0001](0001-mesh-brokers-nodes-host-agents-think.md) already says an agent
holds credentials and that delivery follows its node bindings and modality — so what is delivered
still lands on a machine, and what is **chosen** is chosen per agent. The provisions model has no
consumer identity other than a node.
**4. Switching is a reaction, not a declaration.** Everything here is desired state, reconciled by
comparison. A licence that hits its limit and must be swapped is a response to something observed,
and it cannot be expressed as a declaration without the declaration meaning *whatever is working
right now* — which is not a thing anybody declared. **It belongs with observability, changing a
binding**, and the binding is then declared as usual. Saying this plainly is what stops the
declaration language growing a conditional.
## Consequences
- **The refusing rule applies here and will be felt.** A mesh holding three ways to reach a model
refuses every consumer that has not said which — which is correct and is a great deal of
saying-which the first time. The remedy is one assignment per consumer, and the message names
the candidates.
- **A licence outliving its holder is a live credential nobody is watching.** The same rule the
provisioner follows applies: what the mesh granted and no longer grants is withdrawn.
- **Nothing here makes a node authenticate to a model provider.** ADR 0001 holds: an agent does.
What changes is that the mesh can now say *which agent, which licence, which node it lands on*,
which is the fact ADR 0001 records as missing.
## References
- [ADR 0009](0009-modules-and-the-graph.md) — provisions, scope, choosing, and sealed credentials
- [ADR 0001](0001-mesh-brokers-nodes-host-agents-think.md) — agents hold credentials, not nodes;
`hal/ai` as the context owning provider grants and rotation