The scenario model, the lifecycle, and what the lab actually costs #6

Merged
jschoubben merged 9 commits from design/scenario-underlay-detail into main 2026-08-23 22:40:55 +00:00
Showing only changes of commit b1874f1d0f - Show all commits
+93 -5
View File
@@ -308,6 +308,86 @@ not first.
- **Steps.** A scenario is a desired state. Anything expressed as an ordered list of actions
belongs in the lifecycle, not the declaration.
## What the model deliberately does not contain
A real network is full of equipment: a modem, a router, switches, access points, controllers.
Almost none of it appears here, and the omission is deliberate rather than an oversight.
**The test is whether a device changes what an IP packet can do.** If two machines can exchange
packets, at the same addresses, with the same reachability and the same MTU, whether or not the
device exists — then the device is invisible to the mesh, and modelling it would add a fixture
with no fault to catch.
| Equipment | Modelled? | Why |
|---|---|---|
| **Switch** | no | Moves frames within a segment. Two machines on a switch are two machines on a segment. |
| **Access point** | no | Bridges wireless clients onto a segment. A machine on wifi and a machine on cable are the same machine to IP. |
| **Network controller** | no | Configures equipment. Its effects appear as segments and policy; it has no packets of its own. |
| **Cabling, PoE, uplink speed** | no | Change performance and availability, not reachability. |
| **Router / security gateway** | **yes — it *is* the gateway** | Translation, forwarding and inter-segment policy all live here. |
| **ISP modem** | **only in router mode** | In bridge mode it is a media converter and invisible. Doing its own NAT, it is a second gateway — and that is double NAT. |
| **VLANs** | **yes — they are segments** | Machines on separate VLANs cannot reach each other except through the router, which is the definition of a separate segment. |
| **Inter-VLAN firewall rules** | **yes** — see below | A rule stopping one segment reaching another is a reachability fact, and the mesh will hit it. |
The two entries worth dwelling on are the ones where a common household setup produces
something the mesh has to survive.
**A modem in router mode gives you double NAT.** Your gateway holds a private address from the
modem, which holds the public one. Forwarding then requires a rule on *both*, and one of them
may not be configurable. This is expressible as nested segments — `to:` chains — but publishing
through two gateways is not, and remains open.
**Segmented networks are the common case, not the exotic one.** A router with separate networks
for trusted machines, guests and devices is ordinary, and the rules between them are ordinary
too. A mesh node on one segment and a mesh node on another are, as far as reachability goes, on
different networks that happen to share a gateway.
## Segments may share a gateway
Several segments can name the same parent and the same external address. That is one router
with several networks behind it, which is what a VLAN-capable gateway is:
```yaml
segments:
trusted:
kind: private
cidr: [192.168.1.0/24]
gateway: { to: internet, address: 203.0.113.50, nat: [v4], forwardable: true }
devices:
kind: private
cidr: [192.168.30.0/24]
gateway: { to: internet, address: 203.0.113.50, nat: [v4], forwardable: true }
```
Identical gateway declarations mean **one gateway machine**, not two. The lab materialises a
single router serving both, because that is what the topology being reproduced is — and two
routers sharing one address would not work anyway.
## Policy between segments
Sharing a gateway does not mean segments can reach each other. What they may do is stated
separately, because it is a fact about a pair rather than a property of either:
```yaml
policy:
- { from: devices, to: trusted, allow: false } # devices may not initiate to trusted
- { from: trusted, to: devices, allow: true } # the reverse is fine
```
Default is `true` between segments behind the same gateway, matching a router with no rules
configured. Asymmetry is the normal case and the reason this cannot be a single flag: the
useful configuration is almost always one-directional.
This is **not** the same as `machines[].inbound`, and conflating them loses a real distinction:
| | Enforced by | Blocks |
|---|---|---|
| `policy` | the gateway, between segments | everything crossing, regardless of what the destination thinks |
| `inbound` | the machine itself | unsolicited traffic that already reached it |
A mesh node behind a `policy` deny cannot be reached even by a peer that knows exactly where it
is — and that is a real topology, not a contrived one.
## Is this general? — the axes a setup can vary along
The question that matters is not *does this cover our mesh*, but **can it express any mesh**.
@@ -320,7 +400,9 @@ how the mesh behaves**. Bandwidth does not change correctness; MTU does.
| **Reachability** | attached · forwardable gateway · unforwardable gateway · isolated | yes | the core of the model, and **per family** |
| **Address family** | IPv4 · IPv6 · dual-stack · neither | yes | `cidr:` and `address:` take both; `nat:` names which families are translated |
| **Interfaces per machine** | one · several | yes | `at:` takes a list |
| **Reachability policy** | symmetric · asymmetric | yes | `inbound:` — a routable machine that refuses everything |
| **Reachability policy, host** | symmetric · asymmetric | yes | `inbound:` — a routable machine that refuses everything |
| **Reachability policy, network** | open · segmented · asymmetric between segments | yes | `policy:` — inter-segment rules, as a segmented router enforces |
| **Segments per gateway** | one · several behind one router | yes | identical gateway declarations mean one gateway machine |
| **Gateway state** | permanent · expiring mappings | yes | `mapping_ttl:` |
| **Path MTU** | standard · reduced | yes | `segments[].mtu` |
| **Overlapping ranges** | distinct · two sites both on `192.168.1.0/24` | yes | segments may carry the same range |
@@ -357,11 +439,17 @@ revisited — and it would be revisited by a real failure, which is the right tr
### The honest summary
The model now covers **where a machine sits** and **what the path between machines is like**,
across both address families, which together are what the mesh's reachability logic turns on.
The model now covers **where a machine sits**, **what the path between machines is like**, and
**what is permitted between them** — across both address families. Together those are what the
mesh's reachability logic turns on.
What it does not model is *change over time* beyond moving a machine, and *degradation* short of
failure. Both are absences chosen rather than overlooked.
It contains almost no equipment, by the test above: a device that does not change what an IP
packet can do has no fault for a scenario to catch. What it does contain is every device that
does — which turns out to be the router, and a modem only when the modem is also a router.
What it still does not model is *change over time* beyond moving a machine, *degradation* short
of failure, and *publishing through two gateways at once*. The first two are chosen; the third
is the one real absence, and it is exactly the double-NAT case.
## Open