ADR-0019: The program set is part of the state transition and is not committed to the chain

A Solieum chain executes the programs an operator names at startup:

Status: proposed (Nothing here is built). Dated 2026-09-10.

Status: proposed, 2026-09-10. Nothing here is built.

The defect

A Solieum chain executes the programs an operator names at startup:

The node's own help already states the consequence: "The set is part of the state transition: a datadir replays only under the same set." That is correct and it is not enough, because nothing in what the chain publishes says what the set was.

Checked rather than assumed, on 2026-09-10:

  • getAccountInfo on a registered program id returns nothing. The program is not an account. There is no program-data account, no deploy transaction, no bytecode on the chain.
  • genesis.json records the L1 program ids — settlement, DA, inbox, portal — and no L2 program set. The genesis hash therefore does not cover it.
  • Nothing writes the set into state, so no state root covers it either.

So an independent verifier re-deriving a chain from its published bytes cannot learn which programs were in play. It must be told, out of band, by the operator whose work it is checking. That is the one relationship the whole design exists to avoid.

Why this matters more than it looks

The beta chain registers no programs, so this bites nothing today. It bites the first chain that registers one, and it bites in three ways.

Re-derivation stops being self-contained. solieum-node verify rebuilds every block from Solana's published bytes and compares roots. Against a chain with programs, the same bytes produce different roots under a different set, and the verifier has no way to detect that it was given the wrong one. It would report a divergence it cannot explain, or agree for the wrong reason.

A fraud proof cannot name what it is disputing. Steps through registered programs are already opaque to the dispute game, which is a known limit. But an opaque step at least says this step used a program the verifier cannot execute. It does not say which, because the chain does not know.

The explorer can only publish a claim. This is what prompted the question. An EVM explorer shows verified source because the bytecode is on chain: recompile, compare, display. Here there is nothing on chain to compare against, so the strongest honest thing an explorer can publish is the node's own statement about a file on the operator's disk. That is what it now publishes, with the caveat attached (getPrograms carries bytes and sha256, and the programs page says plainly that these are not a proof).

What is proposed

Commit the set to chain identity. Genesis gains a programs array of { id, sha256 }, covered by the genesis hash exactly as the L1 ids already are. A node started with a set that does not match its genesis refuses to start, in the same way and for the same reason it refuses a genesis whose L1 ids disagree with the cluster.

That much is small, and it turns "the operator told me" into "the chain says so". It costs a chain restart to change the set, which is correct: changing the set is changing the state transition.

Then, separately and later, a deploy path. Programs as on-chain accounts written by a loader instruction, as on Solana. That is a much larger change and it should not be bundled: it introduces program accounts into the state tree, makes the program set mutable within a chain's life, and interacts with the challenge window in ways nothing here has thought through. The commitment above is worth having on its own and does not depend on it.

What this does NOT fix

Committing the hash makes the set checkable. It does not make a program provable. Executing arbitrary compiled code inside a one-step verifier is the SBF interpreter class that notes/readiness-2026-09-03.md lists as the remaining protocol gap, and this ADR does not touch it. After this change a verifier can say "the chain committed to this program and this is the program that ran". It still cannot say "and this instruction's result was correct".

Nor does it make published source trustworthy. A hash plus a reproducible build is what makes source checkable, and this project already builds that way — the pinned backpackapp/build image, and the byte-identical clean-worktree rebuilds recorded in DEVNET.md. The missing piece is only that the hash has nowhere on the chain to live.

Sequence

  1. Genesis carries programs: [{ id, sha256 }], inside the genesis hash.
  2. The node refuses to start when its --program set does not match.
  3. verify reads the set from genesis and loads the same programs, so re-derivation is self-contained again.
  4. The explorer's caveat shrinks to what remains true: the source is the operator's claim, the binary is the chain's.
  5. Deploy path, if ever: its own ADR.

Steps 1 to 3 are the ADR. Step 4 is a paragraph. Step 5 is a project.