Handed Over Once
The code base behind this network is going to be opened to everyone, and sooner than I would have chosen. For a project like this one, ready cannot mean finished. It has to mean something more modest and more useful: that whoever picks it up can start from the point where I stopped. They should be able to stand up a sovereign frame from a specification rather than from my memory, watch its ledger balance, hand its keys to exactly one person, and take it down again without leaving anything behind.
Tonight I did all of that from a form on the Brussels frame. At 02:03:28 UTC there was a sovereign frame on this network that had not existed when I first pressed the button, with its own currency, its own trust domain and its own ledger, which read delta zero at every check the run made and still reads zero on the public auditor as I write this. It had a certificate from a public authority for four names that were not in DNS when the job began, and an administrator credential waiting to be handed to exactly one person, exactly once.
This post is about the pieces that had to be in place for that to be true. Each section is one of them, shown with the screens the run produced and the records behind them. Several are refusals, because on a page that can create a jurisdiction, the list of what the button will not do is the more important half of its documentation.
The button that used to be root
There was a New frame button in this console before tonight. It was built in July for a demo, which is a polite way of saying it was built to be shown rather than handed to anyone. When I sat down with it properly in September, my first decision was not to press it again until it had been replaced. I count that among the better engineering decisions of the year.
Here is what it did. The registry, which is a web application with a login page, asked the host's container runtime for a privileged container that shared the host's process space, stepped out of that container into the host, and ran the standup script there as root. The door to all of it was an admin flag on an account. That is a perfectly good way to show a frame being born, and exactly the wrong thing to give a stranger: root on the machine that runs every frame, with a login form in front of it.
So the button now talks to something that cannot do very much. A small service runs on the host itself, outside every container, and listens on a local socket that only root and the registry's own user are allowed to open. It understands twelve operations: plan, stand up, resume, tear down, cancel, hand over a credential, three that manage DNS names and three that report. Anything else gets a one-line refusal and the connection closes. The registry serving this page holds that socket and nothing else. No container runtime, no host directories, no web server configuration.
The most important property of an admin button is the list of things it cannot do.
That list is short enough to state. Someone who steals an administrator's session on this frame can plan a pilot frame, stand one up, or tear down a frame this runner built. They cannot touch a frame it did not build, they cannot reach the host, and they cannot ask for anything the specification does not name, because the specification is a closed list and an unknown field is refused before it leaves the registry. The three fields an attacker would want most, federate with somebody, grant host access, choose a production posture, are not fields at all.
with the platform admin flag"]:::f --> R{"the registry's admin rail
mainframe only, closed spec"}:::q R -->|"an unknown field: federate_with,
grant_host_reach, posture"| X1["422, before anything
leaves the registry"]:::x R -->|"a valid spec"| S{"a unix socket on the host
root or the registry's own uid"}:::q S -->|"any other caller"| X2["refused at the socket"]:::x S --> N{"the frame runner
twelve operations and no others"}:::q N -->|"a protected name, a frame it did not build,
a DNS record somebody else created"| X3["the plan refuses,
in a sentence"]:::x N -->|"another job running,
load, memory or disk"| X4["Start refuses,
in a sentence"]:::x N -->|"a plan that passed
for exactly this spec"| D["the standup driver
seventeen steps, resumable"]:::a D --> F["a sovereign frame
standalone, pilot posture"]:::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 service, its twelve operations and the specification it accepts are part of the code base, so whoever runs frames next inherits the short list along with the button.
A plan before a press
The form asks what a frame is, not how to build one: a name that becomes its hostnames, a display name for its signed card, a ticker and a currency name, a treasury in whole units, a theme, a description, who operates it and where it is, an optional website, and the registry and TEG images it runs, chosen from a list the host allows. Tonight's was lantern: Lantern Cooperative, a currency called the Lantern Unit with the ticker LNT, a million units in the treasury, the sovereign mark, and Tallinn, at 59.437 north and 24.7536 east.
Before Start standup can be pressed, Check plan has to come back, and the form will not even ask until the frame's latitude and longitude are filled in. A place on this network is a claim printed on a signed card, so it is declared by a person and never filled in by software trying to be helpful. That lesson once cost this project a globe full of agents standing in the wrong cities.
The plan is not a preview. The runner hands the specification to the same driver that will do the work, in a mode that writes nothing, and adds the things only the host can know: whether the name is protected, whether a frame by that name already exists, which ports are free, and what public DNS currently says about the four names the frame will need. A name with no record yet is a warning, since the runner will create it. A record the runner did not create is a refusal, because the runner never adopts a name somebody else put there.
Start stays grey until a plan has passed for exactly what the form says. Change the ticker after planning and the result on screen reads The plan passed, for an earlier version of this form, and Start standup goes grey again. It costs a second click. I have not yet regretted a single one.
5 frames - scroll sideways
The first plan I asked for tonight was for a frame called asia. There is already an ASIA on this network, in Singapore, with agents, a currency and a ledger of its own. The runner answered with a 409 and one sentence: refused: name: asia is a protected frame (/etc/theprotocol/protected-frames.list). That list is a plain file on the host, outside anything the registry can write, and if the runner ever fails to read it, it refuses every name rather than guessing which ones were on it. A form that could create a jurisdiction by typing the name of an existing one would have made a far more exciting post. I prefer this one.
Pressing Start plans again, on the host, at that moment, and refuses if anything has moved since: another job running, the machine's load above forty, less than fifteen gigabytes of memory available, less than sixty gigabytes of disk. One job at a time. A frame that cannot be started cleanly does not start at all, and nothing is left behind to tidy.
Seventeen steps, resumable by name
The job reports as seventeen named steps, and the panel folds each into a single row as it arrives.
- dns. The runner creates the frame's four address records, each marked with the runner's name and the job that made it, and waits until a public resolver answers for all four before anything else is allowed to happen.
- validate, preflight, render, compose-up, compose. The driver checks its arguments again, checks the host, renders the frame's compose file and environment from the template, and starts the containers: twenty-five services on a frame like this one, one of which does its job and exits.
- spire. The frame's identity authority registers an identity for every service that needs one. The driver then waits for the registry to answer and runs two gates that have no rows of their own, which is why they get sections of their own below.
- seed and emission. The administrator account, and then, before any visible mint, proof that a mint reaches the ledger: the step checks that the ledger, the TEG and the broker answer, mints one unit for the canary, and requires the event to land and the ledger to read delta zero. On the way it switches the new ledger's projections to a single writer, so every figure the delta sums is counted exactly once.
- vhosts. The web server's configuration, and a certificate for the four names from a public authority once the names resolve.
- admin. The treasury, minted through the frame's own registry in pieces no larger than the frame permits, then held until the ledger shows exactly that much issued at delta zero.
- jurisdiction. The frame's jurisdiction profile, published complete and named for this frame: the regulated actions it observes, in shadow, and the authorizations behind them. Then four service accounts, one for each onboarding job, because the founder account onboards nothing. Then the agent that routes cross-currency transfers, minted by the provisioning account, and only once it exists is the route engine switched on, so the frame never declares a capability whose precondition nobody supplied. The step ends by checking that the registry card reports the version the running image actually carries.
- federate and operators. Nothing to do, on purpose. A frame from this page is born standalone. Federation here is bilateral, and an administrator on each side decides it later.
- canary and wire. A pilot has no canary paths to seed, so that row reports done having had nothing to do. Wire tells the host's own monitoring and the independent supply auditor that the frame exists, and names any missing piece in a sentence before anything is written.
- mtls. The last thing before a frame is called live is a census of its own gateways, taken from the running containers rather than from the template, followed by a check that every address its registry card advertises actually answers.
Every step can be picked up again by name, and tonight that mattered. The standup stopped twice at spire, both times on a defect in the driver rather than in the frame, and each time the same page resumed the job from a named step. Both defects are fixed in the driver now, and the second resume took the frame from compose to live in seventy-seven seconds. On the ledger's side, its log reads:
EMISSION GATE PASSED: canary projected into the frame's own ES, delta=0
SHOW MINT 1000000 LNT: 1 piece(s) accepted in this pass, 1000000 minted in all
SHOW MINT is in the frame's own ledger at delta 0
A standup that only works in one go needs its author standing next to it.
One that can be picked up at a named step, by whoever is looking at the page, does not. That is also why every frame this runner has built so far has left what it taught in the driver's gates and tests, rather than in my head, where nobody else could read it.
Something else was already in place during those stopped minutes. At 01:47:07 the new registry wanted to record a routine compliance poll while its event store's gateway had not been started yet. The event went into the registry's own outbox, waited through twelve delivery attempts, and was delivered at 01:56:29, seconds after the first resume started the gateway. Nobody replayed anything by hand, which is the only acceptable number of people for that job.
4 frames - scroll sideways
Born with the kit
Until this week, a frame from this template booted as a registry, a TEG and a ledger, and most of what makes a frame governable arrived afterwards, by hand, from whoever remembered. Now, straight after spire, the driver refuses to go on until the frame's own kit answers, one component at a time, asked from inside the frame:
- A policy engine in shadow. Every authorization decision the registry makes is also put to it, and disagreements are counted rather than enforced.
- Prometheus, Alertmanager and Grafana, watching the frame from inside it.
- A solo auditor, reading the frame's databases through roles that cannot write, and reporting whether its ledger balances.
- The Directory, the federated index, holding an identity of its own and answering only for its own registry. The probe that counts reads it through the registry's public proxy, which answers only when its own mutual TLS dial to the index verified in both directions.
- The event store, which from its first boot trusts a client certificate only when the frame's own gateway verified it and says so with a marker, and refuses the older licence fallback, so a neighbour on the same network cannot dress up as a verified caller.
The last gate before the frame goes on is the one I care about most, and the one nobody will ever look at on a screen.
The ledger's own bus
Every frame has a message broker between its services and its ledger, and until this week it was shared: the registry and the TEG wrote to it directly, and the ledger's consumer accepted whatever arrived from any container on the frame's network. A side door to a ledger is not something to hand to a stranger.
On a frame born tonight the broker has exactly one neighbour, the event store, on an internal network that nothing else on the frame can even resolve. It speaks only SCRAM over TLS, on the frame's own identity. Its admin interface and both of its HTTP proxies listen on its own loopback and nowhere else. The registry and the TEG hold no producer at all: they write to the store over mutual TLS, and the registry keeps a durable outbox in its own database for the moments the store cannot answer. The store signs every record it puts on the bus and drops any record it did not sign.
The broker image's own default, it turned out, told it to acknowledge records without waiting for them to reach the disk. That is a perfectly sensible setting for a laptop and a remarkable one for a ledger. It is off.
None of that is taken on trust, because on every standup the driver makes the broker refuse things. It reads the three settings that matter back from the cluster itself, never from the file that asked for them. It checks the topics exist at the partitions the environment names. It sends an anonymous client over TLS, a plaintext client and a wrong password, and each one has to be refused with a refusal that names itself, because a broker that is simply down fails every probe and proves nothing. Then it checks that the registry cannot reach the broker at all, that the store's own health report says SASL over TLS with record signing enforced, and that the store's consumer group has settled. Tonight's run, verbatim:
kit/kafka: broker private to the store (cluster: enable_sasl true, superusers has es, auto-create off): 3 topics x 4 partitions, anonymous refused, wrong password refused, registry cannot reach lantern-redpanda, store SASL_SSL + record auth enforce (derived key), consumer group Stable
A check that cannot fail is not a check. It is a compliment.
Tonight's frame is the first where all of it ran together, from a press on a page: the kit, the private bus, the gate that proves it, the store that signs and the registry's outbox.
no producer, durable outbox"]:::a TEG["TEG
durable outbox"]:::a GW["the store's gateway
verifies the certificate, adds the marker"]:::f KIT["policy engine in shadow, Prometheus,
Alertmanager, Grafana, solo auditor"]:::f end subgraph LN["the ledger network: internal, two members"] ES["event store
trusts only certificates its gateway verified
signs every record"]:::g BR["broker
SCRAM over TLS on the frame's identity
admin on loopback, fsync on"]:::g end REG -->|"mutual TLS"| GW TEG -->|"mutual TLS"| GW GW --> ES ES <-->|"SCRAM over TLS"| BR REG -.->|"cannot resolve the name"| NO["no route to the broker"]:::x classDef x fill:#3a1a1a,stroke:#a85454,color:#ffd6d6 classDef f fill:#2a1f47,stroke:#7a5cc4,color:#eadcff classDef a fill:#0d3a4a,stroke:#2f8fb0,color:#d6f4ff classDef g fill:#0b3d2e,stroke:#1f8a5f,color:#d6ffe9
The frames that were already running have made the same move: the sandbox pair and the pilot in the evening, Brussels and Singapore after lantern went live, and every ledger balanced throughout. A frame born from the template never has to make that move at all, which is most of the argument for being born with the right shape rather than acquiring it.
Handed over once
2 frames - scroll sideways
When the last step reports, the frame's row shows its addresses and one new action: Admin credential.
The credential was generated during the standup and written into a directory only the runner can read. Pressing the button reads it, deletes the runner's copy, records the moment and returns it to one browser, in a response that forbids caching. The dialog offers a copy button beside each value and one way out, labelled I stored it, after which the page forgets what it showed. The frame keeps what it needs in its own environment on the host. What is gone is the copy anyone could ask for.
Afterwards the button is simply gone from the frame's row. Ask the rail directly and the answer is a refusal that names the moment of the hand-over. The first frame built from this page answered its second request, two days ago, with exactly that: handed over at 2026-09-13T09:26:19Z. A secret you can ask for twice has a copy somewhere, and the second request is how you find out where.
A frame you can take back
A provisioning page that can only create is half a tool, and the more dangerous half.
Orchard was the first frame born with the kit, stood up through this same runner on the fourteenth. Tonight it went the other way, from this page. Tear down opens only when the frame's name is typed in full; an empty field and a partial name leave it grey. The runner tears down only a frame whose own run record names this runner as its builder, and never a protected name, and when the frame is gone it records what else it removed: the DNS records it had marked as its own, the certificate, and the copies of secrets in its own job directories. Tonight the job itself took forty-one seconds, and the cleanup followed within three: four names, one certificate and two secret files.
6 frames - scroll sideways
The first time a frame was removed this way, I compared the host against a snapshot taken before that frame existed. Fifty-eight things had changed while it was alive, every one of them the frame's. After the teardown, one had: the backup copy that the script which edits the TLS routing table keeps of its own file before editing it. I have decided that one is allowed to stay.
A frame that cannot be removed cleanly was never provisioned. It was installed.
Nobody in the middle, including me
The last two posts argued 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 carry an awkward footnote, and this page is about the footnote. As long as the only way to get a frame is to ask me, and I stand it up by hand over an evening, I am the middle. A polite one, with good documentation, but the middle.
A form does not fix that on its own, and I want to be careful not to claim that it does. What it does is move the work out of my hands and into something that can be read, repeated and eventually handed over: a closed specification, a plan that says what it will do, steps that report, gates that refuse, and a teardown that leaves the host the way it found it. Joining the federation still takes a licence, and at the start the licences come from us. That is a stage of this network, not its design. I am also one person, and a network that needs its founder's evenings for every new jurisdiction grows exactly as fast as one person's evenings, which my sleep schedule informs me is not a scaling strategy.
Opening the code is the other half of leaving the middle. A network whose frames can only be built by one person's host is still one person's network, however good the form is.
Where I left off
A frame from this page is a pilot, and the page says so. It skips the rate limiter, because a pilot is somewhere our own test sweeps are allowed to run. It sends no mail, because it holds none of the host's mail credentials. It has no cloud operators, and it federates with nobody until an administrator on each side decides that it should.
It runs on one host, ours, and every frame on this network was stood up by us. Nothing in this post can be pressed by anyone outside that today: there is no download, no hosted version and no sign-up. There is a public form for asking for a frame, and asking is all it does. The runner stands up the pilot shape whatever the request says.
What is still open, written down for whoever picks this up next, myself included:
- A plan asks public DNS about a frame's names before they exist, and some public resolvers remember the empty answer for half an hour.
- A treasury piece whose reply is lost on the way back can be minted again by a resume: at most one piece over, still at delta zero.
- The page shows that a teardown finished, but not what it cleaned up, and it keeps a removed frame listed as live until somebody presses Refresh.
- There is no field yet for choosing the ledger, auditor or Directory image, so every frame takes the first one the host allows.
- The console's notices, refusals included, do not reach the page yet: the component that draws them has never been introduced to the store that sends them.
Each of them is written down with the reason it is still open.
Handing it over
Everything in this post is in the code base I am about to open: the runner, the driver, the template and the contract that pins it, the kit, the gates, and the tests that hold what the runs taught them. It is not a tidied demo, and it is not a subset with the interesting parts filed off.
A project that lives in one place can be made to disappear from one place. A project that has been handed over cannot.
That is what the pieces in this post are for. A specification instead of a memory, steps that can be picked up by name, gates that refuse in sentences, a teardown that leaves the host as it found it, and a written list of where I stopped are what make a handover worth receiving.
I intend to keep working on it, and opening it changes nothing about that. What it changes is that the work stops depending on one person's evenings. When it is out, I will read and merge improvements and bug fixes from anyone who cares to send them, including the ones that make me wince.
The frame that did tonight's provisioning was brought up to the current version first. The frame it built balances, 1,000,001 LNT issued and all of it accounted for at delta zero, and the frame it removed took its four names, its certificate and its secrets with it. The auditor that checks every ledger in this post still does not work for us.
Skepticism remains the local currency. It is accepted on exactly as many frames as it was this morning: one was born tonight and one was retired, which may be the most balanced thing I have written all week.
Patchnotes
The last post left the fleet at V0342. What follows is V0343 through V0372, four days, supply delta zero throughout on every frame the auditor watches. A line that does not say where it runs is live on EU and ASIA.
Frames
- Frames are stood up and torn down from Frame Management on the Brussels frame, through a service on the host that accepts twelve operations and refuses the rest.
- The runner creates a frame's four DNS names, waits until they answer, and on teardown removes only the names it created.
- New frames are born with their kit: a policy engine in shadow, Prometheus, Alertmanager, Grafana, a solo auditor on a read-only role, and a Directory of their own behind mutual TLS.
- New frames are born with the agent that routes cross-currency transfers, and switch the route engine on only once that agent exists.
- A new frame's standup proves its ledger bus is private before it goes on: an anonymous client, a plaintext client and a wrong password are each refused, by name.
- A treasury larger than a frame's mint ceiling is minted in pieces. The first frame to ask for more than the ceiling got one refused call, one log line and an empty treasury.
- A frame without mail credentials boots cleanly, and can send its sign-up mail through a parent frame's relay.
- The public form for requesting a frame was rebuilt: six steps, partner frames read live, a map for the location, a live preview of the registry card, English and German, and a first stage called Pilot.
- A stored request sends the requester one receipt with fixed wording, and the welcome link names the console they actually used.
- Standing up a federated pair of frames is one command on our host, with the runbook turned into 78 automated checks.
- The operator command line backs up and restores a whole frame, identity and signing volumes included, against a checksummed manifest, proven on a throwaway frame.
Identity and security
- Every event store now trusts a client certificate only when its own gateway verified it: a forwarded certificate counts only beside a marker that gateway alone adds, and the licence fallback is refused, on EU, ASIA and the sandbox pair.
- The federated Directory answers only its own registries, checked by identity, and presents a certificate of its own when it crawls.
- A frame's registry and token ledger write to its event store through the frame's own mutual TLS gateway, on EU, ASIA and the sandbox pair.
- Registries mask the credentials in the addresses they log when they connect to their own ledger.
- Cloud operators no longer hold their parent frame's ledger key. They write by identity, and the provisioner no longer writes one for a new operator.
- The API tester's key has no built-in default, and optional logins refuse revoked, suspended, deleted and refresh tokens, on every running registry.
- A frame's identity trust bundle on the host refreshes on its own, instead of quietly rotting at the next certificate authority rotation.
- Restarting a registry no longer logs out the console that is watching it, on EU, ASIA and the sandbox pair.
Ledger and money
- A registry that cannot reach its event store keeps the write in its own database and delivers it when the store is back. Proven by stopping a store's gateway for eighteen seconds: one write deferred, none lost.
- A ledger write that fails after money has moved is logged, counted and reported instead of swallowed.
- The registry and the token ledger no longer write to the message bus at all. The event store is its only client, over SCRAM on TLS and a network of two, and it signs every record it puts there and drops any record it did not sign. Live on EU, ASIA, the sandbox pair and every new frame.
- The cross-frame refund job waits when it cannot read the ledger, instead of refunding a transfer it could not see settle.
- Discovery cards carry a bond maturity bar, and it travels to peer frames. A matured bond will let its owner unlock a declared share, behind a relative gate of at least three independent raters at or above the median. Nothing on the network is old enough to mature yet.
- Cards show the bond actually charged and what set it, and list cards carry the bond chip, on every running registry.
- The treasury alarm counts only the days stakers were owed and the reward pool really was empty, not every quiet day, on every running registry.
- The token ledger no longer loses reputation updates that arrive at the same moment, the four cloud operators and the sandbox pair included. Measured before the fix: 9 of 764 concurrent signals lost, every one of them answering 200 applied.
Federation and trust
- First contact with a peer records a compliance baseline, the card and the policy endpoints agree on one hash, and agents calling the API receive the trust verdict too. Enforcement is proven on the sandbox pair; EU and ASIA still only observe.
- The Senate no longer invents a quorum, and a vote that would move a rule an operator setting outranks is refused, on every running registry.
- Cross-frame federation proposals work again. They had answered 500 whenever the voters held any vote power, which is to say whenever there was anybody to vote.
- Every night, on the sandbox pair, the tester proves that a suspension reaches a peer's mirror within one sync and lifts again, that an unlisting travels and relists, and that a policy change is read by the peer.
- The status page is built from the frame itself, one row per component, with peer frames grouped as frames. The flagship's page had been showing zero of ninety days.
- A fresh operator no longer lists itself among its own peers.
Discovery and the front door
- Discovery and the agent page speak German, and the translation gate now checks every key those pages ask for, in every bundle.
- On a frame that is not the flagship, the landing page uses that frame's own addresses and no longer presents the flagship's agent roster as its own. Live on ASIA and the sandbox pair.
- The price ticker says why a feed is empty or unreachable instead of quietly disappearing.
- Frame Management speaks in pilot terms and shows a standup step by step.
- Frame Management reads each frame's ledger mTLS posture from the store itself, on the Brussels frame.
Testing and instrumentation
- The MCP tester runs every night at 03:00 UTC as two jobs from the Brussels console, the lifecycle sweep and the federation suite, and keeps thirty days of history.
- A test whose precondition was not met now reads
NOT MEASUREDinstead of red, and the reds that are red by design are listed apart from the ones that are news. - Every frame is scraped from a target file of its own and ships with its alert rules and a dashboard, and the auditor takes a frame on or off without a restart.
Build and release
- A release export built from commits, not from a working tree, was built again from a clean clone with nothing but Docker and ran a two-frame quick start at delta zero. It lives on a private branch. Nothing is published yet.
- CI now runs the registry test suite for real. Until this week it had been collecting zero tests from an empty directory.
- Every script in both repositories is now tracked as executable. A fresh clone had been taking the old plain-file mode literally.