Eleven commits, 43 files, ~5 300 lines. More than one change — recorded rather than hidden, and merged on explicit instruction.
What it does
A scenario declares an underlay — segments, the gateways between them, machines placed on them. raise materialises it on incus; exec, snapshot, restore and destroy operate it. Gateways do NAT, port forwarding, asymmetric policy and mapping expiry; several public networks reach each other through a transit router, routed and never bridged.
diagram draws a scenario from its declaration or from the running hypervisor, through one layout, so a difference between what was asked for and what exists is visible.
And place: [host] puts tier 0 inside a raised machine — which is what stops the lab being infrastructure with no consumer.
What it found
Every one of these was found by running, not by reasoning:
A snapshot missed a file written seconds earlier — not stale, absent, because the write was still in the guest's page cache. The design listed this as an open question; the test answered it, and snapshot now flushes first.
Two gateways held the same address.home declared a v4 and a v6 address, devices only the v4, so grouping on the exact list produced two routers both holding 198.51.100.7 on one segment. The transit router resolved it to two different MACs across a cache flush, so a published port worked or did not, run to run. Gateways sharing an address are now one gateway.
Every virtual machine showed no addresses. A container's interface carries the device's name; a VM names its own. Joining by name silently dropped one whole class of machine. Found by drawing the declared and live pictures side by side.
A gateway was drawn across an unrelated network — its link ran the height of the picture through three networks it had nothing to do with, and overlapped another link so the two read as one wire.
incus hung with empty stderr because it reads YAML from stdin when stdin is not a TTY.
exec … true and network delete succeed with empty stdout, which truthiness-testing read as failure. Twice.
What defends it
75 unit tests, 20 integration against a real hypervisor. Mocking the boundary is forbidden (novox/hq ADR 0034) — integration tests skip with a reason on a machine that cannot raise scenarios rather than passing having checked nothing.
Each test names the decision it defends. Two of them failed the moment placement started working, which is what they were for: they defended "there is nothing to place yet" while that was true.
What it does not do
Raises two of five scenarios in the gate. Both faults above lived in scenarios nothing ever built; they were found by eye. Steps 1 and 2 of closing that are in this branch.
Nothing opens the generated draw.io file. The tests assert on the XML and check stencil names against draw.io's own library; no test has opened one.
Everything above tier 0 is refused by name — the substrate, a control plane, a forge. Those tiers do not exist.
Decisions live in novox/hq. This repository carries implementation.
Eleven commits, 43 files, ~5 300 lines. More than one change — recorded rather than hidden, and merged on explicit instruction.
## What it does
A scenario declares an **underlay** — segments, the gateways between them, machines placed on them. `raise` materialises it on incus; `exec`, `snapshot`, `restore` and `destroy` operate it. Gateways do NAT, port forwarding, asymmetric policy and mapping expiry; several public networks reach each other through a transit router, routed and never bridged.
`diagram` draws a scenario from its declaration **or** from the running hypervisor, through one layout, so a difference between what was asked for and what exists is visible.
And `place: [host]` puts tier 0 inside a raised machine — which is what stops the lab being infrastructure with no consumer.
## What it found
Every one of these was found by running, not by reasoning:
- **A snapshot missed a file written seconds earlier** — not stale, *absent*, because the write was still in the guest's page cache. The design listed this as an open question; the test answered it, and `snapshot` now flushes first.
- **Two gateways held the same address.** `home` declared a v4 and a v6 address, `devices` only the v4, so grouping on the exact list produced two routers both holding `198.51.100.7` on one segment. The transit router resolved it to two different MACs across a cache flush, so a published port worked or did not, run to run. Gateways sharing an address are now one gateway.
- **Every virtual machine showed no addresses.** A container's interface carries the device's name; a VM names its own. Joining by name silently dropped one whole class of machine. Found by drawing the declared and live pictures side by side.
- **A gateway was drawn across an unrelated network** — its link ran the height of the picture through three networks it had nothing to do with, and overlapped another link so the two read as one wire.
- **`incus` hung with empty stderr** because it reads YAML from stdin when stdin is not a TTY.
- **`exec … true` and `network delete` succeed with empty stdout**, which truthiness-testing read as failure. Twice.
## What defends it
75 unit tests, 20 integration against a real hypervisor. **Mocking the boundary is forbidden** (novox/hq ADR 0034) — integration tests skip with a reason on a machine that cannot raise scenarios rather than passing having checked nothing.
Each test names the decision it defends. Two of them failed the moment placement started working, which is what they were for: they defended *"there is nothing to place yet"* while that was true.
## What it does not do
- **Raises two of five scenarios in the gate.** Both faults above lived in scenarios nothing ever built; they were found by eye. Steps 1 and 2 of closing that are in this branch.
- **Nothing opens the generated draw.io file.** The tests assert on the XML and check stencil names against draw.io's own library; no test has opened one.
- **Everything above tier 0 is refused by name** — the substrate, a control plane, a forge. Those tiers do not exist.
Decisions live in `novox/hq`. This repository carries implementation.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Eleven commits, 43 files, ~5 300 lines. More than one change — recorded rather than hidden, and merged on explicit instruction.
What it does
A scenario declares an underlay — segments, the gateways between them, machines placed on them.
raisematerialises it on incus;exec,snapshot,restoreanddestroyoperate it. Gateways do NAT, port forwarding, asymmetric policy and mapping expiry; several public networks reach each other through a transit router, routed and never bridged.diagramdraws a scenario from its declaration or from the running hypervisor, through one layout, so a difference between what was asked for and what exists is visible.And
place: [host]puts tier 0 inside a raised machine — which is what stops the lab being infrastructure with no consumer.What it found
Every one of these was found by running, not by reasoning:
snapshotnow flushes first.homedeclared a v4 and a v6 address,devicesonly the v4, so grouping on the exact list produced two routers both holding198.51.100.7on one segment. The transit router resolved it to two different MACs across a cache flush, so a published port worked or did not, run to run. Gateways sharing an address are now one gateway.incushung with empty stderr because it reads YAML from stdin when stdin is not a TTY.exec … trueandnetwork deletesucceed with empty stdout, which truthiness-testing read as failure. Twice.What defends it
75 unit tests, 20 integration against a real hypervisor. Mocking the boundary is forbidden (novox/hq ADR 0034) — integration tests skip with a reason on a machine that cannot raise scenarios rather than passing having checked nothing.
Each test names the decision it defends. Two of them failed the moment placement started working, which is what they were for: they defended "there is nothing to place yet" while that was true.
What it does not do
Decisions live in
novox/hq. This repository carries implementation.