Extends ADR 0075. Surfaced fixing builder's hand-faked package-registry binding tonight: gitea's manifest declares the provision once with a single npm-path, conflating what should be independently assignable per ecosystem (npm/cargo/docker/...) the same way artifact-store and package-registry were themselves split. Cited in 22-the-work-ahead.md's Phase 2, where the target state this decision points at was already described a week ago. Numbered 109, not 108: route-proxy's policy feature (mesh-controller PR still-unwritten decision record — reserved but never committed. Renumbered around it rather than colliding.
125 lines
7.3 KiB
Markdown
125 lines
7.3 KiB
Markdown
---
|
||
layer: to-be
|
||
status: in-progress
|
||
code:
|
||
- mesh-host
|
||
- mesh-controller
|
||
- mesh-catalog
|
||
- mesh-lab
|
||
updated: 2026-09-15
|
||
decisions:
|
||
- 02-DECISIONS/0067-genesis-is-a-pivot.md
|
||
- 02-DECISIONS/0074-the-wire-is-specified-not-the-types.md
|
||
- 02-DECISIONS/0075-two-stores-and-which-provides-what.md
|
||
- 02-DECISIONS/0014-no-npm-workspace.md
|
||
- 02-DECISIONS/0109-a-package-registry-seat-is-one-per-ecosystem.md
|
||
---
|
||
|
||
# The work ahead
|
||
|
||
Everything decided this cycle and not yet built, in the order its dependencies allow. Each phase
|
||
ends at something provable on a running mesh, because a phase that ends at a claim is a phase that
|
||
went missing without anything complaining.
|
||
|
||
## How this is built, and when it is run
|
||
|
||
**Implemented as code with unit tests, committed per change, and run in the lab ONCE the pieces that
|
||
would change the outcome are in place.** The mistake this corrects: repeatedly running a 20-minute
|
||
lab against a mesh mid-transformation, debugging paths the next phase deletes. The base-build hang,
|
||
for instance, is almost certainly the SDK resolving from a git URL inside a docker build (issue
|
||
053) — which Phase 2 removes. Debugging it on the current shape is debugging deprecated code.
|
||
|
||
So the lab run is the acceptance test at the end of the assembled work, not the tool for finding
|
||
each bug. Where a fault can be reasoned out of the code path, it is — reading, not running.
|
||
|
||
## Phase 0 — folded in
|
||
|
||
The installer's own regressions (stale carried builder, a diagnostic on the parsed stdout) are
|
||
fixed and committed. Whether it reaches a full green run is answered by the final lab run below,
|
||
after the phases that change its build path are in — not before.
|
||
|
||
## Phase 1 — the protocol is one thing, and correct *(mostly done: the drift was dead types)*
|
||
|
||
**Why here.** The Go controller and the TypeScript SDK disagree about what a grant carries
|
||
(`consumer` is the module in one, the node in the other). That is exercised by the installer's own
|
||
provisioning — the catalogue's database — so it belongs before more is built on it.
|
||
|
||
- [x] 1.1 the drift was not live — inspection showed the wire agrees (contributions file; envelope
|
||
required headers). The dead types that disagreed are removed, ADR 0074 and doc 19 corrected
|
||
- [ ] 1.2 a conformance fixture for the two live cross-language contracts — the event envelope and
|
||
the contributions file — checked in both suites, as **prevention** rather than repair
|
||
- [ ] 1.3 (deferred) a full per-capability suite when a third language is actually added; not
|
||
needed to keep two honest
|
||
|
||
**Done when.** A fixture pins the envelope and the contributions file, and a change to either side
|
||
that breaks agreement fails a test rather than a mesh.
|
||
|
||
## Phase 2 — the private package registry, and the SDK in it
|
||
|
||
**Why here.** ADR 0014 says a module consumes its dependencies, the SDK included, from the private
|
||
registry. Nothing installs one, so the SDK comes from a git URL (issue 053). Needs the installer
|
||
(Phase 0) and gitea.
|
||
|
||
- [ ] 2.1 gitea provides `package-registry` in full — the endpoints, an account the builder may
|
||
publish with
|
||
- [ ] 2.2 the builder publishes the SDK there on build, by version
|
||
- [ ] 2.3 modules consume it by version; the git URL and the sibling-path lock are gone
|
||
- [ ] 2.4 a second language's SDK published the same way, proving the path is not TypeScript-only
|
||
|
||
**Done when.** A module builds against the SDK resolved from the mesh's own registry, and issue 053
|
||
closes.
|
||
|
||
**Where it stands (2026-09-16).** The decision the bootstrap turned on is settled and recorded
|
||
([ADR 0076](../../02-DECISIONS/0076-the-sdk-is-a-published-package.md)): the SDK is a published
|
||
package, built on a public base image and published before the toolchain that consumes it; mesh-tools
|
||
stays the thin toolchain base but `npm ci`s the SDK by version. On the code, the builder now resolves
|
||
a package-registry credential — from a binding the mesh writes or from the environment for a hand-run
|
||
or bootstrap build — and injects it into an image build as a buildkit secret, never a layer, so a
|
||
token is not baked into the toolchain image. Unit-tested. Still ahead: gitea serving the registry in
|
||
full with a provisioner that mints tokens (2.1), the SDK built and published by the mesh (2.2), the
|
||
mesh-tools manifest flipped off the git URL to `npm ci` by version (2.3), and the genesis step that
|
||
raises gitea and publishes the SDK before the base build. Those close together in one lab run.
|
||
|
||
## Phase 3 — nothing is special after installation
|
||
|
||
**Why last.** The hardest and riskiest, and it needs everything above: an installer that completes,
|
||
a protocol that agrees, a registry to publish to. Issue 051.
|
||
|
||
- [x] 3.1 the store is adopted — raised at genesis, then held as the `postgres` module; one server,
|
||
the controller's records and the database of each module that asks for one. *(Adopted in place: the module's
|
||
server names the same container and the same pinned upstream image the foundation runs, so the
|
||
applier reconciles it rather than raising a second postgres. Proven 22/22 in the one-node lab —
|
||
the catalogue, a `postgres-database` consumer, gets its database from it. Follow-up: the store
|
||
binds `0.0.0.0` from genesis so a mesh consumer can reach it, but the packet filter is
|
||
installed later — a brief pre-filter window where `mesh-store` is open before `from: mesh`
|
||
clamps it; bring the filter up earlier or bind narrower at genesis.)*
|
||
- [x] 3.2 the broker is adopted — one `lavinmq`, the `/` vhost for the mesh bus, a vhost per
|
||
consumer that requires `amqp`; the second server gone. *(Adopted in place like the store; the
|
||
module names mesh-broker with the foundation's TLS spec, the provisioner runs host-networked
|
||
as lavinmq's default guest, amqp-ping reaches it. Proven 22/22.)*
|
||
- [x] 3.3 an upgrade of each, proven: a store the controller reads from, a broker over the
|
||
broker, each with a stated window. *(Lab steps S1/S2: move each module's source, build, roll
|
||
out, then a full push applies the server change and recreates the container. The store's
|
||
window is a pool reconnect; the broker's is longer — recreating the bus the push travels over,
|
||
so the mesh reconnects to the one that returns. Data survives on the named volumes.)*
|
||
- [x] 3.4 `status` can say the foundation is behind its source, which today it cannot form.
|
||
*(Given by the adoption: postgres/lavinmq are ordinary modules with a source now, so
|
||
`module list`/`status` reports them behind or current like any other — the question could
|
||
not form when they were bundle containers.)*
|
||
|
||
**Done when.** A mesh built from bare metal runs one postgres and one lavinmq, and can upgrade
|
||
either — so the twelve-module floor has no specialty left in it.
|
||
|
||
## The through-line
|
||
|
||
Each phase leaves the mesh more able to describe and rebuild itself, and each ends at a run rather
|
||
than a paragraph. The order is dependency, not preference: the installer carries everything, the
|
||
protocol is what everything speaks, the registry is what everything is built from, and adoption is
|
||
what makes the last two things ordinary.
|
||
|
||
## The one lab run
|
||
|
||
After Phases 1–3 are implemented and unit-tested and committed: rebuild the images, **verify each
|
||
carries its change**, and run the one-node installer once. All 22 checks green, twice, is the
|
||
acceptance test for the whole cycle — not a debugging loop, a proof that the assembled thing works.
|