A session for the mesh itself, addressed as the mesh, differing from a node's in exactly three things: the context it starts in, its engram, and its licence binding. Not a new kind of agent — the same mechanism pointed at a different root. Two implementations of one mechanism drift, and the vocabulary collision 0001 exists to undo began exactly that way. It runs on the control-plane node, and the reasoning is easy to get backwards: not "the important agent on the important machine", but that this node is already the one place excepted from "compromise of a node is compromise of that node". Placed anywhere else it would create a second such place. It is an addition to per-node messaging and never a replacement. 0001 holds that losing the control plane costs change, not operation — and a mesh whose only conversational surface lived there would lose the ability to ask anything while every machine kept running perfectly. Writing it up exposed that the node session's setup was never designed at all. 0004 gives behaviour and stops: nothing said how a session starts, where its context lives, or how a broker message becomes a prompt. That gap was invisible until something had to be built *like* a node session. 15-the-agent-session.md covers both as one mechanism. It also makes "a consumer that is not a machine" undeferrable. The control-plane node now hosts two sessions that must hold different licences, and a per-machine binding cannot express that at all. Noted in 14-model-access.md against the gap it was already recorded as. Also completes the to-be index, which stopped at 10 and omitted four documents. Pre-existing broken ADR references in the older rows are left alone rather than guessed at.
105 lines
5.6 KiB
Markdown
105 lines
5.6 KiB
Markdown
---
|
|
layer: to-be
|
|
status: designed
|
|
code:
|
|
- mesh-control internal/licences
|
|
- mesh-control cmd/mesh-control/licence.go
|
|
updated: 2026-08-31
|
|
decisions:
|
|
- 02-DECISIONS/0024-model-access-is-a-provision.md
|
|
- 02-DECISIONS/0009-modules-and-the-graph.md
|
|
---
|
|
|
|
# 14 — Model access
|
|
|
|
*[ADR 0024](../../02-DECISIONS/0024-model-access-is-a-provision.md) decided it and listed four
|
|
things the mesh did not have. Written 2026-08-31, when two of them were built. **The other two are
|
|
still gaps and are still written as gaps** — the record's own warning is that pretending otherwise
|
|
is how a plan becomes a surprise.*
|
|
|
|
## What was built
|
|
|
|
**A licence is a record, and the first provision no machine answers.** Everything else the mesh
|
|
brokers is answered by something running on a node. A hosted model is on nobody's machine and is
|
|
reached over the public internet, so the rule that refuses two ends sharing no private network —
|
|
correct everywhere else — must not apply to it. A machine on no private network at all can hold a
|
|
licence, and that is not a special case to remember: it falls out of the answer not being a
|
|
machine.
|
|
|
|
**The name is the operator's.** *The personal account*, *the organisation's account*. Those are
|
|
names a person uses, and the mesh uses them too, because the whole point is saying **which one** a
|
|
consumer uses — and an anonymous credential hanging off a provider cannot be said. Many to many,
|
|
so deliberately **not a claim**: two machines sharing an account is the ordinary case rather than
|
|
a collision.
|
|
|
|
**One provision name for all of them.** A module requires `model-access`, never `anthropic`. A
|
|
module that named a provider could not be moved onto a model the mesh runs itself without editing
|
|
it — and moving it is the point.
|
|
|
|
**A model in a machine's own set answers it locally**, and no record is consulted. That is what
|
|
makes *the mesh's own model* an ordinary answer rather than a parallel arrangement.
|
|
|
|
### Accept: taking a value the mesh did not make
|
|
|
|
Every other credential here the mesh generated, sealed to both ends and discarded. An API key
|
|
arrives from a person, and the missing verb was *accept*: **take a value, seal it to each holder,
|
|
discard the plaintext.** A mesh that kept operator-supplied keys readably is the arrangement this
|
|
project measured and rejected.
|
|
|
|
**It seals to the holders that exist at that moment**, and this has a consequence that must be
|
|
said out loud rather than discovered:
|
|
|
|
> A consumer put on a licence *after* the key was supplied has no key, and **the mesh cannot make
|
|
> one** — it discarded the only copy.
|
|
|
|
So that state is reported at every point a person could meet it: when the consumer is put on the
|
|
licence, in `licence list`, and — decisively — **the declaration is refused** rather than written
|
|
without the file. A machine that resolves cleanly and receives nothing fails later, somewhere that
|
|
names neither the licence nor the mesh.
|
|
|
|
**A key is read from a file or standard input, never an argument.** A key on a command line is a
|
|
key in shell history and in every process listing taken while it ran. It is never echoed back:
|
|
what is stored is unreadable by whoever holds it, the control plane included, and printing it
|
|
would put the one copy that matters on a terminal.
|
|
|
|
## Refusing is felt, and that is the design working
|
|
|
|
ADR 0024 predicted it: *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.*
|
|
|
|
It is correct, and correct is not the same as usable. So the refusal names **the candidates and
|
|
the exact command**. The difference between a mesh that refuses helpfully and one that merely
|
|
refuses is whether anybody can act on it without going and reading something else.
|
|
|
|
## Still gaps
|
|
|
|
Unchanged from [ADR 0024](../../02-DECISIONS/0024-model-access-is-a-provision.md), and deliberately
|
|
not half-built:
|
|
|
|
**A consumer that is not a machine.** *This worker uses that licence* is a binding to an agent, not
|
|
to a node. What is delivered still lands on a machine; what is **chosen** is chosen per agent, and
|
|
the provisions model has no consumer identity other than a node. What exists today is per module
|
|
per machine, which is a step toward it and is not it.
|
|
|
|
*2026-08-31: this gap now has named consumers rather than hypothetical ones.*
|
|
[ADR 0026](../../02-DECISIONS/0026-the-mesh-has-a-session-of-its-own.md) puts two sessions on the
|
|
control-plane node — the node's own and the mesh's — each bound in its own right. **A per-machine
|
|
binding cannot express that at all**, not merely awkwardly: the two sessions share a machine and
|
|
must be able to hold different licences. See
|
|
[`15-the-agent-session.md`](15-the-agent-session.md).
|
|
|
|
**Switching is a reaction, not a declaration.** A licence that hits its limit and must be swapped is
|
|
a response to something observed. Expressing it as a declaration would make the declaration mean
|
|
*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**, and nothing built here
|
|
grew one.
|
|
|
|
## How it is checked
|
|
|
|
In the lab, on real machines, in the order a person would meet it: a consumer is refused with both
|
|
candidates named; put on one and still refused because no key exists; the key is given on standard
|
|
input and not echoed; the public half arrives saying it came from a record rather than a machine;
|
|
the key arrives readable only by that machine — and it is **nowhere in the control plane's own
|
|
database**, nor in anything that crossed the broker.
|