Cellula.id,.cell is a naming protocol on CKB, live on mainnet since 21 September. A name is one live cell under a fixed type script; its records ride in the witness and the cell commits to their
hash. The product story, why a name here can be paid in ways an ENS name cannot, is on Scryve: A name you can be paid at | Scryve .
The type script cannot check a signature
account-cell-type verifies no signature and has no code that could. Every account cell sits
under a uniform always-success lock, so the type script is the only guardian, and it runs on
inputs and outputs alike. Ownership is a lock hash in the cell’s data. An owner action
(edit, delegate, transfer, sell) is proven by spending, in the same transaction, a cell under
that lock. Which algorithm that lock runs is the lock’s business.
Two things fall out of that:
- Every lock CKB has works today, and every lock CKB gets later works without a release from us. JoyID, secp256k1, OmniLock, a passkey.
- A name can be owned by
sphincs-all-in-one-locktoday. We ran the whole loop countless times: transfer to a SPHINCS+ owner, delegate an everyday manager, edit from the manager, transfer back. The contract was not changed for it; the app grew a screen. The footnote belongs in the same paragraph: on mainnet that lock is a type-id cell under a multisig that is not ours, so the resolver fingerprints the deployed code every ten minutes and the app refuses to protect a name while the fingerprint differs.
The other way to support many wallets is a verifier per scheme compiled into the script. We put that question in the lock, where the cell model already answers it, so nothing on our side has to grow when a new wallet appears
Registration is permissionless, and the predecessor cannot be harmed
Names are kept unique by a ring: registering x spends the predecessor cell and recreates it
pointing at x. The predecessor belongs to someone else, so a stranger has to be able to
spend it. The type script allows exactly that and nothing else: on register the predecessor
is preserved byte for byte except its next pointer, and its capacity may not shrink. Whoever
drives the transaction can splice in a well-formed name and can do nothing to the neighbour,
which is why no signature is asked of them. Genesis is gated by a one-time token whose type
hash is the namespace id, because a type script cannot see global state.
The arithmetic a percentage fee cannot survive
1% of 100 CKB is 1 CKB. No cell can be 1 CKB, because a cell holds its own bytes, and ours is
63 of them. A small fee is not small here; it is impossible. So the sale lock charges nothing
under 630 CKB, a flat cell up to 6,300, and 1% above. The same reasoning is why the fee in the
contract is a ceiling and not an oracle: a separate price cell may discount it, by at most a
sixteenth every six hours, within a band the contract enforces, and nothing can charge above
the schedule. The keeper that steps it is one key that can move the factor a sixteenth
either way, once every six hours, and never above the ceiling.
What is public, and what is not
-
The contracts: GitHub - LusoCryptoLabs/cells-contracts: The four Nervos CKB scripts behind .cell names: unique, owned, priced, sellable. The mainnet binaries reproduce from this source. · GitHub . Four scripts and a shared
crate, 182 tests, CI runsmake teston every push and a second job rebuilds the mainnet
binaries and compares hashes. The four data hashes on mainnet:-
account-cell-type0xf86bdba9ff22b5018dcb90a22cdb3720cd5ea8868ab94959ac8146a6789aae30 -
account-lock0xa46c19f2262abc0d0db0de3952b7477792b36b392645e0f60e74637ae3e3f13b -
sale-lock0xf1160f64a82e3509211b2903fc54f9f15cbdc4758d058f107608d30727072a93 -
price-cell-type0x238e74e1d2d5bf8f06f9f4b0bb352c2addd531351a45cfeddf3ff5f6f5662342 -
cellula.id/api/verifyfetches the deployed code and hashes it against these; the answer is JSON, and the same check takes one get_live_cell and one blake2b against any node.
-
-
The resolver: GitHub - LusoCryptoLabs/cells-resolver: The HTTP read API for .cell names on Nervos CKB, as it runs at cellula.id. Run your own with one docker command. · GitHub , the service as it runs at
cellula.id/api.docker run -e CKB_NETWORK=mainnet -p 8787:8787 cells-resolveris a complete second instance; there is no database. Every/resolveanswer carries the outpoint, the type script and the data hash, so any node can confirm it. -
The bug classes we met, as toy scripts with failing tests:
GitHub - LusoCryptoLabs/ckb-script-pitfalls: Ways a Nervos CKB script goes wrong that have each cost somebody something: a toy script and a test per class, accepted as written and refused once fixed · GitHub . Five of them are from these contracts,
each linked to the real test. -
The app: its built bytes are on both chains as CKBFS cells under the operator key, and
cellula.id/developersfetches them back and compares them with what it is running: 66 of 66 files. Its source is private. The upgrade key exists and is stated in TRUST.md rather than hidden; constraining it is the next piece of work. No outside audit yet, by choice.
What we would like from here
- It is live on mainnet, at cellula.id.
- Tell us what a wallet needs in order to resolve a name.
- If you run a resolver, say so. A second one anywhere is a full one.
Questions answered here, same day.

