CellFabric: A UTXO-Generation and Inter-Protocol Composition Layer for CKB

Note on evolution: The framing of this proposal shifted significantly through the discussion. @janx pushed me to clarify the categorisation — this is not a transaction-DAG L2 or a new ledger, but a UTXO-generation surface. @phroi’s questions about inter-protocol composition helped me separate CellScript’s single-protocol focus from CellFabric’s cross-protocol coordination role. The current structure reflects those clarifications.


One conclusion i currently draw from this discussion is that CellFabric should not be framed as a transaction-DAG L2 or a new ledger. The better category is narrower:

CellFabric should be explored as a UTXO-generation and inter-protocol composition surface for CKB.

In particular, I now see two related but separate problems.

The first problem is individual protocol authoring.
How do we make a single CKB cell protocol easier to write, inspect, compile, and reason about? This is the role I currently see for CellScript. It should make lock/type boundaries, witnesses, receipts, assumptions, and ProofPlan coverage explicit for one protocol.

The second problem is inter-protocol transaction generation.
How do wallets, apps, solvers, and protocol-specific builders coordinate multiple protocols before final CKB settlement? How do they expose intents, conflict surfaces, settlement plans, verifier interfaces, and user-facing receipts without turning the upper layer into a new ledger?

This second problem is what I would now place under the name CellFabric.

CellFabric is a UTXO-generation and inter-protocol composition surface for CKB.

It asks whether wallets, apps, solvers, and protocol-specific builders can share a minimal language for intents, conflict surfaces, receipts, and settlement plans before producing final CKB transactions.

CKB remains the verification and settlement layer. CellFabric lives on the generation side.

So the proposed split is:

CellScript = author and compile individual cell protocols.
CellFabric = coordinate intents and settlement plans across protocols.
CellIntent = a pre-settlement generation object, not a ledger object.
CKB transaction = the final verification and settlement object.

In this framing, CellFabric is not primarily a DAG protocol. A graph may still be useful for indexing dependencies, conflicts, lineage, and bundle history, but the graph is an observability and coordination structure, not the protocol identity.

CellFabric is not trying to make the graph itself into a ledger. It is meant for intent propagation, conflict visibility, solver/builder coordination, deterministic settlement planning, and auditable pre-settlement receipts. Settlement, exits, and disputes should still return to CKB.

A receipt in CellFabric is not finality. A bundle is not a block. An orderer or solver is not the owner of state.

Why CellIntent as the Pre-Settlement Object?

I would not state CellIntent as a hard theorem. It is more of an engineering boundary between user intent and concrete CKB transaction generation.

If the coordination object is a block, the upper layer starts to inherit block-layer questions: who creates blocks, how often, what order exists inside the block, what happens when producers disagree, and whether empty blocks exist. That is not wrong for an execution network, but it is more structure than a UTXO-generation surface needs.

If the coordination object is already a concrete CKB transaction, the design is still possible, but the boundary is awkward. Replacement means rebuilding transactions. Batching means taking apart already-shaped transactions. Matching and solver work need templates, placeholders, partial signatures, or side conventions. Those tools can work, but at that point an intent layer is being rebuilt around the transaction anyway.

CellIntent sits between the two. It is not final enough to be settlement, but it is concrete enough to expose expected cells, app resources, witnesses, signatures, expiry rules, and conflict surfaces. For CellFabric, that seems like the least awkward pre-settlement object.

Proposed Architecture

The architecture is basically a generation pipeline. Users submit CellIntents. A gateway or app-specific builder checks basic validity. A coordination index records dependencies, conflicts, expiry, and lineage. Solvers or orderers select compatible bundles under declared rules. A settlement compiler turns selected bundles into concrete CKB transactions. A tracker follows those transactions on L1.

The important boundary is that a solver/orderer may select and sign a bundle, but the settlement compiler still has to produce real CKB transactions. The receipt is useful for accountability and UX, but it is not a substitute for CKB settlement.

Conflict Model

For cells, the obvious hard conflict is simple: two intents trying to consume the same outpoint cannot both settle. That part fits CKB naturally.

The more interesting problem is protocol-level or app-level conflict. AMM pools, launchpad allocations, order-book levels, registries, auctions, and oracle windows often have shared resources that are not fully captured by the cells a user names at intent time. If the generation layer ignores those resources, the solver/orderer becomes a private matcher that everyone has to trust.

So each protocol or app should expose conflict keys in a deterministic way. A CellIntent can declare the keys it touches, but the app policy should be able to recompute them. If the two do not match, the intent should not be treated as safely orderable.

Ideally, conflict keys should not be arbitrary strings asserted by the intent submitter. They should be recomputable from the protocol’s declared policy, CellScript metadata, or a verifier interface. Otherwise the conflict model becomes another hidden trusted convention.

This is deliberately less clean than pretending conflicts only exist at L1. The whole point of the upper layer is to see likely contention before the final transaction is built.

