Introducing Myelin: a CKB-aligned off-chain Cell session runtime

I hope everyone’s had a great time with the hackathon and am really looking forward to seeing what fellow builders have come up with. Today, I’m sharing a brand new project that I’m likely to be throwing myself into over the coming months.

Myelin in one paragraph

Myelin is an off-chain Cell session runtime initially inspired by xuejie’s earlier post. It runs high-throughput, finite state transitions outside CKB and keeps every transition projectable back to a CKB-style transaction. It schedules independent chunks in parallel, wraps a batch of verified chunks into a finalised session block, and emits a self-contained court bundle (a packaged evidence file a future on-chain verifier can consume to adjudicate a single disputed chunk) for any disputed chunk. The idea is that a future on-chain verifier can adjudicate a single chunk without re-running the whole session. The closed-validator fast path ships today as a prototype; the permissionless path remains an item in the roadmap.

Myelin is not a new L1, and neither a finished permissionless L2, but rather e a protocol seed: the execution, state, evidence, and session-finality pieces needed to test the shape of an off-chain Cell ledger.

The problem

CKB-VM is powerful enough to run real, complex logic. A single chunk of a real-time game, a metering window, or a settlement batch can execute inside the VM and verify correctly. What it cannot do on its own, is to answer the questions that turn “this chunk executed correctly” into “this session is a finalised, contestable state transition with a path back to L1.” A layer above the chunk has to handle five things:

  • Scheduling. How do many chunks in a batch execute together, and which
    can run in parallel?
  • Finality. When is a batch committed, and by whom?
  • Projection. Can each chunk be mapped to a CKB-style transaction?
  • Dispute. If a chunk is wrong, what does a court re-run, and what is
    the input shape?
  • Data availability. Where does the evidence live?

I built Myelin to prototype parts of that layer.

The isomorphism principle: why I leave the VM alone

CKB uses the Cell Model, and Myelin follows it to the letter. A transaction consumes live Cells and creates new Cells. State changes happen through Cell replacement. Cells carry data, a lock script, and an optional type script. Scripts run in CKB-VM. Myelin does not hide session state inside an account-style contract, and it does not modify CKB-VM. The VM is a fixed oracle.

Myelin runs the same CKB-VM, with the same RISC-V ISA, the same script semantics, and the same Molecule transaction serialisationas L1. That means a Myelin CellTx is structurally projectable into a CKB transaction. The projection layer asks a simple question: can this CellTx be serialised with the CKB Molecule transaction layout, and do its script and witness assumptions match a CKB-strict profile? When the answer is yes, the chunk is ckb_compatible. A future on-chain court verifier can re-run the same bytes through the same VM and reach the same verdict.

Therefore every host-side optimisation I add (a cache, a parallel scheduler, a different finality engine) is transparent. It changes how Myelin reaches an answer but does not change what the answer is. The VM result is the ground truth both off-chain and on-chain.

If I changed the VM (added an opcode, relaxed a cycle limit, swapped the ISA), that isomorphism would break. A chunk that verifies under a modified VM might fail under CKB’s, and the projection path would be worthless.

Because it follows the Cell Model directly, a Myelin session can always report five things about any transition:

  • what Cells were consumed or created,
  • which lock and type-script-like rules were checked,
  • which VM and profile assumptions were used,
  • whether the transition can be projected into a CKB-style context,
  • and which evidence would be relevant during a dispute.

How Myelin works

The pipeline below runs from source to evidence. CellScript is the DSL that compiles Cell-model programs (actions, resources, flows) into typed-cell metadata and a RISC-V VM artefact. The CellDAG is Myelin’s dependency scheduler: it figures out which CellTxs in a batch can run in parallel and which must be ordered.

Every box is a real crate in the workspace: cellscript, myelin-exec, myelin-state, myelin-mempool, myelin-consensus, myelin-cli. Support crates live under core-utils/, crypto/, and math/.

What is in the repository

Path Role
exec/ Cell transactions, script verification, VM and syscall glue, scheduler witnesses, and CellDAG conflict scheduling.
state/ Live Cell state roots (incremental MuHash, a multiplicative hash accumulator that lets the root update in O(1) per cell change) and data-availability proof primitives.
mempool/ Cell transaction pool and deterministic conflict scoring.
consensus/ Static closed-committee and Tendermint-shaped weighted-precommit certificate verification with deterministic fixture signatures; not a complete Tendermint state machine.
cli/ Command-line fixtures and report generation for CellTx, session, DA, settlement, and submission flows.
cellscript/ CellScript compiler, vendored in sync with upstream (0.21.1). The typed-cell model lives in exec/, separate from the compiler.
docs/ and MYELIN_*.md Architecture notes, evidence reports, positioning, and rehearsal records.
website/ Marketing and docs landing site (Astro).

