Files
hq/02-DECISIONS/0170-the-firewall-seat-serves-its-verbs.md
T
jschoubben 98eb3aa76f ADR 0169 → 0170: the firewall seat's record renumbered after a collision on main; its built note; cycle.py refuses two records sharing a number
Another session's 0169 landed first. The collision check from issue 155
covered issue folders only; it covers decision records now, and would have
refused this.
2026-10-02 14:51:22 +02:00

7.0 KiB

topic, status, date, deciders, reconstructed, extends
topic status date deciders reconstructed extends
the mesh accepted 2026-10-02 jochen false 02-DECISIONS/0168-a-converged-machine-is-filtered-by-the-mesh-alone.md

170. The firewall seat serves its verbs, and a foreign rule set is removed through one of them

Context

ADR 0168 made the mesh say truthfully what filters a converged machine, and left the removal of what it did not write to the operator's hand. The first time that hand was needed — two machines, five rule sets a predecessor and a retired front end had left — there was no mesh way to lend it: the packet filter is a seat (ADR 0121), a seat's holder serves its verbs (ADR 0154, ADR 0159), and the firewall seat declared none. The only remaining path was a shell on the machine, which is the path the mesh exists to replace, and which the operator's own tooling rightly refused to an agent.

A seat's verbs are the contract every holder implements, whatever filter it speaks. What a person asks a machine's packet filter is the same whether nftables, a front end or a legacy filter answers: what are the rules, reload the mesh's own, remove this thing the mesh did not write. What differs by filter is the holder's own business and may be its own tools beside the seat's.

Decision

1. The node-packet-filter seat serves three verbs, and a module that claims it serves all three or is refused the claim, as with every seat:

  • rules — the packet filter as the machine enforces it now: the nftables ruleset, and the legacy filter's listings where that tool exists; narrowed to one table or chain when asked. Read-only.
  • reload — load the mesh's own filter again from the file the mesh writes, and answer with the mesh's table as loaded. The holder's own act on the holder's own rules.
  • remove — remove one rule set the mesh did not write, named exactly as the host reports it under ADR 0168 (chain HAL-MESH-ONLY (iptables-legacy), table ip6 filter, chain DOCKER-USER), and answer with what was done. It refuses the mesh's own tables, the container runtime's own chains, a built-in chain other than the runtime's user chain, and any chain of a found firewall that is in force. The runtime's user chain is emptied back to its one return; another chain loses the jumps into it, is flushed and deleted; a table of the machine's own is deleted whole. Each is an operator's act, by name, on one thing the mesh reported — never a flush, never a rule the mesh itself marked.

2. A holder may serve its own tools beside the seat's. The nftables module keeps its reading of the mesh's table as its own tool, and a holder speaking a filter with specifics of its own may add tools for them; the seat's three are what every holder owes.

3. A container may ask for a capability. Serving remove and reload needs the machine's network namespace and the right to change its packet filter; a holder's runtime declares capabilities: ["NET_ADMIN"] on its container and runs on the machine's network. The host grants exactly the capabilities declared, names them in the container's spec so a change recreates it, and refuses a name that is not a capability's. A privileged container stays undeclarable.

4. ADR 0168's "by hand" is read as "by the operator, through the seat". Removing what the mesh reports as other is still the operator's act and is still never the mesh's own doing; the verb is how the act reaches the machine, recorded on the bus like every other, instead of a shell.

Consequences

  • The seat's row gains the three verbs; a mesh that already runs widens its row at the next controller start. The nftables module claims them and gains a runtime — a tool server with the packet filter's tools in its image, on the machine's network, with NET_ADMIN.
  • The host's container vocabulary grows by capabilities; an older host refuses a declaration that carries it, so the host rolls before the module.
  • The two machines of this mesh that ADR 0168 found not filtered by the mesh alone are cleaned through remove, and read the mesh alone afterwards; status returns to well without a hand on either machine.

How this is checked

Rule Checked by
The seat declares the three verbs; a claim that serves fewer is refused by name the catalogue's seat tests
remove refuses the mesh's tables, the runtime's chains, a built-in chain and an active front end's chains, and removes a user chain with its jumps, empties the user chain, deletes an own table the module's tests over a fake command runner, with the shapes the host reported live
A container's capabilities reach the runtime and its spec; an unknown name is refused host tests
Live node-packet-filter.remove@<node> on the home server and the control node; node show reads the mesh alone on both; status is well

Built and proven live, 2026-10-02

Progressive insight — 2026-10-02. The decision stands; these are the facts of its building. Written as 0169 for three hours and renumbered to 0170: another record took 0169 on main first, and the check that refuses a shared number covered issues only (now records too).

Built in mesh-host 68 (capabilities on a container), mesh-controller 212 (the seat's three verbs) and 213 (the filter file a module names under filtering.into counts as declared for a mount — the module's first build was refused without it), mesh-catalog 216 (the nftables module's runtime and verbs) and mesh-tools 27 (the console lists a node-scoped seat's verbs with their scope and carries the machine; before it, the verbs were live on four machines and unreachable from the console — issue 199). Each machine's holder was issued its bus account with mesh-controller.issue, the broker node pushed first. At 12:46Z the five rule sets ADR 0168 had named were removed through node-packet-filter.remove, three on the home server and two on the control node, each answering with the commands it ran; the next report read the mesh alone on all four machines and status listed nothing under filtered. The live row is read. What it cost on the way is issue 200.

References