Coordination Receipt

A Coordination Receipt should be boring and explicit. It means a solver/orderer has selected a bundle under some declared rule and signed that statement. It does not mean the state change is final.

The receipt should contain enough information to audit the coordination statement: bundle id, selected intents, selection rule or version, expiry, coordinator identity, signature, and a commitment to the data needed to inspect the bundle.

It would appear to be something like this struct snippet :

pub struct BundleReceipt {
    pub bundle_id: BundleId,
    pub orderer_id: OrdererId,
    pub ordered_intents: Vec<IntentId>,
    pub selector_version: u16,
    pub compiler_version: u16,
    pub intent_root: [u8; 32],
    pub conflict_root: [u8; 32],
    pub data_availability_root: [u8; 32],
    pub created_at_ms: u64,
    pub issued_at_ms: u64,
    pub valid_until_ms: u64,
    pub non_final: bool,
}

If two orderers make different choices, or the same orderer signs inconsistent receipts, that should be visible. It still does not replace CKB settlement.

Settlement Compiler

The compiler is where the upper-layer object becomes CKB-shaped. It should take an immutable bundle and lower it into concrete CKB transactions. It should not read a live mutable DAG while compiling. It should not depend on the orderer’s private memory. Given the same bundle and compiler configuration, it should produce the same settlement plan.

Practical Scenarios

1, low-latency transfers, CellFabric can give wallets fast feedback while keeping final settlement on CKB. The useful distinction is that a user can see whether an intent is indexed, conflicted, coordination-receipted, submitted, or settled instead of treating all of those states as one vague ‘pending.’

2, launchpads, conflict keys can make allocations visible before settlement. A sale might have keys for the pool, the round, and each user’s allocation. If two claims touch the same allocation key, the conflict is visible before the CKB transaction exists.

3, AMM batching, many swap intents can compete over the same pool. A pool-specific orderer can choose a batch and compile one pool transition back into CKB. The pool is still a CKB cell; the upper layer only coordinates the batch.

4, RFQ or order-book flows, makers can publish constrained intents and solvers can compose them. Replacement and partial matching are much easier before everything is fixed as a concrete transaction.

5, cross-application solving, one bundle might include a mint, transfer, swap, claim, and fee payment. The compiler still has to lower the result into real CKB transactions, so the system does not need an account-style global state machine above CKB.

Observability (intent explorer)

A CellFabric explorer should not just be a block explorer with smaller blocks. The useful view is the coordination graph: where an intent sits, what it conflicts with, which bundle selected it, who signed the receipt, what data the receipt commits to, what settlement plan was produced, and whether the CKB transaction has settled.

The coordination graph is relevant here as an observability structure. It gives users a way to reason about intent ancestry, conflict surfaces, and coordination waypoints. CellFabric should aim for similar graph-native visibility, but with CKB-specific objects: cells, app conflict keys, intent bundles, settlement plans, and L1 status.

Decentralisation Path

I would likely not try to make the first version fully decentralised. Probably wiser to begin with one orderer and prove that the intent format, conflict model, bundle selection, compiler, and direct CKB settlement path work. Then move to a small known orderer set. Only after data availability, auth verification, exit paths, and dispute evidence are more mature does it make sense to discuss open orderer admission, stake, reputation, or quorum rules.

The important thing is not to describe the early system as more trustless than it is. A coordination receipt is not final. Conflicts should be queryable. Settlement lineage should be auditable. Users should keep an L1 path if the orderer fails.

A Few Open Questions

  1. How should apps define/expose conflict keys without turning the app compiler into hidden trusted logic?

  2. UIUX-wise, what should wallets show before L1 settlement?

  3. What is the smallest forced-exit design that is actually useful?

  4. What data must be available for a coordination receipt to be meaningful?

  5. How can multiple orderers converge without becoming a new consensus layer?

  6. Can AMM, launchpad, and order-book workloads stay cell-shaped instead of drifting into account-style global state?

References

10 Likes

Thanks for sharing this. It points in a different but important direction, so let me expand a bit.

Nervos is a family of protocols, with CKB at its core as the system’s verification layer and source of truth. Because CKB is optimized for decentralized verification, it intentionally leaves computation and scalability to off-chain layers. This naturally gives rise to two categories of protocols that complement CKB.

One category is Layer 2 protocols that improve system scalability. Examples include Godwoken (rollup), Fiber / Perun (channel), PoB-based Invisibook, and perhaps, someday, a transaction-DAG design.

The other category is UTXO-generation protocols. These protocols execute user intents and turn them into results that are ultimately settled as CKB transactions. They add programmability and make it easier to build interoperable applications on CKB, but they do not directly improve scalability, because in the end everything is still compiled into CKB transactions and settled on-chain. This category is unique to the UTXO model because its computation and verification are separated. For example, UTXO wallets are doing this because they need to translation user’s “payment” intention into actual UTXOs through some clever UTXO picking and generation algorithms. Several open-transaction efforts fall into this category too.

