diff --git a/01-RESEARCH/013-the-forge-and-the-registries/00-overview.md b/01-RESEARCH/013-the-forge-and-the-registries/00-overview.md new file mode 100644 index 0000000..714aaa3 --- /dev/null +++ b/01-RESEARCH/013-the-forge-and-the-registries/00-overview.md @@ -0,0 +1,54 @@ +--- +status: active +initiated: 2026-09-23 +touches: + - 02-DECISIONS/0075-two-stores-and-which-provides-what.md + - 02-DECISIONS/0079-the-foundation-seats-are-named-after-their-servers.md + - 02-DECISIONS/0071-genesis-builds-from-a-mesh-that-already-exists.md + - 02-DECISIONS/0082-the-registry-is-reached-by-name-and-trusted-by-the-overlay.md + - 04-ISSUES/085-the-packages-port-given-at-genesis-is-not-a-setting/00-report.md + - 04-ISSUES/090-the-forge-module-does-not-take-over-the-forge-genesis-raised/00-report.md +--- + +# 013 — The forge and the registries: what a seat is for, and what it is not + +**The question, asked during the first migration:** the mesh runs a forge that serves git and +packages, an image registry, and a bootstrap forge genesis raises before any module exists. Should +there be a mesh-scoped **git seat**, the way there is one for the store and the broker? + +**The short answer: no, and the question points at a different hole.** A seat answers *is there +exactly one of you*. Every symptom around the forge is about **an address and a credential nobody +resolves**, which a seat does not answer. The mechanism that would answer it — a provision, with +`serves` and a grant — already exists, is already used for the image store, and is already +declared for packages. It simply has no consumer: the one module that needs it carries a literal +instead. + +See [the survey](the-survey.md) for what the code does today, with evidence. + +## What was found + +- **A seat does exactly two things**: it refuses a second claimant at resolution, and in one place + it answers *where is the broker*. It is a resolution-time predicate; no host ever hears of it. +- **Four mesh-scoped seats exist**, all named after a singular server the mesh runs **for its own + working** — controller, store, broker, catalogue. A forge is an application the world made, not + part of that set. +- **Nothing in the mesh requires git.** There is no provision for it, and the forge's git service — + over https and ssh — is declared nowhere. Only its npm half is a provision. +- **`package-registry` is a fully built provision with zero consumers.** The builder, its only + real consumer, bypasses it with a file naming the module, the address `127.0.0.1` and the port. +- **The builder is told where source lives, per build, by a person.** The clone URL is an opaque + string; each module remembers its own; no credential is ever attached; nothing polls a forge. +- **A seat would forbid something the mesh should allow**: a second forge — one for the mesh's own + source, one for something else — which ADR 0075 already argues for against a node-scoped claim. + +## What this leaves open + +- Should the forge's git service become a **provision** (`source-forge`), so the builder resolves + the address and a credential the way it already resolves the image store? That is what would let + a forge move, be renamed, or be replaced without editing every module's stored URL, and it is the + unanswered half of [issue 085](../../04-ISSUES/085-the-packages-port-given-at-genesis-is-not-a-setting/00-report.md). +- The obstacle is known and written down: such a requirement **may go unanswered during genesis**, + and the mesh has no optional requirement. Deciding that is the real work. +- Should the mesh learn when a source moves? It records the fact and never discovers it: nothing + polls, and there is no receiver for a forge's push. `build --behind` answers a question only a + person can currently make true. diff --git a/01-RESEARCH/013-the-forge-and-the-registries/the-survey.md b/01-RESEARCH/013-the-forge-and-the-registries/the-survey.md new file mode 100644 index 0000000..ab79202 --- /dev/null +++ b/01-RESEARCH/013-the-forge-and-the-registries/the-survey.md @@ -0,0 +1,130 @@ +# The survey — seats, the forge, the registries, as the code has them + +*2026-09-23. Read from the code and the records, not from memory. Every claim here was checked +against a file.* + +## What a seat is + +The glossary calls a seat *a named position at a scope with a capacity*, and a claim *a module +taking a spot on it*. **Capacity does not exist in the code.** There is no capacity field and no +bench; every claim is exclusive. The "shared seat" of the design is the word `provides` wearing +that name. + +Claiming does two things and no others: + +- **It refuses a second claimant when a node's modules are resolved** — within one node at any + scope, and against every other node for mesh and site scope. +- **It answers one question, once:** the controller finds where the broker is by looking for the + module claiming the broker's seat. + +No host is ever told about a seat. It is a resolution-time predicate. + +Two mechanical asymmetries matter. A mesh-scoped seat's exclusivity is defended only by nodes +that *resolve*: a node whose modules fail to resolve contributes nothing, so a broken node does +not hold its seat against a second claimant. And a site-scoped seat does nothing at all when +either node has no site. + +## The seats that exist + +Twelve claims, twelve names. Four are mesh-scoped: the controller, the store, the broker, the +catalogue. All four are things the mesh runs **for its own working**, and three of them are raised +by genesis and adopted in place — which is the argument of the record that named them. + +The node-scoped ones split in two: a **role on this machine** (the build machine, the packet +filter, the intrusion prevention, the private network) and **a scarce machine resource** (the +resolver's configuration file, the DNS port). The second kind is a claim for the reason a port is: +there is one of it on the machine. + +Two oddities worth stating: + +- The image store claims its seat at **node** scope while providing its service at **mesh** scope. + The record that discussed this left open whether it should hold that claim at all. +- **Nothing claims the two public ports.** The proxy binds them and claims nothing, which is the + hole the route handover fell into. + +## The forge, and what it serves + +The forge serves three things: git over https, git over ssh on its own port, and an npm registry. +**Only the npm half is in the mesh's vocabulary** — the forge provides `package-registry` and says +where it answers. Git is served and declared nowhere: no provision, no `serves`, nothing that can +require it. The only trace is the public label in its route contribution, which the mesh is +explicitly not meant to interpret. + +The image store is a separate module and a separate provision, plain HTTP, trusted because it is +reachable only over the private network. + +A second module also provides `package-registry`. Two providers of one provision is not a refusal +but an ambiguity, and the mesh already has the command that settles it: pin one. + +## Where the builder's three addresses come from + +| What it needs | How it finds it | +|---|---| +| the broker | a sealed secret of its own | +| the image store | **a real provision binding**, resolved by the mesh | +| the package registry | **a file in its own manifest**, naming the module, `127.0.0.1` and the port | +| the source repository | nothing at all — a person types a clone URL per build | + +The installer says this plainly in a comment: *the one binding nothing resolves: the builder dials +the forge by a number it carries.* Genesis can override the port of that literal, and nothing +else: not the forge's identity, not its address. + +`package-registry` therefore has **zero consumers**. The builder does not require it. The +provision is fully built — two providers, grants, a served description including the registry's +path — and nothing asks for it. + +## How a module's source is found + +It is not found; it is supplied. The clone URL arrives as an argument, travels to the build machine +and reaches `git clone` unexamined. No credential is attached, so a private forge works only if the +build machine's own git configuration already authenticates. Each module remembers the URL it was +built from, so there are as many forge addresses as modules, each frozen at whatever was typed. + +At genesis there are four independent source URLs, one per flag, unrelated to each other. + +If the forge moves or is renamed: every stored URL goes stale independently, each failing at its +own clone, and there is no command to re-point them. Genesis keeps working, because it takes URLs +as flags — which is the existing record's position: the forge is reached *by a name outside the +mesh*, and failover is repointing that name. + +## What genesis's forge is + +A bare container run before any module exists, because the toolchain resolves the mesh's own +packages by version from an npm registry. It holds **no seat, no manifest, no provision**. The +mesh is told one thing about it — a port — and only when that port is not the default. **On an +ordinary genesis the mesh never learns the bootstrap forge exists.** + +Its successor module differs from it in five ways: container name, network, data directory, the +address it is told to call itself, and — contradicting a comment that says they are pinned +identically — **the image digest**. So assigning the module raises a second forge beside the first. + +A seat cannot fix that. A seat is checked between manifests; the bootstrap forge has no manifest, +so no seat can see it. The check that is actually wanted — same name, same data, same network — is +between an installer constant and a manifest. + +## Would a git seat be coherent? + +In form, yes: add the claim and a second forge is refused. In effect it buys one refusal nobody +has hit, and forbids an arrangement the mesh should allow — a second forge for something other +than the mesh's own source. + +Measured against the four mesh seats, it does not fit: those are singular servers the mesh runs +for itself, and a forge is an application. Nothing would *use* it, because the only seat consumer +answers "where is the broker", and a forge address is not one fact: a module's source is a +repository **and a path and a ref**. + +## What the code already supports and nobody uses + +- **`package-registry` with no consumers.** The builder's literal file is a hand-rolled copy of + the binding the mesh would have written for it. +- **Pinning a provider** already expresses *this* forge, of several, without forbidding the second. +- **Grants** on the forge are the credential mechanism that would hand the builder an account. + Today the installer creates that account directly, because nothing requires the provision and so + the grant has no consumer to write for. + +## The reading + +The question *should there be a git seat* is the wrong shape for the problem under it. Every +symptom — the literal address, the port that would not follow a node, the takeover that is not a +takeover — is about **an address and a credential nobody resolves**. A seat resolves neither. The +provision mechanism does, and is already built. diff --git a/04-ISSUES/085-the-packages-port-given-at-genesis-is-not-a-setting/00-report.md b/04-ISSUES/085-the-packages-port-given-at-genesis-is-not-a-setting/00-report.md index c2a9c01..afd9ac9 100644 --- a/04-ISSUES/085-the-packages-port-given-at-genesis-is-not-a-setting/00-report.md +++ b/04-ISSUES/085-the-packages-port-given-at-genesis-is-not-a-setting/00-report.md @@ -59,6 +59,11 @@ The forge's module is registered at genesis without being assigned, so the setti to hang on — which also has the controller reserve that machine port, something it could not do before because it did not know the bootstrap forge existed. +**On a default-port genesis none of that happens.** The recording is skipped when the port is the +catalogue's own, so the ordinary mesh raises a forge the controller has never heard of, holding a +port the controller has not reserved. Noticed while surveying this ground on 2026-09-23 +([research 013](../../01-RESEARCH/013-the-forge-and-the-registries/00-overview.md)). + **Open question 2 of this report is not answered**: a binding still names a port rather than resolving it from the provider. Answering it needs a requirement that may go unanswered during genesis, and the mesh has no such thing. **Open question 3 is not answered either**: nothing diff --git a/04-ISSUES/090-the-forge-module-does-not-take-over-the-forge-genesis-raised/00-report.md b/04-ISSUES/090-the-forge-module-does-not-take-over-the-forge-genesis-raised/00-report.md index 2de3756..84879cc 100644 --- a/04-ISSUES/090-the-forge-module-does-not-take-over-the-forge-genesis-raised/00-report.md +++ b/04-ISSUES/090-the-forge-module-does-not-take-over-the-forge-genesis-raised/00-report.md @@ -25,7 +25,11 @@ four ways at once: - **the network** — genesis runs it on the machine's own network, the module's runs bridged; - **where its data lives** — genesis gives it no data volume of its own; the module mounts one; - **the address it is told to call itself** — genesis sets it from the port given; the module - sets none. + sets none; +- **the image itself.** Checked in the code on 2026-09-23: the two are pinned to **different + digests**, while the comment above the installer's constant says they are *pinned identically so + the module adopts the running server rather than replacing it*. A comment asserting a fact about + the system, and the fact is not true. Assigning the module therefore does not adopt what is running. It raises a second forge, with a different name, on a different network, with a different data directory, beside the first.