Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cb4b3c2398 |
+14
-24
@@ -98,9 +98,6 @@ and repair).
|
|||||||
*Not:* ~~tools-sdk~~
|
*Not:* ~~tools-sdk~~
|
||||||
- **invokes** — the manifest word for the tools a module calls, `<module>.<tool>` each or `*` for every
|
- **invokes** — the manifest word for the tools a module calls, `<module>.<tool>` each or `*` for every
|
||||||
one. A grant on the publish side and nothing else; a module that declares none calls nothing.
|
one. A grant on the publish side and nothing else; a module that declares none calls nothing.
|
||||||
- **run for** — an assignment, or a start of a module, that serves one agent identity with that identity's machine
|
|
||||||
account, home and bus password. Only a module whose manifest declares it may be run for an identity; running for
|
|
||||||
the operator is the default run, as the operator account, with no setting (ADR 0412).
|
|
||||||
- **upgrade policy** — how a module's new builds reach its nodes: `roll` (one node first, judged, then
|
- **upgrade policy** — how a module's new builds reach its nodes: `roll` (one node first, judged, then
|
||||||
the rest), `together`, or `record` (register the build, send it nowhere)
|
the rest), `together`, or `record` (register the build, send it nowhere)
|
||||||
(ADR 0236).
|
(ADR 0236).
|
||||||
@@ -234,7 +231,7 @@ every other domain has something to run on. **Recorded by** the controller and t
|
|||||||
|
|
||||||
**Purpose.** To decide, send and apply what each node runs, and to say why.
|
**Purpose.** To decide, send and apply what each node runs, and to say why.
|
||||||
**Recorded by** the controller's `inventory` database. **Upstream of** Provisioning, Connectivity, Data
|
**Recorded by** the controller's `inventory` database. **Upstream of** Provisioning, Connectivity, Data
|
||||||
and Health and repair. **Decided by** ADR 0100, 0126, 0176, 0181, 0207, 0221, 0252, 0261, 0411.
|
and Health and repair. **Decided by** ADR 0100, 0126, 0176, 0181, 0207, 0221, 0252, 0261.
|
||||||
**Uses:** node, machine, module, seat, manifest (Module), config value (Config).
|
**Uses:** node, machine, module, seat, manifest (Module), config value (Config).
|
||||||
|
|
||||||
- **assignment** — a module put on a node. Its config values on that node (module-assignment config,
|
- **assignment** — a module put on a node. Its config values on that node (module-assignment config,
|
||||||
@@ -267,15 +264,15 @@ and Health and repair. **Decided by** ADR 0100, 0126, 0176, 0181, 0207, 0221, 02
|
|||||||
other things (see [Homonyms](#homonyms)).
|
other things (see [Homonyms](#homonyms)).
|
||||||
- **apply** — the node-engine making its node match its declaration; **reconcile** is doing so again
|
- **apply** — the node-engine making its node match its declaration; **reconcile** is doing so again
|
||||||
until nothing differs.
|
until nothing differs.
|
||||||
- **operator account** — the machine account of the operator's person identity on a node, read from the
|
- **operator account** — the login name of the person who works on a node, stated on the node record;
|
||||||
identity and no longer stated on the node record; empty for a node nobody logs into. Everything the mesh
|
empty for a node nobody logs into. Everything the mesh places under a person's home is resolved
|
||||||
places under a person's home is resolved against this account's home and owned by it
|
against this account's home and owned by it
|
||||||
(ADR 0181, ADR 0411).
|
(ADR 0181).
|
||||||
Not a name a manifest carries.
|
Not a name a manifest carries.
|
||||||
- **agent account** — the machine account of the agent identity the identity seat marks as a node's agent account,
|
- **agent account** — the login every coding agent on a node runs as, when the node names one of its own;
|
||||||
on the operator's Approve, never by an assignment or a config value. Where none is marked, agents run as the
|
stated on the node record at the controller's terminal, never by a verb or a config value. Empty means agents
|
||||||
operator account. It cannot
|
run as the operator account. It cannot become root without a person, and the node-engine judges that on
|
||||||
become root without a person, and the node-engine judges that on every look (ADR 0266, ADR 0411).
|
every look (ADR 0266).
|
||||||
- **account group** — a group of the machine's group database that a module puts an account in, by
|
- **account group** — a group of the machine's group database that a module puts an account in, by
|
||||||
declaring the account with that group and nothing else of it. Several modules may each add one to the
|
declaring the account with that group and nothing else of it. Several modules may each add one to the
|
||||||
same account; the mesh takes back only an account group it added
|
same account; the mesh takes back only an account group it added
|
||||||
@@ -344,7 +341,7 @@ and vault (Identity and access).
|
|||||||
**Purpose.** To issue, hold, rotate and check the credentials and grants by which modules, nodes, agents
|
**Purpose.** To issue, hold, rotate and check the credentials and grants by which modules, nodes, agents
|
||||||
and the operator act. **Recorded by** the controller's `identity` and `licences` databases and the vault.
|
and the operator act. **Recorded by** the controller's `identity` and `licences` databases and the vault.
|
||||||
**Upstream of** Provisioning, Change and delivery, and Operator and conversation.
|
**Upstream of** Provisioning, Change and delivery, and Operator and conversation.
|
||||||
**Decided by** ADR 0085, 0113, 0183, 0225, 0234, 0386, 0387, 0388, 0411, 0413, 0414.
|
**Decided by** ADR 0085, 0113, 0183, 0225, 0234, 0386, 0387, 0388.
|
||||||
**Uses:** module, node, seat, tool (Module), operator, consumer and provision (Provisioning), agent and ask (Operator and conversation).
|
**Uses:** module, node, seat, tool (Module), operator, consumer and provision (Provisioning), agent and ask (Operator and conversation).
|
||||||
|
|
||||||
- **credential** — anything that proves an identity: a password, a key, a token. A **pair credential**
|
- **credential** — anything that proves an identity: a password, a key, a token. A **pair credential**
|
||||||
@@ -358,13 +355,8 @@ and the operator act. **Recorded by** the controller's `identity` and `licences`
|
|||||||
- **vault** — the module that owns every secret (`mesh-vault`,
|
- **vault** — the module that owns every secret (`mesh-vault`,
|
||||||
[24 — The secrets vault](../03-DESIGN/01-to-be/24-the-secrets-vault.md)).
|
[24 — The secrets vault](../03-DESIGN/01-to-be/24-the-secrets-vault.md)).
|
||||||
- **identity** — a person, an agent or a module as the mesh knows it: a name, a kind, a role, and whether it
|
- **identity** — a person, an agent or a module as the mesh knows it: a name, a kind, a role, and whether it
|
||||||
is switched on; a person or an agent also has a login name, the machines it is present on and, for a person,
|
is switched on. Every identity and role is held by the identity seat, in the bus's key-value store, and
|
||||||
the login kept on a machine where it differs. Every identity and role is held by the identity seat, in the
|
changes only through its verbs (ADR 0386).
|
||||||
bus's key-value store, and changes only through its verbs (ADR 0386, ADR 0411). Each person and agent
|
|
||||||
identity has a bus account of its own, carrying its role (ADR 0413).
|
|
||||||
- **machine account** — an identity's login on one machine, made by the mesh from the identity: the
|
|
||||||
controller composes it and the node-engine applies it, adopting a login that exists and never renaming or
|
|
||||||
deleting one (ADR 0411).
|
|
||||||
- **role** — what an identity may call, as a set of grants (ADR 0386). Not a seat: a bare *role* is a
|
- **role** — what an identity may call, as a set of grants (ADR 0386). Not a seat: a bare *role* is a
|
||||||
homonym (see [Homonyms](#homonyms)).
|
homonym (see [Homonyms](#homonyms)).
|
||||||
- **grant** — one line of a role: an effect everywhere, an effect on one seat, or one named tool, and may be
|
- **grant** — one line of a role: an effect everywhere, an effect on one seat, or one named tool, and may be
|
||||||
@@ -626,8 +618,8 @@ asking, never the act. **Decided by** ADR 0152, 0175, 0234, 0259, 0272, 0383, 03
|
|||||||
line as the **controller's terminal** or as an ordinary call
|
line as the **controller's terminal** or as an ordinary call
|
||||||
(ADR 0272).
|
(ADR 0272).
|
||||||
- **nox** — `mesh-cli`'s alias, in interactive zsh only. A script, `sudo` and a document meant to work everywhere
|
- **nox** — `mesh-cli`'s alias, in interactive zsh only. A script, `sudo` and a document meant to work everywhere
|
||||||
say `mesh-cli`. The operator also calls the mesh itself *nox mesh*. *nox* is also the agent (ADR 0414 §7): see
|
say `mesh-cli`. The operator also calls the mesh itself *nox mesh*; on this page and in the records *nox* is the
|
||||||
[Homonyms](#homonyms), and say which is meant. It is the one command the operator types: every line the mesh addresses to the operator — a
|
alias alone. It is the one command the operator types: every line the mesh addresses to the operator — a
|
||||||
condition's words, a verb's answer, a README, a design — names a `nox` line, never a daemon's (the controller's
|
condition's words, a verb's answer, a README, a design — names a `nox` line, never a daemon's (the controller's
|
||||||
or the node-engine's binary followed by a command), which `words.py` and the catalogue's check refuse
|
or the node-engine's binary followed by a command), which `words.py` and the catalogue's check refuse
|
||||||
(ADR 0272,
|
(ADR 0272,
|
||||||
@@ -825,8 +817,6 @@ renaming one sense, the old sense moves to a *Not:* line and becomes mechanical.
|
|||||||
| record | a numbered decision | **decision record** | The record |
|
| record | a numbered decision | **decision record** | The record |
|
||||||
| | the literal reader of this repository, searched first for a symptom | **the record** (the `records` module) | Operator and conversation |
|
| | the literal reader of this repository, searched first for a symptom | **the record** (the `records` module) | Operator and conversation |
|
||||||
| | the upgrade policy that sends nowhere | `record` | Module |
|
| | the upgrade policy that sends nowhere | `record` | Module |
|
||||||
| nox | the mesh's one agent identity, its logins and accounts (ADR 0414) | **`nox`, the agent** | Identity and access |
|
|
||||||
| | the operator's terminal command, `mesh-cli` | **the `nox` command** | Operator and conversation |
|
|
||||||
| knowledge base | the module holding the decision records and research efforts whole | **knowledge base** | The record |
|
| knowledge base | the module holding the decision records and research efforts whole | **knowledge base** | The record |
|
||||||
| | the predecessor's operational memory, in older playbooks (issue 430) | none: it no longer exists | — |
|
| | the predecessor's operational memory, in older playbooks (issue 430) | none: it no longer exists | — |
|
||||||
| check | a pull request's two statuses | **merge check**, **merge gate**, **repository check** | Change and delivery |
|
| check | a pull request's two statuses | **merge check**, **merge gate**, **repository check** | Change and delivery |
|
||||||
|
|||||||
@@ -12,12 +12,8 @@ A design becomes **milestones**, and each milestone becomes **issues**:
|
|||||||
- **A milestone is usable on its own.** Once its delivery plan is done, the operator or the mesh can do
|
- **A milestone is usable on its own.** Once its delivery plan is done, the operator or the mesh can do
|
||||||
something new, said in one sentence ("a Claude Code session is mirrored into a thread on the bus").
|
something new, said in one sentence ("a Claude Code session is mirrored into a thread on the bus").
|
||||||
A milestone that changes nothing anyone can use is not a milestone; fold it into the next.
|
A milestone that changes nothing anyone can use is not a milestone; fold it into the next.
|
||||||
- **A milestone is one release** of playbook [07](07-feature-branches.md): prepared, developed whole, reviewed,
|
- **A milestone is one feature step** of playbook [07](07-feature-branches.md): prepared, developed in one to
|
||||||
checked and delivered as one, never cut for its size or for time (the operator, 2026-10-17).
|
two hours, reviewed, checked and delivered as one. A milestone larger than that is split.
|
||||||
- **Milestones are cut by release order.** A milestone ends where one repository's change must be released before
|
|
||||||
another can use it (a new manifest word before the catalogue reads it, an SDK change before its users, until
|
|
||||||
design 47's milestones 3 and 6 lift those orders); the lead checks this when cutting, so no milestone needs a
|
|
||||||
second release.
|
|
||||||
- **An issue is one repository's part of a milestone,** sized so a fixing agent finishes it inside the
|
- **An issue is one repository's part of a milestone,** sized so a fixing agent finishes it inside the
|
||||||
milestone's development time. Issues that must land in order are linked `depends-on`.
|
milestone's development time. Issues that must land in order are linked `depends-on`.
|
||||||
|
|
||||||
@@ -44,10 +40,8 @@ A design becomes **milestones**, and each milestone becomes **issues**:
|
|||||||
entry in the design.
|
entry in the design.
|
||||||
7. **Flip the status.** `status: in-progress`, `updated:` today; the design change goes through playbook
|
7. **Flip the status.** `status: in-progress`, `updated:` today; the design change goes through playbook
|
||||||
[08](08-small-fixes.md) (one hq pull request).
|
[08](08-small-fixes.md) (one hq pull request).
|
||||||
8. **Hand each milestone to playbook 07, one at a time.** The milestone is one release of playbook 07; its issues
|
8. **Hand each milestone to playbook 07, one at a time.** The milestone is the feature step; its issues are the
|
||||||
are the release's issues; the next milestone starts when the previous one's delivery plan is done. A design waits
|
step's issues; the next milestone starts when the previous one's delivery plan is done.
|
||||||
while another design's milestones are being built: one design is built at a time (playbook
|
|
||||||
[07](07-feature-branches.md)), so this step starts only when the design before it is delivered.
|
|
||||||
9. **On completion of the last milestone**, run the "when something ships" section of playbook
|
9. **On completion of the last milestone**, run the "when something ships" section of playbook
|
||||||
[02](02-graduation.md).
|
[02](02-graduation.md).
|
||||||
|
|
||||||
|
|||||||
@@ -40,37 +40,26 @@ opened **once**, after the review. It runs in five phases, and its time is bound
|
|||||||
| Phase | What happens | Bound |
|
| Phase | What happens | Bound |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| 0. Prepare | the lead settles the open questions and writes one context file per repository | 15 minutes |
|
| 0. Prepare | the lead settles the open questions and writes one context file per repository | 15 minutes |
|
||||||
| 1. Develop | one fixing agent per repository builds every issue locally, unit tests only | the milestone's issues, 30 minutes each |
|
| 1. Develop | one fixing agent per repository builds every issue locally, unit tests only | 1 to 2 hours |
|
||||||
| 2. Review | one reviewer reads all of it; reports go both ways; at most 3 rounds | 15 minutes |
|
| 2. Review | one reviewer reads all of it; reports go both ways; at most 3 rounds | 15 minutes |
|
||||||
| 3. Check | push; the forge's merge checks run the integration tests; no further review | one check run |
|
| 3. Check | push; the forge's merge checks run the integration tests; no further review | one check run |
|
||||||
| 4. Deliver | merge; the delivery plan builds, publishes and sends the whole feature | its delivery plan |
|
| 4. Deliver | merge; the delivery plan builds, publishes and sends the whole feature | its delivery plan |
|
||||||
|
|
||||||
**One milestone is one release** (the operator, 2026-10-17). A milestone is developed whole, reviewed once,
|
A feature that cannot be developed in about two hours is split into **steps**. Each step runs all five
|
||||||
pushed once and delivered once. It is never cut into steps for its size or for time: every cut adds a review, a
|
phases on its own; the next step starts only when the previous step's delivery plan is done. A step is the
|
||||||
merge check, an Approve and a delivery plan of waiting, so the fewest cuts is the fastest. Milestones are cut by
|
unit that ships, so nothing finished waits for work that is not.
|
||||||
release order when the design is broken up (playbook [04](04-build-handoff.md)); a release-order dependency found
|
|
||||||
inside a milestone is a fault of that cut, said to the operator, never a silent extra step.
|
|
||||||
|
|
||||||
**One design is built at a time** (the operator, 2026-10-17). While the milestones of one design are being
|
**Scope is fixed at phase 0.** An issue found during a feature goes to the next step or the next feature,
|
||||||
built, no milestone of another design starts: its issues stay `open`, and nobody moves them to `diagnosing` or
|
|
||||||
`fixing`. The next design starts when the last milestone of the one being built has its delivery plan done. A
|
|
||||||
lead who finds another design's milestone already in `fixing` asks its owner before starting its own. A small
|
|
||||||
fix ([playbook 08](08-small-fixes.md)) and an issue that is in no design's milestones are not held by this rule.
|
|
||||||
|
|
||||||
**Scope is fixed at phase 0.** An issue found during a feature goes to the next milestone or the next feature,
|
|
||||||
never into the running one, unless it blocks the running one.
|
never into the running one, unless it blocks the running one.
|
||||||
|
|
||||||
## Phase 0 — Prepare (the lead, larger model)
|
## Phase 0 — Prepare (the lead, larger model)
|
||||||
|
|
||||||
1. **Name the feature once:** `feat/<slug>`, the same branch name in every repository it touches. The
|
1. **Name the feature once:** `feat/<slug>`, the same branch name in every repository it touches. The
|
||||||
branch is never protected. **One milestone is one branch per repository** (the operator, 2026-10-17), with one
|
branch is never protected.
|
||||||
folder `.work/<slug>/`; never a second name for part of it (`-2`, `-step2`).
|
|
||||||
2. **Make the worktrees:** one per touched repository under `.work/<slug>/<repo>`, branched off `main`
|
2. **Make the worktrees:** one per touched repository under `.work/<slug>/<repo>`, branched off `main`
|
||||||
(`git worktree add .work/<slug>/<repo> -b feat/<slug> origin/main`). The lab beds find the other
|
(`git worktree add .work/<slug>/<repo> -b feat/<slug> origin/main`). The lab beds find the other
|
||||||
repositories by sibling path, so add the untouched siblings the lab reads as detached worktrees on
|
repositories by sibling path, so add the untouched siblings the lab reads as detached worktrees on
|
||||||
`main` (`git worktree add --detach .work/<slug>/mesh-tools main`), never symlinks. **A repository's own
|
`main` (`git worktree add --detach .work/<slug>/mesh-tools main`), never symlinks.
|
||||||
clone stays on `main`** (the operator, 2026-10-17): no work is done in it and no branch is checked out
|
|
||||||
there, so a session that opens in it reads what holds now; fetch and fast-forward it before reading.
|
|
||||||
3. **Settle every open question** in the feature's issues before any agent starts: on the issue, through
|
3. **Settle every open question** in the feature's issues before any agent starts: on the issue, through
|
||||||
the knowledge base, or by asking the operator. A fixing agent that meets an undecided design question
|
the knowledge base, or by asking the operator. A fixing agent that meets an undecided design question
|
||||||
stops, and its run is spent again.
|
stops, and its run is spent again.
|
||||||
@@ -144,21 +133,21 @@ never into the running one, unless it blocks the running one.
|
|||||||
second review: phase 2 reviewed this tree.
|
second review: phase 2 reviewed this tree.
|
||||||
3. **A check that fails** goes back to the reviewer with the failure, for **one more cycle** of phase 2 with
|
3. **A check that fails** goes back to the reviewer with the failure, for **one more cycle** of phase 2 with
|
||||||
the same fixing agents; then push once again.
|
the same fixing agents; then push once again.
|
||||||
4. **The milestone's acceptance is proven before it is marked ready.** A milestone that adds or changes a module
|
4. **The step's acceptance is proven before it is marked ready.** A step that adds or changes a module
|
||||||
carries an integration test that starts the module whole, its process against the real bus and store, and
|
carries an integration test that starts the module whole, its process against the real bus and store, and
|
||||||
shows the milestone's acceptance sentence; the forge's checks run it (ADR 0391 §1, §6). Unit tests alone are not
|
shows the step's acceptance sentence; the forge's checks run it (ADR 0391 §1, §6). Unit tests alone are not
|
||||||
that proof; the mesh has no end-to-end tests yet, and the lab is not used (the operator, 2026-10-16).
|
that proof; the mesh has no end-to-end tests yet, and the lab is not used (the operator, 2026-10-16).
|
||||||
5. **The reviewer marks each pull request ready as soon as the acceptance is shown** with
|
5. **The reviewer marks each pull request ready as soon as the acceptance is shown** with
|
||||||
`mesh-delivery.mark-ready` (the delivery of the head it approved, who reviewed it and why), which writes the
|
`mesh-delivery.mark-ready` (the delivery of the head it approved, who reviewed it and why), which writes the
|
||||||
label `mesh/ready` and then `ready at <full sha>`, the order the forge counts (issue 913); mesh-delivery merges it when its checks pass
|
label `mesh/ready` and then `ready at <full sha>`, the order the forge counts (issue 913); mesh-delivery merges it when its checks pass
|
||||||
and the operator approved the merge into main on Telegram, the channel that proves who answered today (ADR 0364,
|
and the operator approved the merge into main on Telegram, the channel that proves who answered today (ADR 0364,
|
||||||
ADR 0398 §3; an Approve at the terminal is issue 953). Beyond that
|
ADR 0398 §3; an Approve at the terminal is issue 953). Beyond that
|
||||||
Approve, the operator's word is waited for only when the milestone changes what the operator sees, removes
|
Approve, the operator's word is waited for only when the step changes what the operator sees, removes
|
||||||
data, or needs a step only the operator's terminal can take; the lead says which before the milestone starts.
|
data, or needs a step only the operator's terminal can take; the lead says which before the step starts.
|
||||||
|
|
||||||
**One exception: a catalogue change that uses a new manifest word.** The catalogue's merge check reads
|
**One exception: a catalogue change that uses a new manifest word.** The catalogue's merge check reads
|
||||||
manifests with the controller the mesh runs, so the controller's pull request is merged and delivered first;
|
manifests with the controller the mesh runs, so the controller's pull request is merged and delivered first;
|
||||||
then `mesh-delivery.recheck` the catalogue's and merge it on the new verdict. Such work is two releases, the controller's first, and playbook 04 cuts its milestones that way.
|
then `mesh-delivery.recheck` the catalogue's and merge it on the new verdict. Plan such a feature as two steps.
|
||||||
The exception ends when milestone 3 of design 47 is delivered (issue 977, ADR 0402, ADR 0403): the group's composed
|
The exception ends when milestone 3 of design 47 is delivered (issue 977, ADR 0402, ADR 0403): the group's composed
|
||||||
check, judged by the group's controller, is then each member's gate, and the feature is one group.
|
check, judged by the group's controller, is then each member's gate, and the feature is one group.
|
||||||
|
|
||||||
@@ -207,7 +196,7 @@ unit and its bound or baseline; an issue is named by its number and a few words,
|
|||||||
waits for, marked ✓ landed, ▶ fixing, ⏸ waiting, ✗ stopped:
|
waits for, marked ✓ landed, ▶ fixing, ⏸ waiting, ✗ stopped:
|
||||||
|
|
||||||
```
|
```
|
||||||
Feature <slug> · milestone <n> of <m> · phase <name>, <minutes used> of <bound> min
|
Feature <slug> · step <n> of <m> · phase <name>, <minutes used> of <bound> min
|
||||||
Issues: <landed> of <total> landed · <fixing> fixing · <waiting> waiting
|
Issues: <landed> of <total> landed · <fixing> fixing · <waiting> waiting
|
||||||
|
|
||||||
mesh-controller (agent: fixing, 2 of 3 landed)
|
mesh-controller (agent: fixing, 2 of 3 landed)
|
||||||
@@ -228,11 +217,11 @@ Next: <the next action, one line>
|
|||||||
**The short layout** (on a channel such as Telegram, or when nothing changed but time):
|
**The short layout** (on a channel such as Telegram, or when nothing changed but time):
|
||||||
|
|
||||||
```
|
```
|
||||||
<slug> milestone <n>/<m> · <phase> <used>/<bound> min · <landed>/<total> landed · past bound: <none | what> · needs you: <nothing | what> · week <n> %
|
<slug> step <n>/<m> · <phase> <used>/<bound> min · <landed>/<total> landed · past bound: <none | what> · needs you: <nothing | what> · week <n> %
|
||||||
```
|
```
|
||||||
|
|
||||||
A report says what changed since the last one first. In the session every report is the full layout, the whole
|
A report says what changed since the last one first. In the session every report is the full layout, the whole
|
||||||
tree of the milestone's issues included, also when only time passed: the operator reads the state from it without
|
tree of the step's issues included, also when only time passed: the operator reads the state from it without
|
||||||
asking. The short layout is for a channel.
|
asking. The short layout is for a channel.
|
||||||
|
|
||||||
## The context of the lead's own session
|
## The context of the lead's own session
|
||||||
@@ -242,28 +231,9 @@ lead prepares a `/clear` as the mesh's instruction "prepare before clear" says:
|
|||||||
feature's phase, its branches and heads, the context files' paths, the agents' ids, the wait list and the
|
feature's phase, its branches and heads, the context files' paths, the agents' ids, the wait list and the
|
||||||
next action; then the prompt the operator pastes. The context files make the feature resumable after a clear.
|
next action; then the prompt the operator pastes. The context files make the feature resumable after a clear.
|
||||||
|
|
||||||
## The design's state is written back
|
|
||||||
|
|
||||||
Other sessions know what is built only from the design's frontmatter and the issue tracker; what the lead
|
|
||||||
knows and does not write back, nobody else knows. A design merged in one delivery with three of its tasks
|
|
||||||
left, its frontmatter still saying `designed`, read to another session as not built, and a second design
|
|
||||||
was started beside it (issue 1009, 2026-10-17). So the lead writes the design's state back at each of these
|
|
||||||
moments, the same hour, an hq change going through [playbook 08](08-small-fixes.md):
|
|
||||||
|
|
||||||
- **The first milestone starts:** the design's `status: in-progress` ([playbook 04](04-build-handoff.md) step 7),
|
|
||||||
if it is not already.
|
|
||||||
- **A milestone's delivery plan is done:** its issues are resolved (phase 4), and the design's milestone entry says
|
|
||||||
it is delivered, with the date.
|
|
||||||
- **The building pauses with work left** (another design takes its turn, a hand-over, the session's end): the
|
|
||||||
design stays `in-progress`, and each issue left gets a note with `issue-tracker.note` saying what it waits
|
|
||||||
for and what comes after it ("built after design N is delivered, before design M"). The first issue of
|
|
||||||
a design that waits its turn gets the same note.
|
|
||||||
- **The last milestone is delivered:** [playbook 02](02-graduation.md), "When something ships": the as-is is
|
|
||||||
written and the design says `implemented`.
|
|
||||||
|
|
||||||
## Issues that stop
|
## Issues that stop
|
||||||
|
|
||||||
An issue stays in `fixing` only while an agent works on it. A milestone that stops, for a wait past its bound, a
|
An issue stays in `fixing` only while an agent works on it. A step that stops, for a wait past its bound, a
|
||||||
hand-over, or the session's end, moves each of its issues not delivered back to `open` with
|
hand-over, or the session's end, moves each of its issues not delivered back to `open` with
|
||||||
`issue-tracker.move` and a note saying how far it got (its branch, its last commit, what is left). A list of
|
`issue-tracker.move` and a note saying how far it got (its branch, its last commit, what is left). A list of
|
||||||
issues in `fixing` that nobody works on reads to the operator as stuck work.
|
issues in `fixing` that nobody works on reads to the operator as stuck work.
|
||||||
@@ -274,25 +244,14 @@ issues in `fixing` that nobody works on reads to the operator as stuck work.
|
|||||||
request into a feature branch exists. A pull request per issue, or an agent waiting on a merge check in
|
request into a feature branch exists. A pull request per issue, or an agent waiting on a merge check in
|
||||||
phase 1, is the failure this playbook prevents.
|
phase 1, is the failure this playbook prevents.
|
||||||
- A context file exists per touched repository under `.work/<slug>/` while the feature runs.
|
- A context file exists per touched repository under `.work/<slug>/` while the feature runs.
|
||||||
- Every repository's own clone says `main` (`git -C <clone> branch --show-current`); another branch there is
|
|
||||||
work done outside a worktree (issue 1010).
|
|
||||||
- After a feature's delivery, `git branch -r | grep feat/<slug>` and `git worktree list` return nothing for it.
|
- After a feature's delivery, `git branch -r | grep feat/<slug>` and `git worktree list` return nothing for it.
|
||||||
- A design whose milestone issues are partly resolved says `in-progress`, and one whose issues are all
|
- More than one open pull request in a repository that share no feature name, or a feature step whose issues
|
||||||
resolved and delivered says `implemented`: read its Milestones section against `issue-tracker.get` for each
|
|
||||||
issue. A design that says `designed` with a resolved milestone issue is a state not written back.
|
|
||||||
- A milestone whose issues are all resolved and delivered has the date in its entry in the design's Milestones
|
|
||||||
section; an entry without it is a delivery not written back.
|
|
||||||
- Every open issue of a design that says `in-progress` and has no agent on it shows, in `issue-tracker.get`, a
|
|
||||||
note saying what it waits for; one without such a note is a pause not written back.
|
|
||||||
- The milestone issues in `fixing` (`issue-tracker.list`) belong to one design at most: milestone issues
|
|
||||||
of two designs in `fixing` at once is the failure of "one design is built at a time".
|
|
||||||
- More than one open pull request in a repository that share no feature name, or a milestone whose issues
|
|
||||||
stay in `fixing` for more than a working day, is the smell: stop and bring it to the operator.
|
stay in `fixing` for more than a working day, is the smell: stop and bring it to the operator.
|
||||||
- A review report that asks a change outside the issues' done-when, or a fourth round, is the failure of the
|
- A review report that asks a change outside the issues' done-when, or a fourth round, is the failure of the
|
||||||
review rules; `issue-tracker.list` with `flag: abandoned` lists issues left in `fixing` with nobody on them.
|
review rules; `issue-tracker.list` with `flag: abandoned` lists issues left in `fixing` with nobody on them.
|
||||||
- A pull request reviewed and green but not marked ready for more than 15 minutes is a lead that did not
|
- A pull request reviewed and green but not marked ready for more than 15 minutes is a lead that did not
|
||||||
mark it.
|
mark it.
|
||||||
- A milestone that adds or changes a module marked ready with no integration test that starts the module whole is
|
- A step that adds or changes a module marked ready with no integration test that starts the module whole is
|
||||||
the failure of phase 3's fourth step.
|
the failure of phase 3's fourth step.
|
||||||
- A bed run from `.work/<slug>/mesh-lab` that fails naming a `.work/<slug>/<repo>/…` path it cannot find is a
|
- A bed run from `.work/<slug>/mesh-lab` that fails naming a `.work/<slug>/<repo>/…` path it cannot find is a
|
||||||
missing sibling worktree, not a mesh fault.
|
missing sibling worktree, not a mesh fault.
|
||||||
|
|||||||
@@ -29,8 +29,7 @@ one delivery**. Everything before the push is local and fast.
|
|||||||
need a second repository, or more than about an hour, stops here and becomes a feature; one that needs a
|
need a second repository, or more than about an hour, stops here and becomes a feature; one that needs a
|
||||||
decision stays a small fix.
|
decision stays a small fix.
|
||||||
2. **Move the issue to `fixing`** with `by` as who@node.
|
2. **Move the issue to `fixing`** with `by` as who@node.
|
||||||
3. **Build on a local branch** `fix/<issue>-<slug>`, in a worktree of its own branched from `main`, never in
|
3. **Build on a local branch** `fix/<issue>-<slug>`, in a worktree of its own branched from `main`. Write the
|
||||||
the repository's own clone, which stays on `main` (playbook [07](07-feature-branches.md), phase 0 step 2). Write the
|
|
||||||
test that fails today first; make the change; keep `gofmt`, `go vet` and the unit tests of what changed green
|
test that fails today first; make the change; keep `gofmt`, `go vet` and the unit tests of what changed green
|
||||||
(each well under a second; never a merge check or a database locally: how-we-build §5, ADR 0391).
|
(each well under a second; never a merge check or a database locally: how-we-build §5, ADR 0391).
|
||||||
Commit, naming the issue. For a playbook or design edit, run the repository's own checks (in hq,
|
Commit, naming the issue. For a playbook or design edit, run the repository's own checks (in hq,
|
||||||
@@ -100,5 +99,3 @@ reading itself and uses one subagent only for the review.
|
|||||||
asking a change outside the issue's done-when, is the failure of steps 4 to 6.
|
asking a change outside the issue's done-when, is the failure of steps 4 to 6.
|
||||||
- An issue in `fixing` with nobody on it shows in `issue-tracker.list` with `flag: abandoned`; a stopped fix
|
- An issue in `fixing` with nobody on it shows in `issue-tracker.list` with `flag: abandoned`; a stopped fix
|
||||||
moves it back to `open` with its note.
|
moves it back to `open` with its note.
|
||||||
- The repository's own clone says `main` (`git -C <clone> branch --show-current`); another branch there is a
|
|
||||||
fix built outside a worktree (issue 1010).
|
|
||||||
|
|||||||
@@ -1,144 +1,69 @@
|
|||||||
---
|
---
|
||||||
layer: as-is
|
layer: as-is
|
||||||
status: implemented
|
status: implemented
|
||||||
code: [mesh-tools, mesh-host, mesh-controller]
|
code: [mesh-catalog modules/mesh-console, mesh-tools src/mesh.ts, mesh-tools src/http.ts, mesh-tools src/runtime.ts, mesh-controller internal/broker, mesh-controller cmd/mesh-controller/check.go, mesh-controller cmd/mesh-controller/seatverbs.go]
|
||||||
updated: 2026-10-17
|
updated: 2026-10-10
|
||||||
decisions:
|
decisions:
|
||||||
- "ADR 0152"
|
- "ADR 0152"
|
||||||
- "ADR 0175"
|
- "ADR 0095"
|
||||||
- "ADR 0195"
|
- "ADR 0037"
|
||||||
- "ADR 0197"
|
|
||||||
- "ADR 0367"
|
|
||||||
- "ADR 0154"
|
- "ADR 0154"
|
||||||
---
|
---
|
||||||
|
|
||||||
# The mesh MCP server, as it runs
|
# The mesh MCP server, as it runs
|
||||||
|
|
||||||
**The `mesh-console` container no longer exists.** Read 2026-10-17 from `mesh-tools` (package
|
**The mesh's tools reach a person through a module the mesh assigned to their machine.** Since
|
||||||
`node-tools`), `mesh-host` and `mesh-controller` on their forges' main, and from the live mesh
|
2026-09-30 a workstation that is a node can be assigned `mesh-console`; the mesh mints a bus account
|
||||||
through `mesh_overview`, `mesh_machine`, `mesh_runtimes` and `mesh_describe`. Checked: the
|
`<node>.mesh-console`, seals its credential to the machine, and the container binds
|
||||||
catalogue (`mesh-catalog`) carries no module named `mesh-console` any more — the name survives only
|
`127.0.0.1:<port>` with the port the mesh assigned for the manifest's declared one. An agent on the
|
||||||
in the glossary's retired-words list and in a test fixture — and none of the four nodes answers a
|
machine is pointed at `http://127.0.0.1:<port>/mcp` and sees the mesh's tools; a person uses the same
|
||||||
module of that name.
|
endpoint. Nothing on the machine holds a credential a person had to carry.
|
||||||
|
|
||||||
## What it is now
|
|
||||||
|
|
||||||
**The tool runner is one of the mesh's core components**, named `node-tools` beside
|
|
||||||
`mesh-controller`, `mesh-host` (the node-engine) and `nats` (the bus) — the mesh's own repositories'
|
|
||||||
check treats the four the same way, as the core whose health the mesh itself defines rather than a
|
|
||||||
module's own. `mesh-host` and `mesh-controller` supervise, compose and account for it as a core
|
|
||||||
process rather than as an ordinary assignment (`ComponentNodeTools`/`BusNodeTools` in both, the
|
|
||||||
witness and lease code that restarts and rolls it back, the account named `<node>.node-tools` on
|
|
||||||
the bus).
|
|
||||||
|
|
||||||
**It still carries a manifest, `node-tools/module.json` in `mesh-tools`,** and that manifest is
|
|
||||||
where its grants live: `invokes` names `*`, so its account may call every tool on the mesh;
|
|
||||||
`waits` names the five event families `mesh_wait` may subscribe to (the knowledge base's reviews,
|
|
||||||
mesh-delivery's transitions, the forge's status, the controller's checked event, and the operator
|
|
||||||
channel's decision for the agent module) — the bus refuses a subscription outside this list
|
|
||||||
(ADR 0367 §2); and it
|
|
||||||
`provides` one provision, the MCP endpoint, at node scope, and `listens` on the port it serves from,
|
|
||||||
opened only to the machine itself. Its code lives under `node-tools/` in the `mesh-tools`
|
|
||||||
repository (`internal/console`, `internal/announce`, `internal/bus`).
|
|
||||||
|
|
||||||
**One runtime per node, run by the node-engine as a sibling process, never a container**
|
|
||||||
(ADR 0175). It holds the node's one bus credential and may call every tool on the mesh; its account
|
|
||||||
publishes nothing else. It binds `127.0.0.1:<port>` and refuses to bind anything else, said before
|
|
||||||
binding rather than after (`node-tools/internal/console/http.go`, citing ADR 0152): whoever is on
|
|
||||||
the machine owns the mesh there, and nobody else may reach this port. An agent or a person on the
|
|
||||||
node is pointed at `http://127.0.0.1:<port>/mcp` once; the `serverInfo.name` the handshake answers
|
|
||||||
is `mesh`.
|
|
||||||
|
|
||||||
## What it answers
|
## What it answers
|
||||||
|
|
||||||
`initialize`, `notifications/initialized`, `ping`, `tools/list`, `tools/call`, over HTTP, one JSON
|
`initialize`, `tools/list`, `tools/call`, over HTTP, one JSON body per request, no session and no event
|
||||||
body per request, no session and no event stream (`node-tools/internal/console/mcp.go`).
|
stream. `tools/list` is what the running modules answered: every tool runner built on or after that day
|
||||||
|
serves a `tools` verb for its module, and the mesh MCP server asks the catalogue for the roster and each module
|
||||||
|
for its tools. A module that did not answer is named in the list's `_meta.notAnswering`. On the day it
|
||||||
|
shipped that was 36 of 51 modules — those that serve no tools at all, and those whose rebuilt runtime the
|
||||||
|
mesh records rather than rolls out — and 62 tools from the rest.
|
||||||
|
|
||||||
**It announces seven tools, not five** (ADR 0195, extended by ADR 0367): `mesh_overview`,
|
`tools/call` reaches any tool by `<module>.<tool>`, listed or not. The mesh MCP server's grant is `*`, so what it
|
||||||
`mesh_machine`, `mesh_search`, `mesh_describe`, `mesh_call`, `mesh_runtimes` and `mesh_wait`. The
|
may call is every tool on the mesh; its account may publish nothing else and subscribes nothing.
|
||||||
handshake's `instructions` text names all seven and the four address forms. `mesh_overview` is
|
|
||||||
where the mesh-wide count of modules and tools is read live — a number copied into this document
|
|
||||||
would drift; asked on 2026-10-17 it answered 103 modules and 645 tools across the whole mesh, with
|
|
||||||
two module instances (`route-proxy` on two of the nodes) named as not answering rather than silently
|
|
||||||
absent.
|
|
||||||
|
|
||||||
**Addresses, exactly as the handshake states them:** `<seat>.<verb>` for a seat held once for the
|
No call is refused for want of a person's approval: a tool runner serves every call it is issued, whoever made
|
||||||
mesh (`mesh-controller.status`); `<node>/<seat>.<verb>` for a seat every machine holds
|
it, and cannot tell an agent from the operator (ADR 0259 §6, issue 340). An act waits for the operator's warrant
|
||||||
(`<node>/node-packet-filter.rules`); `<node>/<module>.<tool>` for a module assigned to one machine;
|
only where its module asks first, in its own code: the agent module's instruction proposals, and the
|
||||||
and `<module>.<tool>` also for a module whose instances are interchangeable. A tool not listed can
|
controller's settings proposals and the actions it offers on a condition.
|
||||||
still be called by address — listing is discovery, calling is the grant — and a module that is not
|
|
||||||
interchangeable is refused with the machines it runs on rather than guessed at.
|
|
||||||
|
|
||||||
**What exists is what announced itself** (ADR 0197). Every node's tool runner answers the NATS services
|
|
||||||
protocol's `$SRV.PING`, `$SRV.INFO` and `$SRV.STATS`, one service per process, naming in each
|
|
||||||
endpoint's metadata which module, seat, scope and machine serves it. `mesh_overview` and
|
|
||||||
`mesh_machine` gather one request's answers: at least 750ms so a runtime that only just answered
|
|
||||||
`$SRV.PING` is still waited for, up to 5s for one that said it is there but has not yet sent its
|
|
||||||
full announcement — a fixed shorter window lost the largest runtime's listing once a live
|
|
||||||
measurement found how slow its round trip over the bus could be. An announcement unchanged since
|
|
||||||
last asked (by its digest) is not re-fetched, which is why `mesh_runtimes` can show a runtime
|
|
||||||
answering in under 100ms for hundreds of kilobytes of discovery.
|
|
||||||
|
|
||||||
## mesh_wait
|
|
||||||
|
|
||||||
**The seventh tool, added by ADR 0367**, lets a session wait on the bus for an event instead of
|
|
||||||
polling: a call names one or more granted event families and an optional filter, and either answers
|
|
||||||
the first match or says it timed out. The tool runner's own bus account hears only the families its
|
|
||||||
manifest's `waits` list names; an event it hears is never taken as the state — a session reads the
|
|
||||||
state from the verb that owns it once woken. It is meant to be called in a loop from a background
|
|
||||||
command, not inline, because one call blocks until it answers or times out.
|
|
||||||
|
|
||||||
## The mesh's own verbs
|
## The mesh's own verbs
|
||||||
|
|
||||||
**These already answer directly, not as a stated future.** Beside every assigned module's tools,
|
*Since 2026-09-30 evening (ADR 0154).*
|
||||||
the tool runner asks the `mesh-controller` seat for its own verbs the same way (ADR 0154): measured
|
The mesh MCP server asks the `mesh-controller` seat's `tools` verb beside the modules and lists every role's
|
||||||
live, the mesh-controller runtime is one instance, mesh-wide, and it answers 63 verbs for that one
|
tools as `<seat>.<verb>` — `mesh-controller.status`, `mesh-controller.push` and the other ten. A call
|
||||||
seat (`status`, `push`, `assign`, `build`, `artifacts` and the rest), and the `mesh-controller`
|
to `<prefix>.<name>` reaches the seat when the prefix is a seat declaring that verb, and the module
|
||||||
*module* answers none of its own — it serves no module tools, only its seat's, exactly as design 33
|
otherwise; `seat:<seat>.<verb>` says so outright. When the controller does not answer, the list
|
||||||
described while it was still only designed. The 56-to-93 seat-verb counts each node's tool runner
|
names `mesh-controller (seat)` as not answering and carries the modules' tools regardless. The
|
||||||
answers (below) are a different thing: the tool runner serves the verbs of whichever node-scoped
|
`mesh-controller` *module* is always named as not answering: it serves no module tools, only its seat's.
|
||||||
seats are held on that node (`node-backup`, `node-build-agent`, `node-service-manager` among them),
|
|
||||||
on behalf of the modules holding them, not the mesh-controller seat's — that one is served by the
|
|
||||||
mesh-controller runtime itself, a separate process, and the tool runner only asks it and relays what
|
|
||||||
it answers.
|
|
||||||
|
|
||||||
## Where it runs
|
## Around it
|
||||||
|
|
||||||
**Every node, not a per-operator assignment.** Measured live: all four of the mesh's machines
|
- **`invokes`** in a manifest is the grant. It is composed into the bus's user list exactly as a
|
||||||
answer a `node-tools` runtime, each with its own module and tool count (between 294 and 312 tools,
|
person's account is; the mesh MCP server is the only module that declares it.
|
||||||
56 to 93 seat verbs, in this reading). This is a fact that shipped past the design it came from: the
|
- **`module check <file|dir>…`** on the controller's binary judges a manifest with no mesh: the strict
|
||||||
to-be document still describes it as assigned to "whichever machines an operator sits at"; what
|
parse, every per-manifest problem, and the rules between the manifests given. It prints what it cannot
|
||||||
actually runs is the opposite — every node the mesh holds carries one, because it is core
|
judge without a store rather than refusing. The mesh MCP server's own manifest was the first thing checked
|
||||||
infrastructure the mesh itself depends on (discovery, the controller's own verbs, every module's
|
with it, and the whole catalogue passes.
|
||||||
tools), not a convenience assigned to a workstation. There is accordingly no assign or unassign verb
|
- **The person's client remains.** `operator issue` and `mesh tools|call|mcp` with a credential file
|
||||||
for it: removing it from a node is not an operation the controller or the catalogue exposes.
|
still work, for a machine that is not a node and for a mesh not yet able to assign anything.
|
||||||
|
`mesh tools --console <url>` goes through a running mesh MCP server with no credential; it is covered by the
|
||||||
|
runtime repository's tests and was not exercised on the live mesh.
|
||||||
|
|
||||||
## What this repository's prior text got wrong
|
## What shipped bent
|
||||||
|
|
||||||
The document this replaces described `mesh-console` as a catalogue module served by a container and
|
- A module registered by hand from the catalogue with `--source <url> --path modules/<m>` records a URL,
|
||||||
assigned only to workstations. That module and its container are gone; the manifest is not — it
|
not a place on the git seat: `--self` takes the forge path form (`<owner>/<repository>`), which the
|
||||||
moved to `node-tools/module.json`, still declares `invokes: ["*"]`, and gained `waits` and the
|
operator did not pass. The rebuild-on-merge matched the URL anyway.
|
||||||
`mcp-endpoint` provision, described above. What also moved is which language builds it: the
|
- Modules whose upgrade policy is *record* — the forge among them — answered `tools` only once
|
||||||
manifest's `build.artifacts` names a Go binary built from `cmd/node-tools`, and that is what a node
|
something pushed their rebuilt runtime; until then they are listed as not answering while still
|
||||||
runs. The repository still carries the earlier TypeScript sources (`node-tools/src/mesh.ts`,
|
callable. That is the policy doing what it says, not a fault of the mesh MCP server.
|
||||||
`http.ts`, `runtime.ts`, `announce.ts`, `broker-nats.ts`, `client.ts`, `launch.ts`, `main.ts`,
|
|
||||||
`mcp.ts`) on main, but the manifest does not build from them. The quirks the prior text recorded
|
|
||||||
from the `mesh-console` era (a module registered by hand recording its source as a URL; a
|
|
||||||
*record*-policy module staying listed as not-answering until its rebuilt runtime is pushed) are not
|
|
||||||
re-checked here: they were about the `mesh-console` container's registration path, which no longer
|
|
||||||
exists.
|
|
||||||
|
|
||||||
## How it is checked
|
|
||||||
|
|
||||||
- `mesh_overview` and `mesh_machine <node>`: the live tool and seat-verb counts, and which modules
|
|
||||||
are named not-answering.
|
|
||||||
- `mesh_runtimes`: which runtimes answered discovery, how long they took, how large their
|
|
||||||
announcement was, and whether it was shortened to fit the bus — read this when an address is said
|
|
||||||
to be missing or on another machine.
|
|
||||||
- `mesh_describe <address>` for one tool's schema; a call through `mesh_call` reaches the module or
|
|
||||||
seat over the bus and the answer is its own, unshaped.
|
|
||||||
- The code: `mesh-tools` (`node-tools/internal/console`, `internal/announce`, `internal/bus`),
|
|
||||||
`mesh-host` and `mesh-controller`'s core-component handling of `node-tools` (witness, lease,
|
|
||||||
bus-user naming). Their unit tests hold the loopback-only refusal, the seven-tool handshake, the
|
|
||||||
discovery gather window and `mesh_wait`'s event-then-read-state rule.
|
|
||||||
- `knowledge-base.get` with `kind: decision` and number 152, 175, 195, 197, 367 or 154.
|
|
||||||
|
|||||||
@@ -1,116 +0,0 @@
|
|||||||
---
|
|
||||||
layer: as-is
|
|
||||||
status: implemented
|
|
||||||
code: [mesh-controller, mesh-catalog modules/claude-code, mesh-catalog modules/gitea, mesh-catalog modules/ssh-client]
|
|
||||||
updated: 2026-10-17
|
|
||||||
decisions:
|
|
||||||
- "ADR 0266"
|
|
||||||
- "ADR 0399"
|
|
||||||
- "ADR 0409"
|
|
||||||
- "ADR 0410"
|
|
||||||
---
|
|
||||||
|
|
||||||
# Accounts on a machine, as they run
|
|
||||||
|
|
||||||
**A node record names at most two logins: the operator account, always, and an agent account, on one node of
|
|
||||||
four. Where a node names no agent account, agents run as the operator account. Nothing ties an identity to a
|
|
||||||
login, and a module that needs one declares it itself.** Read on 2026-10-17 from the controller's and the
|
|
||||||
catalogue's code on main (both local clones were a few commits behind the forge, so each claim below was
|
|
||||||
also checked against a live read) and from the live node records. The change planned in ADR 0411 to 0414
|
|
||||||
replaces this document when it is built.
|
|
||||||
|
|
||||||
## The node record
|
|
||||||
|
|
||||||
- **The operator account is stated at the controller's terminal** with `node account <node> <account> [home]`.
|
|
||||||
It is the person's login on that machine. The controller refuses to set it to the node's agent account.
|
|
||||||
- **The agent account is stated at the controller's terminal only** with `node agent-account <node> <account>
|
|
||||||
[home]`, and cleared with `--clear`. It is no verb and no setting, so an agent cannot name itself one
|
|
||||||
(ADR 0266 §1). The controller refuses root, a malformed login, a well-known service account and the
|
|
||||||
operator account.
|
|
||||||
- **Five machine facts come from the record.** `account` and `account-home` are the operator's, absent when
|
|
||||||
the node has no operator account. `agent-account` is the agent account, or the operator account where none is
|
|
||||||
named; `agent-home` is its home (`/home/<account>` when no home is stated); `agent-root` is `never` only where
|
|
||||||
the node names an agent account of its own, otherwise empty. Modules read them as `${machine:...}` words.
|
|
||||||
- **One node of four names an agent account today**, the control node. The workstation, the laptop and the
|
|
||||||
home server name none: `mesh-controller.node` says there "agents run as the operator account; no agent
|
|
||||||
account is named", and the plan for the workstation declares the `claude-code` account resource with the
|
|
||||||
operator's name and no `root` field. The tool runner also receives `MESH_AGENT_ACCOUNT` and `MESH_AGENT_HOME`.
|
|
||||||
- **Not verified:** whether the mesh-config snapshot carries either account. The controller's code reads both
|
|
||||||
from its own node table (columns for account, account home, agent account and agent home); no second place
|
|
||||||
that states them was found.
|
|
||||||
|
|
||||||
## What claude-code does with the agent account
|
|
||||||
|
|
||||||
- **It declares a `user` resource** named `${machine:agent-account}` with home `${machine:agent-home}` and
|
|
||||||
`root: ${machine:agent-root}`, and a `~/.claude` directory under that home owned by it, mode 0700. On the
|
|
||||||
control node the plan holds the resource with `root: never`; elsewhere it holds the operator's account.
|
|
||||||
Where the account is new, the mesh creates it with a home, its own group and no password (ADR 0266 §2).
|
|
||||||
- **`claude-agent`** is a file the module writes at `/usr/local/bin/claude-agent`. Run as the agent account
|
|
||||||
it starts `claude`; run as any other account it runs `sudo -iu <agent account> claude` through that
|
|
||||||
account's own sudo, carrying the operator's guard override only if it is set in that shell. The file
|
|
||||||
`/etc/claude-code/agent-account` holds the account's name.
|
|
||||||
- **The guard of ADR 0266 §28** is the module's hook on every tool call. On a node with an agent account
|
|
||||||
of its own it refuses each call of a session that runs as another account, with no override, and tells the
|
|
||||||
session to start with `claude-agent`. Its record goes to the machine's journal. The same guard also refuses
|
|
||||||
ssh to a mesh machine and writes to `/etc/hosts` and the resolver file (ADR 0245).
|
|
||||||
- **Not verified live:** this rests on the guard's code and README, not on a refusal seen on a running machine.
|
|
||||||
|
|
||||||
## The judgement that the agent account cannot become root
|
|
||||||
|
|
||||||
- **The node-engine judges `root: never` on every look**, by reading only: uid 0, a group that grants root,
|
|
||||||
a sudo, doas or polkit rule, a container runtime's socket or API port, a secret the mesh placed for another
|
|
||||||
account, a setuid program no package owns, and kernel link protection off (ADR 0266 §12 to §16). A way found,
|
|
||||||
or a question unanswered, makes the account resource unhealthy. It does not prevent any way to root.
|
|
||||||
- **The controller's self-check holds** that a node naming an agent account has it judged, in a verdict heard
|
|
||||||
within 15 minutes, as unable to become root (ADR 0266 §21 to §22). Otherwise it raises the urgent condition
|
|
||||||
`agent-can-become-root`. `mesh-controller.node` shows the verdict: for the control node it reads "cannot
|
|
||||||
become root without a person", judged the same day.
|
|
||||||
- **On the three nodes without an agent account** the check does not apply. The controller instead holds an
|
|
||||||
urgent condition `root-not-free` on each machine that runs reads or the mail notifier. Two of the three are
|
|
||||||
silenced until 2026-10-23 on the operator's decision; the home server's is open and not silenced.
|
|
||||||
|
|
||||||
## The accounts agents push as on the forge
|
|
||||||
|
|
||||||
- **Today the forge has a user per machine**, made by the gitea module's `forge-account` provision and
|
|
||||||
holding that machine's key, plus a write token the module still mints for each (ADR 0398, ADR 0400). ADR 0410
|
|
||||||
decides one forge user, `nox-agent`, in the agents' write team, each machine's key titled by machine. No tool
|
|
||||||
reads the forge's user list, so whether `nox-agent` exists yet is **not verified**.
|
|
||||||
- **The forge key is meant for the agent account** (ADR 0409 §2), but a module cannot run as that account:
|
|
||||||
the controller refuses it (issue 989, open). So the `ssh-client` module's key keeper still runs as the
|
|
||||||
operator account.
|
|
||||||
- **An ordering deadlock** between `ssh-client`, which wants `forge-account`, and `gitea`, which provides it,
|
|
||||||
stopped every node's declaration from composing on 2026-10-17 at 00:35 until gitea was registered three
|
|
||||||
minutes later (issue 986). The four urgent conditions it raised were not observed again but stay raised,
|
|
||||||
because the controller's declaration check times out and never clears them (issue 992).
|
|
||||||
Git over ssh was proven from the laptop only; the proof on the other three, and retiring the forge token the
|
|
||||||
module still places, wait on issues 989 and 991 (the latter in `fixing`).
|
|
||||||
- **Main stays closed**: a push to the main branch is refused, and a merge waits for a person (ADR 0398).
|
|
||||||
|
|
||||||
## Logins on the bus
|
|
||||||
|
|
||||||
Not here: see [`16-logins-on-the-bus.md`](16-logins-on-the-bus.md).
|
|
||||||
|
|
||||||
## What it cannot do
|
|
||||||
|
|
||||||
- **No identity carries a login.** An agent, a person and a module are not tied to an account on a machine;
|
|
||||||
the node record names a login per node and nothing else.
|
|
||||||
- **A module declares a login itself.** Each module that needs an account writes a `user` resource of its own,
|
|
||||||
and nothing checks that two modules agree or that a login exists on every machine that needs it.
|
|
||||||
- **Agent and operator share a login on three of four machines.** There the agent runs as the operator account,
|
|
||||||
which may become root; the guard's account rule does not apply, and the exposure is a held condition, not a fix.
|
|
||||||
- **No per-agent bus password.** Every session on a machine calls through that machine's tool-runner login
|
|
||||||
(document 16), so one agent's calls cannot be told from another's.
|
|
||||||
- **Detects, does not prevent.** The judgement above lists the ways to root it looks for; a way it does not list
|
|
||||||
is not seen (ADR 0266 §17).
|
|
||||||
|
|
||||||
## How it is checked
|
|
||||||
|
|
||||||
- `mesh-controller.node` with a node name: "agents run as" line, the verdict and when it was judged, the open
|
|
||||||
conditions (`agent-can-become-root`, `root-not-free`, `uncomposable`).
|
|
||||||
- `mesh-controller.plan` with a node name: the `claude-code.agent-account` resource and its `root` field; with
|
|
||||||
`files` set, the files given, including `claude-agent` and `agent-account`.
|
|
||||||
- `mesh-controller.conditions` for the held conditions; `claude_code_guard` for the guard's record, `claude-code`
|
|
||||||
tools for its status.
|
|
||||||
- `knowledge-base.get` with `kind` decision and number 266, 409 or 410; `issue-tracker.get` with 989 or 991.
|
|
||||||
- The code: the controller's node-record and agent-account files, and the `claude-code` module's manifest and
|
|
||||||
guard in the catalogue. Their unit tests hold the node record's refusals and the guard's account rule.
|
|
||||||
@@ -25,7 +25,6 @@ Where the two disagree, the implementation wins and the disagreement is stated.
|
|||||||
| [`14-a-modules-state.md`](14-a-modules-state.md) | A module's current state on the bus: what the controller creates, the runtime serves, and the first live use showed |
|
| [`14-a-modules-state.md`](14-a-modules-state.md) | A module's current state on the bus: what the controller creates, the runtime serves, and the first live use showed |
|
||||||
| [`15-the-agent-and-its-licences.md`](15-the-agent-and-its-licences.md) | The operator's agent on every machine and the licence manager that keeps its licences |
|
| [`15-the-agent-and-its-licences.md`](15-the-agent-and-its-licences.md) | The operator's agent on every machine and the licence manager that keeps its licences |
|
||||||
| [`16-logins-on-the-bus.md`](16-logins-on-the-bus.md) | The bus's one list of logins, a login per module assignment, persons limited to their tools, sessions calling through the tool runner's login, and no effect on any tool |
|
| [`16-logins-on-the-bus.md`](16-logins-on-the-bus.md) | The bus's one list of logins, a login per module assignment, persons limited to their tools, sessions calling through the tool runner's login, and no effect on any tool |
|
||||||
| [`17-accounts-on-a-machine.md`](17-accounts-on-a-machine.md) | The operator and agent accounts on the node record, claude-code's own login and guard, the judgement that the agent account cannot become root, and the forge accounts agents push as |
|
|
||||||
|
|
||||||
## What these documents are not
|
## What these documents are not
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
---
|
---
|
||||||
layer: to-be
|
layer: to-be
|
||||||
status: implemented
|
status: designed
|
||||||
code: [mesh-tools, mesh-host, mesh-controller]
|
code: [mesh-catalog, mesh-tools, mesh-controller]
|
||||||
updated: 2026-10-17
|
updated: 2026-10-03
|
||||||
decisions:
|
decisions:
|
||||||
- "ADR 0197"
|
- "ADR 0197"
|
||||||
- "ADR 0195"
|
- "ADR 0195"
|
||||||
- "ADR 0175"
|
|
||||||
- "ADR 0367"
|
|
||||||
- "ADR 0160"
|
- "ADR 0160"
|
||||||
- "ADR 0159"
|
- "ADR 0159"
|
||||||
- "ADR 0152"
|
- "ADR 0152"
|
||||||
@@ -165,16 +163,11 @@ matched it by URL.
|
|||||||
|
|
||||||
## What this does not settle
|
## What this does not settle
|
||||||
|
|
||||||
- **The mesh's own verbs on the bus.** Design 33's third family: these now answer directly, through
|
- Narrowing a mesh MCP server's grant per assignment. ADR 0046 makes it a setting; nothing reads one yet.
|
||||||
the `mesh-controller` seat, under ADR 0154 — settled, not open. [As-is 13](../00-as-is/13-the-console.md)
|
- The mesh's own verbs on the bus. Design 33's third family; this document only says where they appear
|
||||||
records it live.
|
once they exist.
|
||||||
- **Narrowing the tool runner's grant per assignment.** ADR 0046 makes it a setting; nothing reads
|
- A person's identity behind the mesh MCP server. The mesh sees the mesh MCP server's account; design 15 keeps the
|
||||||
one yet. Owned by [design 58](58-agent-work-as-the-meshs-own.md) milestone 6 (issue 883): an
|
question open.
|
||||||
identity asked for each assignment is one of that milestone's parts.
|
|
||||||
- **A person's identity behind the mesh MCP server.** The mesh still sees only the tool runner's own
|
|
||||||
account, never the caller's; design 15 raised the question and leaves it open. Owned by the same
|
|
||||||
milestone (design 58 milestone 6, issue 883): its outcome is that every person, agent and module
|
|
||||||
becomes an identity with a role, and a call carries which one made it.
|
|
||||||
|
|
||||||
## References
|
## References
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,8 @@
|
|||||||
layer: to-be
|
layer: to-be
|
||||||
status: in-progress
|
status: in-progress
|
||||||
code: [mesh-catalog modules/claude-code]
|
code: [mesh-catalog modules/claude-code]
|
||||||
updated: 2026-10-17
|
updated: 2026-10-13
|
||||||
decisions:
|
decisions:
|
||||||
- "ADR 0411"
|
|
||||||
- "ADR 0412"
|
|
||||||
- "ADR 0343"
|
- "ADR 0343"
|
||||||
- "ADR 0274"
|
- "ADR 0274"
|
||||||
- "ADR 0273"
|
- "ADR 0273"
|
||||||
@@ -308,27 +306,21 @@ operator's word — and, for the operator's own home, only at the terminal or on
|
|||||||
|
|
||||||
## 9. The account the agent runs as
|
## 9. The account the agent runs as
|
||||||
|
|
||||||
*Added 2026-10-08 by ADR 0266; changed 2026-10-17 by ADR 0411 and ADR 0412.*
|
*Added 2026-10-08 by ADR 0266.*
|
||||||
Everywhere above, "the operator's home" and "the operator account" mean **the account the agent runs as**:
|
Everywhere above, "the operator's home" and "the operator account" mean **the account the agent runs as**.
|
||||||
the machine account of the identity the module runs for (ADR 0412). claude-code declares that it can run for
|
That is the operator account, unless the node names an **agent account** of its own. The control node does.
|
||||||
an agent identity, in its assignment or when a session is started: started for `nox` it runs with the agent's
|
|
||||||
home; started for the operator, it is the default run, with the operator's configuration. On a
|
|
||||||
machine where no agent is marked as the agent account, its assignment is the default run, as every module's is.
|
|
||||||
|
|
||||||
**The fact.** The machine accounts come from the identity seat (ADR 0411); the operator no longer states the
|
**The fact.** The operator states the agent account at the controller's terminal. No verb and no setting
|
||||||
agent account at the controller's terminal. A machine's agent account is the machine account of the agent the
|
writes it. The controller resolves the machine facts `agent-account`, `agent-home` and `agent-root`, and gives
|
||||||
identity seat marks as the agent account there, on the operator's Approve: `nox` on the control node. There this module's
|
the tool runner the account and its home. Where the node names none, the facts name the operator account and
|
||||||
assignment is run for that agent, and the controller refuses one that is not. The controller resolves the
|
`agent-root` is empty, so nothing changes there.
|
||||||
machine facts `agent-account`, `agent-home` and `agent-root` from it, and gives the tool runner the account and
|
|
||||||
its home. Where no agent is marked (every other machine today), the facts name the operator account and `agent-root`
|
|
||||||
is empty, so nothing changes there, though a login `nox` exists for the modules run for it.
|
|
||||||
|
|
||||||
**What the module declares.**
|
**What the module declares.**
|
||||||
|
|
||||||
- No login. The mesh makes the agent's machine account (home, own group, no password) from the identity, and the
|
- A `user` named by `agent-account`, with `root` set from `agent-root`. Where the account is the agent's own,
|
||||||
node-engine judges on every look that it cannot become root without a person, by the ways ADR 0266 §12 to §17 list.
|
the mesh creates it (home, own group, no password) and the node-engine judges on every look that it cannot
|
||||||
It detects those ways and no others; it prevents nothing. The `user` resource named by `agent-account`,
|
become root without a person, by the ways ADR 0266 §3 lists. It detects those ways and no others; it
|
||||||
which this module declared until ADR 0411, goes.
|
prevents nothing.
|
||||||
- `~/.claude` under that account's home, owned by it.
|
- `~/.claude` under that account's home, owned by it.
|
||||||
- `claude-agent`, a command that starts the agent as that account through the operator's own sudo, or
|
- `claude-agent`, a command that starts the agent as that account through the operator's own sudo, or
|
||||||
directly when already run as it.
|
directly when already run as it.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
layer: to-be
|
layer: to-be
|
||||||
status: in-progress
|
status: in-progress
|
||||||
code: [mesh-controller, mesh-catalog modules/mesh-delivery]
|
code: [mesh-controller, mesh-catalog modules/mesh-delivery]
|
||||||
updated: 2026-10-17
|
updated: 2026-10-16
|
||||||
decisions:
|
decisions:
|
||||||
- "ADR 0408"
|
- "ADR 0408"
|
||||||
- "ADR 0407"
|
- "ADR 0407"
|
||||||
@@ -485,8 +485,8 @@ What the build proves before this design is implemented (research 055's directio
|
|||||||
|
|
||||||
## Milestones
|
## Milestones
|
||||||
|
|
||||||
Confirmed by the operator on 2026-10-17 (playbook 04). Each is one release of playbook 07; the next starts
|
Confirmed by the operator on 2026-10-17 (playbook 04). Each is one feature step of playbook 07; the next starts
|
||||||
when the previous one's delivery is done.
|
when the previous one's delivery is done. Milestones 5 and 6 are split into steps when they come up.
|
||||||
|
|
||||||
| # | Outcome | Acceptance tests | Repositories | Issues |
|
| # | Outcome | Acceptance tests | Repositories | Issues |
|
||||||
|---|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
---
|
---
|
||||||
layer: to-be
|
layer: to-be
|
||||||
status: in-progress
|
status: designed
|
||||||
code: [mesh-controller, mesh-host, mesh-sdk, mesh-catalog]
|
code: [mesh-controller, mesh-host, mesh-sdk, mesh-catalog]
|
||||||
updated: 2026-10-17
|
updated: 2026-10-14
|
||||||
decisions:
|
decisions:
|
||||||
- "ADR 0348"
|
- "ADR 0348"
|
||||||
- "ADR 0349"
|
- "ADR 0349"
|
||||||
@@ -61,10 +61,6 @@ both, by the form of its manifest, and the old forms stay accepted until the las
|
|||||||
Inside the release this still holds: the media catalogue and the applications kept in their own
|
Inside the release this still holds: the media catalogue and the applications kept in their own
|
||||||
repositories move after it, so the old forms stay accepted until they have (R1).
|
repositories move after it, so the old forms stay accepted until they have (R1).
|
||||||
|
|
||||||
**Where it stands, 2026-10-17** (the Milestones section below). The packages' branches were merged into
|
|
||||||
main of mesh-controller, the node-engine (`mesh-host`) and mesh-catalog on 2026-10-16. T6 moved after them, as
|
|
||||||
planned above, and is resolved. B3 was left out as well, so B3, R1 and R2 are what is left.
|
|
||||||
|
|
||||||
## What exists already, measured
|
## What exists already, measured
|
||||||
|
|
||||||
Counted 2026-10-14 at mesh-controller `1601cb1`, the node-engine (`mesh-host`) `1a26778`, mesh-sdk `a2d2af0`, mesh-catalog
|
Counted 2026-10-14 at mesh-controller `1601cb1`, the node-engine (`mesh-host`) `1a26778`, mesh-sdk `a2d2af0`, mesh-catalog
|
||||||
@@ -337,17 +333,6 @@ removed code.
|
|||||||
amended, the 67 documents that still say "setting" in running prose are reworded so the glossary can
|
amended, the 67 documents that still say "setting" in running prose are reworded so the glossary can
|
||||||
retire the word (F4 left it off the *Not:* line for them), design 49 lists the Config domain, ADR 0348's and 0349's built status is said, and this document's status becomes `implemented`.
|
retire the word (F4 left it off the *Not:* line for them), design 49 lists the Config domain, ADR 0348's and 0349's built status is said, and this document's status becomes `implemented`.
|
||||||
|
|
||||||
## Milestones
|
|
||||||
|
|
||||||
Written back on 2026-10-17 (playbook 07, "The design's state is written back"): the packages above were built
|
|
||||||
as one delivery, not milestone by milestone, so the milestones are what was delivered and what is left.
|
|
||||||
|
|
||||||
| # | Outcome | Packages | Issues | State |
|
|
||||||
|---|---|---|---|---|
|
|
||||||
| 1 | Config, provisioning, secrets, bindings, collections and runtime asks run the new way; the old forms are still accepted | every package but T6, B3, R1, R2 | 647, 650 to 679 but 659, 681, 684, 685 | merged into main and delivered on 2026-10-16; issues resolved |
|
|
||||||
| 2 | The media catalogue and the applications carry their file text as templates | T6 | 659 | resolved; delivered on 2026-10-16 |
|
|
||||||
| 3 | The other providers answer their consumers; the old forms are refused; the record says what runs | B3, R1, R2 | 680, 682, 683 | open: built after design 47's milestones are delivered and before design 58 starts (the operator, 2026-10-17; one design is built at a time, playbook 07) |
|
|
||||||
|
|
||||||
## What is deliberately not here
|
## What is deliberately not here
|
||||||
|
|
||||||
- **Secrets in a module's own code** wait on issue 569 (a module's tools run as the tool runner's
|
- **Secrets in a module's own code** wait on issue 569 (a module's tools run as the tool runner's
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
layer: to-be
|
layer: to-be
|
||||||
status: designed
|
status: designed
|
||||||
code: [mesh-catalog, mesh-media-catalog, mesh-sdk, mesh-tools, mesh-controller, mesh-host, mesh-lab]
|
code: [mesh-catalog, mesh-media-catalog, mesh-sdk, mesh-tools, mesh-controller, mesh-host, mesh-lab]
|
||||||
updated: 2026-10-17
|
updated: 2026-10-16
|
||||||
decisions:
|
decisions:
|
||||||
- "ADR 0383"
|
- "ADR 0383"
|
||||||
- "ADR 0384"
|
- "ADR 0384"
|
||||||
@@ -12,10 +12,6 @@ decisions:
|
|||||||
- "ADR 0388"
|
- "ADR 0388"
|
||||||
- "ADR 0389"
|
- "ADR 0389"
|
||||||
- "ADR 0390"
|
- "ADR 0390"
|
||||||
- "ADR 0411"
|
|
||||||
- "ADR 0412"
|
|
||||||
- "ADR 0413"
|
|
||||||
- "ADR 0414"
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 58 — Agent work as the mesh's own
|
# 58 — Agent work as the mesh's own
|
||||||
@@ -25,10 +21,8 @@ bus by a thread seat; a thread handler on an always-on node runs the agent harne
|
|||||||
of its own, with the thread's own branch on the forge as its workspace; the agent works through a workspace toolkit
|
of its own, with the thread's own branch on the forge as its workspace; the agent works through a workspace toolkit
|
||||||
and named tools, never a general command runner. Every person, agent and module is an identity held by one identity
|
and named tools, never a general command runner. Every person, agent and module is an identity held by one identity
|
||||||
seat, with a role made of grants; the bus checks each login by its signature, and the tool runner checks each call
|
seat, with a role made of grants; the bus checks each login by its signature, and the tool runner checks each call
|
||||||
against the role, tool by tool, by the effect every tool carries. An identity is also its logins: the mesh makes each
|
against the role, tool by tool, by the effect every tool carries.**
|
||||||
person's and agent's machine account on every machine it is present on, gives each its own bus password, and runs a
|
(ADR 0383 to ADR 0390, from research 053.)
|
||||||
module for an identity only where the module is set to; everything else runs as the operator.**
|
|
||||||
(ADR 0383 to ADR 0390, from research 053; ADR 0411 to ADR 0414, from research 057.)
|
|
||||||
|
|
||||||
**Acceptance.** The operator starts a thread from the terminal client, an agent on an always-on node works it in a
|
**Acceptance.** The operator starts a thread from the terminal client, an agent on an always-on node works it in a
|
||||||
sandbox, asks a question the operator answers in place, and the thread moves to another node and carries on with
|
sandbox, asks a question the operator answers in place, and the thread moves to another node and carries on with
|
||||||
@@ -112,95 +106,6 @@ agent carries a token the identity seat signed for that agent, and the tool runn
|
|||||||
refusal names the missing grant; every call is recorded with its identity and thread. A session on an operator's
|
refusal names the missing grant; every call is recorded with its identity and thread. A session on an operator's
|
||||||
machine calls as the operator's person identity or as an agent, never through the machine's tool-runner login.
|
machine calls as the operator's person identity or as an agent, never through the machine's tool-runner login.
|
||||||
|
|
||||||
### 5.1 An identity on the machines (ADR 0411, ADR 0414)
|
|
||||||
|
|
||||||
A person or agent identity also holds a **login name**, the **machines** it is present on and, for a person, the
|
|
||||||
login name kept on a machine where it differs. The seat starts with two: the operator's person identity and `nox`, the one
|
|
||||||
agent; every piece of agent work is a thread of `nox` (§2), and a second agent identity is made only when a rule needs
|
|
||||||
another role. The agent is `nox` everywhere: its login, its forge account (the forge account `nox-agent` renamed,
|
|
||||||
keys and team kept), Keycloak, mail and the bus. A login or service account the mesh makes for the operator from now on carries the operator's identity name;
|
|
||||||
the operator's existing logins and forge account are kept as they are. On a machine where agents ran as the
|
|
||||||
operator's login, that login stays the operator's and `nox` gets a login of its own; nox's bus account is `agent.nox`. `nox` is also the
|
|
||||||
operator's terminal command (`mesh-cli`), and prose says which is meant.
|
|
||||||
|
|
||||||
An identity's login on one machine is its **machine account**. The controller composes the machine account of every
|
|
||||||
identity present on a machine into that machine's declaration, and the node-engine applies it with its `user`
|
|
||||||
resource. A login that exists is adopted under its name, uid and home; the mesh never renames or deletes one. An
|
|
||||||
agent's machine account has its own home and group, no password, no root group and no sudo rule, and the node-engine
|
|
||||||
judges on every look that it cannot become root without a person, by the ways ADR 0266 §12 to §17 list. No module declares a login for a
|
|
||||||
person or an agent: claude-code's `agent-account` resource goes, and the catalogue's check refuses a manifest that
|
|
||||||
declares one. The node record's operator account and agent account are read from the identities, no longer stated
|
|
||||||
at the controller's terminal or in the mesh-config snapshot; the machine facts `account`, `agent-account` and their
|
|
||||||
homes keep their names. A machine names an agent account only where the identity seat marks an agent as its agent
|
|
||||||
account: the control node, marked `nox`, where the guard and the licence stay as they are. The other machines are
|
|
||||||
marked with none, so the guard stays off there and the operator's sessions keep the mesh's tools, while the login `nox` serves the
|
|
||||||
modules run for `nox`. Where a person or an agent is present, its kept logins and the agent account mark change only
|
|
||||||
through a verb of the identity seat that only the operator calls, on an Approve; an assignment never sets the mark,
|
|
||||||
because an agent may call the verb that changes an assignment (ADR 0266 §4). The verb, and the controller at composition,
|
|
||||||
refuse an agent whose login on a machine would be `root`, a person's login there or a well-known service account
|
|
||||||
(ADR 0266 §5). The self-check judges every agent login,
|
|
||||||
marked or not. The seed: the operator on every machine, with the operator's existing login kept where it differs from the
|
|
||||||
identity's name; `nox` on every machine, the agent account on the control node.
|
|
||||||
|
|
||||||
```
|
|
||||||
identity seat controller each machine (node-engine)
|
|
||||||
───────────── ────────── ──────────────────────────
|
|
||||||
person operator, machines, composes, per machine: the operator's login (adopted)
|
|
||||||
the login kept on each the machine accounts present nox, root: never (adopted on the control node,
|
|
||||||
agent nox, its machines the facts account, agent-account made elsewhere)
|
|
||||||
each identity's bus password ──► its secret, owned by its login, 0600
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5.2 Run for an identity (ADR 0412)
|
|
||||||
|
|
||||||
Every module runs as the operator account of its machine, as today, with no setting; running for the operator is that
|
|
||||||
default run. A manifest may declare that its module can **run for** an agent identity, at assignment, at start or both: serve one identity with that identity's machine account, home and bus password.
|
|
||||||
An assignment of such a module may name the identity it runs for, and the module may let the one who starts it
|
|
||||||
choose: claude-code started for the operator is the default run with the operator's configuration, and started for `nox`
|
|
||||||
it runs for `nox` with the agent's home. A start
|
|
||||||
goes down only: a module is started for an identity by a process already running as that identity's login, or by the
|
|
||||||
operator's own login for an agent; an agent never starts one for a person, and the module refuses it, naming who asked
|
|
||||||
and for whom. A module may be assigned to one machine once per identity it runs for there, each its own process under
|
|
||||||
its own login, and the controller keys an assignment by machine, module and identity. The first two to declare it are
|
|
||||||
claude-code and ssh-client. On a machine the identity seat marks with an agent account, claude-code's assignment is
|
|
||||||
run for that agent (`nox` on the control node), so its licence and home scope stay in the agent's home, and the controller refuses
|
|
||||||
one there that is not. ssh-client keeps its default assignment for
|
|
||||||
the operator wherever `nox` is present and gains a second one run for `nox`, which makes the agent's forge key in the
|
|
||||||
agent's home and closes issue 989; gitea's provider registers that key on the forge account `nox`. A module run for an identity calls as that
|
|
||||||
identity and gets no module identity of its own (this narrows ADR 0386 §4, which holds for every other assignment). A module with an account of its
|
|
||||||
own (`runs-as`, ADR 0259 §8) cannot declare it, and a run for an identity with no machine account on that machine is
|
|
||||||
refused at composition and at start. ADR 0266's guard
|
|
||||||
stands: on the control node, a session started for the operator gets the operator's configuration and its tool calls are still
|
|
||||||
refused.
|
|
||||||
|
|
||||||
### 5.3 A bus password per identity (ADR 0413)
|
|
||||||
|
|
||||||
Each person and agent identity has a bus account of its own (`person.<name>` for the operator, `agent.nox`), whose memberships are made
|
|
||||||
from its role, and its password is a secret owned by its machine account at mode 0600 on each machine it is present
|
|
||||||
on, which no other account but root can read. A person's password is placed only on a machine that names an agent
|
|
||||||
account of its own judged unable to become root, by a verdict at most fifteen minutes old; on the other machines, where agents can still run as the operator,
|
|
||||||
it is not placed, and the controller says so. A module run for an identity reaches the bus with that password, can do
|
|
||||||
only what the identity's role allows, and every call is recorded as that identity's. Nox's role never holds an approval
|
|
||||||
grant, a trusted party's grant or a dangerous tool named in it (ADR 0387 §4 does not apply to it), and the identity seat
|
|
||||||
refuses one; each dangerous act of nox's is asked one by one. The break-glass agent (§6) keeps its own rules and gets
|
|
||||||
no login and no bus password from this. This changes ADR 0259 §8, which kept every bus credential out of an agent's account; the rest of §8 stands.
|
|
||||||
The password lives in today's list of bus accounts and is replaced by a signed login for the same role at the bus
|
|
||||||
switch (§7, §8).
|
|
||||||
|
|
||||||
### 5.4 Traced to GENESIS
|
|
||||||
|
|
||||||
Checked on 2026-10-17 (playbook 02, step 1); no conflict was found.
|
|
||||||
|
|
||||||
- [Mission](../../00-META/mission.md): agents "hold identity", and there is one kind of participant, some of them
|
|
||||||
human; persons and agents are held alike, with the same fields, and differ in what their role allows, which the
|
|
||||||
mission calls modality. "The mesh brokers credentials": the bus password per identity and the service accounts made
|
|
||||||
from it. "The mesh owns the truth": logins are derived from identities, no longer stated per node. "Dogfood": logins
|
|
||||||
go through the node-engine's existing resource.
|
|
||||||
- [Context](../../00-META/context.md): "Identity is mesh-internal", with no directory and no SSO, so the identity seat
|
|
||||||
holds it and Keycloak only receives accounts; "Nodes are personal" and the mesh is a guest, so existing logins are
|
|
||||||
adopted, never renamed; human agents are few, often one, so the seat holds two identities and one agent.
|
|
||||||
- [Effect](../../00-META/effect.md): a human agent's environment is part of the mesh, its login included.
|
|
||||||
|
|
||||||
## 6. Effects, asks and break-glass (ADR 0387)
|
## 6. Effects, asks and break-glass (ADR 0387)
|
||||||
|
|
||||||
Every seat verb and module tool is one permission and carries one **effect**, set in its module's manifest:
|
Every seat verb and module tool is one permission and carries one **effect**, set in its module's manifest:
|
||||||
@@ -251,36 +156,12 @@ carry them:
|
|||||||
`research/053-signed-bus-logins`, loses nothing and leaves every identity on the new bus.
|
`research/053-signed-bus-logins`, loses nothing and leaves every identity on the new bus.
|
||||||
5. The acceptance sentence at the top, end to end.
|
5. The acceptance sentence at the top, end to end.
|
||||||
|
|
||||||
From research 057 (ADR 0411 to ADR 0414):
|
|
||||||
|
|
||||||
6. The identity seat lists the operator and `nox` with their machines, kept logins and the control node's agent account mark; a
|
|
||||||
create or a presence change by any caller but the operator's Approve is refused.
|
|
||||||
7. On every other machine a login `nox` exists; on the control node the login `nox` is adopted with its uid and home unchanged;
|
|
||||||
on every machine the node-engine judges `nox` unable to become root; a login `nox` given a sudo rule in a test is left
|
|
||||||
unaltered and the self-check raises `agent-can-become-root` for it, on a machine that marks no agent account too;
|
|
||||||
the operator's existing logins are
|
|
||||||
unchanged.
|
|
||||||
8. No manifest in the catalogue declares a login for a person or an agent; the catalogue's check refuses one.
|
|
||||||
9. For every machine, the declaration before and after differs, for the modules not set to run for an identity, only
|
|
||||||
in the machine accounts' `user` resources (now the controller's, and `nox` added on the other machines); claude-code on the control node, run for `nox`, places the same
|
|
||||||
files in the same home; on the other machines the guard stays off and the operator's sessions keep the mesh's tools.
|
|
||||||
10. A session started for `nox` on the control node gets the agent's home configuration and one started for the operator the
|
|
||||||
operator's, with no tool call through; a session as `nox` that starts claude-code for the operator is refused; with
|
|
||||||
ssh-client run for `nox`, nox's key is on the forge account `nox` and the session pushes a branch over SSH as `nox`.
|
|
||||||
11. Nox's bus password is owned by `nox` at mode 0600 on every machine nox is present on; a call with it outside
|
|
||||||
nox's role is refused; a grant test finds no approval grant, trusted party's grant or dangerous tool in nox's
|
|
||||||
role; the operator's password is placed only on a machine with an agent account judged root-free within fifteen
|
|
||||||
minutes, and the controller says so for every other.
|
|
||||||
12. The forge account `nox-agent` is renamed `nox`, keeps every machine's key, stays in the agents' write team, and
|
|
||||||
every `forge-account` entry names `nox`.
|
|
||||||
|
|
||||||
## 11. Milestones
|
## 11. Milestones
|
||||||
|
|
||||||
Each milestone is one feature step (playbook 07), usable as soon as its delivery plan is done, and starts when
|
Each milestone is one feature step (playbook 07), usable as soon as its delivery plan is done, and starts when
|
||||||
the previous one's is. What it replaces is in the as-is documents
|
the previous one's is. What it replaces is in the as-is documents
|
||||||
[15 — The agent and its licences](../00-as-is/15-the-agent-and-its-licences.md) and
|
[15 — The agent and its licences](../00-as-is/15-the-agent-and-its-licences.md) and
|
||||||
[16 — Logins on the bus](../00-as-is/16-logins-on-the-bus.md), and for milestones 11 to 14
|
[16 — Logins on the bus](../00-as-is/16-logins-on-the-bus.md). The issues are opened once the operator confirms
|
||||||
[17 — Accounts on a machine](../00-as-is/17-accounts-on-a-machine.md). The issues are opened once the operator confirms
|
|
||||||
the milestones, and their numbers written here.
|
the milestones, and their numbers written here.
|
||||||
|
|
||||||
**Confirmed by the operator on 2026-10-16.** One issue per milestone: 1 is issue 878, 2 is 879, 3 is 880, 4 is
|
**Confirmed by the operator on 2026-10-16.** One issue per milestone: 1 is issue 878, 2 is 879, 3 is 880, 4 is
|
||||||
@@ -305,18 +186,3 @@ and the issue tracker takes only registered owners. The building waits until the
|
|||||||
|
|
||||||
The front ends' later parts, the inference seat and the other agent harnesses are tracked by issue 850; the
|
The front ends' later parts, the inference seat and the other agent harnesses are tracked by issue 850; the
|
||||||
seat-sense "role" in older designs by issue 872.
|
seat-sense "role" in older designs by issue 872.
|
||||||
|
|
||||||
**Milestones 11 to 14, from research 057, confirmed by the operator on 2026-10-17.** One issue per repository
|
|
||||||
part, opened at the confirmation, so a fixing agent per repository can start when its step does. Milestone 11
|
|
||||||
waits for nothing; 12 waits for milestone 6.
|
|
||||||
|
|
||||||
| # | Outcome, in one sentence | Proves | Repositories and their parts (issues) | Depends on |
|
|
||||||
|---|---|---|---|---|
|
|
||||||
| 11 | The agent's forge account is `nox`, with its keys and its place in the agents' write team kept | test 12 | mesh-catalog gitea: the rename, keys registered on `nox`, every `forge-account` entry (993) | — |
|
|
||||||
| 12 | The mesh makes the operator's logins and nox's on every machine from the identities, and judges nox unable to become root everywhere | tests 6 to 9 | identity module: logins, machines, kept logins, the agent account mark, the operator's verb (994); the node-engine (`mesh-host`): existing logins adopted, every agent login judged (995); mesh-controller: machine accounts composed, node accounts read from the identities, the terminal lines retired (996); mesh-catalog: claude-code declares no login, the catalogue's check refuses one (997) | 6 |
|
|
||||||
| 13 | The operator and nox each reach the bus with their own password, held to their role; the operator's only where no agent runs as the operator | test 11 | mesh-controller: the operator's and nox's bus accounts, secrets at 0600, the fifteen-minute verdict (999); identity module: nox's role refuses approvals and dangerous tools (998); until milestone 7 builds the asks, a dangerous act of nox's is refused, not asked | 12 |
|
|
||||||
| 14 | The operator starts a claude-code session for nox or for themself, and nox pushes over SSH with its own key | test 10 | mesh-controller: run for, assignments keyed by identity, the refusals (1001); mesh-sdk: calls as the identity, a start goes down only (1002); claude-code: started for nox or for the operator (1003); ssh-client and gitea: nox's own key, closing issue 989 (1004) | 11, 13 |
|
|
||||||
|
|
||||||
The agent's and the operator's accounts in Keycloak and mail (ADR 0414 §2 and §3) follow milestone 13 and are
|
|
||||||
tracked by issue 1000, which needs a design section first. A second agent identity is made only when a rule
|
|
||||||
needs another role (ADR 0414 §1); no milestone makes one.
|
|
||||||
|
|||||||
Reference in New Issue
Block a user