Keep design 50 to what ADR 0678 asks, and hold the wait at 3 s per try
mesh/delivery merged: source.main moved to novox/hq@7247b92fd6b1
mesh/check being carried to ce0287dd2d87 on 7247b92fd6b1 | from 0d04a16560e4

A restart of resolved and a timed ask at the own resolver answered in 44-46 ms
with the first mesh resolver silent, so no read catches the pass today: the
value stays ADR 0678's until a read of resolved's server switches exists, and
the proof is the Go build steps at the next silence. The phases table and the
as-built notes go back to main: a to-be design states no live state.
This commit is contained in:
2026-11-12 18:06:41 +01:00
parent 067de23181
commit ce0287dd2d
2 changed files with 18 additions and 23 deletions
@@ -47,16 +47,12 @@ check passes against the other's main, whichever merges first.
`timeout:1 attempts:2 edns0` it asserts today (ADR 0678, *How it is checked*). The reason: a test literal of
another repository's template fails one repository's check on whichever merges first, the kind issue 1748's note
of 2026-11-10 met.
4. **The pass is measured, and the value follows it.** After the delivery, on the workstation first and then the
laptop: `<node>/node-reads.read` of `/etc/resolv.conf` shows the one server and §1's line. At the next time the
machine's first mesh resolver is silent (its machine unreachable, raised in `mesh-conditions.conditions`), the
worker of issue 1748 restarts resolved with `<node>/node-service-manager.restart` and at once asks a mesh name with
`<node>/netcheck.netcheck_dns`, `server` the machine's private address and `timeout_ms` 30000; the time it answers
in is the pass. The restart comes first because resolved keeps asking the server it passed to, so only the first
lookup after it starts meets the pass (the 42 ms answer read on 2026-11-12 with the first mesh resolver silent);
the machine has no names for the moment of the restart. A pass under 6 s leaves design 50 §2 as it is; a pass of
6 s or more raises §2's timeout to the next whole second above the pass, the attempts staying two, and §1 is
delivered again. The measured time is written on issue 1748 as a note by its worker.
4. **The value stays 3 s per try until the pass can be read.** design 50 §2's `timeout:3 attempts:2` stays ADR
0678's starting value until a read of resolved's own server switches exists. A restart of resolved and lookups
at the own resolver with its first mesh resolver silent answered in 44 to 46 ms (the lead's reading of
2026-11-12 18:05 CET, issue 1748), so a restart and a timed ask do not catch the pass. After the delivery, on the
workstation first and then the laptop, `<node>/node-reads.read` of `/etc/resolv.conf` shows the one server and
§1's line; the proof that the wait outlasts the pass is §5's.
5. **The live proof.** At the next silence of the first mesh resolver, `mesh-build-go.build_go_steps` lists no Go
build step on a machine holding `node-resolver` failed with `i/o timeout` at that machine's own address. Issue
1748 is resolved once §4's read of the file holds, naming this line as where the proof will show, and reopened
@@ -74,9 +74,8 @@ not share a machine.
(ADR 0678 §1): `options timeout:3 attempts:2 edns0`, a total wait of 6 s, the timeout times the attempts, where
ADR 0223's `timeout:1 attempts:2` gave about 2 s. Its program asks only the own resolver, so it waits on that
pass; a file that lists the mesh's resolvers keeps `timeout:1 attempts:2`, because its programs try the second
after one second (ADR 0678 §2, design 08 §2). The value holds while the pass, measured on such a machine with its
first mesh resolver silent, is shorter than the total; a pass of 6 s or more raises the timeout by whole seconds,
in this line, the attempts staying two as design 08 §2 gives every file (milestone resolver-wait §4).
after one second (ADR 0678 §2, design 08 §2). The value stays ADR 0678's 3 s per try until a read of resolved's
own server switches exists, since no read today catches the pass (milestone resolver-wait §4).
- **The uplink's holder steps back** from that path on that machine, by the controller's rule. It does not
write the file and lose a race; it is never given the file to write.
- **resolved** is given every mesh resolver as its global servers, with `~.` so they answer every name no
@@ -146,22 +145,22 @@ resolver's hold, and is therefore raised. A VPN that tells resolved its link's D
| the own resolver module's manifest test: its file names one server, the machine's private address, with §2's options | ADR 0247 §6, ADR 0678 §1 |
| the node-engine's test that composes the catalog's own resolver on a machine reads the options from the catalog's template, not a literal, and its names check bounds each ask by the file's `timeout:` | ADR 0678 §1 |
| the test over every uplink's resolver file: both mesh resolvers, `timeout:1 attempts:2` | ADR 0678 §2, design 08 §2 |
| live, on a machine holding `node-resolver` with its first mesh resolver silent: a timed lookup at the own resolver answers within the file's total wait; at the next silence no Go build step there fails on `i/o timeout` | ADR 0678 §1 |
| live, on a machine holding `node-resolver`: at the next silence of its first mesh resolver no Go build step there fails on `i/o timeout` (milestone resolver-wait §5); a timed lookup waits for a read of resolved's own server switches (§4) | ADR 0678 §1 |
| each phase's *Done when* below | ADR 0247 |
## Milestones
## Phases
| Milestone | Repository | Delivers | Done when | State |
|---|---|---|---|---|
| A — the seat | mesh-controller | `node-resolver` and its verbs; the uplink stepping back | its tests pass; the controller is running on the mesh | running: the seat is held on the laptop and the workstation (read 2026-11-12, issue 1748); no as-is design yet |
| B — the resolver | mesh-catalog `systemd-resolved` | the module, its guard and verbs | its tests pass; assigned to every machine that runs the VPN client's module, each one's file names its own address, `routes` answers | running: each holder's file names its own address and `routes` answers (read 2026-11-12, issue 1748) |
| C — the adapter | mesh-catalog `forticlient` | `split-dns` required; the adapter | its tests pass; on the laptop, connected, a company name and a mesh name both resolve and no rewrite stays raised | running: the adapter's service runs beside the resolver on the workstation (issue 1748, 2026-11-10); its connected test is not recorded here |
| resolver-wait | mesh-catalog `systemd-resolved`, `mesh-host` | §2's options (ADR 0678), under issue 1748 | the live check above | designed; its part is [`50-milestones/resolver-wait.md`](50-milestones/resolver-wait.md) |
| Phase | Repository | Delivers | Done when |
|---|---|---|---|
| A — the seat | mesh-controller | `node-resolver` and its verbs; the uplink stepping back | its tests pass; the controller is running on the mesh |
| B — the resolver | mesh-catalog `systemd-resolved` | the module, its guard and verbs | its tests pass; assigned to every machine that runs the VPN client's module, each one's file names its own address, `routes` answers |
| C — the adapter | mesh-catalog `forticlient` | `split-dns` required; the adapter | its tests pass; on the laptop, connected, a company name and a mesh name both resolve and no rewrite stays raised |
| resolver-wait | mesh-catalog `systemd-resolved`, `mesh-host` | design 50 §2's options (ADR 0678), under issue 1748; its part is [`50-milestones/resolver-wait.md`](50-milestones/resolver-wait.md) | its tests pass; *How it is checked*'s live row holds |
## As built
One pull request in mesh-controller (phase A), and two in mesh-catalog (B, then C on top of it). What the
build chose where ADR 0247 left it open:
One pull request in mesh-controller (phase A), and two in mesh-catalog (B, then C on top of it). None is
merged. What the build chose where ADR 0247 left it open:
- **The kept copy.** The mesh renders the module's file twice, at `/etc/resolv.conf` and beside resolved's
configuration. The guard compares the live file with the copy and puts the copy back, so it needs nothing