# DID Web5 Method Local ID Extension ## Metadata **Status**:: #x **Zettel**:: #zettel/permanent **Created**:: [[2025-06-23]] **GitHub**:: [github.com](https://github.com/cryptape/web5-wips/blob/master/02.md) ## Abstract This document defines the Local ID Extension to the `did:web5` method. A Local ID in the `did:web5` method provides a lightweight, off-chain identity mechanism that enables users to establish and migrate DIDs without immediately incurring the costs and consensus requirements of on-chain operations. Local IDs are particularly useful for scenarios where only limited consensus (such as within a small group) is needed, and they can later be migrated to a full on-chain `did:web5` DID. When a Local ID is migrated, its reference is recorded in the `local_id` field of the newly created `did:web5` DID Document Cell. The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119. **Document History:** - v0.1: Initial formal specification **Editors:** - Ian Yang (Cryptape) **Authors:** - Ian Yang (Cryptape) **Contributing Organizations:** - Nervos Foundation - Cryptape ## 1. Syntax Format Local IDs follow the format `web5:<method-name>:<method-specific-id>`. The different format allows resolver to handle the redirection after the migration. See 2.2 Resolution Process. ## 2. Operations ### 2.1 Migration Process A user MUST perform following steps to migrate a Local ID while creating the `did:web5` DID. 1. A user MUST provide a Local ID to a `did:web5` controller while creating the `did:web5` DID. 2. The controller MUST prepare the Local ID authorization in the transaction witness data. 3. The user MUST authorize the creation transaction using the Local ID. When a Local ID is migrated to a `did:web5` DID, the new DID Document Cell includes the Local ID in its `local_id`. The cell's type script (`did-web5-ts`) MUST verify that: - The `local_id` field MUST contain either no value or a single Local ID. It MUST use molecule option value `None` instead of the empty string to indicate no value. - If present, the `local_id` field MUST be a string encoded in UTF8 which starts with `web5:` and follows the format `web5:<method-name>:<method-specific-id>`. - If present, the transaction MUST provide valid authorization for the Local ID in the witness data. - The authorization MUST be validated according to the Local ID method. The associated Local ID is immutable. The `did-web5-ts` Type Script implementation MUST validate each update transaction to ensure that the Local ID cannot be altered: 1. The Local ID stored as the field `local_id` must be the same in the input and output DID Document Cell in an update transaction. The authorization for Local ID migration is embedded in the CKB transaction's `witnesses` array. The position of the witness data corresponds to the output index of the DID Document Cell in the transaction's outputs array. Specifically, if the DID Document Cell is at `tx.outputs[i]`, the corresponding witness data is stored in `tx.witnesses[i]`. To avoid conflicts with input lock scripts and type scripts, the witness data is structured using the [WitnessArgs](https://docs.nervos.org/docs/tech-explanation/witness#witnessargs) molecule table, with the relevant data placed in the `output_type` field. ### 2.2 Resolution Process Before migration, the resolver MUST use the algorithm specified by the Local ID’s method name to resolve the DID document. After migration, the resolver MUST redirect resolution requests for the Local ID to its corresponding `did:web5` DID. A resolver MUST index all on-chain `did:web5` DID Document Cells and look for the Local ID in their `local_id` field. The process is as follows: - Process all newly created DID Document Cells in order of transaction and output index. - For a given Local ID, the first `did:web5` DID Document Cell that references it in `local_id` becomes the initial candidate for migration. - Local ID methods CAN add additional recovery operation to potentially overwrite the candidate. See 4.1.4. - After 72 hours have elapsed since the latest candidate's creation (plus 50 block confirmations), the migration is considered final and cannot be overwritten. The 72-hour window MUST be measured by using the timestamp in the CKB block header. This mechanism ensures that, during the migration window, authorized key holders have the opportunity to contest or rotate control of the DID before the migration is permanently settled. ## 3. Local ID Methods ### 3.1 PLC Local IDs #### 3.1.1 PLC Local ID Migration A PLC Local ID has the format `web5:plc:<plc-specific-id>`. Creating and updating of a PLC Local ID MUST follow the PLC specification[^DID-PLC]. [^DID-PLC]: \[DID-PLC] Bluesky. (2023). did:plc Specification v0.1. <https://web.plc.directory/spec/v0.1/did-plc> The witness data for a PLC Local ID migration uses the following Molecule schema: ``` vector Bytes <byte>; vector Uint8Vec <byte>; vector BytesVec <Bytes>; table PlcAuthorization { history: BytesVec, sig: Bytes, signing_keys: Uint8Vec, } table DidWeb5Witness { local_id_authorization: PlcAuthorization, } ``` The `PlcAuthorization` Table contains the following fields: - **`history`**: The PLC operation history, beginning with the PLC Genesis Operation. It MUST contain at least one operation. It MAY contain only the Genesis Operation, and it is RECOMMENDED to include only the Genesis Operation. Each element in the vector is a PLC operation encoded with DAG-CBOR, as specified in the `did:plc` specification under *Operation Serialization, Signing, and Validation*. - **`sig`**: A signature produced by one of the `rotationKeys` from the last operation in `history`, authorizing the creation of the `did:web5` DID on-chain. - **`signing_keys`**: To minimize on-chain computation. This field's length MUST equal the length of `history` plus one. The last element indicates the index (zero-based) of the key in `rotationKeys` used to produce the `sig` in this `PlcAuthorization`. The preceding elements correspond to the indices of the keys in the respective `rotationKeys` used to sign each operation in `history`. The `did-web5-ts` Type Script MUST perform the following validations: - The length of `history` MUST be greater than zero. - Every item in `history` MUST be a DAG-CBOR encoded JSON object. The first operation (the Genesis Operation) MUST have a `type` field with value `plc_operation` or `create`. All subsequent operations (if any) MUST have `type` equal to `plc_operation`. - The PLC method-specific-id MUST be computed from first operation in `history` and MUST match the corresponding Local ID in the `DidWeb5Data`. - For each operation in `history`, the `prev` field MUST be validated. The first operation MUST have a `prev` field with value `null`. All subsequent operations (if any) MUST have `prev` pointing to the CID hash of the previous operation. - The length of `signing_keys` MUST equal the length of `history` plus one. - Using `signing_keys`, the script MUST verify the `sig` field of each operation in `history`. Specifically, the first operation MUST be signed by its own `rotationKeys`, and all subsequent operations (if any) MUST be signed using `rotationKeys` from the previous operation. If the first operation uses the deprecated legacy operation format, its `rotationKeys` SHALL be an array with a single element, which is the value of the field `recoveryKey` in the operation. - Using `signing_keys`, the script MUST verify the `sig` field in `PlcAuthorization`, which MUST be a valid signature over the CKB transaction hash, produced by a key in the `rotationKeys` of the last operation in `history`. As the transaction hash is already a 32-byte binary sequence, it MUST NOT be hashed again with SHA-256. `DidWeb5Witness` wraps the authorization information for the Local ID referenced in `local_id_authorization`. In addition to the general resolution process as described in 2.2, a resolver MUST support rotation keys recovery operation for a PLC Local ID. - Subsequent migrations within a 72-hour window (measured by block timestamps) can potentially overwrite the candidate, provided they are authorized by a higher-priority rotation key (determined by lexicographical comparison of `signing_keys`). - If a new candidate with a higher-priority key appears within the 72-hour window, it replaces the previous candidate and restarts the window. - After 72 hours have elapsed since the latest candidate's creation (plus 50 block confirmations), the migration is considered final and cannot be overwritten. #### 3.1.2 Example This is an example CKB transaction that migrate from `web5:plc:bxvfvvygwbcnbmknn73t6pbu` ```javascript { "cellDeps": [ // did-web5-ts for outputs[0].type { "depType": "code", "outPoint": { "index": "0x0", "txHash": "0x1ecbf88d692a14d7cbc0bfd1a3d5019e4b613247ae438bad52f94148c6009559", }, }, // always-success for inputs[0].lock { "depType": "code", "outPoint": { "index": "0x1", "txHash": "0x1ecbf88d692a14d7cbc0bfd1a3d5019e4b613247ae438bad52f94148c6009559", }, }, ], "headerDeps": [], "inputs": [ // The input that provides the capacity { "previousOutput": { "index": "0x2", "txHash": "0x1ecbf88d692a14d7cbc0bfd1a3d5019e4b613247ae438bad52f94148c6009559", }, "since": "0x0", }, ], "outputs": [ // The new created DID Document Cell { "capacity": "0x258", "lock": { "args": "0x", "codeHash": "0x3dc9cdb5b2dcff8d4999004406558d4456ecf0f14c757ca353684c0bd4d3b50d", "hashType": "type", }, "type": { "args": "0x8434cfe81aa825c275d513eee20e4235294e3420", "codeHash": "0xb95123c71a870e3f0f74a7ee1dab8268dbfbc1407b46733ebd1b41f854b4324a", "hashType": "type", }, }, ], "outputsData": [ "0x00000000d90000000c000000b4000000...", ], "version": "0x0", "witnesses": [ "0x4c020000100000001000000010000000...", ], } ``` The cell data and witness are truncated for better display of this document. The full content of the cell data is: ``` 0x 00000000d90000000c000000b4000000a4000000a3687365727669636573a16b 617470726f746f5f706473a264747970657819417470726f746f506572736f6e 616c4461746153657276657268656e64706f696e747468747470733a2f2f6578 616d706c652e746573746b616c736f4b6e6f776e4173816f61743a2f2f616c69 63652e7465737473766572696669636174696f6e4d6574686f6473a167617470 726f746f736469643a6b65793a7a5369676e696e674b65792100000077656235 3a706c633a62787666767679677762636e626d6b6e6e37337436706275 ``` The cell data is the Molecule encoding of `DidWeb5Data` using `DidWeb5DataV1` that `local_id` is `web5:plc:bxvfvvygwbcnbmknn73t6pbu` in UTF-8 and `document` is the DAG-CBOR encoding of the following JSON object: ```javascript { "verificationMethods": { "atproto": "did:key:zSigningKey" }, "alsoKnownAs": ["at://alice.test"], "services": { "atproto_pds": { "type": "AtprotoPersonalDataServer", "endpoint": "https://example.test" } } } ``` The full content of the witness is: ``` 0x 4c02000010000000100000001000000038020000380200000800000030020000 10000000e60100002a020000d601000008000000ca010000a76373696778564c 2d6b72665f6f385f4b4a5754362d51316532572d52466252443777313564575f 4e4b726b6d5f634c2d355147346b5f70736270714a5f314f48626a3034716a70 764f70724143507357384765582d6e5253433030416470726576f66474797065 6d706c635f6f7065726174696f6e687365727669636573a16b617470726f746f 5f706473a264747970657819417470726f746f506572736f6e616c4461746153 657276657268656e64706f696e747368747470733a2f2f6578616d706c652e63 6f6d6b616c736f4b6e6f776e4173817661743a2f2f616c6963652e6578616d70 6c652e636f6d6c726f746174696f6e4b6579738278396469643a6b65793a7a51 33736859314d5351545a6f584431505370616a566237487642734841754a6945 5039354271426b5358316d6365794278396469643a6b65793a7a446e61657068 4464367a5a4c433353397873536e6b7579784b39316d62663467654e53666e6a 334c715a65666158693373766572696669636174696f6e4d6574686f6473a167 617470726f746f78396469643a6b65793a7a513373686e6d517963716d38344d 684c48544a6675614a6843326a66774a69597a697a784c6162574e466269796d 515440000000a3d767cb71da12c25e4a32366990600f64b0683934034982961c fc439d172d87797523c5d93207889bdfd3045bdff75d96d7294307011afb9f19 8f152f0347e2020000000000 ``` The witness is the Molecule encoding of: ```javascript { "localIdAuthorization": { "history": [ "0xa76373696778564c2d6b72665f6f385f...", ], "sig": "0xa3d767cb71da12c25e4a32366990600f64b0683934034982961cfc439d172d87797523c5d93207889bdfd3045bdff75d96d7294307011afb9f198f152f0347e2", "signingKeys": [ "0x0", "0x0", ], }, } ``` The full content of the first element in `history` is: ``` 0x a76373696778564c2d6b72665f6f385f4b4a5754362d51316532572d52466252 443777313564575f4e4b726b6d5f634c2d355147346b5f70736270714a5f314f 48626a3034716a70764f70724143507357384765582d6e525343303041647072 6576f664747970656d706c635f6f7065726174696f6e687365727669636573a1 6b617470726f746f5f706473a264747970657819417470726f746f506572736f 6e616c4461746153657276657268656e64706f696e747368747470733a2f2f65 78616d706c652e636f6d6b616c736f4b6e6f776e4173817661743a2f2f616c69 63652e6578616d706c652e636f6d6c726f746174696f6e4b6579738278396469 643a6b65793a7a5133736859314d5351545a6f584431505370616a5662374876 42734841754a69455039354271426b5358316d6365794278396469643a6b6579 3a7a446e616570684464367a5a4c433353397873536e6b7579784b39316d6266 3467654e53666e6a334c715a65666158693373766572696669636174696f6e4d 6574686f6473a167617470726f746f78396469643a6b65793a7a513373686e6d 517963716d38344d684c48544a6675614a6843326a66774a69597a697a784c61 62574e466269796d5154 ``` The first element is the DAG-CBOR encoding of the PLC genesis operation: ```javascript { "alsoKnownAs": [ "at://alice.example.com", ], "prev": null, "rotationKeys": [ "did:key:zQ3shY1MSQTZoXD1PSpajVb7HvBsHAuJiEP95BqBkSX1mceyB", "did:key:zDnaephDd6zZLC3S9xsSnkuyxK91mbf4geNSfnj3LqZefaXi3", ], "services": { "atproto_pds": { "endpoint": "https://example.com", "type": "AtprotoPersonalDataServer", }, }, "sig": "L-krf_o8_KJWT6-Q1e2W-RFbRD7w15dW_NKrkm_cL-5QG4k_psbpqJ_1OHbj04qjpvOprACPsW8GeX-nRSC00A", "type": "plc_operation", "verificationMethods": { "atproto": "did:key:zQ3shnmQycqm84MhLHTJfuaJhC2jfwJiYzizxLabWNFbiymQT", }, } ``` The `sig` field in `localIdAuthorization` is a signature of the CKB tx hash `0xf7d3002ebd20f694fa974a0d680607471c48a526887fab62bba2908af3c507b2` using the rotation key `did:key:zQ3shY1MSQTZoXD1PSpajVb7HvBsHAuJiEP95BqBkSX1mceyB`. ## 4. References - \[DID-PLC] Bluesky. (2023). did:plc Specification v0.1. https://web.plc.directory/spec/v0.1/did-plc - \[RFC2119] Bradner, S. (1997). RFC 2119: Key words for use in RFCs to Indicate Requirement Levels. https://www.rfc-editor.org/rfc/rfc2119