Updates

6 October 2026 — node v0.23.0.

This is an archived entry. See the latest update.

Earlier updates: 2 October 2026 — node v0.22.1 · 2 October 2026 — node v0.22.0

6 October 2026 — node v0.23.0

A node-only release. @mcsherrylabs/evolver-client stays at 0.17.0 and pi-evolver at 0.8.2 — unchanged, so unlike the last release this one asks nothing of your application. The first item is the one that matters.

The committee could not actually grow
A node that is not in the genesis file can be admitted to the committee by the quorum ledger. Until now it could never take its seat, and the way it failed was the uncomfortable kind: the admission commits, the validator list shows the new weights, every component reports success — and then the chain stops at the round the new member was supposed to start voting in.

Two things met. Consensus is point-to-point: a vote is sent to one node and nothing relays it. And the rule deciding who a node may dial asked whether the peer was in genesis — written when genesis and committee were the same set, and frozen at boot besides. They stopped being the same set the day the quorum ledger could admit somebody new. The admitted node could not be dialled, and with allowed-inbound = 0 — the deliberate posture on our own front nodes — it could not dial in either.

Permission, reservation and dial order now come from one published snapshot of the scheduled committee, refreshed by the component that already knows the committee changed. Growing and shrinking a live committee is now exercised end to end on every run: 1000 → 500/500 → 300/300/400 and back, by admitting nodes that claim their own identities and reach the chain only through a relay.
A node admitted after genesis voted while its own state lagged
The same genesis-shaped assumption sat at a second place: an admitted node kept the start-up mode meant for nodes that only follow, which skips applying blocks to its own state. It voted on rounds whose results it had not yet worked out for itself. The guard now reads the chain's committee rather than the genesis file.
A node's view of the committee is now the committee of a round
Certificates are signed by a bitmask over the committee, so verifying one means knowing the committee as it was at that round, not as it is now. At start-up a node read the committee once and checked old certificates against it, which is correct only while membership never changes. It now resolves the committee per round, bounded by how far its own state has been rebuilt.
An inbound budget of zero is legal at any committee size
The requirement that committee members form a complete mesh used to be expressed against the inbound budget, which is not a budget a node can aim: it constrains who may arrive, not who you reach. It is now expressed against the outbound budget, and checked again at runtime rather than only at boot — a committee that grows after start-up walks straight past a one-off check. The practical effect is that a node can refuse all unsolicited inbound connections and still be a full committee member.
Devnet is running v0.23.0
Upgraded in place rather than rebuilt: the chain was kept and carried straight on committing across the restart. The explorer and the fleet monitor moved with it, and the monitor still crawls the whole fleet.
The test suite stopped sharing a machine with the things it tests
Less visible, worth saying. The end-to-end suite binds 224 fixed ports. Run on a working machine, anything already holding one breaks the run — and because the probe and the listener shared a network namespace, the port would be reported free while a connection to it succeeded, so it got misdiagnosed rather than found. The suite now runs in its own network namespace, from its own clone, and the move immediately exposed two faults that had been invisible: a date command that silently returned garbage for sub-second timings, and a permission test that could not fail because it was running as root. Both are fixed. All 32 stages pass.