Reading a converged machine's rendered rules showed the cause: the chain blocks everything passing through and then allows the machine's own containers back by listing their address ranges. 0137 made that list typeable and 0139 tried to generate it; both refined a list that should not exist, because the mesh has no position on a container reaching outward. Constrain what arrives from outside, allow what did not, and let the machine report which links face outside — one fact instead of a list. Ports keep following the modules unchanged. The records check now allows one record to supersede several, and stops requiring a withdrawn record's own citations to be live.
138 lines
8.4 KiB
Markdown
138 lines
8.4 KiB
Markdown
---
|
|
topic: what runs on it
|
|
status: superseded
|
|
date: 2026-09-28
|
|
deciders: jochen
|
|
reconstructed: false
|
|
extends: 02-DECISIONS/0137-a-machine-says-which-networks-it-routes.md
|
|
superseded-by: 02-DECISIONS/0140-the-filter-constrains-what-arrives-from-outside.md
|
|
---
|
|
|
|
# 139. A network is forwarded because a module declared it
|
|
|
|
## Context
|
|
|
|
[ADR 0137](0137-a-machine-says-which-networks-it-routes.md), decided the same week, gave a machine a
|
|
way to say which networks it routes for its guests. It was written because the derived filter's
|
|
forward chain allowed two ranges named as constants in the control plane's source — the container
|
|
runtime's bridge pool, and part of the pool its compose files are given — with a comment admitting
|
|
the gap: *a machine whose runtime is configured with something else needs this to say so, which is a
|
|
thing the mesh cannot derive.*
|
|
|
|
**It can be derived, and from the right place.** Measured on the last machine still to be converged
|
|
([issue 141](../04-ISSUES/141-the-forward-chain-does-not-follow-the-modules/00-report.md)): twenty-one
|
|
container networks, nine inside the runtime's bridge pool, twelve in the other private range, and six
|
|
of those outside the constant's lower bound — so the flip would have cut their guests off exactly as
|
|
it did on the workstation that produced 0137.
|
|
|
|
Naming a range to cover the six is what 0137 provides for, and it is the wrong instrument. Of those
|
|
six networks, **four are networks the mesh's own modules declare**, present as network resources in
|
|
the node's plan and created by the host because a module asked for them. **Two are the predecessor's
|
|
leftovers** — compose networks of services the mesh does not run. Any range wide enough to keep the
|
|
four forwards the two as well: a firewall widened by hand to protect networks that should not exist.
|
|
|
|
The mesh already knows which of the twenty-one are its own, because it made them.
|
|
|
|
**And the node's configuration is meant to follow the modules assigned to it.** That is the mesh's
|
|
founding shape — the machine runs modules, and its files, its filter and its accounts are composed
|
|
from what runs there ([ADR 0005](0005-the-node-host.md),
|
|
[ADR 0010](0010-delivery.md),
|
|
[ADR 0045](0045-a-machine-firewall-is-the-sum-of-what-it-listens-on.md)). The forward chain is the
|
|
one derived thing that consults a constant and a list a person types.
|
|
|
|
## Considered Options
|
|
|
|
1. **Keep 0137 as it stands** — two constants plus a named list. Rejected: the list is written in
|
|
addresses, and addresses are what the runtime allocates, so the only entry safe enough to keep a
|
|
machine working is wider than the truth. It cannot distinguish a network the mesh made from one
|
|
left behind, which is the distinction that decides whether forwarding it is correct.
|
|
2. **Derive it from what the machine reports.** Still rejected, on 0137's own grounds: a test bed
|
|
creates its bridge between one declaration and the next, and a filter that follows whatever
|
|
appeared on a machine is a firewall that widens itself. **This decision is not that** — see below.
|
|
3. **Have the control plane allocate each module network's range from a pool it owns,** so it can
|
|
render the address itself. Rejected: more machinery for no gain. The runtime already allocates and
|
|
the host already knows, and taking allocation over means the mesh owning an address space it has no
|
|
other reason to own.
|
|
4. **Have each module declare its network's range.** Rejected by
|
|
[ADR 0112](0112-a-module-definition-names-no-node-mesh-or-path.md): a definition names no address,
|
|
and the same definition runs on machines whose runtimes have allocated differently.
|
|
|
|
## Decision
|
|
|
|
**A network is forwarded because a module declared it.** Per node, the forward chain forwards the
|
|
networks of the modules assigned there, and by default nothing else. A module unassigned stops being
|
|
forwarded at the next reconcile.
|
|
|
|
**The host resolves a declared network to its addresses.** A network resource carries a name; the
|
|
runtime allocates the subnet when the network is created. So the control plane declares *forward the
|
|
networks these modules asked for* and the host — which made them, and already resolves a container by
|
|
its name — renders the addresses. [ADR 0005](0005-the-node-host.md) holds: the host applies, it does
|
|
not decide.
|
|
|
|
**Deriving from the declaration is not deriving from the machine.** Both of 0137's objections fall
|
|
away. The set is known before the network exists, because a module declared it, so a network created
|
|
between two declarations is already in the one that asked for it. And it cannot widen itself: a
|
|
network nobody declared is never forwarded, however it appeared on the machine.
|
|
|
|
**The runtime's own default bridge is forwarded, from what the runtime reports.** Containers that name
|
|
no module network attach to it, and it belongs to the runtime rather than to any module — so the host
|
|
renders it from what the runtime says, not from a range named in the control plane. The constants go.
|
|
|
|
**What a machine says is for guests no module declares.** A test bed is not a module and its range is
|
|
not a module's; that is the case 0137's mechanism is for, and it keeps it — added to the derived set,
|
|
never replacing it, as 0137 decided. Narrowed to that, it is named for it.
|
|
|
|
**Their guests keep address and name service**, per declared network, unchanged from
|
|
[ADR 0137](0137-a-machine-says-which-networks-it-routes.md): the input chain admits that network's own
|
|
DHCP and DNS and nothing else.
|
|
|
|
## Consequences
|
|
|
|
- **The two constants are removed**, and with them the class of fault that a machine's guests depend
|
|
on a range that describes some other machine.
|
|
- **This is a behaviour change, not a refactor.** On the machine measured, the derived set and the
|
|
constant do not cover the same ground — that is the whole reason for the record. A machine whose
|
|
module networks happen to fall inside the old ranges renders the same rules.
|
|
- **A range that exists only to keep a leftover alive becomes visible as such**, because it will not
|
|
be in the derived set and has to be said out loud to survive.
|
|
- **`node networks` narrows** to guests no module declares, and the preview says which of a machine's
|
|
networks are the mesh's and which are not, so the difference is readable before a flip rather than
|
|
after.
|
|
- **A module's declaration gains nothing.** It already declares its network; what changes is that the
|
|
filter reads it.
|
|
- **What got harder:** the host renders part of the forward chain from what it created, so the
|
|
control plane no longer holds the whole rule set as text. The rule the mesh states is the set of
|
|
networks; the addresses are the machine's.
|
|
|
|
## How it is checked
|
|
|
|
- **Only declared networks are forwarded.** A node with two modules that declare networks renders
|
|
forward rules for exactly those two, and none for a third network present on the machine that no
|
|
module declared. This fails against the previous behaviour, which forwards by range and cannot tell
|
|
them apart, and that is how it is written.
|
|
- **Unassigning a module removes its network's rule** at the next reconcile, asserted on the rendered
|
|
chain rather than on the intent.
|
|
- **Guests of a declared network keep address and name service**, asserted per chain body so a line in
|
|
the wrong chain cannot pass — carried from 0137.
|
|
- **The runtime's own default bridge comes from the runtime**, asserted by rendering for a runtime
|
|
whose default bridge is somewhere other than the range the constant named.
|
|
- **A machine that names a range for guests no module declares still gets it**, added to the derived
|
|
set and not replacing it.
|
|
- **Each family is matched in its own syntax**, carried from 0137: one set holding both is a syntax
|
|
error, and a ruleset that does not load is a machine filtering nothing while its unit reports a
|
|
fault.
|
|
|
|
## References
|
|
|
|
- [ADR 0137](0137-a-machine-says-which-networks-it-routes.md) — narrowed here; its mechanism keeps the
|
|
case it is right for
|
|
- [ADR 0005](0005-the-node-host.md) — the host applies; the addresses are the machine's
|
|
- [ADR 0045](0045-a-machine-firewall-is-the-sum-of-what-it-listens-on.md) — the filter is derived from
|
|
what runs there
|
|
- [ADR 0112](0112-a-module-definition-names-no-node-mesh-or-path.md) — why a module does not name its
|
|
range
|
|
- [issue 141](../04-ISSUES/141-the-forward-chain-does-not-follow-the-modules/00-report.md) — the
|
|
measurement
|
|
- [issue 137](../04-ISSUES/137-converging-a-machine-cut-off-its-own-guests/00-report.md) — the
|
|
breakage that produced 0137
|