What I built

1. The typed-cell model

A Cell in CKB carries capacity, data, a lock script, and an optional type script. The type script gives a Cell its kind: token cell, order cell, game-state cell. CKB verifies type scripts on chain, but the runtime has no structured way to reason about typed-cell identity, conflict dimensions, ownership, or mutability before the VM runs. I added that layer.

The typed-cell model is a runtime-side type system. For each type script, a TypedCellDecl declares its ownership (one-of-a-kind vs fungible), its mutability, and its conflict key (ConflictKeySpec: by cell id, by field, by composite key, or none). The conflict key is the crucial bit. It lets the scheduler decide whether two transactions touch the same state and must be ordered, or touch disjoint state and can run in parallel.

The typed-cell runtime types (TypedCellDecl, ConflictKeySpec, TypedCellStore, CellScriptSchedulerWitness, CellScriptSchedulerAccessWitness, compute_conflict_hash) all live in Myelin’s own exec crate (exec/src/celltx/types.rs). A witness bridge decodes the compiler’s generic witness at runtime and recomputes Myelin’s stronger conflict_hash and typed_data_hash from the transaction’s concrete cells. The compiler cannot emit these directly because it does not know the deployed type-script identity at compile time. Only the runtime does.

2. Inter-transaction conflict scheduling (CellDAG)

Given typed cells with conflict keys, the CellDAG builds a read/write dependency graph over the transactions in a session batch and schedules independent transactions across topological layers, parallelised with Rayon (a Rust data-parallelism library). Two transactions that read the same conflict domain stay in the same layer (parallel). A read/write or write/write pair on the same domain creates a dependency edge (serial).

The conflict edges come from the typed-cell model, so the scheduler understands semantic conflicts (two transactions touching the same logical resource), which is a good deal more useful than only seeing structural OutPoint-level chaining.

3. Closed-validator finality with dual engines

A batch of verified chunks is wrapped into a MyelinBlock and checked under a pluggable committee. Today that is a static closed committee plus a stripped, Tendermint-shaped weighted-precommit certificate verifier. Both use deterministic BLAKE3-derived fixture signatures.
(This is session-finality scaffolding for deterministic demos, not a complete BFT protocol or a permissionless consensus claim.)

4. CKB-style projection and the court bundle

For each chunk, Myelin emits a projection report: is this chunk projectable into a CKB-style transaction? For a disputed chunk, it emits a self-contained court bundle that packages the witness layout, the molecule transaction, the chunk data, and the committee finality evidence. The bundle passes 22 verification checks today. This is the input shape a future on-chain court verifier would consume. The court script itself is not deployed yet, but the bundle is real, the shape is fixed, and the path is documented end to end.

5. Data-availability evidence path

Myelin emits a DA manifest over sealed segments (Merkle-rooted, parallel leaf hashing), with a replicated-committee availability layer and a hook for an external DA receipt. The DA path is local-only today (no external provider), but the commitment shape is there.

Optimisations: what each one buys, and why none of them touch the VM

Every optimisation below lives on the host side. The VM is never modified. This is the isomorphism contract I described above, and each item states the effect it achieves.

Already shipping

