Research 013: the forge and the registries — a seat answers the wrong question #82
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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
|
||||
|
||||
+5
-1
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user