I believe UTXO-generation protocols are a very important yet still underexplored area for both CKB and UTXO chains more broadly. Because the UTXO model emphasizes verification, it needs a complementary protocol layer for computation and execution to complete the stack. The lack of UTXO-generation protocols in UTXO systems is one reason they are often difficult to use and build on. A blockchain for verification alone is not enough if there is no shared understanding of what the UTXO-generation side should look like. The freedom and flexibility of off-chain UTXO construction are powerful, but they also create fragmentation, compatibility issues, and poor interoperability across applications.

The main difficulty in creating a UTXO-generation protocol lies in deployment. In practice, it usually requires coordinated adoption and upgrade across wallets and existing on-chain scripts; simply using it in a new application is often not enough. That said, these challenges can be managed if the protocol is developed openly and communicated clearly with ecosystem participants in advance.

I wanted to make this distinction because, if I understand correctly, CellFabric is a protocol in the UTXO-generation category rather than a layer 2. It may deserve even more exploration than the transaction-DAG L2 idea as the direction remains underexplored yet essential.

Looking forward to seeing more of your work in this area.

3 Likes

Thanks Jan.

This clarification, together with Nervos’ original separation of state generation and verification, makes the picture much clearer to me.

And you are right in the sense that if things like CellFabric belong on the UTXO-generation side, then the real challenge is indeed probably not designing the full layer in the abstract, but finding the smallest generation surface that can be shared without forcing the whole ecosystem to upgrade at once.

My intuition now is that this likely has to start in a deliberately narrow domain, either wallet-level transaction generation for a limited class of transfers, or an app-scoped protocol for one namespace such as launchpad allocation or AMM batching (where generation semantics, conflict policies, and settlement lowering can become practical & stable before broader adoption is asked for. ) Otherwise the protocol risks being conceptually correct but deployment-intractable, which is probably the main failure mode for this entire category.

I’ll share a more concrete follow-up once there’s progress.

3 Likes

Hey Arthur, the CellScript / CellFabric split reads much clearer now! I think that part is mostly resolved: CellFabric makes sense as a UTXO-generation / inter-protocol coordination surface, not as a new ledger or transaction-DAG L2.

One concrete positive is that the current CellScript code already supports some of the single-protocol side you describe. receipt is a first-class cell type, claim only accepts receipt values, and read_ref lowers through Source::CellDep. That already supports “compose around existing cells” without implying a new settlement layer.

This is the one part where I still want to hear your view. The repo already exposes per-action touches_shared, read_refs, verifier_obligations, and transaction_runtime_input_requirements, and cellc scheduler-plan can derive simple shared-state conflicts from that metadata. But that still reads to me as intra-module compiler metadata, not yet a standard inter-protocol conflict-key or verifier surface.

The same semantics_preserving_claim says stateful lowering is represented in metadata and asm but is “not yet a proved schema decoder/verifier”. Structured WitnessArgs / Source views are still planned for v0.14, and the ProofPlan audit surface with trigger/scope/coverage and builder_assumption reporting is still planned for v0.15.

So my current read is: the split is right, the single-protocol foundation is already in the code, and the cross-protocol conflict-key / verifier surface still sits on the roadmap.

I was wondering: How do you see token standards and the wider inter-protocol interface evolving from here? The small Token / MintAuthority base already in the repo makes that especially interesting to me.

Phroi

2 Likes

Thanks Phroi.

I agree that the current CellScript metadata is still mostly intra-protocol compiler metadata. It exposes useful raw material : read_refs, touches_shared, verifier obligations, runtime input requirements, receipts, and so on, but it is not yet a stable CellFabric-level conflict-key or verifier-interface standard.

I also agree that conflict keys should not be arbitrary strings asserted by the intent submitter. At most, they can be hints.

The meaningful key should eventually be derivable or checkable from protocol policy, script args, cell schema, CellScript metadata, or a verifier interface.

That said, I want to be careful about scope. I probably do not have the bandwidth to design the full CellFabric conflict-key / verifier-interface layer in the current CellScript sprint. My near-term focus is still to make the single-protocol side safer and more inspectable first.

So my rough layering is:

  • CellScript now / near term: expose the raw materials clearly;
  • v0.14/v0.15: make trigger, scope, reads, coverage, enforcement, and builder assumptions explicit through ProofPlan;
  • CellFabric later: decide which parts are stable enough to become inter-protocol conflict keys, verifier interfaces, and token/composition standards.

The token / MintAuthority example may indeed be a good first narrow domain for that later work. But I would not want to overclaim that it is already designed.

Your suggestions have been consistently useful for me in sharpening the boundary here. For now I probably need to let the design breathe a little and spend more time building. Maybe at some point, once the lower layers are more solid, we could try some application-level collaboration to stress-test how strong the CellScript / CellFabric split really is.

2 Likes

That makes sense, Happy Building!! :hugs:

Phroi

1 Like