Optimisation What it achieves Why it preserves isomorphism
CellDAG parallel inter-tx verification (exec/src/scheduler/executor.rs) Independent transactions in a batch verify concurrently across Rayon topological layers. A session of N independent chunks finishes in O(depth) sequential verification rounds instead of O(N). Scheduling changes when each transaction is verified, never whether it passes. Every transaction still runs through the unmodified CKB-VM verifier. The parallel layers just run disjoint transactions at the same time, and the union of results is identical to serial.
Within-tx script-group parallelism (exec/src/vm/verifier.rs) The lock and type script groups within one transaction are verified in parallel (script_groups.par_iter()). A transaction with K script groups finishes in O(1) rounds instead of O(K). Same VM, same scripts, same cycle accounting. Only the order of independent group evaluations changes. Cycle totals are summed deterministically.
Incremental MuHash state root (state/src/cell_tree.rs) Each cell insert or remove updates the session state root in O(1) (a single 384-byte modular operation) instead of re-hashing the entire cell set. A session that commits thousands of cells pays no O(n) root cost per commit. MuHash is an associative, order-independent accumulator. The root is identical whether computed incrementally or from scratch. The VM never sees the root computation; it is host-side accounting.
Parallel DA Merkle leaf hashing (state/src/store/proof.rs) Sealing a 1 GB DA segment hashes its leaves in parallel (par_chunks(2) per Merkle level) instead of serially. Seal latency drops near-linearly with core count. Merkle hashing is a pure, deterministic function. Parallelising it yields a byte-identical root that the on-chain court would verify from the same leaves.
Segment-reader lock release (state/src/store/segment.rs) The DA segment reader releases its file-handle cache lock before doing disk I/O (it clones the handle under the lock and reads outside it). One slow disk read no longer serialises all concurrent readers. Pure host-side I/O scheduling. The bytes read are identical; only the locking discipline changes.
Serialization cache (exec/src/serialization/cache.rs) A thread-safe LRU caches serialised bytes of versioned values, avoiding redundant re-serialisation of the same structures across a session. The cached bytes are the exact Molecule encoding the VM and chain would see. Caching avoids recomputing an identical byte string.

Planned

Optimisation What it will achieve Status
Mempool batch admission Admit a batch of transactions under one write lock, with conflict keys computed in parallel. Turns N serial O(pool) scans into one parallel pass plus one critical section, and closes a size-check race. Planned (M)
Sighash reused-values cache Fill the NoCache placeholder for CKB’s StandardSigHashReusedValues, caching repeated sighash sub-computations across inputs of the same transaction. Planned (M)
Content-addressable VM-result cache Cache (script_code_hash, args, inputs_hash) to (cycles, exit_code) so re-verification of unchanged script groups (court replays, re-runs) is a lookup instead of a full VM run. This is the biggest throughput win for dispute workloads. Planned (L)
Parallel consensus signature verification Once real secp256k1/BLS replaces the current deterministic-blake3 stubs, verify committee precommit signatures in parallel. Planned (waits on real crypto)

None of the above changes a single VM instruction, cycle budget, or serialisation rule. The off-chain path can be made as fast as host hardware allows, without diverging from what the chain would verify. The full list lives in the concurrency and optimisation plan.

Acknowledgements

Myelin’s flagship reference workload uses Xuejie Xiao’s Teeworlds-on-CKB replayer unchanged. The replayer, fixture tooling and in-VM game optimisations are his work; Myelin uses them as a demanding public CKB-VM workload for testing execution, projection and dispute evidence.

His writings on Teeworlds, One Hour One Life and Archipelagos were also useful prior references when surveying persistent and partitioned game workloads on CKB.

Myelin’s session runtime, typed-cell model, conflict scheduler, state commitments, finality machinery, witness bridge and court-bundle design were developed independently.

From a single chunk to a session

Xuejie’s Teeworlds design treats one game session as a chunk validated by one CKB transaction; the OHOL follow-up uses one minute of a persistent world as a chunk linked by world-state hashes.

Myelin asks a different question: how can several Myelin CellTx values be scheduled and committed inside an off-chain session while retaining a CKB-shaped projection and an explicit evidence trail? That is a prototype extension around the chunk model, not a stronger proof than direct CKB execution.

The model gains seven dimensions the single-chunk demonstration did not carry:

Dimension Single-chunk model Myelin session model
Inter-chunk scheduling None. Each chunk is verified in isolation. CellDAG builds a typed read/write dependency graph and schedules independent chunks across parallel topological layers.
State continuity A world-state hash is threaded manually across chunks. An incremental MuHash accumulator (CellStateTree) maintains the session state root in O(1) per cell transition. Every commit produces a state_root_before to state_root_after pair.
Conflict reasoning binding_hash: a hash of the source-level variable name, carrying no type-script identity. ConflictKeySpec and compute_conflict_hash bind the type-script identity into the conflict domain. Two transactions conflict iff they touch the same typed resource.
Projection to L1 Execution is the endpoint. No notion of “can this land on CKB?” A projection report tests whether each chunk serialises to a valid CKB Molecule transaction under CkbStrict semantics, producing a ckb_compatible verdict per chunk.
Dispute shape The chunk-in-one-tx philosophy (no bisection). Adopted unchanged, then wrapped in a self-contained court bundle (22 verification checks) that packages the witness layout, molecule transaction, chunk data, and finality evidence a future on-chain verifier would consume.
Finality One-shot execution. No notion of a committed block. A batch of verified chunks is finalised into a MyelinBlock under a pluggable committee (static closed committee plus Tendermint precommit, domain-separated).
Data availability None. Sealed Merkle segments with a replicated-committee availability layer and a hook for an external DA receipt.

