Somewhere to Break It
Next week the code that runs this network stops being something you have to ask us for. I have written about why nobody should own the middle of a network like this, and about what a handover has to contain to be worth receiving, so I will not do either again here. This post is about a smaller question that has been keeping me up: what happens in the first hour after somebody downloads it.
Not the first month. The first hour. A stranger unpacks an archive on a host that has never heard of us, and either something real happens or this project joins the very large pile of repositories that build exactly once, on the author's machine, in the author's presence.
Two things had to exist for the answer to be something real. The first is a kit: one commit, packed, that stands a working sovereign frame up on that host. The second is somewhere to break it, because the moment a stranger can run this, the next thing they will want is to change it, and nobody sane tests a change on the frame their users are on.
Both are live, and the way I convinced myself of that was to become the stranger. Yesterday evening I made an account on the flagship that had nothing to do with me, asked for a frame through the public form like anybody else would, approved it from the other side, downloaded the kit through the browser, checked it against its seal, and stood the frame up from nothing but what that page told me. Every screenshot below is from that run.
What is actually in the box
The kit is a release of one commit. Not a copy of my working tree, not a tidied subset with the interesting parts filed off: an archive of a commit, with a file beside the code listing the sha256 of every file in it. Twenty-one megabytes, and what it builds is the registry, the token ledger, the event store, the auditor, the federated index and the certificate writer that this network runs.
On your host, one command builds it. Six images, a few minutes, and no account of ours anywhere in it: no image registry to log in to, no token, nothing to sign up for. It pulls public base images and the packages the release pins, and that is the whole supply chain. The build reads the file list as it goes and stamps what it found into every image: the digest of the tree, whether that tree differs from the release, and which files differ if it does. Each frame then reports that at its own health endpoint.
That last part sounds like bookkeeping and is the reason the rest of this post works. A frame can always answer two questions about itself: which release am I, and has anybody changed me. Later on, those two answers are the whole difference between evidence and an anecdote.
Asking for a frame
There is a form on the Brussels frame for asking for one. Six steps: who you are, what the frame should be called, what its currency is, where it is, how it should join the network, and a review page that shows the registry card the answers will produce. It is a request, not a purchase, and nothing about it is automatic on our side: a person reads it and approves it.
3 frames - scroll sideways
When it is approved, the applicant's own page fills in: the host checklist, the five commands with that frame's values already in them, and a card holding the kit, its release, its size and its seal. Five downloads, thirty days. The count and the expiry are not a licence check, and there is nothing to enforce: the code is open. They are there so a link that leaks does not quietly become a permanent anonymous mirror of a build nobody is watching.
The seal is the part I would actually use. The page prints the sha256 of the archive, and the command to print it yourself where the file landed. If those two strings differ, you are not holding what we published, and no amount of reading the code afterwards will tell you that as quickly as one line will.
Somewhere to break it
Now the part this post is named after. Filling that form in as a stranger, I wrote this in its free-text box, which was half an answer and half a note to myself:
We want them on our own server with our own currency, and we want to test every change in a sandbox before we suggest it.
The frame that came out of last night's run is called larch: Larch Cooperative, in Tartu, with a currency called the Larch Unit, its own trust domain, its own ledger, and a federation with Brussels that it asked for and we approved. It is not a demo tier. It runs what the flagship runs.
Which is precisely the problem. Once somebody has a frame like that, the next thing they want is to change something in it, and a frame with a ledger and users is the worst possible place to find out what a change does. Our own answer to this for the last few months was a test tier that exists on this host and nowhere else. That is a perfectly reasonable arrangement for the people who own the host, and completely useless to somebody standing a frame up in Tartu.
So a frame from the kit can now stand up its own sandbox, from its own console, in about fifteen minutes.
That sandbox is where the work happens from now on. It is where you try a change, where the tests run, where you break something deliberately to see what the ledger does about it, and where you find out that the thing you were sure about is not true. Nothing in it is for users and nothing in it is for show. It exists to be treated badly.
A sandbox here is not a mode, a flag or a mocked backend. It is a whole second frame beside yours: its own database, its own token ledger, its own event store, its own identity authority, its own console on a name of its own (sandbox. in front of your frame's name), and its own administrator login, which the panel will show you once. The test sweep runs inside it, against it. Every write a test makes lands in that frame's ledger and nowhere near yours.
You get two shapes. One frame runs the lifecycle sweep, which is the one that calls every tool a single frame serves. Two federated frames adds the federation suite, which is the one that tests what happens between frames, so it needs two of them and a cloud operator on each. The panel prints what each shape costs in memory next to what your host has free, before you choose, because the most common way this could waste somebody's evening is by starting something their machine cannot finish.
4 frames - scroll sideways
If a step stops, the panel says which one and why, and Try again picks up from there rather than starting over: the steps that already stand are skipped. Tear down removes the operators through their own frames' admin rails, then each sandbox through the kit's own teardown, and closes the ports it opened. The test reports stay behind, because those are the point.
Through the same doors as a real frame
This is the part I care most about, and it is invisible in every screenshot.
The sandbox is stood up by the kit's own frame-up, the same script the operator ran for the frame itself. The pair is federated by the invitation rail, the same one every frame on this network used to join it. Each cloud operator goes through the application, the approval, the activation and the provisioning, in that order, exactly as an operator does on the flagship.
A test frame built by a shortcut tests the shortcut.
There is no test mode in any of it. Nothing is mocked, nothing is seeded around the front door, and the sandbox does not know it is a sandbox beyond its own posture and the fact that another frame stood it up. So when a test passes in there, it passed through the doors a real frame has, on the software a real frame runs. What the sandbox shares with your frame is the host, the web server in front of it and the small service that does the host work. Nothing else: not a database, not a ledger, not an identity, not a user.
There is a way to see all of that at a glance, which is to look at what the evening actually made on this host. Sorted by the moment each container was created, newest first, the list runs from this morning's rebuilds down to the identity authority that came up first, and it is one hundred and sixteen rows long. The frame is thirty-two of them. Each sandbox is thirty-one. Each cloud operator is eleven. They are all still running.
larch-spire-server, the identity authority the standup brings up before anything else can have a name. Read it from the bottom and you are watching the evening happen: the frame, then its first sandbox, then the second, then a cloud operator on each.That is the honest answer to how heavy this is. A frame of this kind is not a service you start, it is a small fleet, and standing one up by hand is a weekend for somebody who already knows where everything goes. What the kit does is make that one command on a machine we have never seen, and what the sandbox does is let it happen twice more, beside itself, to be broken.
What the sweep asks of 738 tools
The thing that runs in there is our own regression sweep, the one we use on this network, and it is the reason a sandbox has to be a real frame.
It calls every tool both machine interfaces serve, 421 public and 317 administrative, and holds each one to twelve checks: the status, the shape of the answer, the types of the values, the read-after-write side effect, whether the event reached the ledger, whether the audit row was written, whether the tool is in the tier it claims, whether a caller in the wrong tier is refused, whether a peer frame sees the same thing, whether the wrapper and the route agree on the schema, the latency against that tool's own baseline, and how long the system takes to become consistent. On top of that, 86 multi-step lifecycles chain real mutations, transfers, stakes, disputes, governance, cross-frame work, and prove the side effects survive the round trip. A skipped tool counts against the result: a skip is a coverage hole, not a victory.
Four to five minutes for the lifecycle sweep, on a host that is also running everything else we run. The federation suite, 572 checks across both sandboxes and their two operators, takes about twelve.
3 frames - scroll sideways
Baseline, change, compare
A sandbox on its own is a place to look at things. What makes it a way of working is the order you do things in, and the kit ships that order as five commands.
frame-sandbox.pystands the sandbox up, which is what the panel above is doing.frame-test.py --baselineruns the suite on the release, before you have touched anything, and keeps the report.build.sh --versionbuilds your tree under a version name of your own.frame-update.pyputs that build on the sandbox, and takes it off again.frame-test.py --compareruns the suite again and compares the two runs.
one commit, a digest per file"]:::g --> S{"frame-sandbox.py
a whole frame beside yours"}:::q S --> B["frame-test.py --baseline
the suite, on the release"]:::a B --> C["your change
build.sh --version 1.0.0-mine.1"]:::f C --> U["frame-update.py
only the images that changed"]:::a U --> R{"frame-test.py --compare
the same suite, again"}:::q R -->|"a red that was not red before"| X["exit 1, each one named
with the reason the tester gave"]:::x R -->|"another release, another suite,
a report older than your last edit"| X2["refused, in a sentence,
before anything is compared"]:::x R -->|"nothing new is red"| P["frame-contribute.py
the patch, both reports,
their comparison, your sign-off"]:::g classDef x fill:#3a1a1a,stroke:#a85454,color:#ffd6d6 classDef f fill:#2a1f47,stroke:#7a5cc4,color:#eadcff classDef q fill:#1a2740,stroke:#3f6ea8,color:#dce9ff classDef a fill:#0d3a4a,stroke:#2f8fb0,color:#d6f4ff classDef g fill:#0b3d2e,stroke:#1f8a5f,color:#d6ffe9
The comparison is the whole thing, and it is worth being exact about what makes it worth anything at all. The baseline has to have run on the release, unmodified. The second run has to have measured the tree you are actually proposing, not one you have edited since. Both runs have to have used the same test suite. And then the only question that matters is whether anything is red now that was green before.
The interesting number is not how many tests pass. It is whether your change made anything fail that did not fail before.
Those conditions are checked rather than trusted. Each of the five commands refuses, in a sentence, when its evidence would not be about your change: a baseline taken on a build that differs from the release, a comparison against a report from a different release, two runs whose test suites are not the same file set, a report older than your last edit. I wrote each refusal the way I would want to read it at two in the morning, which is to say it names the thing that is wrong and what to do about it, and it does not tell me it is sorry.
The exit code says which of those happened: nothing new is red, something is, the run refused before starting, it ran out of time, or the two runs cannot be compared. That is enough for a person, and enough for a script.
Here is that loop on the sandbox in the figures above, run twice on purpose: once with a change that breaks nothing, and once with a change that breaks something, because a detector nobody has watched fail is not a detector.
First the baseline, on the release, before anything of mine was in it:
$ ./frame-test.py --name larchsbx --baseline
larchsbx: run bbd8d9ece9fc40c2 (flow_mcp_lifecycle_complete) started
larchsbx: run bbd8d9ece9fc40c2 completed after 240 s
report: 2026-09-19T182734Z-flow_mcp_lifecycle_complete.json (758 tools: 690 pass, 0 fail, 68 skip)
baseline set: baseline-flow_mcp_lifecycle_complete.json
Then a change of mine, built under a version of my own, put on the sandbox, and measured against that baseline:
$ ./frame-test.py --name larchsbx --compare
report: 2026-09-19T183641Z-flow_mcp_lifecycle_complete.json (758 tools: 690 pass, 0 fail, 68 skip)
baseline: 758 tools: 690 pass, 0 fail, 68 skip (53a30aedf0d7, modified=False)
this run: 758 tools: 690 pass, 0 fail, 68 skip (ad1b38695eff, modified=True)
RESULT: no new red
The two digests in those lines are the trees the two runs actually measured, and the flag beside each says whether that tree was the release. Then the same loop with a fault I put there on purpose, to watch the thing fail:
$ ./frame-test.py --name larchsbx --compare
report: 2026-09-19T184527Z-flow_mcp_lifecycle_complete.json (758 tools: 689 pass, 1 fail, 68 skip)
baseline: 758 tools: 690 pass, 0 fail, 68 skip (53a30aedf0d7, modified=False)
this run: 758 tools: 689 pass, 1 fail, 68 skip (03f3fdd36997, modified=True)
NEW RED (1): theprotocol_getApyRates
theprotocol_getApyRates: expected success but got HTTP 400: Error: staking/apy-tiers returned 404
RESULT: 1 new red(s): the change breaks what the release passed
One tool, named, with the sentence the tester gave, out of seven hundred and fifty eight. That is the whole product: not a score, a difference.
And once more, from this morning, because it is the rule biting me rather than helping me. I moved that same sandbox onto the release this post ships and asked for a comparison against the baseline it still had from the night before. It refused:
the suite itself differs between the two runs (suite hash 9b96dc4ef5eb -> 24a557c1f42a): a changed test is not evidence about the code; not comparable
Which is correct. Between those two runs the test suite had moved as well as the code, so the two sets of numbers are not answers to the same questions, and a tool that pretended otherwise would be worse than no tool. Take a new baseline on the release you are actually on, and go again.
To try another build, update again. To go back, frame-update.py --rollback puts the release images back and the sandbox is a baseline machine again. Volumes, data and secrets stay where they are. Both directions check the ledger's supply invariant before and after, and refuse to call it done if it moved.
The archive that travels with the patch
The last command packs the argument. frame-contribute.py writes one archive holding the patch (a plain diff of every file your tree changed against the release, worked out from the file list, with the originals taken from the archive you downloaded), both test reports, their comparison, a manifest with a digest of each file in it, and your sign-off.
It refuses to pack evidence that is not about the change it is packing. If you edited a file after the run, it says so. If the baseline was taken on a build that was already modified, it says so. If the two reports came from different releases or different suites, it says so. If the comparison has a new red in it, it will not package it at all, which is a deliberately rude way of saying that the loop is finished when the evidence is clean, not when you are tired.
What lands in a pull request, then, is not a claim. It is a patch, and a run, on a frame the author stood up themselves, that says the suite this release passed still passes with the change in it. That does not tell anybody the change is a good idea. Nothing can do that except a person reading it. It does mean the argument starts somewhere better than trust.
What this is for
I have argued on this blog that nobody should own the middle of a network like this one, and that a stranger should be able to decide whether to deal with a frame for the price of one request. Both arguments have the same awkward footnote: as long as I am the only person who can change the software every frame runs, I am the middle. A polite middle, with reasonable documentation and a genuine intention to be fair, which is exactly what every middle has been at the beginning.
Opening the code removes half of that. This post is the other half. A code base you can read is not the same as a code base you can change, and the gap between them is filled with the question every maintainer asks and no stranger can answer: how do I know this does not break anything?
Now they can answer it, on their own machine, in an evening, with a run they did themselves. And so can I: the same loop is how I will judge what arrives, which means I am accepting the same discipline I am asking for, on the same instruments.
The frame in these screenshots was stood up by the five commands on that page, its sandbox pair came from its own console, the sweep ran inside the sandbox, and the supply invariant on every ledger involved read zero throughout. Nothing in that chain needed anything from our side that a stranger cannot have: a form, an approval, and a page with five commands on it.
Next week, and possibly from somewhere else
We are aiming to open the code base next week. Not a subset, not a demo: the frame, the ledger, the identity fabric, the console, the runner, the kit, the gates and the tests that hold what every run so far has taught them.
Where the code will live is not decided yet. When it is, it will be a link on this page and not a press release.
There is also a real chance that this project has to move to a different country to keep doing what it does. I am not going to speculate in public about something whose ending I do not know yet, and I would rather say the awkward thing plainly than have it turn up later as a surprise. If it happens, it happens, and I will write about it here when there is something to write.
What I can say is what it changes for the network, which is the only part that should matter to anyone reading this: nothing. That is the entire purpose of the last four weeks of work. A project that lives in one place can be made to disappear from one place. A project that has been handed over, with a kit that builds on a stranger's host and a way for that stranger to prove a change, cannot.
Where I stopped
Written down honestly, because a handover with a polished front and no list of limits is a sales page:
- Sandboxes are for frames stood up from the kit. The frames we host do not get one yet, for the boring reason that we do not have the spare machine for it.
- A sandbox lives on the same host as its frame, needs the memory the panel names, and needs the wildcard name that frame already uses for its cloud operators. If the host is short, the standup refuses rather than starting something it cannot finish.
- The loop cannot verify a change to the test suite itself, by construction: the comparison needs the same suite on both sides. A new test is a suggestion of its own, and gets read as one.
- There is no hosted version, no sign-up and no button on our side that runs any of this for you. You run it on your host, or it does not run.
The auditor that checks every ledger in this post still does not work for us, and the frame in the screenshots will outlive my interest in it, which is the point of building it this way. Skepticism remains the local currency. This week it is accepted in one new place: a sandbox, which is exactly where it belongs.
Patchnotes
The last post left the fleet at V0372 and the export on a private branch. What follows is V0373 through V0410, the version this post is published from and the one every registry on this network now runs, together with the first two published kits: five days, supply delta zero throughout on every frame the auditor watches. A line that does not name where it runs is live on the whole fleet.
The kit
- The frame kit is published as a release of one commit, with a file listing the sha256 of every file in it, and it builds on the operator's own host into six images with one command. No account of ours, no image registry to log in to, nothing to sign up for.
- Every image carries the digest of the tree it was built from, whether that tree was modified and how many files differ, and each frame reports it at its health endpoint.
- An approved frame request opens a download window on the applicant's own page: the release, its size, its seal, a count of downloads and an expiry, above the five commands for that frame with its own values already in them and the values only the operator knows marked as placeholders.
- The kit ships its own preflight, standup, federation, teardown and backup tools, the licences, the developer certificate of origin, the contributing guide and a written walk-through for each loop in it.
- Kit builds resolve against a frozen package index, and an image refuses to ship if an unexpected package or a duplicate install slipped into it.
- The frame kit page is behind a flag that is off by default, and an approval follows the flag rather than opening a window the frame cannot serve.
The sandbox and the loop
- A kit frame stands up its own sandbox from its MCP tester page: one frame, or two federated frames, each optionally with a cloud operator, with the memory each shape needs and the host's free memory shown before anything is pressed.
- The sandbox is built through the same doors a real frame walks: the kit's own standup, the invitation rail for the pair, and the full application, approval, activation and provisioning for each operator.
- A standup that stops names its step and can be picked up from there; a teardown removes the operators, the sandboxes and the ports it opened, and keeps the reports.
- Test-verified development, as five commands in the kit: a sandbox, a baseline on the release, your build, a comparison that has to show no new red, and one archive holding the patch, both reports, their comparison, a manifest and your sign-off.
- Every step of that loop refuses, in a sentence, when its evidence would not be about the change: a baseline taken on a modified build, reports from different releases or different suites, a report older than your last edit, a comparison with a new red in it.
- A build from a changed tree takes only its own version tag, so nothing that restarts later picks up an experiment.
- Putting a build on a sandbox recreates only the services whose image changed, the ledger first and the registry last, rejoins the networks the tester had, and reads the ledger's supply invariant before and after.
- The tester page reads the frame's own role rather than its address, so a sandbox says it is a sandbox and a kit frame is pointed at the document the kit ships.
- A sweep whose precondition was never met reads as not measured rather than as a failure, and the reds that are red by design are listed apart from the ones that are news.
Frames and operators
- A frame the runner builds can be stood up without handing it the host's container socket, and its cloud operators are provisioned through a second socket that serves operator work only.
- A provisioned operator's database answers only over TLS with a client certificate, and the frame's auditor reads it through a role that cannot write.
- An operator teardown is idempotent, removes the host's half first, and retires the credential the provisioning issued.
- A frame's own auditor reads a provisioned operator's share register over mutual TLS, and refuses a plaintext or certificate-less caller. Proven by tearing the same operator down and provisioning it again, three times, on three versions.
- A re-provisioned operator revives its peer row through one writer, and a provisioning never revives a row an administrator deactivated on purpose.
- Two frames were torn down and one was stood up fresh by the runner, with its cloud operator connected on every measured lane.
- Every registry on this network runs one version again, V0410: the two sovereign frames, their four cloud operators, the sandbox pair and its two operators, and the three older frames that are being retired. Thirteen of them, rolled one at a time, each gated on its own card, its rails and its ledger.
Identity, the ledger bus and workers
- A forwarded client certificate counts only beside a marker the frame's own gateway adds, on the registry and on the token ledger, on the sandbox pair.
- The message bus takes one account per client, and the in-frame clients take their TLS material from the frame's own identity and rebuild it when it rotates, on the sandbox pair.
- A frame's broker serves a current certificate through a rotation without being restarted, on the sandbox pair and in the template every new frame is rendered from. Before it, a broker held the certificate it started with, so a ledger could stall for hours after a routine rotation.
- A registry reaches its own token ledger through the frame's mutual TLS gateway, on the sandbox pair.
- A worker that sleeps longer than its lease now renews while it sleeps. Measured before the fix, one worker was re-acquiring its job every 31 seconds against a declared two minutes; after it, once, and it kept it.
- The websocket no longer takes a token in its address, and a registry restart no longer logs out the console that is watching it.
Console and network
- An administrator can address a broadcast to one developer rather than to everyone, audited as its own action, with mail, scheduling and organisations refused for that shape.
- The public form for requesting a frame reads partner frames live, previews the registry card as you fill it in, and speaks English and German.
- A person who arrives through a magic link is asked to choose a password once, not twice.
Build and release
- The release export is built from committed history rather than a working tree, and the publisher refuses an export that is missing any of the tools this post is about.
- Continuous integration runs the registry's own test suite, and every script in both repositories is tracked as executable, so a fresh clone can run them.