Multiple fixes: issues 064, 066 and 020 resolved; 074 narrowed to two beds #68

Merged
jschoubben merged 4 commits from multiple-fixes into main 2026-09-21 20:13:21 +00:00
8 changed files with 74 additions and 9 deletions
+21 -1
View File
@@ -5,8 +5,9 @@ code:
- mesh-catalog modules/showcase
- mesh-controller internal/builder
- mesh-sdk src
updated: 2026-09-15
updated: 2026-09-21
decisions:
- 02-DECISIONS/0053-a-step-that-runs-on-a-schedule.md
- 02-DECISIONS/0074-the-wire-is-specified-not-the-types.md
- 02-DECISIONS/0040-what-a-module-is.md
- 02-DECISIONS/0039-what-the-sdk-holds-and-refuses.md
@@ -178,3 +179,22 @@ knows. Putting the Plex client anywhere else is the shared-library disease with
the builder records that as the build edge — but *a mesh that can rebuild a commit and get a
different library* is a real change from how everything else here works, and it should be a
decision rather than a consequence.
## A file and the thing that reads it are guarded by a gate
*Written 2026-09-21, from resolving [04-ISSUES/066](../../04-ISSUES/066-a-partly-applied-declaration-leaves-a-mixed-state-with-no-rollback/00-report.md).*
The apply is not a transaction: every resource is attempted, every failure reported, and only a
failed gate stops what follows it ([ADR 0053](../../02-DECISIONS/0053-a-step-that-runs-on-a-schedule.md)).
So a push can half-happen, and the pairing that matters is a configuration file beside the
service that reads it. A module keeps that pair correct by declaring them in this order: the file;
a `run-once` container that validates it; the service, with `restart-on` naming the file. A file
the validator refuses reaches the disk and nothing else — the gate fails, the service after it is
left as it was, and the machine reports the push failed. The service never serves what the
validator refused. That is the grouping, made from what the manifest already has; no primitive
withholds a file from the disk, and a service that reads its file live rather than at start is the
one shape this does not protect, and should not be written.
*How it is checked:* the lab's coupled-pair spike declares exactly this pair, pushes a refused
file, and asserts the file on disk is the new one, the service serves the old one, and the machine
reports the push failed.
@@ -1,8 +1,8 @@
---
status: open
status: resolved
opened: 2026-08-31
located-in: [mesh-control, mesh-lab]
fixed-by:
fixed-by: mesh-lab multiple-fixes — the bed, not the proxy: its backend stub never listened and its check read a subject the authority leaves empty; against both Pebble and the catalogue's step-ca the same proxy now orders, answers the challenge, is issued and serves
amended-design:
---
@@ -0,0 +1,20 @@
# Diagnosis — 2026-09-21
1. The report's own remedy was taken: the same order against a second ACME implementation. The
catalogue's own certificate authority, step-ca, was raised in the bed beside Pebble, pinned as
the catalogue pins it, and the same proxy pointed at it over the same challenge path. It issued
within a second of the order.
2. The name was still not served, and the reason was neither authority's: the bed's stand-in
backend, a netcat loop behind the route, used flags this machine's netcat has not got and never
listened. Every request through the proxy was refused by the backend after a completed
handshake, which read as "never served over TLS" — and, against Pebble, had hidden behind
whatever Pebble refused first. Replaced with a real small server.
3. With a backend that answers, Pebble issued too. The last failure was the bed's check that the
certificate is for the name: it read the subject, which Pebble — like the public authority it
stands in for — leaves empty, putting the name in the alternative names alone.
4. So the symptom was the bed's, twice over, and the proxy's ACME client is right against both
implementations. Ruled out: a Pebble interop detail, and a fault in the client's order or
challenge handling.
**Located in:** the certificate bed. Both authorities remain in it: Pebble because it models the
public authority's strictness, step-ca because it is what a mesh runs.
@@ -1,11 +1,11 @@
---
status: located
status: resolved
opened: 2026-09-18
located-in:
- mesh-catalog
- mesh-controller
fixed-by:
amended-design:
fixed-by: ADR 0097 and mesh-controller #38 (a vendor image is a declared build input; a recipe copying out of an undeclared image is refused); the package half by the facts on current code — the one recipe that installs a public package (the catalogue module, pg) built and installed through the mesh's builder in the genesis bed on 2026-09-21, and no recipe copies from a public image any more
amended-design: 03-DESIGN/01-to-be/18-building-a-module.md
---
# 064 — A mesh build cannot fetch a module's external dependencies
@@ -21,3 +21,12 @@ whose Dockerfiles fetch what no manifest names). The image half is decided and b
is declared under `build.on`, copied in, and a recipe fetching what is undeclared is refused. Still
open: the package half — a build must be re-run against the mesh's proxying registry to place the
404 — and the three recipes themselves, which now declare their images or are refused.
*2026-09-21, later.* The package half was placed by a run rather than a reproduction: the catalogue
was read again, and exactly one recipe installs a public package — the catalogue module's own,
which installs a postgres driver into an empty directory. That module was built through the mesh's
builder and installed in the genesis bed run that day, against the mesh's package registry as it
now proxies the public one. The 404 the report saw is not reproducible on current code. No recipe
copies out of a public image any more either; the vendor-tool case that opened the image half was
rewritten before the issue was, and the rule that would catch its return is
[ADR 0097](../../02-DECISIONS/0097-a-vendor-image-is-a-declared-build-input.md). Resolved.
@@ -1,9 +1,9 @@
---
status: located
status: resolved
opened: 2026-09-20
located-in: [mesh-host internal/apply, mesh-controller internal/inventory (node_report)]
fixed-by:
amended-design:
fixed-by: nothing new — a run-once validator before a service that restarts on the file is the grouping (ADR 0053); the lab's coupled-pair spike proves the service never serves what the validator refused; the visible half was issue 065
amended-design: 03-DESIGN/01-to-be/20-writing-a-module.md
---
# 066 — A partly applied declaration leaves a mixed state, with no rollback
@@ -18,3 +18,12 @@
the grouping question alone; it closes when a coupled pair that must not be half-applied is
found in a module, and the declaration gains a way to say so — or when enough modules have run
that the absence is evidence. The visibility half is done.
*2026-09-21, later.* The pairing was made on purpose, in a lab spike: a config file, a run-once
validator, a service that reads the file once at start and restarts on it. The second push changed
the file and made the validator refuse it. Observed: the file on disk was the new one, the service
served the old one, and the machine reported the push failed. The half-state exists — and it is
harmless, because the gate held: the refused configuration never reached the service. The grouping
the report asked for is this order of three resources, made from what a manifest already has. The
one shape it does not protect is a service that reads its file live, which a module should not
write. Resolved; the pattern is in design 20 with the spike as its check.
@@ -20,3 +20,10 @@
**Located in:** mesh-lab, the ten declared beds, one conversion each with a lab run. Not renamed,
by decision; converted two at a time as the modules they need are raised beside them.
*2026-09-21, later.* Ten conversions were the wrong shape. Six of the ten beds duplicated coverage
that exists elsewhere since the catalogue beds and the whole-mesh beds read the catalogue: the four
sidecar beds proved a sidecar alone, which the module beds prove whole; the minio and postgres
grant beds proved a grant with a second store beside the foundation's, which the grant bed and
the vault bed prove against the catalogue. Retired, with their scenarios. Two remain declared:
route-forwarding, which needs the certificate authority beside the proxy, and the large mesh test.