A future court still re-runs a single disputed chunk through the same CKB-VM, under the same single-chunk replay model. What changes is that the chunk now exists inside a session that has been scheduled, state-rooted, projected, finalised, and made data-available. That is the layer he deferred, and it is the layer Myelin provides.

The reference workload

The flagship workload runs a real CKB-VM binary through Myelin’s verifier end to end. Xuejie’s Teeworlds on CKB replayer is a RISC-V ELF that runs a full multiplayer game tick loop. Myelin runs it through its own CKB-strict verifier, chunks the game tape, projects each chunk to CKB, and produces a court bundle. The measured run is fully reproducible from the runbook: tape_bytes: 2162, vm_cycles: 15,139,695, court_checks: 22.

Demos

Two runnable demos, from zero-dependency to the full reference workload:

Demo What it shows Needs
1 First run CellTx to session open to commit to court bundle to DA manifest, all local Rust only
2 Teeworlds end-to-end (flagship) xuejie’s CKB-VM replayer through Myelin’s verifier, chunked, projected to CKB, court bundle (22 checks) teeworlds fork plus built replayer

To see the CellDAG and parallel VM verification path in action, run session commit-multi after the first-run demo (see the concurrency plan).

What ships today

  • The full off-chain runtime spine: CellTx, CellDAG with parallel VM
    verification, incremental MuHash state root, mempool, dual-engine finality.
  • The typed-cell model and the witness bridge: real compiler metadata drives
    typed conflict edges in the CellDAG.
  • The reference workload running end to end (numbers above).
  • A zero-dependency session demo
    (first run).
  • A published concurrency and optimisation plan
    documenting every host-side optimisation and its effect.

Roadmap

Two near-term directions, both internal to Myelin’s architecture:

  • Per-participant state commitment. Today a session commit rolls the full
    session state root. For workloads with many independent participants (a game with many players, an IoT batch with many gateways, a sharded order book), that is heavier than necessary. Each participant’s state could be committed and scheduled independently via its own typed conflict domain, so the CellDAG runs participant-local transitions in parallel without re-committing the whole world. The CellTx shape and ConflictKeySpec model already support scoping a conflict key to a single participant. The work is to make that a first-class, validated commit path. The generality of this problem was surfaced concretely by persistent-world workloads such as OHOL port.

  • Session-level composition evidence. When a session spans multiple typed
    domains (different type scripts with different logic and conflict rules), the interesting unit of audit is the set of domains the session touched and the transitions between them, rather than a single chunk. The typed-cell model already models each domain as a TypedCellDecl and schedules cross-domain transactions by conflict hash. The work is to aggregate that into a session-level manifest a court or auditor can read as a unit, at the same evidence tier as the single-chunk court bundle. The value of explicit domain-composition evidence was reinforced by partitioned-world designs such as Archipelagos post, but the manifest design and its court-visible shape are my own.

Both build on substrate Myelin already ships. Neither requires changing CKB-VM, the CellTx shape, or the typed-cell model.

What Myelin does not claim

This is a positioning statement, no production-readiness claim.

  • Closed-validator finality only. The committee is static and known.
    Permissionless validator entry is out of scope today. Static committee finality must never be marketed as permissionless L2 security.
  • No on-chain court yet. The court bundle is the input shape for a future
    on-chain verifier. The verifier itself is not deployed (l1_court_implemented: false). Myelin sits at Tier 2 of the claim ladder (executable disputed-chunk input shape), short of Tier 3 (exercised court).
  • No mainnet, no external DA, no custody. All evidence today is
    fixture-backed or local-devnet-backed.
  • The typed-cell model is not in upstream CellScript. It is a Myelin
    runtime-side development. Whether it gets proposed back upstream as a compiler emission target is a separate, open decision.

Quick Start

Prerequisites: a Rust toolchain (1.85+), Python 3, and optionally Node.js/npm for the website.

# verify the workspace builds and tests pass
cargo check --locked --workspace --all-targets
cargo test --locked --workspace
cargo clippy --locked --workspace --all-targets -- -D warnings

