mesh-resolver requires name resolution, which requires the network — so
unassigning the domain module alone leaves the machine on the network, pulled
back by its own requirement. The mesh was right and the test was wrong.
Which is the requirement graph doing its job: a module cannot quietly lose
something it depends on because somebody removed the thing that first brought
it in.
The mesh's half: the data is right, complete on every machine, and agrees with
the hosts file — two accounts of where a machine is, disagreeing, would be
worse than either alone, and this is the one place they could drift because
they are generated separately.
And it follows the machines: a node that leaves the private network stops being
answered for, because a wildcard pointing at nothing resolves and then hangs,
where an unresolvable name fails at once and says which name it was.
The mesh gives its names to the containers it declares. This one is started by
the test with `docker run` — nothing declared it, so nothing configured it, and
removing the workaround here was claiming a reach the change does not have.
The boundary is the right one: a container somebody runs by hand is not the
mesh's to configure. Reaching into every container on a machine, declared or
not, is what a resolver in resolv.conf would be for — and that remains the
case for wanting one.
The proof that names work inside containers is its own test, against a
container the mesh declared, and it passes.
The rotation test resolved the address on the machine and passed it in, because
the name failed inside the container. That workaround is gone, and a test that
checks the name from inside a container is added — on the machine it has always
worked, which is what made this easy to miss.
`networking` is the requirement a module offers; `mesh-wireguard` is the
module, and what caused a rule is the module itself. The rule set was right and
the assertion was looking for the wrong name.
The failure guarded against is not subtle and is very hard to recover from: a
rule set that closes the hub's own port takes the private network down, and the
mesh's way of fixing anything is to send a declaration over it.
So the assertion that matters is not the rule file — it is that a declaration
still reaches the other machine afterwards, and that the other machine still
reaches the hub. A rule file that looks right and a mesh that has stopped are
exactly what this is for.
A file in a missing directory is not impossible: the host creates the parents,
which is correct and meant the first version of this test broke nothing at all
— and then reported that the board could not name a failure that never
happened. A unit that does not exist fails immediately and in the host's own
words, which is also what the board is being asked to show.
A machine is given a declaration it cannot apply, and the board names it,
says 'failed' rather than 'error', and shows the host's own words about what it
could not do — a board that said only 'failed' would send a person to ask the
thing they opened the board to avoid asking.
And it agrees with the command, from the same read: two answers to 'which
machine is broken' would be worse than either alone. Reading it changes
nothing.
This assertion passed twice and failed once on nothing but timing, which is the
worst kind of green: it says the mechanism works when what it measured was the
clock.
`status` cannot stand in for the wait either. A machine that has not applied
yet is not a machine that failed — "not yet" and "never" look identical there,
and only one of them is worth failing over. So it waits for the thing itself,
and says on failure that the machine did apply, which is what separates "the
mesh still thinks this contributes" from "nothing was sent".
The mesh withdrawing a route and the machine acting on it are different things,
and a test that reads the file without checking the second reports the first
wrongly whenever the machine is behind for any unrelated reason. On failure it
now also prints what the mesh would send now, which is what separates 'the mesh
still thinks this contributes' from 'the machine never applied'.
A container does not inherit its host's /etc/hosts, so a name the mesh wrote
there resolves for the machine and not for anything it runs. It fails as "could
not translate host name", which reads like a mesh that never wrote the name —
so the address is resolved on the machine and the container is given that.
And `grep -c` prints 0 and exits non-zero when it finds nothing, so the obvious
`|| echo 0` prints a second one and the count is never what it looks like. The
question was always whether, not how many.
The rotation check now also reports what psql said, not only what the
provisioner said: the failure was on the client side and the diagnostics were
all from the server.
Two setup faults, each of which read as the mesh failing.
The provisioner names a role after the machine and a database after what the
module asked for. The rotation test logged in as the module's name into the
wrong database, so a provisioner that had done its job exactly looked like one
that had not.
And the licence test assigned before recording any licence, so the refusal it
got was "nothing provides model-access" — correct, and a different refusal from
the one being tested. Assigning is also the earliest point a person meets it,
so that is where it is now checked.
Commit, build, catalogue, push — and the machine ends up running what the
source says. With the two halves that make the answer trustworthy: it is still
running the old one until it is told, because the mesh changing its mind is not
a machine acting on it; and it stops being reported as behind once it has
caught up, because a status that says "behind" for ever is one nobody reads.
Refused until somebody says which, with both candidates and the command named.
Refused again while it has no key. Then the key is given on standard input, the
public half arrives saying it came from a record rather than a machine, the key
itself arrives readable only by that machine — and it is nowhere in the control
plane's own database, nor in what crossed the broker.
And the rotation test asked for grants without receives, so the provisioner
found a directory of unexplained secrets and said nothing had been granted:
true, and indistinguishable from a credential never delivered.
The request goes to the name, across the private network, and returns the
workload's own answer. Then the module is unassigned and the same request must
stop working — a stale public name pointing at nothing fails more visibly than
a stale grant.
The workload declares its port as well as its route, because they are different
questions and the earlier test leaves this machine filtering: a module that
asked for a route and not for the port would be unreachable by the proxy it
just asked for.
Two ends holding a matching string proves they agree, not that either is right.
So the check is three logins over the private network from the consumer's own
machine: the delivered credential works, the rotated one works, and the one
that was rotated away does not. Without the last, the test passes against a
provider that added a password without replacing one.
Not over loopback: pg_hba trusts anything there, and a deliberately wrong
password returned a row for a whole afternoon once.
It runs in a container, so a path like /root exists for the machine and not for
it. The first build in this test works because the hand-started builder runs on
the host; the second is done by the module, and asked it to clone a path it has
no way to reach.
A real module is cloned from the forge over a URL. The lab has no forge, so the
repository goes in the directory the module already mounts — the same fact
wearing different clothes.
The distribution's nftables.service is Type=oneshot with no RemainAfterExit: it
loads the rules and goes inactive. A host asked for a service that is "running"
then reports, quite correctly, that it is stopped — every packet filtered as
declared, and the machine marked as not doing what it was told.
There is no state in the vocabulary for "ran and exited having done its job",
so a module that needs one brings a unit that stays. That is also the right
shape: how a machine enforces rules is a fact about the machine, and the mesh
has no business depending on what a distribution happens to package.
And when the builder's build times out, dump the builder's own account of
itself. "Nothing consumed the queue" names no cause and is the same sentence
whether the credential was refused, the queue was never declared, or the
process died three seconds in.
Both assertions were wrong and the mesh was right, which the output made
plain: the rule set named its source, dropped by default, restricted the
declared port and omitted the undeclared one.
"From the mesh" resolves to the addresses on the private network — the whole
point — and the assertion was looking for the segment the two machines happen
to share. So the test now reaches the same machine both ways, and asserts the
declared port answers over the private network and does NOT answer off it.
A test with only one path could not tell "open to the mesh" from "open".
And the fingerprint is delivered with a sha256: prefix, which the regex did not
allow.
A builder that cannot connect sits there, and every outward sign — container
up, credential on disk — says it is working. The failure surfaced five minutes
later as nothing consuming the build queue, which names no cause at all.
Two faults in one line of the harness, and the second is the serious one.
Every command was wrapped as `<cmd> 2>&1; echo "__exit=$?"` on a single line,
so any command containing a heredoc broke: the terminator line became
`MARKER 2>&1; echo ...`, matched nothing, and the heredoc swallowed the rest of
the script — the echo with it. `exec 2>&1` on its own first line fixes that: a
heredoc then terminates where it says it does.
And when the marker was gone, `Number("")` is 0, so the missing exit status
read as exit 0. A command whose output was swallowed reported that it worked,
which is the one answer a test harness must never give. It is now a failure,
with whatever was said returned so the reason is visible.
Found because the firewall test's listener is written with a heredoc and never
started, and the test failed on its own setup — which reads exactly like the
firewall working.
Two setup faults, each of which looked like the thing being tested failing.
The builder module was assigned without its artifact ever being built, so
nothing could start — and the build has to happen while the hand-started
builder is still alive. Same chicken-and-egg as the registry, resolved the same
way: the builder that exists builds the one that replaces it.
The firewall test's listeners were squeezed through three levels of shell
quoting and never started, so the test failed on its own setup — which reads
exactly like the firewall working.
Written and loaded are different things, and loaded and enforcing are different
again. The test opens two ports on a machine, declares one of them, and checks
from the other machine that the declared one answers and the undeclared one
does not — then removes the module and checks the port closes with nobody
editing a rule.
The base image gains nftables, read back through `nft --version` like the other
three: a machine that cannot load a rule set applies the mesh's filtering,
reports success and filters nothing, which is the exact fault the derivation
exists to remove.
Two earlier tests were asking for things that are not there. The lab's registry
drops tags when it stocks, so `registry:2` is not served and the mirror test
failed with "not found" — it now uses the pinned digest, which is what a
declaration carries anyway.
went
`exec` waited two minutes always. A build, or anything that waits on
another machine, needs longer — and a caller that cannot say so has to
split the work to fit, which is a test shaped by its harness rather than
by what it is testing.
The scenario also places a build machine when one is given, so anything
in it can ask the mesh to build something. Nothing else here would start
one.
And the mesh-runs-its-own-artifact-store test is not here. It needs a
fourth image so the module has a registry to mirror, and that is caught
behind 04-ISSUES/012 — left as a note saying where it went and why,
rather than silently deleted, because what it asserted is worth
asserting.
Broken with a package that does not exist, so the failure is real and
fixable. The mesh reports it failed; the resources that could be applied
were, because one broken thing no longer blocks the rest; `push --behind`
names that machine and not the one that is fine; the module is corrected;
and the machine recovers with nobody naming it.
And with nothing behind, it says so rather than doing nothing quietly.
Two properties the design claims and neither had been run.
A push to a machine that is switched off must not be lost — a machine is
disconnected as an ordinary situation, not an exception. The queue is
durable and the message persistent, which ought to be enough, but a lost
declaration is silent and "ought to be" is not a property. It waits: the
machine's host is stopped, the push happens, nothing changes on the
machine, and when it listens again it applies what it missed with no
second push and nobody saying anything.
Getting there found a real fault, now fixed in mesh-host and recorded as
04-ISSUES/011: the machine stopped at the first failing resource, so one
broken module blocked every module after it for ever. The evidence was
the broker's queues being EMPTY — the declaration had been delivered and
read.
And removal: two modules assigned, one unassigned, and the machine loses
exactly that one's file while keeping the other's — and keeps the store,
broker and control plane it raised from its own bundle, which the mesh
never declared and must never remove.
Two of my own traps recorded in the test, because both cost real time:
`pkill -f` matches the shell running it, which kills the connection
carrying the command and hangs the caller for ever; and a test that
depends on state another test left behind fails for a reason that has
nothing to do with what it claims.
The status path was demonstrated with inserted rows, which proves the
query and not the path. This sends a real machine something it will
genuinely fail at — a package that does not exist — and asks the mesh
afterwards.
A failure of the ordinary kind: the host tries, the package manager says
no, some of the declaration is applied and some is not. That is the
situation `status` exists to distinguish from a machine that refused
everything, and the test asserts the distinction survives the whole way:
the machine is listed as failed rather than refused, the failing resource
is named in the host's own words, and the machine that did as it was told
is not implicated.
The chain this closes: a repository exists, the mesh asks for it, a build
machine takes the work, publishes what it made, and the catalogue then
says what the module is, which commit it came from, and — after the
source moves — that it is behind.
Three assertions, against a real broker and registry, because what is
under test is four processes agreeing over a wire:
- the mesh asks, a machine builds, and the artifact is really in the
registry at the digest the manifest names
- a build that cannot succeed says why and records nothing. A failure
that is silent is indistinguishable from a builder that is not running
- the source moving makes the catalogue say "behind", and rebuilding
catches it up
git is now in the base image, with the same reasoning as docker and
wireguard-tools: a machine that builds modules clones them, and a sealed
scenario cannot install anything. Read back from `git --version` rather
than from the package manager — an installed package is not a
capability, and a build machine whose clone fails does so three minutes
into a scenario with the failure reported as a build problem rather than
a lab one.
Everything before this proved a part. This proves the parts meet, which
the project keeps saying cannot be checked any other way.
A bare machine applies the substrate bundle and becomes a mesh — store,
schemas, broker with a certificate it generated itself, control plane
serving. Both machines then join it with nothing but a token. A database
is declared on one and an application on the other, and after a push:
- both ends hold the SAME password, or nothing could authenticate
- it is mode 0600 on the machine that uses it
- it appears in neither machine's stored declaration, neither machine's
reported state, nor the control plane's database — so it was not
readable by the broker that carried it or the mesh that sent it
- the consumer is also told where its database is, by a name the mesh
wrote into that machine's hosts file
The bundle's image references are rewritten to the ones this scenario's
registry serves. A digest belongs to whatever registry serves it, so a
committed bundle names a registry that is not this one — rewriting is
what makes it applicable rather than a placeholder to tidy away.
Two faults found getting here, both fixed in mesh-host: `apply` could not
read a file the bundle could, and the token did not say what the mesh
calls the machine.
The mesh generates a password, seals it to the machine that must accept
it, and discards the plaintext — so it cannot tell PostgreSQL to start
accepting it. Something on that machine reads what the host wrote and
makes it true. Everything up to that step is proven elsewhere; this is
where a password either becomes a login or does not.
A scenario with one machine and a database, and six assertions: the
password works, running again reaches the same state and says nothing,
rotation makes the new one work and the old one stop, a departed consumer
loses its login, a role nobody here made is left alone, and a manifest
naming a credential that was never written is refused rather than
creating a login with no password.
Each was confirmed to fail — and only it to fail — with the behaviour
removed from the provisioner: only-creates breaks rotation, no-revoke
breaks revocation, revoking everything breaks the bystander role, and
ignoring a missing credential breaks the refusal.
Two faults in the test itself, both worth recording:
- it checked logins from inside the database's own container over
127.0.0.1, which PostgreSQL's default pg_hba trusts. No password was
ever verified. Demonstrated directly: over loopback a deliberately
wrong password still returns a row. Only the rotation assertion
noticed, because it is the one that requires a password to STOP
working — which is an argument for writing that assertion every time.
- the fix then read .NetworkSettings.IPAddress, which docker 29 no
longer populates. It templates to empty, psql falls back to a unix
socket that is not there, and every login looks impossible rather than
misconfigured.
Comments naming records that no longer exist now point at the consolidated
record holding their reasoning -- the four lab records are 0016, a test defends
a decision is 0017.
The lab raised an underlay and put nothing on it: correct, and useless, because
the thing it exists to test did not exist. Tier 0 now does, so `place: [host]`
works and a raised scenario finally contains something.
The refusal narrows rather than disappearing. A scenario placing a host and a
substrate is told which half is missing, by name — not that `place:` is
unsupported when half of it now works.
Placement reads back rather than assuming. A file arriving is not a host
working, so the binary is run before it is trusted to answer questions, and what
it reports is read from the machine (ADR 0035). The binary comes from an
explicit path, because the declaration design leaves where artifacts come from
open and a search would harden into the answer by accident.
The integration test that matters is the one asserting the host reports the
MACHINE and not the workstation that placed it. A raised VM and this workstation
differ in every capability — root versus uid 1000, a clean init versus a
degraded one, no docker versus docker, no wireguard versus wg0 — so a host
reporting the wrong machine is obvious here and invisible anywhere else.
And the placed host independently confirms ADR 0031: overlay absent on a freshly
raised machine. The underlay suite already asserted that by looking for
wireguard interfaces; this is a second witness rather than the same check twice.
Two tests failed the moment placement worked, which is what they were for. They
defended "there is nothing to place yet" while that was true; the decision
changed, so they change with it rather than being deleted.
Gate: 75 unit, 20 integration.
The suite next door raises one scenario and asks deep questions of it. This one
asks shallow questions of every scenario — the half that was missing, since both
faults found by hand lived in scenarios nothing ever built.
Adds bootstrap-single, the cheapest, and the loop that lets the list grow. Also
adds the second universal invariant: every address a scenario declared is one
the machine actually holds. A machine that came up bare looks identical to one
that came up correctly until something asks it.
Verified to bite rather than assumed: against a live instance, the real
declaration passes and a declaration claiming an address nothing holds fails
with 'anchor declared 192.0.2.99 on hosting but holds 192.0.2.10'.
Integration now runs with --test-concurrency=1. Two files raise real instances,
node --test runs files in parallel by default, and two concurrent runs of this
suite already produced a whole-suite failure once — every test red, from
resource contention rather than from any fault in the code.
Gate: 45.7s -> 60.2s.
The address collision was found by eye. This is the mechanical form of it: no
two machines hold one address on one segment.
Pure over already-collected facts, so the logic is tested without a hypervisor
— including the cases that would make it useless if got wrong: the same address
on DIFFERENT segments is normal and must not be reported, and one machine
holding an address twice is not two machines.
Asserted against whatever the integration suite has standing, read from the
hypervisor rather than from the declaration. The declaration is what was
accepted, and it was accepted.
`mesh-lab diagram` renders a scenario as draw.io, from either source, through
one layout — so a difference between what was asked for and what exists is a
difference you can see.
The shape says what a resource is and is fixed per kind. The badges say what is
true about that particular one and come entirely from metadata: translation,
forwardability, mapping expiry, refuses-inbound, container-or-VM, running. The
interesting properties of a network are exactly the ones with no visual
consequence — a translated address looks identical to an untranslated one.
For the live picture to be a record rather than a restatement, raise now writes
down what it applied: a segment's kind, ranges and MTU on the link; a gateway's
translation, forwardability and expiry on the gateway; inbound: deny on the
machine. Every behavioural tag is written AFTER the thing works, never at
creation — a failed raise leaves wreckage standing on purpose, and a picture of
that wreckage must not badge translation the router never got.
The pairing earned itself immediately: drawn side by side, every virtual machine
held no addresses. A container's interface carries the device's name and a VM
names its own, so joining them by name silently dropped one whole class of
machine. Fixed by joining on MAC.
Also brings tests under the typecheck gate, which caught integration timeouts
being passed as a 4th argument and therefore ignored entirely.
Reviewed and the criticism was right: 1,072 of 2,128 lines untested, all of
it the half that touches the hypervisor, and no gate. The verification I had
done was real — pings across NAT, TTL counts, ruleset comparisons — and none
of it survived the terminal it ran in, which is 04-ISSUES/005 in miniature.
Ten integration tests against a real hypervisor, each named for what it
defends. ADR 0031: a raised machine carries no overlay, no wireguard, no
mesh config — a scenario that pre-built peering would certify its own work.
ADR 0032: exec is the only way in. ADR 0033: routers are containers while
machines are virtual machines. And the design's claims: raise waits for
usable, snapshots are whole-scenario, NAT hides a private address,
published reaches the machine at the gateway's address.
Mocking the hypervisor is forbidden, so they skip with a reason on a
machine that cannot raise scenarios rather than passing green having
checked nothing.
The suite earned itself on its first run. It found that a snapshot of a
running machine could miss a file written seconds earlier — not stale,
absent — because the write was still in the guest's page cache. That is
exactly the question the lifecycle design listed as open: does a scenario
snapshot need the machines stopped? It does not, but it does need them
flushed. snapshot now syncs every machine before capturing, and the design
records the answer.
The fix buys write-durability, not application-consistency: anything
mid-transaction is still captured mid-transaction, and that is now stated
rather than assumed.
npm run check is the gate — typecheck, 40 unit tests, 10 integration tests.