Merge pull request 'Issues 079 and 080, found by running the large mesh bed; 074's addendum' (#71) from multiple-fixes into main

This commit was merged in pull request #71.
This commit is contained in:
2026-09-22 02:19:17 +02:00
6 changed files with 148 additions and 0 deletions
@@ -47,3 +47,14 @@ its long idleness had hidden: the consumer fixture's login exceeded a backend's
upstream artifact by bare image ID, which a registry copy cannot fetch since ADR 0096 — retired,
since genesis installs the catalogue's builder and proves the same. The large test as a whole has
not been run end to end; its renamed tests and the tests around them have.
*2026-09-22.* The large mesh test was run end to end, for the first time in weeks: 12 of 25 passed.
Everything that failed was the bed lagging the mesh, and one defect of the mesh's own. The bed now
derives the anchor's filter after placing it on the overlay (ADR 0088), declares the ports its
fixtures listen on, waits for an image pulled at apply, starts the hand-started host and builder
once on a warm return, and reads the resolver modules from the catalogue instead of the example
modules that moved there. Three more tests were retired for the catalogue beds that prove them
(the modules resolving together and the forge, whole-mesh-novox; the cache grant, the grant bed)
and one for what every later test proves (the hub filtered without severing the mesh). The
defect: every machine named twice over in the files the mesh writes
([issue 079](../079-every-machine-is-named-twice-over/00-report.md)).
@@ -0,0 +1,32 @@
---
status: resolved
opened: 2026-09-22
located-in: [mesh-controller internal/catalogue (facts)]
fixed-by: mesh-controller multiple-fixes (the facts take a name as the control plane keys it, internal or bare, and write each machine once); a unit test holds both keyings to the same files
---
# 079 — Every machine is named twice over in the hosts file and the resolver's zones
## Symptom
The hosts file the mesh writes on every machine, and the zones file it hands a resolver, name each
machine as `<machine>.internal.internal`, with the intended name `<machine>.internal` in the
hosts file's second column. Names still resolve on the machine itself, because the second column
matches, so nothing noticed. A resolver serving the zones answers for `*.<machine>.internal.internal`
and for nothing under `<machine>.internal`, so a service named under a machine does not resolve
from anywhere.
Found by running the large mesh bed end to end for the first time since the mesh's knowledge became
a fact a module asks for: its name test read the zones back. No smaller bed reads them.
## Why it matters beyond the instance
Two functions agreed on a contract nobody wrote down: the caller keyed the map by the internal name,
the facts appended the suffix to a bare one. Each was tested alone, with its own keying, and both
passed. A file the mesh writes and nothing reads back in a test is a file that can be wrong for
as long as it takes someone to look.
## What would close it
The facts take either keying and write each machine once, held by a test that feeds both. And a bed
reads the files back — the large mesh bed does now.
@@ -0,0 +1,31 @@
# Diagnosis — 2026-09-22
1. The control plane collects every placed machine's name and address for a resolution keyed by
the internal name (`homer.internal`), because that is the map every container is given as its
hosts. The facts that render the hosts file and the resolver's zones took the same map as bare
names and appended `.internal` to each — their unit tests fed them bare names and passed.
2. Fixed in the facts: a name is read as either form, and each machine is written once as
`<machine>.internal` with its bare name beside it. A test feeds both keyings and holds the files
equal, and holds the zones free of a doubled suffix.
**Located in:** the facts. Not a decision. Proven by the unit test and by the large mesh bed's name
tests, once its controller image is rebuilt from the fix.
*On review.* The first fix wrote the suffix a second time, in the facts, which is the shape that
produced the defect: two places composing one name. The control plane now hands the suffix it
composed the names with down to the facts, so an operator who chose another gets that one and
nothing appended. What remains unproven by a bed is a mesh with a suffix other than the default.
*Later.* With the suffix right, the large bed's name test asked the second half of its question:
a machine that leaves the private network must not be answered for. The names were every placed
machine with an address, while the resolver's "on the private network" is a machine that also
runs the module that puts it there. The names follow the resolver's rule now — one predicate,
used by both — held by a controller test that unassigns one machine's networking and reads the
names back.
Two consequences of the one predicate, on review. The filter's "from the mesh" set is that same
set now, so a rule admits exactly the machines the mesh names. And a machine whose assigned set
fails to resolve is no longer on the network in the mesh's eyes: its name leaves every other
machine's hosts file and every resolver's zones on the next push, and the mesh's report of that
machine's failure is what says why — the stated preference for a name that fails at once over a
connection that hangs, at the cost of one machine's failure being visible mesh-wide.
@@ -0,0 +1,29 @@
---
status: resolved
opened: 2026-09-22
located-in: [mesh-catalog modules/redis (the provisioner's ACL)]
fixed-by: mesh-catalog multiple-fixes (the consumer's ACL user loses the dangerous command category); proven by the grant end-to-end bed, which now asserts a write outside the consumer's keys and FLUSHALL are refused
---
# 080 — A cache grant lets the consumer flush the server
## Symptom
The cache provider's provisioner creates each consumer an ACL user confined to keys under its own
login and allowed every command. A key pattern confines only commands that name keys. `FLUSHALL`,
`FLUSHDB`, `CONFIG`, `SHUTDOWN` and the rest of the dangerous category name none, so a consumer
granted "its own keys" could wipe every other consumer's, or stop the server.
Found by carrying the large mesh bed's retired tenancy assertions into the grant end-to-end bed:
`FLUSHALL` as the consumer answered `OK`.
## Why it matters beyond the instance
A grant is the mesh's promise that a consumer gets what it asked for and nothing else. The
promise was checked on the key pattern and never on the command set, and the one bed that had
asked was retired before it was run against the catalogue's module.
## What would close it
The ACL user is allowed the ordinary command set minus the dangerous category, and the grant bed
asserts a write outside the consumer's keys and a `FLUSHALL` are both refused.
@@ -0,0 +1,14 @@
# Diagnosis — 2026-09-22
1. The provisioner's `ACL SETUSER` gave `~<login>:*` and `+@all`. Redis applies a key pattern to
commands that take keys; a command taking none is governed by the command categories alone,
and `@all` includes `@dangerous`.
2. Fixed with `-@dangerous` after `+@all`. `KEYS` goes with the category; `SCAN` stays, and is
what a consumer scoped to a prefix should use.
**Located in:** the redis module's client. Not a decision. Proven by the grant end-to-end bed.
*On review.* `INFO` is in the dangerous category and several client libraries ask it at connect;
it reads nothing a consumer keeps, so it is allowed back. Whether the catalogue's two cache
consumers need anything else the category removes is unverified — and moot until they present the
login they were granted ([issue 081](../081-a-cache-consumer-does-not-use-the-login-it-was-granted/00-report.md)).
@@ -0,0 +1,31 @@
---
status: open
opened: 2026-09-22
located-in: [mesh-catalog modules/n8n, mesh-catalog modules/baserow]
---
# 081 — A cache consumer does not use the login it was granted
## Symptom
Two catalogue modules require the cache and are given a login and a password under a keyspace of
their own ([issue 080](../080-a-cache-grant-lets-the-consumer-flush-the-server/00-report.md)),
and neither tells its software the login: each hands it the host, the port and the password only,
so the software authenticates as the server's default user and writes wherever it likes — or is
refused, if the default user is closed. The grant is honoured by the provider and ignored by the
consumer.
Found on review of issue 080, by reading the two manifests. Not run: no bed installs either
module against the catalogue's cache and reads what it wrote.
## Why it matters beyond the instance
A grant is two ends agreeing. The provider's end is checked by the grant bed; the consumer's end,
that the software actually presents what it was given, is checked by nothing but the module
working — and a module that works as the default user works.
## What would close it
Each module passes the login it was bound (`${bound:redis-cache:as}`) to its software's username
setting, and a bed installs one of them against the catalogue's cache and reads back that its keys
sit under its login.