Merge pull request 'ADR 0119: a taken tunnel's predecessor is retired once the take is proven' (#143) from decision/0119-a-taken-tunnels-predecessor-is-retired into main

This commit was merged in pull request #143.
This commit is contained in:
2026-09-26 22:59:15 +00:00
3 changed files with 96 additions and 1 deletions
@@ -0,0 +1,87 @@
---
topic: the mesh
status: accepted
date: 2026-09-27
deciders: jochen
reconstructed: false
extends: 0105-the-mesh-adopts-the-predecessors-tunnel-in-place.md
---
# 119. A taken tunnel's predecessor is retired once the take is proven
## Context
[ADR 0105](0105-the-mesh-adopts-the-predecessors-tunnel-in-place.md) has the private network take
over the tunnel it finds: the found unit stopped and disabled, never flushed, and **its
configuration left on disk, kept like any held file.** That was the right caution for the take
itself — if the mesh's interface failed to come up, the host starts the found unit again and the
peers never notice — and every apply since stops the found unit again should anyone start it.
What it leaves is a predecessor that never finishes leaving. On every machine that has enrolled,
the tunnel is the mesh's and has been proven so — its interface up with the found key, the peers
handshaking, the machines resolving and reaching each other over it — and still the predecessor's
configuration sits where its unit reads it, held for a module that has long since replaced it.
The predecessor itself is being deprecated. A tunnel that can be started again by one command, with
a configuration nothing maintains any more, is not a rollback path; it is a second way onto the
network that nobody is watching. And the hold never ends, so every node report keeps listing it.
## Considered Options
**1. Keep it, as 0105 says.** Rejected: the caution it bought is spent once the take is proven, and
what remains is a live, unmaintained way back onto the network.
**2. Delete it at the take.** Rejected: the take is exactly the moment the fallback is needed. If
the mesh's interface does not come up, the host must still be able to raise the found one.
**3. Retire it once the take is proven.** Chosen.
## Decision
**Once the mesh's interface has proven it carries the tunnel, the found interface's configuration
is removed from where its unit reads it.**
- **Proven means:** the tunnel's state is *taken* — the found unit down and disabled, the mesh's
interface up with the found key — and the mesh's interface has completed a handshake with at
least one peer. Not before: until then, a failed take still falls back to the found unit.
- **Retired means:** the configuration file the found unit reads is removed. Its original was
already kept, before anything happened to it
([ADR 0100](0100-a-node-in-use-is-adopted-before-it-is-converged.md)), and stays kept; that copy
is the record of what the predecessor was, and a person's way back if one is ever wanted.
- The found unit stays disabled. Without its configuration it cannot raise the interface, so the
every-apply stop that guarded against it becomes a check that finds nothing to do.
- **The hold ends.** What was held for the private network has been replaced; the node stops
reporting it.
- **The mesh never brings it back.** Undeclaring the private network does not restore the found
tunnel: the mesh stopped it, and nothing is started on the way out
([ADR 0118](0118-undeclaring-gives-a-unit-back-the-state-it-was-found-in.md)). A machine whose
private network is unassigned has no tunnel until it is assigned again — which is what
unassigning it means.
## Consequences
- On every machine that took a tunnel, the predecessor's tunnel configuration disappears at the
first apply after the take is proven. Nothing a peer sees changes; the mesh's interface already
carries the same key, port, address and peers.
- A take that is never proven — no peer ever handshakes — keeps the found configuration, and the
node says so, so a broken take is visible rather than silently retired.
- Rolling back to the predecessor's tunnel becomes a deliberate act, in this order: **unassign the
private network first**, then copy the kept original back and start its unit. The mesh does
neither. While the private network is still assigned, the tunnel is the mesh's: a restored
configuration is held and retired again at the next proven apply, and the found unit cannot
bind the port the mesh's interface holds. The node says so when it happens.
- A configuration something keeps writing back — the predecessor's own tooling, say — is retired
again each time it appears, but the first original stays the one kept; a different content is
kept once beside it, and the node reports that the configuration came back.
- The host retires only the found interface's own configuration file (`/etc/wireguard/<iface>.conf`),
never a path the mesh writes, and never a link: a configuration that is a link to somewhere else
is left, with its target, for a person to retire.
- 0105's "its configuration stays on disk, kept like any held file" holds until the take is proven,
and not after.
## References
- [ADR 0105](0105-the-mesh-adopts-the-predecessors-tunnel-in-place.md): the take, and why it keeps
the found configuration during it
- [ADR 0100](0100-a-node-in-use-is-adopted-before-it-is-converged.md): kept originals
- [ADR 0118](0118-undeclaring-gives-a-unit-back-the-state-it-was-found-in.md): nothing is started on the way out
- mesh-host `internal/apply/takeover.go`
+1
View File
@@ -133,6 +133,7 @@ python3 00-META/checks/index.py fail if stale
- **0105** — [The mesh adopts the predecessor's tunnel in place](0105-the-mesh-adopts-the-predecessors-tunnel-in-place.md) - **0105** — [The mesh adopts the predecessor's tunnel in place](0105-the-mesh-adopts-the-predecessors-tunnel-in-place.md)
- **0106** — [The bus is NATS](0106-the-bus-is-nats.md) - **0106** — [The bus is NATS](0106-the-bus-is-nats.md)
- **0116** — [The bus is built in five steps, and the protocol moves with it](0116-the-bus-is-built-in-five-steps.md) - **0116** — [The bus is built in five steps, and the protocol moves with it](0116-the-bus-is-built-in-five-steps.md)
- **0119** — [A taken tunnel's predecessor is retired once the take is proven](0119-a-taken-tunnels-predecessor-is-retired.md)
### Its tiers, from the bottom up ### Its tiers, from the bottom up
+8 -1
View File
@@ -7,11 +7,12 @@ code:
- mesh-controller internal/identity/authority.go - mesh-controller internal/identity/authority.go
- mesh-host internal/identity/serving.go - mesh-host internal/identity/serving.go
- mesh-host internal/apply (the service that reflects a rule set) - mesh-host internal/apply (the service that reflects a rule set)
updated: 2026-09-25 updated: 2026-09-27
decisions: decisions:
- 02-DECISIONS/0104-a-provision-may-be-answered-by-an-adapter-to-the-predecessor.md - 02-DECISIONS/0104-a-provision-may-be-answered-by-an-adapter-to-the-predecessor.md
- 02-DECISIONS/0106-the-bus-is-nats.md - 02-DECISIONS/0106-the-bus-is-nats.md
- 02-DECISIONS/0105-the-mesh-adopts-the-predecessors-tunnel-in-place.md - 02-DECISIONS/0105-the-mesh-adopts-the-predecessors-tunnel-in-place.md
- 02-DECISIONS/0119-a-taken-tunnels-predecessor-is-retired.md
- 02-DECISIONS/0108-a-route-carries-the-policy-applied-to-a-request.md - 02-DECISIONS/0108-a-route-carries-the-policy-applied-to-a-request.md
- 02-DECISIONS/0103-what-an-adopted-node-holds-and-what-its-guard-refuses.md - 02-DECISIONS/0103-what-an-adopted-node-holds-and-what-its-guard-refuses.md
- 02-DECISIONS/0100-a-node-in-use-is-adopted-before-it-is-converged.md - 02-DECISIONS/0100-a-node-in-use-is-adopted-before-it-is-converged.md
@@ -768,6 +769,12 @@ where a found tunnel is left running beside the mesh's; where it is adopted ther
The guard admits the mesh's ports from that one interface, and the predecessor's peers arrive on The guard admits the mesh's ports from that one interface, and the predecessor's peers arrive on
it. it.
*2026-09-27, [ADR 0119](../../02-DECISIONS/0119-a-taken-tunnels-predecessor-is-retired.md).* The
found configuration is kept only until the take is proven — the found unit down, the mesh's
interface up and handshaking with a peer. Then it is removed from where the found unit reads it
(its original stays kept), the hold ends, and the predecessor's tunnel cannot be raised again by
anything but a person restoring it by hand. Undeclaring the private network does not bring it back.
## The bus is NATS ## The bus is NATS
*2026-09-23, [ADR 0106](../../02-DECISIONS/0106-the-bus-is-nats.md). Architecture to be written *2026-09-23, [ADR 0106](../../02-DECISIONS/0106-the-bus-is-nats.md). Architecture to be written