Reconcile: adopt initialization's consolidated HQ as canonical, re-home this session's new work #24
@@ -0,0 +1,130 @@
|
||||
---
|
||||
topic: what runs on it
|
||||
status: accepted
|
||||
date: 2026-08-31
|
||||
deciders: jochen
|
||||
reconstructed: false
|
||||
extends: 02-DECISIONS/0004-a-node-and-how-it-joins.md
|
||||
---
|
||||
|
||||
# 26. The mesh has a session of its own, and it is the node session's mechanism
|
||||
|
||||
## Context
|
||||
|
||||
[ADR 0004](0004-a-node-and-how-it-joins.md) gives every node a session: one per node, permanent,
|
||||
remembering across callers, its system prompt the node's engram, reachable over the broker like
|
||||
everything else. **Any node can message any node**, and that is called the one part of the system
|
||||
that is genuinely a mesh — symmetric, with no centre.
|
||||
|
||||
**There is no way to address the mesh itself.** A question that spans machines — *what is running
|
||||
across all of this*, *which nodes are behind*, *why is it built this way* — has to be put to some
|
||||
node, which then asks the others. That works, and it makes a mesh-wide question **nobody's
|
||||
question**: every node answers it as a foreigner, from a position where the whole is not in view.
|
||||
|
||||
**Three things independently arrived at the same missing piece.**
|
||||
|
||||
[ADR 0025](0025-the-design-record-is-read-not-copied.md), taken hours before this one, commits to
|
||||
an agent that reads the design repository directly and answers into search. That agent has to
|
||||
exist, run somewhere, and be askable — and nothing in the record says what it is or where it
|
||||
lives.
|
||||
|
||||
[`14-model-access.md`](../03-DESIGN/01-to-be/14-model-access.md) records, as a gap deliberately
|
||||
not half-built: *this worker uses that licence is a binding to an agent, not to a node* — and the
|
||||
provisions model has no consumer identity other than a node. A session that must be assigned a
|
||||
licence is exactly that consumer, and node sessions are already one.
|
||||
|
||||
**And ADR 0004 never said how a session is set up.** It describes behaviour and stops: nothing
|
||||
states how a session starts, where its context lives, how the engram reaches it, or how a message
|
||||
off the broker becomes a prompt. There is no design document for it. That gap was invisible until
|
||||
something had to be built *like* a node session, because describing a second instance of a
|
||||
mechanism requires the mechanism to have been described once.
|
||||
|
||||
## Considered Options
|
||||
|
||||
1. **No mesh session; keep relaying through a node.** Costs nothing and works today. **Rejected.**
|
||||
It leaves mesh-wide questions belonging to nobody, and it does not survive contact with
|
||||
ADR 0025 — that agent still needs a home, so the thing gets built anyway, unnamed, as an
|
||||
attachment to whichever node happened to host it.
|
||||
|
||||
2. **A new kind of agent, built separately.** Purpose-built for the whole mesh. **Rejected.** It
|
||||
would hold a session, a memory, a licence and broker plumbing — every one of which the node
|
||||
session already has. Two implementations of one mechanism drift, and the vocabulary collision
|
||||
that [ADR 0001](0001-mesh-brokers-nodes-host-agents-think.md) exists to undo began exactly this
|
||||
way: two things that were nearly the same, built twice, until neither word meant one thing.
|
||||
|
||||
3. **The same mechanism, started in a different context.** **Adopted.**
|
||||
|
||||
## Decision
|
||||
|
||||
**The mesh has one session, addressed as the mesh, and it is a node session in every respect but
|
||||
three.**
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **the context it starts in** | the mesh's, not a machine's — this is the whole of what makes it different |
|
||||
| **its engram** | the mesh's system prompt, as a node's engram is that node's |
|
||||
| **its licence binding** | assigned in its own right, not inherited from the machine it runs on |
|
||||
|
||||
Everything else is unchanged and deliberately so: it is permanent, it remembers, it is reachable
|
||||
over the broker, it holds its own tools, and switched off it still answers *I am switched off*
|
||||
rather than falling silent.
|
||||
|
||||
**It runs on the node that holds the control plane** — not for convenience, but because that node
|
||||
is already the one place excepted from *compromise of a node is compromise of that node*
|
||||
(ADR 0004). An agent able to reach everything, placed anywhere else, creates a **second** such
|
||||
place. Putting it where the authority already sits concentrates nothing new.
|
||||
|
||||
**It is an addition to per-node messaging and never a replacement.** Every node remains directly
|
||||
addressable. This is not a preference: ADR 0001 holds that losing the control plane costs *change,
|
||||
not operation*, and a mesh whose only conversational surface lives on that node would lose the
|
||||
ability to ask anything while every machine kept running perfectly. **The front door may not be
|
||||
the single point.**
|
||||
|
||||
**It is not an employee** ([ADR 0003](0003-agents-are-persistent-employees.md)). Nobody hires it,
|
||||
it holds no task queue, it is never drained or reassigned. What it does with work that belongs
|
||||
somewhere else is **dispatch it** — to node sessions, or to workers — which is what a node session
|
||||
already does when asked something it does not have.
|
||||
|
||||
**It is ADR 0025's reader.** The agent that reads the design repository and answers into search is
|
||||
this session, not a second one. One agent, one memory, one place to reach; two would both need
|
||||
that repository and would eventually disagree about what it says.
|
||||
|
||||
**"One per node" is about address, not about process count.** ADR 0004's rule — *two and nothing
|
||||
decides which replies* — forbids ambiguity in who answers when a **node** is addressed. The mesh
|
||||
session answers when the **mesh** is addressed. The control-plane node therefore hosts two
|
||||
sessions and no ambiguity, and stating this here is what stops it reading as a contradiction
|
||||
later.
|
||||
|
||||
## Consequences
|
||||
|
||||
**The node session's setup must now be designed, and it never was.** This decision is expressed as
|
||||
*the same as a node session, elsewhere*, which is only meaningful once that mechanism is written
|
||||
down. The design document covering both is the immediate consequence of this record, not a
|
||||
follow-up to it.
|
||||
|
||||
**A consumer that is not a machine stops being deferrable.** The licence binding above is the gap
|
||||
`14-model-access.md` names, and it now has two consumers rather than a hypothetical one. Until it
|
||||
exists, a session's model access can only be expressed as *this module on this machine*, which
|
||||
cannot say *this node's session uses the personal licence and the mesh's uses the company one* —
|
||||
the thing the binding is for.
|
||||
|
||||
**Symmetry is preserved, and it is worth being precise about why.** ADR 0004's claim is about what
|
||||
a node can reach, and it is untouched: node-to-node messaging is unchanged, nothing is routed
|
||||
through the mesh session, and it is a participant rather than a hop. What arrives is a
|
||||
participant that happens to be the one a person usually addresses.
|
||||
|
||||
**Availability degrades to inconvenience rather than to silence** — but only because of the
|
||||
addition rule above. If that rule is ever relaxed, this consequence inverts, and it inverts
|
||||
quietly: everything keeps working and nobody can ask about it.
|
||||
|
||||
**The surface a person uses is not decided here.** That a board is a good place to talk to it is
|
||||
likely and is not this record's business; the session is reachable over the broker like everything
|
||||
else, and what puts a text box in front of it is a separate choice.
|
||||
|
||||
## References
|
||||
|
||||
- [ADR 0004](0004-a-node-and-how-it-joins.md) — the node session this extends
|
||||
- [ADR 0025](0025-the-design-record-is-read-not-copied.md) — the reader this session is
|
||||
- [ADR 0003](0003-agents-are-persistent-employees.md) — the vocabulary this is not
|
||||
- [`03-DESIGN/01-to-be/14-model-access.md`](../03-DESIGN/01-to-be/14-model-access.md) — *a
|
||||
consumer that is not a machine*, the gap this makes concrete
|
||||
@@ -98,6 +98,7 @@ python3 00-META/checks/index.py fail if stale
|
||||
- **0009** — [Modules and the graph](0009-modules-and-the-graph.md)
|
||||
- **0010** — [Delivery](0010-delivery.md)
|
||||
- **0024** — [Model access is a provision, and a licence is a thing with a name](0024-model-access-is-a-provision.md) *(proposed)*
|
||||
- **0026** — [The mesh has a session of its own, and it is the node session's mechanism](0026-the-mesh-has-a-session-of-its-own.md)
|
||||
|
||||
### How it is built
|
||||
|
||||
|
||||
@@ -81,6 +81,13 @@ to a node. What is delivered still lands on a machine; what is **chosen** is cho
|
||||
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
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
---
|
||||
layer: to-be
|
||||
status: designed
|
||||
code: [mesh-control, mesh-host]
|
||||
updated: 2026-08-31
|
||||
decisions:
|
||||
- 02-DECISIONS/0004-a-node-and-how-it-joins.md
|
||||
- 02-DECISIONS/0026-the-mesh-has-a-session-of-its-own.md
|
||||
- 02-DECISIONS/0024-model-access-is-a-provision.md
|
||||
- 02-DECISIONS/0025-the-design-record-is-read-not-copied.md
|
||||
---
|
||||
|
||||
# The agent session
|
||||
|
||||
**One mechanism, started twice.** A node's session and the mesh's session are the same thing
|
||||
pointed at different context. This document describes the mechanism; where the two differ it says
|
||||
so, and the differences are few enough to list here:
|
||||
|
||||
| | node session | mesh session |
|
||||
|---|---|---|
|
||||
| **address** | the node's name | the mesh |
|
||||
| **context root** | the node's | the mesh's |
|
||||
| **engram** | that node's | the mesh's |
|
||||
| **licence** | bound in its own right | bound in its own right |
|
||||
| **runs on** | that node | the node holding the control plane |
|
||||
| **how many** | one per node | one |
|
||||
|
||||
Everything below applies to both unless it says otherwise.
|
||||
|
||||
## What a session is, restated for what is being built
|
||||
|
||||
[ADR 0004](../../02-DECISIONS/0004-a-node-and-how-it-joins.md) settled the behaviour: permanent,
|
||||
remembering across callers, its own tools, reachable over the broker, and — switched off — still
|
||||
answering *I am switched off* rather than falling silent.
|
||||
|
||||
**Nothing said how one is set up**, and that gap is what this document closes. It was invisible
|
||||
until [ADR 0026](../../02-DECISIONS/0026-the-mesh-has-a-session-of-its-own.md) needed to describe
|
||||
a second instance, because *the same as that one, elsewhere* means nothing until the first has
|
||||
been written down.
|
||||
|
||||
## The context root is the whole of the difference
|
||||
|
||||
**A session is defined by the directory it starts in.** That directory holds the engram, the
|
||||
session's tools, and whatever standing instruction it works under. Two sessions differing only in
|
||||
their root are two different agents, and nothing else has to differ to make them so.
|
||||
|
||||
This is deliberately a **small** definition. The alternative — a session type, with the mesh
|
||||
session as a distinct kind — would mean two implementations of one mechanism, and
|
||||
[ADR 0001](../../02-DECISIONS/0001-mesh-brokers-nodes-host-agents-think.md) records what that
|
||||
costs: two things nearly the same, built twice, until neither word means one thing.
|
||||
|
||||
**The root is delivered as declared state, not carried by the session.** It is files on a machine,
|
||||
which is precisely what the host applies
|
||||
([ADR 0005](../../02-DECISIONS/0005-the-node-host.md)). A session's context therefore changes the
|
||||
way anything else changes — the mesh declares it, the host writes it — and there is no second
|
||||
mechanism for shipping an engram.
|
||||
|
||||
**Changing the engram is changing a file.** So it is versioned, reviewable, and rolled back like
|
||||
any other declared state; and a node whose engram was changed reports having applied it, the same
|
||||
as it reports anything else.
|
||||
|
||||
## Where each one runs
|
||||
|
||||
**A node's session runs on that node**, and cannot be moved. Moved, one machine is answering as
|
||||
another (ADR 0004).
|
||||
|
||||
**The mesh's session runs on the node holding the control plane.** The reasoning is in ADR 0026
|
||||
and is worth carrying here because it is easy to get backwards: this is not *the important agent
|
||||
goes on the important machine*. It is that the control-plane node is already the one place
|
||||
excepted from *compromise of a node is compromise of that node*, and an agent able to reach
|
||||
everything, placed anywhere else, would create a second such place.
|
||||
|
||||
## Messages
|
||||
|
||||
**A session is reached over the broker**
|
||||
([ADR 0002](../../02-DECISIONS/0002-nodes-communicate-over-a-broker.md)). There is no second
|
||||
transport, nothing is dialled at a session, and the mesh session is not a hop: node-to-node
|
||||
messages continue to travel directly, and nothing is routed through it.
|
||||
|
||||
**A message becomes a prompt; a reply travels back the same way.** Whoever asked — a person at a
|
||||
surface, another session, a worker — is a caller, and the session remembers what each of them
|
||||
asked, together, over time.
|
||||
|
||||
**Being asked something it does not have, a session may ask another.** How it does so is its own
|
||||
business and follows from its engram rather than from a message format: it may say who wants to
|
||||
know, or simply ask. A person relaying a question makes the same choice.
|
||||
|
||||
**A switched-off session answers.** The queue is still read and the state is the reply, with no
|
||||
model involved. This is the same rule the host follows about a service that does not exist, and
|
||||
it is the rule this repository has now paid for three times: **absence must never be
|
||||
indistinguishable from a failure to answer**
|
||||
([005](../../04-ISSUES/005-pipeline-test-harness-unbuildable/00-report.md),
|
||||
[008](../../04-ISSUES/008-the-documented-node-rescue-does-not-exist/00-report.md)).
|
||||
|
||||
## Model access is bound to the session, not to the machine
|
||||
|
||||
**A session is a consumer in its own right.** This is the gap
|
||||
[`14-model-access.md`](14-model-access.md) records as *a consumer that is not a machine*, and it
|
||||
is the one part of this design that cannot be built from what exists: the provisions model has no
|
||||
consumer identity other than a node, so today a binding can only say *this module on this
|
||||
machine*.
|
||||
|
||||
That is not sufficient here, and the shortfall is concrete rather than theoretical:
|
||||
|
||||
- the control-plane node hosts **two** sessions, which must be able to hold **different**
|
||||
licences — a per-machine binding cannot express it at all;
|
||||
- *this node's session uses the personal licence, the mesh's uses the company one* is the ordinary
|
||||
case, not an exotic one.
|
||||
|
||||
**What is delivered still lands on a machine. What is chosen is chosen per session.** Delivery
|
||||
follows the session's context root, which is where its credentials belong — the same rule ADR 0001
|
||||
states for an agent's config directory, with the root standing in for it.
|
||||
|
||||
**Switching a licence remains a reaction, not a declaration** (ADR 0024). A session that exhausts
|
||||
a licence is observed and its binding changed; the binding is then declared as usual. Nothing here
|
||||
grows a conditional in the declaration language.
|
||||
|
||||
## What the mesh session knows
|
||||
|
||||
**It holds the design record by reading it**
|
||||
([ADR 0025](../../02-DECISIONS/0025-the-design-record-is-read-not-copied.md)), not by holding a
|
||||
copy. It is that reader; there is not a second agent for it.
|
||||
|
||||
**It answers into a symptom search**, so what it knows appears beside ordinary results rather than
|
||||
only when it is asked. **And when it cannot be reached, the search says so.** A result set that
|
||||
silently omits this material looks identical to one where nothing matched — the same rule as the
|
||||
switched-off session above, at a different layer.
|
||||
|
||||
**Reading is one-way.** It reads the repository and answers from it; nothing flows back. The
|
||||
repository is public and the mesh is not, and a return path is how installation-specific detail
|
||||
arrives into documents that must not carry it.
|
||||
|
||||
## What it does with work
|
||||
|
||||
**It dispatches; it does not hold a queue.** Asked for something that belongs elsewhere, it asks a
|
||||
node session or hands the work to a worker. It is not an employee
|
||||
([ADR 0003](../../02-DECISIONS/0003-agents-are-persistent-employees.md)): nobody hires it, it is
|
||||
never drained, and it is not reassigned.
|
||||
|
||||
The distinction is the one ADR 0001 was written to keep: a session comes with the thing it belongs
|
||||
to and goes when that thing goes; a worker is hired, holds tasks, and moves. Built from the same
|
||||
parts, run on entirely different terms.
|
||||
|
||||
## The surface is separate
|
||||
|
||||
That a person usually reaches the mesh session through a board is likely and is not settled here.
|
||||
The session is reachable over the broker like everything else; what puts a text box in front of it
|
||||
is a different design, and the session does not know which surface asked.
|
||||
|
||||
## Consequences
|
||||
|
||||
**Two sessions on one node, and no ambiguity.** The control-plane node hosts its own node session
|
||||
and the mesh session. ADR 0004's *one per node* forbids ambiguity about who answers when a **node**
|
||||
is addressed; these answer to different addresses.
|
||||
|
||||
**A new node gets a session by being declared, not by being set up.** The context root is declared
|
||||
state, so a joining node's session arrives the way its packages and services do.
|
||||
|
||||
**The mesh session is a single point of convenience, and must never become one of reach.** Every
|
||||
node stays directly addressable. If that ever stops being true, the failure is quiet in the worst
|
||||
way: every machine keeps working and nobody can ask about it.
|
||||
|
||||
## How it is checked
|
||||
|
||||
**A test names the decision it defends** ([ADR 0017](../../02-DECISIONS/0017-a-test-defends-a-decision.md)),
|
||||
and these run in the lab on real machines:
|
||||
|
||||
| Check | Defends |
|
||||
|---|---|
|
||||
| a node is asked something and its session answers | ADR 0004 |
|
||||
| the mesh is asked something and the mesh session answers, on the control-plane node | ADR 0026 |
|
||||
| both sessions on that node answer, to their own addresses, without ambiguity | ADR 0026 |
|
||||
| a session switched off replies saying so, rather than timing out | ADR 0004 |
|
||||
| a session whose engram was changed reports having applied it, like any declared file | ADR 0005 |
|
||||
| the two sessions on one node hold different licences, and each uses its own | ADR 0024 |
|
||||
| a node is messaged directly while the mesh session is stopped, and answers | ADR 0001 |
|
||||
| a search consulting an unreachable mesh session says it was not consulted | ADR 0025 |
|
||||
|
||||
The last two are the ones worth writing first. Both defend properties that are invisible while
|
||||
everything works, and both describe a mesh that looks entirely healthy at the moment it has
|
||||
stopped telling the truth.
|
||||
|
||||
## Deliberately not decided
|
||||
|
||||
**Whether the mesh session's memory is its own or assembled from the node sessions each time.**
|
||||
It remembers what it has been asked, which is settled. Whether *what the mesh knows* is a thing it
|
||||
holds or a thing it gathers on demand is a real question with a cost either way, and nothing here
|
||||
depends on the answer yet.
|
||||
|
||||
**How a person's identity reaches a session.** Callers are distinguished, but who a caller *is*,
|
||||
and whether a session should act differently for different people, is the human-agent question
|
||||
ADR 0001 leaves open and this does not close.
|
||||
@@ -20,6 +20,11 @@ document is written and this one's status becomes `implemented`.
|
||||
| [`08-connectivity.md`](08-connectivity.md) | One context in full — overlay, resolution, exposure, filtering, certificates | [ADR 0007](../../02-DECISIONS/0007-connectivity.md), [0050](../../02-DECISIONS/0007-connectivity.md), [0051](../../02-DECISIONS/0004-a-node-and-how-it-joins.md), [0055](../../02-DECISIONS/0006-the-substrate-and-the-control-plane.md) |
|
||||
| [`09-the-node-lifecycle.md`](09-the-node-lifecycle.md) | How a machine becomes a node, stays one, and stops being one | [ADR 0004](../../02-DECISIONS/0004-a-node-and-how-it-joins.md), [0051](../../02-DECISIONS/0004-a-node-and-how-it-joins.md) |
|
||||
| [`10-delivery.md`](10-delivery.md) | Modules, the three edges, and how a change becomes a running thing | [ADR 0010](../../02-DECISIONS/0010-delivery.md), [0064](../../02-DECISIONS/0009-modules-and-the-graph.md), [0065](../../02-DECISIONS/0009-modules-and-the-graph.md) |
|
||||
| [`11-a-board.md`](11-a-board.md) | What a person sees of the mesh, and why it is read from what runs | [ADR 0008](../../02-DECISIONS/0008-a-context-owns-its-store.md), [ADR 0001](../../02-DECISIONS/0001-mesh-brokers-nodes-host-agents-think.md) |
|
||||
| [`12-a-module-repository.md`](12-a-module-repository.md) | A module repository, and what builds it | [ADR 0009](../../02-DECISIONS/0009-modules-and-the-graph.md), [ADR 0010](../../02-DECISIONS/0010-delivery.md), [ADR 0005](../../02-DECISIONS/0005-the-node-host.md) |
|
||||
| [`13-credentials-and-their-rotation.md`](13-credentials-and-their-rotation.md) | Credentials, and moving them without a consumer holding one the provider does not know about | [ADR 0001](../../02-DECISIONS/0001-mesh-brokers-nodes-host-agents-think.md), [ADR 0009](../../02-DECISIONS/0009-modules-and-the-graph.md) |
|
||||
| [`14-model-access.md`](14-model-access.md) | Model access as a provision, and what a licence is bound to | [ADR 0024](../../02-DECISIONS/0024-model-access-is-a-provision.md), [ADR 0009](../../02-DECISIONS/0009-modules-and-the-graph.md) |
|
||||
| [`15-the-agent-session.md`](15-the-agent-session.md) | One mechanism started twice — a node's session and the mesh's | [ADR 0004](../../02-DECISIONS/0004-a-node-and-how-it-joins.md), [ADR 0026](../../02-DECISIONS/0026-the-mesh-has-a-session-of-its-own.md) |
|
||||
|
||||
## Not yet written
|
||||
|
||||
|
||||
Reference in New Issue
Block a user