I wish, this DAO thing could also be used as the organization of the first testers and Users of the applications being built on Nervos CKB Ecosystem. This will enable developers to get initial users and testers who are committed to providing feedback on their usage of applications. We dont expect the apps to be adopted from outside inside, but better inside outside.
@BaClaire, Nervos Nation Telegram, March 22, 2026
The V1.1 proposal envisioned exactly this: a shared identity layer for the Nervos ecosystem, not just a governance tool.
This review examines what the identity system is, what was promised, and where it stands.
DAO V1.1 replaces Metaforoâs governance accounts with Web5 DID accounts: a custom identity layer built on AT Protocol. Metaforo already supports email and wallet login, so the change is not from email to wallets but from Metaforo-managed identity to a platform-managed did:ckb identity. Every governance action (proposing, voting, commenting) requires a DID.
1. How It Works
AT Protocol Fork
The platform runs a fork of rsky, a Rust AT Protocol implementation (the same protocol behind Bluesky). Each account gets a handle (e.g. alice.web5.ccfdao.org; Network.ts:20: USER_DOMAIN = 'web5.ccfdao.org') and a profile stored on the platformâs PDS (Personal Data Server).
Standard AT Protocol uses did:plc or did:web for identity; this fork uses did:ckb (Network.ts:22), a custom DID method resolved exclusively by the platformâs own indexer (DID_INDEXER = https://did-indexer.bbs.fans). Combined with 8 custom lexicons under fans.web5.ckb.* (web5-api/lexicons), these accounts cannot interoperate with Bluesky or any other AT Protocol service.
Identity and Funds
The DID itself is an on-chain cell (~455 CKB capacity, locked by the userâs wallet key; createAccount.ts), but the DID signing key does not control CKB funds (those stay under the wallet key, e.g. Neuron, JoyID, or any CCC-supported wallet). Your handle, proposals, and comments are all linked to the DID (AT Protocol records use the DID as the repo identifier). The signing key is what grants access to the DID; losing it means losing that governance identity, not your coins.
Votes are on-chain cells, but profiles, proposals, and comments are AT Protocol records on the PDS, while the app_view backendâs PostgreSQL mirrors proposal content, comments, and profiles from the PDS and adds governance state (voter lists, vote results, admin roles), workflow management (meetings, tasks), and activity timelines.
Key Storage
The DID signing key (secp256k1) is stored unencrypted in browser localStorage (storage.ts:56: setToken writes {did, signKey, walletAddress} as plaintext JSON under @dao:client). The browser cannot protect it: no save-password prompt, no cross-device sync. Functionally a session cookie, except it is your entire governance identity. Clearing browser data destroys it. This can happen without user action: Safariâs Intelligent Tracking Prevention deletes localStorage after 7 days without a site visit, and Chromium updates have occasionally wiped it. On a governance platform where votes happen periodically, a week away is enough to lose the key.
Backup is optional and not prompted at account creation: the file export (ExportDIDInfoModal.tsx) encrypts with AES-GCM (8-character password), but the QR export (KeyQRCodeModal.tsx) prompts for a 4-digit PIN that is included in the plaintext JSON alongside the signing key rather than used for encryption (source comments acknowledge this should be encrypted).
Key rotation is not implemented. The scaffolding exists at multiple levels: the on-chain DID cell uses TYPE_ID (allows cell consumption and recreation), Molecule schemas for PlcAuthorization witnesses exist in the frontend (molecules.ts:84-153), and CCCâs unmerged feat/did-ckb branch implements a full transferDidCkb function that consumes and recreates a DID cell with new data (key rotation, PDS endpoint change). But none of it is wired up: no UI, API, or released SDK exposes this. A compromised or lost key cannot be currently replaced; the DID must be abandoned and a new one created, losing the associated handle, proposals, and history.
Authorization Model
Posting and commenting are signed locally with the stored signing key (AT Protocol session), no wallet popup. Voting requires two steps: the frontend obtains a Sparse Merkle Tree (SMT) membership proof from the backend (/api/vote/prepare), then builds and submits an on-chain CKB transaction that requires wallet approval (votingUtils.ts:563: signer.signTransaction(tx)).
2. What Was Promised
The V1.1 proposal and AMAs describe the identity layer broadly:
User sovereignty.
Data portability.
The proposalâs budget table also lists âmulti-device syncâ as a feature of the registration/login module.
Sovereign data.
Ecosystem reuse.
Reiterated:
Cross-app identity.
Total decentralization.
3. Where It Stands
User sovereignty. The DID signing key is in the userâs browser. The on-chain DID cell is locked by the userâs wallet key. These are the sovereign components. But DID resolution depends on the platform operatorâs indexer, the voter list is built by the operatorâs backend, proof issuance is controlled by the operator, and profile data lives on the operatorâs PDS. The user owns a key and an on-chain anchor; the operator controls everything those connect to.
Data portability. Not implemented. The DID documentâs PDS endpoint is frozen at creation (currently no functional update mechanism, as described in Key Storage above). Users cannot point their DID to a different server. The rsky fork includes relay and firehose components that could enable cross-instance sync, but these are not exposed. Multi-device sync, listed in the proposalâs budget table, is not implemented: the signing key exists in one browserâs localStorage.
Sovereign data. Votes are on-chain cells: sovereign, independently verifiable. Proposals, comments, and profiles are AT Protocol records on the operatorâs PDS: not on-chain, not sovereign, mirrored in the backendâs database but recoverable only by the operator. The proposal describes âall generated governance dataâ as sovereign; the implementation makes a subset sovereign.
Ecosystem reuse. The source code is open, and the did:ckb DID method is chain-native: any service that reads CKB cells can resolve a DID. This is the reusable primitive. But for another platform to adopt it, three gaps remain:
- No DID document updates: a DID cannot register additional services, so each platform would need its own DID per user.
- The DID document uses AT Protocolâs
did:plcoperation format (verificationMethods.atproto,services.atproto_pds) rather than W3C DID Core, and the indexer rejects documents without AT Protocol-specific fields (check_did_docrequiresat://inalsoKnownAsand anatprotoverification method), limiting adoption by non-AT Protocol applications. - The 8 custom lexicons (CKB-anchored account creation, repo writes, and session management via two-phase signing, plus queries and blob upload) are custom to this platform and unsupported by other AT Protocol services.
Cross-app identity. The DID is resolvable by anyone running the indexer. A second application could verify a userâs did:ckb signature. But the signing key lives in one browserâs localStorage with no cross-app sharing mechanism, the handle system (*.web5.ccfdao.org) is DNS-bound to the operator, and without DID document updates a user cannot register a second applicationâs endpoint. Cross-PDS login was mentioned in Dev Log #1 (âIntegrate web5did-indexer for cross-PDS login, enabling BBS users to log into the DAO platformâ), indicating awareness of the gap. The current implementation supports login across PDS instances within the same platform (user-account.ts: setLoginUserPDSClient switches the PDS client based on the userâs DID document), but not across independent platforms.
Total decentralization. The code review documented centralized control over voter lists, proof issuance, and vote tallying. The identity layer adds operator dependency for DID resolution and profile storage. The on-chain components (DID cells, vote cells) are decentralized; the governance workflow built on top of them is not.
4. Independent Operation
All source code is public: the DID on-chain contract (web5fans/did-ckb, MIT, registered in CCC as KnownScript.DidCkb), the PDS (web5fans/rsky fork), the DID and vote indexer (web5fans/web5-indexer, MIT), the bind indexer and vote CLI tools (CCF-DAO1-1/web5-components), and the backend (CCF-DAO1-1/app_view). The full stack is the latter 4 as services, each with its own PostgreSQL, plus a CKB node and S3 storage.
On-chain data (DIDs, votes, address bindings) is independently reconstructable from any CKB node. Off-chain data (profiles, proposals, comments) is not: the fork retains AT Protocolâs relay and firehose infrastructure, which could replicate PDS records to an independent instance, but the operator controls whether these endpoints are exposed, and account migration remains blocked by the frozen DID document.
An independent clone can verify but not override: recomputing the voter listâs SMT root hash and comparing it against the on-chain VoteMeta cell (the cell that configures each vote session, including the eligible voter set) would expose any discrepancy in who was included or excluded. Recomputing the vote tally from on-chain vote cells would expose any divergence from the operatorâs reported results. But the on-chain contract enforces the SMT root hash from the VoteMeta cell. The contract never checks the VoteMeta cellâs lock script, so the operatorâs exclusive control over voter eligibility is application-layer, not contract-enforced. An independent stack can prove misbehavior but cannot let an excluded voter cast a vote. The infrastructure for this verification exists in the source code; what is missing is anyone running it.
did:ckb is a chain-native identity primitive with the right foundation for ecosystem-wide use. The source code is open, the DID is on-chain, and the indexer is independently runnable. What stands between the current state and BaClaireâs vision are DID document updates (so a single identity can span multiple apps), account portability (so users are not locked to one server), and key management beyond browser localStorage.