Looking back · 6 min read
What shipped, and what did not
How big is the published image, how is its release guarded, what does a run with no network prove, and where has the demo been run?
Roar Elias Georgsen, 29 August 2026
Part 12 of 13 in Federating a systems model, written for release v0.3.0.
The story so far
The demo shipped as one container. Inside it were a SysML v2 model, a capacity analysis and a requirements document. They answer as one graph behind a single router, with two web apps in front. The demo as it shipped walked through it. This part gives the image’s size, says how a release is guarded, and says what running it does and doesn’t prove.
docker run --rm -p 8080:8080 ghcr.io/roarge/sysml-federation pulls about 45 MB on amd64, or 41.5 MB on arm64, and answers on port 8080 roughly two seconds after the container starts. There’s no account to make and no login to run.
How big the image is
Read back from the registry, the first release came to 44,850,689 bytes on amd64 and 41,475,216 on arm64. Release 0.2.0 came in about 39,000 bytes heavier on each. The ceiling the publishing job enforces is 80,000,000 bytes per platform, so the image for each platform sits a little over half way into its budget.

The image, layer by layer, base at the bottom. Cut from the architecture views.
Almost all of the image is somebody else’s binary. The vendor’s router accounts for nearly 40 MB of the amd64 total. That leaves under 5 MB for the base image, the Go supervisor with both web apps inside it, the configuration and the SysML model. A size budget for this demo is really a budget for the router, and the number moves when the vendor’s next release does.
One small trap catches anyone who types a version. The git tag is v0.1.0, but the image tag is 0.1.0, because the publishing step drops the leading letter. So ghcr.io/roarge/sysml-federation:v0.1.0 finds nothing. The launch line at the top carries no tag, so it avoids the trap.
How a release is guarded
The design as first accepted moved the latest tag in the same push as the version tag, and measured the image afterwards. In that order the measurement comes too late to stop anything, and it’s an easy mistake to write into a release job that looks correct. The registry can only report an image’s size after the push. With latest already moved, an oversized image is what an untagged pull returns from the moment it goes up. The run that goes red later changes nothing about what a stranger has already downloaded. A check that runs after the thing it guards can only report on it. To act as a gate, it has to run first.
So the release job as shipped works the other way round. The version tag goes up alone. The job measures both platforms at the exact digest the build reported. From v0.3.0 the job then pulls each platform’s image with no credential and starts it on a runner of its own architecture. Only once both have passed does latest move onto that digest (publishing on tags). A run that fails leaves latest pointing at the last release that passed.
What the no-network run proves
In Five spikes before the first line, I ran the image with its network removed. It went quiet: nothing attempted, nothing retried, nothing timed out, and the only network interface inside was the loopback.
That’s a real result, and it’s narrower than it sounds. It shows the stack starts, reports itself healthy and stays quiet with no route anywhere, which readiness alone never showed. It doesn’t show what the image does when a network is present. Silence from a process with nowhere to send anything is weaker evidence than silence from one that could send. When a network is present, the demo relies on the environment variables that switch the router’s telemetry off. The router does log that usage tracking is disabled when they’re set.
Two checks, run late
Two checks were still open when the rest had been run, and I ran both on 14 September on Ubuntu under WSL. Both are in the example’s verification record.
The first is the offline reload. A reload of the viewer app makes all its requests to localhost:8080, with no font and no other host among them. Nobody had yet watched the page still drawing with the machine itself taken off the network. With the host offline, both apps reloaded and drew in full.
The second is the same-origin check on the services’ subscription socket. The earlier proof was by hand. A handshake sent from a foreign origin came back 403 Forbidden from both services that serve subscriptions. Now a page served from another origin, opened in a browser, has tried to open the socket, and the socket was refused.
Where the image runs
I build and test the demo on Ubuntu under WSL, and nowhere else by hand. The image is built for amd64 and for arm64, and arm64 is the one Macs with Apple silicon need. From release v0.3.0, continuous integration also pulls the published image and starts it on GitHub’s Linux runners, one amd64 and one arm64, before latest moves. I haven’t tried it on a real Mac of my own, and I make no promises beyond what those runs show.
Update Changed in v0.4.0
The repository now holds an OpenTofu configuration that runs the published image on a Proxmox VE host on demand, public through a Cloudflare tunnel (the demo on my own Proxmox host). Proxmox VE calls the application containers it uses for this a technology preview. So far the configuration has been checked only against mocked providers, by
make opentofu-check, which continuous integration doesn’t run, and no run on a real host is recorded. It adds nothing yet to where the image is known to run.
What the demo is
What’s here is one command. It puts a SysML model file, an analysis that has never read a model file and a document that has never computed anything behind a single endpoint. Two pages sit in front, and they work nothing out for themselves.
The demo isn’t a product, and it isn’t an implementation of the SysML v2 API. It reads a file instead of fronting a repository, its parser covers a fraction of the notation, it forgets every edit when it stops, and its capacity arithmetic is deliberately simple. The claim it was built to test is narrower than any of that, and it’s the one thing the running image does demonstrate. Three services that share no code and know nothing of each other answer one query about one requirement, and a stranger with Docker can watch them do it.
Previous: The demo as it shipped · Index: Federating a systems model · Next: A model of the demo itself