# generate a simple CellTx report
cargo run -p myelin-cli -- celltx simple-report

# open a deterministic session, commit a chunk, build + verify a court bundle
cargo run -p myelin-cli -- session open-fixture --consensus static-closed-committee --out /tmp/open.json
cargo run -p myelin-cli -- session commit-fixture --session /tmp/open.json --out /tmp/commit.json
cargo run -p myelin-cli -- session court-bundle --commit /tmp/commit.json --chunk-index 0 --out /tmp/court.json
cargo run -p myelin-cli -- session verify-court-bundle --bundle /tmp/court.json --out /tmp/court-verify.json

The full local production gate (broad; includes the Teeworlds acceptance gate when the teeworlds checkout is present):

scripts/myelin_production_gate.sh

For the flagship workload, the teeworlds end-to-end runbook builds the RISC-V replayer and runs it through Myelin end to end.

16 Likes

I really like the direction of this !

It feels very aligned with CKB’s philosophy of keeping computation off-chain and using the chain as the final source of truth.

If this makes working with Cells more intuitive for developers, it could remove a lot of friction.

I’m especially curious to see how session synchronization is handled.

Great work.

2 Likes

Hey @Ophiuchus, appreciate you engaging with it.

For now Myelin does not try to solve P2P relay or mempool gossip. A validator receives a candidate batch, recomputes the CellDAG order, runs the chunks under the same CKB-VM profile, and signs only the resulting MyelinBlock::hash() .

So the shared view is the block hash covering the ordered chunks, scheduler commitment, DA commitments, and state_root_before/after . If validators disagree, the hash differs and no quorum forms. If a finalised result is later challenged, the court path replays the disputed chunk, not the whole session.

1 Like

Oh yes I get it now. Focusing on the final hash and only replaying the disputed chunk sounds much more efficient than what I had in mind.

Thanks for the explanation.

1 Like

Hey @ArthurZhang,

thank you for building on top of @xxuejie original work, I find all this fascinating and it showcases the real strengths of CKB.

Feels like quite a few protocols had to reinvent this very piece of logic, thinking for example about WarSpore: [DIS] WarSpore · Saga — A Fully On-Chain Card Game for CKB & BTC

I was wondering, in your opinion how does this approach compare with a ZK-based one?

After GameFi, any other field in particular where you feel like this kind of tech could be an enabler compared to naive CKB and a ZK one?

Keep up the Great Work,
Phroi

1 Like

I’m sorry, you can treat this as a rant, but I want to clearify one thing:

I’m experimenting infrastructure for games, games only, nothing but games. I want to go back old school where blockchains are just databases.

I actually thought hard on this (someone I deeply respect actually suggested me to think about economic models), and my decision is: I’m neither building GameFi, nor do I want to be in anyway affilicated to any GameFi related projects. I don’t want anything to do with GameFi. My design uses blockchains as databases, period.

Thanks for the understanding.

2 Likes

I think a distinction is worth preserving here.

An off-chain game whose execution is verified on-chain does not, merely by virtue of that architecture, become GameFi. GameFi is an application-level economic category, not a property of the protocol beneath it.

As a protocol builder, I would also hesitate to prescribe the eventual uses of general-purpose infrastructure, those decisions belong to people building applications upon it.

In any event, I am quite happy to respect the boundary you wish to draw.

@phroi, the main distinction from ZK, as I see it, is that Myelin preserves native CKB semantics rather than proving a circuit representation of them. A disputed transition remains a CKB-shaped chunk executed by CKB-VM, which makes integration with existing programmes comparatively direct.

That said, I do think ZK could work well with Myelin. A plausible longer-term architecture would use Myelin for session execution and conflict scheduling, while adding ZK selectively for proof aggregation, evidence compression, or workloads that are too large to replay directly.

Outside gaming, one natural field might be DeSci, specifically reproducible computational workflows and milestone settlement, not simply ‘putting papers on-chain’. I have brainstormed a bit ealier, maybe even something like FlyWire-derived fruit-fly brain computation could be modelled as an off-chain session DAG, where large computation happens outside L1 and CKB only judges bounded chunks, receipts, and commitments. The FlyWire connectome

The same pattern could also fit agent tournaments, scientific simulations, CRO/research milestones, IoT telemetry batches, or RFQ/netting sessions: anywhere the workload is high-frequency, off-chain and finite.

1 Like