The scenario model, the lifecycle, and what the lab actually costs #6
@@ -17,31 +17,60 @@ everything in the lab hangs off, so it is worth getting small.
|
||||
It states what a hosting provider and a home router would provide, and nothing the mesh is
|
||||
responsible for ([ADR 0031](../../02-DECISIONS/0031-the-lab-provides-the-underlay.md)).
|
||||
|
||||
## Three positions a machine can be in
|
||||
|
||||
The underlay's whole job is to reproduce **where a machine sits relative to the internet**,
|
||||
because that is what the mesh has to cope with and what only production currently exercises.
|
||||
There are three positions, and they are genuinely different:
|
||||
|
||||
| Position | Reachable from outside | Address | Example |
|
||||
|---|---|---|---|
|
||||
| **Directly attached** | yes, at its own address | fixed, its own | a hosted server |
|
||||
| **Behind a gateway you control** | only through a forwarded port, at the *gateway's* address | private, plus the gateway's public one | a machine at home |
|
||||
| **Behind a gateway you don't control** | **no** | private, and it changes | a laptop on someone else's network |
|
||||
|
||||
The third is the hard one and the reason this matters. A machine there can dial out and nothing
|
||||
more: it cannot be published, its apparent address belongs to somebody else's router, and that
|
||||
address changes when it moves. Every assumption a mesh makes about reachability breaks there
|
||||
first.
|
||||
|
||||
A declaration has to be able to say all three, and to move a machine between them.
|
||||
|
||||
## The shape
|
||||
|
||||
```yaml
|
||||
scenario: published-behind-nat
|
||||
scenario: roaming-and-published
|
||||
|
||||
segments:
|
||||
wan:
|
||||
cidr: 203.0.113.0/24 # RFC 5737 — never routes on the real internet
|
||||
lan:
|
||||
internet:
|
||||
cidr: 203.0.113.0/24 # the simulated public internet, RFC 5737
|
||||
home:
|
||||
cidr: 192.168.1.0/24
|
||||
behind: wan # NAT; the lab materialises a router
|
||||
gateway:
|
||||
to: internet
|
||||
address: 203.0.113.50 # what the world sees this network as
|
||||
nat: true
|
||||
elsewhere: # a network we do not control
|
||||
cidr: 198.51.100.0/24
|
||||
gateway:
|
||||
to: internet
|
||||
address: 203.0.113.80
|
||||
nat: true
|
||||
|
||||
machines:
|
||||
anchor:
|
||||
segment: wan
|
||||
address: 203.0.113.10
|
||||
at: { segment: internet, address: 203.0.113.10 }
|
||||
|
||||
home-server:
|
||||
segment: lan
|
||||
address: 192.168.1.135
|
||||
forwarded: [443] # reachable from wan through the router
|
||||
at: { segment: home, address: 192.168.1.135 }
|
||||
published:
|
||||
- { port: 443, on: home } # DNAT: 203.0.113.50:443 → 192.168.1.135:443
|
||||
|
||||
workstation:
|
||||
segment: lan
|
||||
address: 192.168.1.250
|
||||
at: { segment: home, address: 192.168.1.250 }
|
||||
|
||||
laptop:
|
||||
segment: detached # reachable by nothing until attached
|
||||
at: { segment: home, address: 192.168.1.98 }
|
||||
|
||||
place:
|
||||
all: [host]
|
||||
@@ -50,41 +79,87 @@ place:
|
||||
snapshot: raised
|
||||
```
|
||||
|
||||
That is a complete bootstrap scenario. Nothing in it mentions the overlay, a hub, peering,
|
||||
names or certificates — all of which are outcomes to be observed.
|
||||
## What each part means, precisely
|
||||
|
||||
## The four parts
|
||||
**`segments`** — a broadcast domain with an address range. A segment with no `gateway:` *is*
|
||||
the internet as far as the scenario is concerned. A segment with one sits behind it.
|
||||
|
||||
**`segments`** — the networks that exist. `behind:` declares NAT, and is the only place a
|
||||
router comes from: the lab materialises one without being asked, because NAT has to run
|
||||
somewhere. This is the one implicit machine in an otherwise explicit declaration.
|
||||
**`gateway:`** — how a segment reaches its parent, and this is where the previous version was
|
||||
too thin. It carries three facts, and all three are load-bearing:
|
||||
|
||||
**`machines`** — what sits where. A machine has a segment and an address, and that is nearly
|
||||
all. `forwarded:` opens a port through the router, which is what makes *published but behind
|
||||
NAT* reproducible — the case that exists only in production today. `segment: detached` is a
|
||||
machine on no network, which is how a roaming node is expressed at rest.
|
||||
- `to:` — the parent segment.
|
||||
- `address:` — **the address the outside world sees this network as.** For a household
|
||||
connection this is the public address the ISP hands out. It is not decoration: it is what a
|
||||
peer records as the endpoint when a machine here dials out, and what a public name for a
|
||||
published machine here resolves to.
|
||||
- `nat:` — whether addresses are translated. `true` gives the ordinary household case: many
|
||||
private machines behind one public address. `false` describes a routed range, where machines
|
||||
keep their own addresses and the gateway only forwards.
|
||||
|
||||
**`place`** — what goes inside. `all:` applies to every machine; a machine name overrides for
|
||||
that machine. This is the only part that differs between the two scenario classes.
|
||||
The lab materialises a machine to be the gateway. That is the one implicit machine in an
|
||||
otherwise explicit declaration, and it exists because NAT has to run somewhere.
|
||||
|
||||
**`snapshot`** — names the state once placement finishes, so a run can return to it without
|
||||
raising everything again. Snapshots are what make repetition cheap, and cheap repetition is
|
||||
what makes the bootstrap path the inner development loop rather than a ceremony.
|
||||
**`machines[].at`** — segment and address. That pair alone determines which of the three
|
||||
positions a machine is in: on a gateway-less segment it is directly attached; on a segment with
|
||||
a gateway it is behind one.
|
||||
|
||||
**`machines[].published`** — a destination-NAT rule on a named gateway, stated as an outcome
|
||||
rather than a port list. `{ port: 443, on: home }` means the `home` gateway forwards its own
|
||||
`203.0.113.50:443` to this machine's `443`. The resulting public endpoint is derivable, which is
|
||||
the point: a scenario never writes an endpoint down, and the mesh has to discover it.
|
||||
|
||||
A machine may be published on **any gateway between it and the internet** — which is how *"our
|
||||
LAN also has a public IP"* is expressed, and why `on:` names the gateway rather than being
|
||||
implied. It cannot be published at all on a gateway the scenario models as foreign; attempting
|
||||
it is a declaration error, because that is precisely the constraint being reproduced.
|
||||
|
||||
**`at: detached`** — on no segment. A machine that exists and can reach nothing.
|
||||
|
||||
## Moving a machine is a lifecycle operation
|
||||
|
||||
`at:` states where a machine *starts*. Moving it is something a run does:
|
||||
|
||||
```
|
||||
move laptop → { segment: elsewhere, address: 198.51.100.23 }
|
||||
move laptop → detached
|
||||
move laptop → { segment: home, address: 192.168.1.98 }
|
||||
```
|
||||
|
||||
This is the roaming case made testable, and it is the one that finds the interesting faults.
|
||||
The same machine, the same identity, three positions in one run: at home where its peers can
|
||||
reach it directly, on a foreign network where it can only dial out and its apparent address
|
||||
belongs to a router it does not control, and asleep.
|
||||
|
||||
Whether the overlay survives that, re-forms, and is noticed to have changed endpoint is
|
||||
**observed**, never arranged
|
||||
([ADR 0031](../../02-DECISIONS/0031-the-lab-provides-the-underlay.md)).
|
||||
|
||||
## Why the addresses are load-bearing
|
||||
|
||||
The routable segment uses RFC 5737 documentation space, and this is not a stylistic choice.
|
||||
The internet segment uses RFC 5737 documentation space, and this is not a stylistic choice.
|
||||
|
||||
The mesh decides *public versus private* by matching the address. A private range on the
|
||||
segment meant to be routable makes the hub test as unreachable, and **the mesh silently never
|
||||
forms** — no error, no failed step, just a mesh that does not exist. Research 004 calls this
|
||||
the single most important fact in its analysis.
|
||||
segment meant to be routable makes a would-be hub test as unreachable, and **the mesh silently
|
||||
never forms** — no error, no failed step, just a mesh that does not exist. Research 004 calls
|
||||
this the single most important fact in its analysis.
|
||||
|
||||
So the format should make this hard to get wrong rather than merely documented: a segment
|
||||
without `behind:` is a routable segment, and an address in it that is not documentation space
|
||||
is a declaration error, refused before anything is raised. That is
|
||||
RFC 5737 reserves three ranges, which is exactly enough for the topology above:
|
||||
|
||||
| Range | Used for |
|
||||
|---|---|
|
||||
| `203.0.113.0/24` | the internet segment itself — directly attached machines, and gateway addresses |
|
||||
| `198.51.100.0/24` | a foreign network, so a roaming machine's apparent address is plainly not ours |
|
||||
| `192.0.2.0/24` | spare — a second foreign network, or a second site |
|
||||
|
||||
Private segments use RFC 1918 and can be **byte-identical to production**, because those
|
||||
addresses mean the same thing everywhere. Only the public side is substituted, and only because
|
||||
it must be.
|
||||
|
||||
The format should make getting this wrong hard rather than merely documented: a segment without
|
||||
a `gateway:` is a public segment, and an address in it — including a gateway's `address:` — that
|
||||
is not documentation space is a declaration error, refused before anything is raised. That is
|
||||
[ADR 0008](../../02-DECISIONS/0008-a-failed-step-fails-the-job.md) applied to a configuration
|
||||
file — the failure it prevents is silent, so the check has to be loud.
|
||||
file: the failure it prevents is silent, so the check has to be loud.
|
||||
|
||||
## The same declaration serves both classes
|
||||
|
||||
@@ -127,15 +202,17 @@ not first.
|
||||
## Open
|
||||
|
||||
- **`user` and `edge` profiles have no scenario.** A lab machine is always privileged, so the
|
||||
two profiles that exist for unprivileged and phone-like participation cannot currently be
|
||||
exercised. Either the lab grows a way to run the host unprivileged, or those profiles are
|
||||
developed against something that is not a virtual machine.
|
||||
- **Attaching and detaching during a run.** `segment: detached` covers a machine at rest;
|
||||
moving one between segments while a scenario is live is what makes a roaming node
|
||||
interesting, and that is lifecycle rather than declaration.
|
||||
two profiles that exist for unprivileged and phone-like participation cannot be exercised.
|
||||
Either the lab grows a way to run the host unprivileged, or those profiles are developed
|
||||
against something that is not a virtual machine. This is the largest gap.
|
||||
- **Where `place:` gets its artifacts from.** Before the mesh is self-hosting these come from
|
||||
outside; afterwards from the mesh itself. The declaration should not have to care, which
|
||||
suggests a named source rather than a path.
|
||||
- **Multiple scenarios at once.** Each needs its own segments and addresses. Whether the
|
||||
declaration carries absolute addresses, as above, or a template the lab allocates from,
|
||||
decides whether two scenarios can run side by side.
|
||||
- **Multiple scenarios at once.** Each needs its own segments and addresses, and the shape above
|
||||
writes addresses absolutely. Whether a scenario carries literal addresses or a template the
|
||||
lab allocates from decides whether two can run side by side — and there are only three
|
||||
documentation ranges to go round.
|
||||
- **Gateway behaviour beyond forwarding.** A real household gateway also has a NAT table with
|
||||
timeouts, and connection tracking that drops idle flows. Whether a scenario can express *"the
|
||||
gateway forgets a mapping after N seconds"* decides whether keepalive behaviour is testable
|
||||
or merely hoped for.
|
||||
|
||||
Reference in New Issue
Block a user