Twice in one file, a statement about a machine that read as reasoned and was wrong — and the module's unit tests all passed while the daemon could not start. That is what a unit test is: it confirms the assertion was made, never that it is true of any machine. 003 in prose rather than in a manifest key.
60 lines
3.2 KiB
Markdown
60 lines
3.2 KiB
Markdown
---
|
|
status: resolved
|
|
opened: 2026-08-31
|
|
located-in: [mesh-control]
|
|
fixed-by: mesh-control — the resolver module's claims about machines are checked on a machine
|
|
amended-design:
|
|
---
|
|
|
|
# 019 — A comment asserting a fact about a machine, which nothing checked
|
|
|
|
## Symptom
|
|
|
|
The resolver module carried two statements about the machine it runs on. Both read as reasoned,
|
|
both were in prose beside the setting they justified, and **both were wrong**:
|
|
|
|
| it said | the machine said |
|
|
|---|---|
|
|
| `127.0.0.54` is free — "not `.53`, that is systemd-resolved's" | systemd-resolved holds **both**; `.54` is its proxy stub. dnsmasq could not create the socket and never started |
|
|
| it takes only `127.0.0.55` | listening on a loopback address takes the rest of loopback with it, `127.0.0.1` included |
|
|
|
|
A third statement in the same file was true and incomplete in a way that mattered as much: the
|
|
config read `/etc/resolv.conf` for upstreams without saying so, and the module that points a
|
|
machine at the mesh writes *this resolver's own address* into that file. So its upstream was
|
|
itself. Its receive queue filled with 15KB of queries and every lookup on the machine hung.
|
|
|
|
## Why this matters
|
|
|
|
**The module had unit tests, and they all passed.** They checked that it names an address, that it
|
|
reads what the mesh writes, that it restarts when that changes, and that the two asking modules
|
|
point where it answers. Every one of those was true while the daemon could not start at all.
|
|
|
|
That is not a gap in those tests. It is what a unit test *is*: it confirms the assertion was made,
|
|
never that it is true of any machine. **Only a machine knows which of its addresses are spare, or
|
|
what a daemon does with a file when it starts.**
|
|
|
|
This is [04-ISSUES/003](../003-firewall-scope-is-read-by-no-code/00-report.md) in prose rather
|
|
than in a manifest key. There, five manifests carried a `scope:` that read as a restriction and
|
|
restricted nothing. Here, a comment read as a reasoned choice of address and chose a taken one. In
|
|
both cases *an unenforced rule is indistinguishable from a wrong one, and costs more, because
|
|
people believe it* — and a comment is the least enforced rule there is.
|
|
|
|
**It cost three full lab cycles**, at fifteen minutes each, because each one revealed exactly one
|
|
of the three faults.
|
|
|
|
## What was done
|
|
|
|
**The wrong statements are corrected, and the correction says what it now knows rather than
|
|
asserting a new comfort.** `.55` is written down as *a convention, not a reservation*: if a future
|
|
systemd takes it, one line changes. What the module takes is what its claim already said — the
|
|
machine's DNS port — rather than a promise about one address.
|
|
|
|
**The unit tests hold what a machine has told us.** They assert the module does not take `.53`,
|
|
`.54` or `127.0.0.1`, and that it does not read resolv.conf for upstreams. A unit test cannot
|
|
discover those facts; it can refuse to forget them.
|
|
|
|
**And the order changed.** A module that asserts something about machines is proven on a machine
|
|
*before* its assertions are believed — the lab test written first, not last. Written here because
|
|
the cost of the old order is measurable: three cycles, forty-five minutes, for a module whose
|
|
mesh-side half was correct from the start.
|