§ did:cid Method Specification

Specification Status: Draft

Latest Draft: https://github.com/archetech/archon/blob/main/docs/scheme.md

Editors:
Archon Contributors
Participate:
GitHub repo
File a bug
Commit history

§ Abstract

The did:cid method specification conforms to the requirements specified in the DID-CORE currently published by the W3C Decentralized Identifier Working Group. For more information about DIDs and DID method specifications, please see the DID Primer.

§ Introduction

The did:cid method is designed to support a P2P identity layer with secure decentralized verifiable credential. DIDs created using this method are used for two categories of DID Subject:

NOTE

The did:cid method is optimized for identity creation that is fast (under 10 seconds) and virtually costless, achieved by anchoring to IPFS rather than requiring an on-chain transaction at creation time. On-chain registration is deferred to the update phase, where it secures the mutation history.

§ Design Goals

  1. Decentralized creation — DIDs are anchored to IPFS prior to any declaration on a registry, enabling immediate use without on-chain transactions or fees.
  2. Decentralized updates — DID mutations are registered on a pluggable registry specified at creation time, preserving the decentralization requirement across the entire lifecycle.
  3. Temporal resolution — DIDs can be resolved at any historical point in time, enabling verification of credentials signed with keys that have since been rotated.
  4. Agent/Asset duality — The method explicitly distinguishes between key-bearing agents and keyless controlled assets, reflecting the natural authority hierarchy in credential ecosystems.

§ DID Format

did:cid

The did:cid method conforms to the DID-CORE generic DID syntax. DIDs using this method have the following ABNF:

did-cid        = "did:cid:" cid-identifier
                 [ ";" did-service ] [ "/" did-path ]
                 [ "?" did-query ] [ "#" did-fragment ]
cid-identifier = CIDv1-base32

; CIDv1-base32: a CID v1 encoded in standard base32
; Example: bafkreiawdmk6fmqc5p237vffyctazpzdgvgqfdj2i3hx2idtodxkwhyj5m

The cid-identifier is the IPFS content identifier generated by applying JCS to the initial seed document and computing its CID v1 in standard base32 encoding.

§ Example

did:cid:bafkreiawdmk6fmqc5p237vffyctazpzdgvgqfdj2i3hx2idtodxkwhyj5m

§ DID URL Components

did:cid DID URLs support the full DID URL syntax defined in DID-CORE. The fragment component (#) is the most commonly used, for referencing verification methods within a DID document:

did:cid:bafkreiawdmk6fmqc5p237vffyctazpzdgvgqfdj2i3hx2idtodxkwhyj5m#key-1

§ Method-Specific Identifier

The method-specific identifier is a CID v1 in base32 encoding. This identifier is deterministic — the same seed document always produces the same CID — which enables content-addressed identity.

WARNING

Because the DID suffix is derived from the content hash of the seed document, any change to the seed document produces a different DID. There is no mechanism to alter a seed document after creation — mutations are expressed through successive update operations recorded on the registry.

§ DID Document

A did:cid resolution response streams five top-level components to the client:

{
  "didDocument": { ... },
  "didDocumentMetadata": { ... },
  "didDocumentData": { ... },
  "didDocumentRegistration": { ... },
  "didResolutionMetadata": {
    "retrieved": "2026-06-15T19:22:45.691Z"
  }
}

didDocument and didDocumentMetadata conform to DID-CORE. didResolutionMetadata conforms to the DID Resolution specification. didDocumentData and didDocumentRegistration are Archon extensions described in the Archon Extensions to DID Core section.

NOTE

The operation chain is the authoritative source of truth for a did:cid DID. The Gatekeeper stores individual operations (create, update, delete) and reconstructs the DID document by replaying them in ordinal key order at resolution time. Implementations MAY cache resolved documents for performance, but any cached result MUST remain consistent with a fresh replay of the canonical operation chain. The didResolutionMetadata.retrieved timestamp records when the resolution response was generated.


§ Agent DID Document

A resolved agent DID document includes a verification method and the standard DID Core verification relationships:

{
  "didDocument": {
    "@context": ["https://www.w3.org/ns/did/v1"],
    "id": "did:cid:bafkreig6rjxbv2aopv47dgxhnxepqpb4yrxf2nvzrhmhdqthojfdxuxjbe",
    "verificationMethod": [
      {
        "id": "#key-1",
        "controller": "did:cid:bafkreig6rjxbv2aopv47dgxhnxepqpb4yrxf2nvzrhmhdqthojfdxuxjbe",
        "type": "EcdsaSecp256k1VerificationKey2019",
        "publicKeyJwk": {
          "kty": "EC",
          "crv": "secp256k1",
          "x": "LRrQabMIkvGVTA2IRk0JdWCpu57MNGm89nugrBZHo24",
          "y": "KHsWAaidAIGCosDjRYDIk-94793e4xVEL4UwFxjWgB8"
        }
      }
    ],
    "authentication": ["#key-1"],
    "assertionMethod": ["#key-1"]
  }
}

§ Verification Relationships

did:cid agent DIDs support the following DID-CORE verification relationships:

Relationship Purpose
authentication Proves control of the DID — used when the agent must authenticate itself to a verifier
assertionMethod Signs verifiable credentials and other assertions

The initial verification method is referenced as #key-1. After key rotation via an update operation, the new method identifier increments (#key-2, #key-3, etc.) and both authentication and assertionMethod are updated to reference the new key. Historical keys remain resolvable via temporal resolution.

§ Authentication

An agent authenticates by signing a challenge with the private key corresponding to the method in its authentication verification relationship. A verifier resolves the agent’s DID (at the time of the challenge) to obtain the then-active public key, then verifies the signature.

This enables DID-native, decentralized authentication: any party that can resolve a did:cid DID can authenticate a did:cid agent without relying on a centralized identity provider or certificate authority.

§ Service Endpoints

Agent DIDs may include service endpoint entries per DID-CORE Section 5.4. Services are declared in the service array and updated via standard DID update operations:

{
  "service": [
    {
      "id": "#lightning",
      "type": "lightning",
      "serviceEndpoint": "https://drawbridge.example.com/lightning"
    }
  ]
}

Service endpoints are optional. Any DID Core-conformant service type may be used; did:cid imposes no constraints on service endpoint structure beyond those defined in DID-CORE.


§ Asset DID Document

A resolved asset DID document identifies its controlling agent and carries application data in didDocumentData. It has no verification methods of its own:

{
  "didDocument": {
    "@context": ["https://www.w3.org/ns/did/v1"],
    "id": "did:cid:z3v8AuahaEdEZrY9BGfu4vntYjQECBvDHqCG3mPAfEbn6No7AHh",
    "controller": "did:cid:bagaaieradidcs4hohalzexldr5mdmbmt553tqq3ifqd56mvhifppvyfdc32q"
  },
  "didDocumentData": {
    "group": {
      "name": "testgroup",
      "members": []
    }
  },
  "didDocumentRegistration": {
    "version": 1,
    "type": "asset",
    "registry": "hyperswarm"
  }
}

Asset DIDs support transfer of control: a controller may update the controller field to a new agent DID via a valid update operation. Only one controller is valid at any given time.


§ Metadata Objects

§ didResolutionMetadata

The didResolutionMetadata object is added by the Gatekeeper at resolution time and conforms to the DID Resolution specification:

Field Description
retrieved ISO 8601 timestamp of when this resolution was computed

Because retrieved is set fresh on every call, no two resolution responses for the same DID are identical — even when the underlying DID document has not changed.

§ didDocumentMetadata

The didDocumentMetadata object conforms to DID-CORE and includes Archon-specific extensions:

Field Source Description
created DID Core ISO 8601 timestamp of initial DID creation
updated DID Core ISO 8601 timestamp of most recent update
deactivated DID Core true if the DID has been revoked via a delete operation
versionId DID Core CID of the most recent operation in the operation chain
versionSequence Archon Sequence number of the most recent operation, returned as a string
confirmed Archon true if the most recent operation is confirmed on the registry
timestamp Archon Blockchain timestamp bounds (blockchain registries only — see below)

§ Blockchain Timestamp Bounds

For DIDs using blockchain-based registries (Bitcoin, Ethereum, Zcash, Solana, Filecoin), the timestamp object provides cryptographic upper and lower bounds on when the most recent operation was submitted, derived directly from block data:

{
  "didDocumentMetadata": {
    "versionId": "bafkrei...",
    "versionSequence": "2",
    "confirmed": true,
    "timestamp": {
      "chain": "BTC",
      "lowerBound": {
        "time": 1705312800,
        "timeISO": "2024-01-15T10:00:00Z",
        "blockid": "00000000000000000002a7c4...",
        "height": 826000
      },
      "upperBound": {
        "time": 1705316400,
        "timeISO": "2024-01-15T11:00:00Z",
        "blockid": "00000000000000000001b8f2...",
        "height": 826005,
        "txid": "a1b2c3d4e5f6...",
        "txidx": 42,
        "batchid": "bafkrei...",
        "opidx": 3
      }
    }
  }
}

Lower bound (lowerBound): Present when the operation included a blockid field at submission time, referencing a recent block. This proves the operation was created after that block was mined — establishing a cryptographic “not before” constraint.

Upper bound (upperBound): Always present for confirmed blockchain operations. Identifies the block in which the operation batch was anchored, proving the operation existed before the subsequent block — establishing a “not after” constraint.

Together, the bounds define an independently verifiable time window without relying on self-asserted client timestamps. The bounds can be verified by any party with access to the relevant blockchain, providing legal-grade timestamping for DID operations.

NOTE

For registry without blockchain consensus (e.g., Hyperswarm), the timestamp object is absent. Operation ordering on such registries relies on the P2P consensus mechanism of the registry itself rather than external block timestamps.

§ DID Lifecycle

All did:cid DIDs begin life anchored to IPFS. Once created they can be used immediately by any application or service connected to a node that can resolve the seed document from IPFS. Subsequent updates to the DID (meaning that a document associated with the DID changes) are registered on a registry such as a blockchain (BTC, ETH, etc.) or a decentralized database (e.g., hyperswarm). The registry is specified at DID creation so that nodes can determine which single source of truth to check for updates.

The key concept of this design is that DID creation is decentralized through IPFS, and DID updates are decentralized through the registry specified in the DID creation. The DID is decentralized for its whole lifecycle, which is a hard requirement of DIDs.

§ Lifecycle States

State deactivated Document Description
Created false Seed document Initial anchor on IPFS, resolvable immediately
Active false Latest version One or more valid updates applied
Revoked true Empty Controller removed, no further mutations possible

§ DID Creation

create operation

DIDs are anchored to IPFS prior to any declaration on a registry. This allows DIDs to be created very quickly (less than 10 seconds) and at (virtually) no cost.

The did:cid method supports two main types of DID Subject: agent and asset. Agents have keys and control assets. Assets do not have keys, and are controlled by a single agent (the owner of the asset). The two types have slightly different creation methods.

§ Create an Agent DID

To create an agent DID, the client must sign and submit a create operation to a node:

  1. Generate a new private key

    • We recommend deriving a new private key from a Hierarchical Deterministic (HD) wallet (BIP-32).
  2. Generate a public key from the private key.

  3. Convert the public key to JWK format.

  4. Create an operation object with these fields in any order:

    Field Required Description
    type Yes Must be "create"
    registration.version Yes Version number, e.g. 1
    registration.type Yes Must be "agent"
    registration.registry Yes A valid registry identifier, e.g. "BTC", "hyperswarm"
    publicJwk Yes The public key in JWK format
    created Yes ISO 8601 timestamp
    blockid No Current block ID on registry (if registry is a blockchain)
  5. Sign the JSON with the private key corresponding to the public key (this enables the node to verify that the operation is coming from the owner of the public key).

    • The proof.verificationMethod must be set to #key-1 (a relative reference) since the DID does not yet exist.
  6. Submit the operation to a node (e.g., POST /api/v1/did/).

§ Agent Create Example

{
    "type": "create",
    "created": "2026-01-14T19:29:06.924Z",
    "registration": {
        "version": 1,
        "type": "agent",
        "registry": "hyperswarm"
    },
    "publicJwk": {
        "kty": "EC",
        "crv": "secp256k1",
        "x": "LRrQabMIkvGVTA2IRk0JdWCpu57MNGm89nugrBZHo24",
        "y": "KHsWAaidAIGCosDjRYDIk-94793e4xVEL4UwFxjWgB8"
    },
    "proof": {
        "type": "EcdsaSecp256k1Signature2019",
        "created": "2026-01-14T19:29:06.927Z",
        "verificationMethod": "#key-1",
        "proofPurpose": "authentication",
        "proofValue": "qNT0EhtojDxOJBh71pddmWnMharQZJxOelW71ehFfuZqrqPls32zSP4bD2CyYNEvAXSJRA-3X5DwR1vHVyTPHw"
    }
}

Upon receiving the operation, the node must:

  1. Verify the proof.
  2. Apply JCS to the operation object.
  3. Pin the seed document to IPFS.

The resulting content address (CID) in standard CID v1 base32 encoding is used as the DID suffix. For example the operation above corresponds to CID bafkreig6rjxbv2aopv47dgxhnxepqpb4yrxf2nvzrhmhdqthojfdxuxjbe, yielding the DID:

did:cid:bafkreig6rjxbv2aopv47dgxhnxepqpb4yrxf2nvzrhmhdqthojfdxuxjbe


§ Create an Asset DID

To create an asset DID, the client must sign and submit a create operation to a node. Unlike an agent, an asset does not possess its own keys — it is controlled by an existing agent.

  1. Create an operation object with these fields in any order:

    Field Required Description
    type Yes Must be "create"
    registration.version Yes Version number, e.g. 1
    registration.type Yes Must be "asset"
    registration.registry Yes A valid registry identifier
    controller Yes The DID of the owner/controller agent
    data Yes Any non-empty JSON data payload
    created Yes ISO 8601 timestamp
    blockid No Current block ID on registry (if blockchain)
  2. Sign the JSON with the private key of the controller.

    • The proof.verificationMethod must be the full DID reference of the controller (e.g., did:cid:abc123#key-1).
  3. Submit the operation to a node (e.g., POST /api/v1/did/).

§ Asset Create Example

{
    "type": "create",
    "created": "2026-01-14T19:32:24.354Z",
    "registration": {
        "version": 1,
        "type": "asset",
        "registry": "hyperswarm"
    },
    "controller": "did:cid:bagaaieradidcs4hohalzexldr5mdmbmt553tqq3ifqd56mvhifppvyfdc32q",
    "data": {
        "group": {
            "name": "testgroup",
            "members": []
        }
    },
    "proof": {
        "type": "EcdsaSecp256k1Signature2019",
        "created": "2026-01-14T19:32:24.375Z",
        "verificationMethod": "did:cid:bagaaieradidcs4hohalzexldr5mdmbmt553tqq3ifqd56mvhifppvyfdc32q#key-1",
        "proofPurpose": "authentication",
        "proofValue": "NGQMBq5venJ2i4F3-Uo0p_rEAlY0zr-YJeTTu7vUlZ0NfyqirIPISGGyy8KU-QrBvsCrfc0fsQm8sh-2BfAzqQ"
    }
}

Upon receiving the operation, the node must:

  1. Verify the proof is valid for the specified controller.
  2. Apply JCS to the operation object.
  3. Pin the seed document to IPFS.

§ DID Update

update operation

A DID Update is a change to any of the documents associated with the DID. To initiate an update, the client must sign an operation that includes the following fields:

Field Required Description
type Yes Must be "update"
did Yes The DID being updated
doc Yes The new version of the document set (may include any of didDocument, didDocumentData, didDocumentRegistration)
previd Yes The CID of the previous operation (collision-prevention hash link)
blockid No Current block ID on registry (if blockchain)

§ Update Flow

  1. Create an update operation object with the fields above.
  2. Sign the JSON with the private key of the controller of the DID.
  3. Submit the operation to a node (e.g., POST /api/v1/did/).
NOTE

It is recommended that the client fetches the current version of the document and metadata, makes changes to it, then submits the new version in an update operation in order to preserve fields that should not change.

§ Key Rotation Example

{
    "type": "update",
    "did": "did:cid:bagaaieradidcs4hohalzexldr5mdmbmt553tqq3ifqd56mvhifppvyfdc32q",
    "previd": "bagaaieradidcs4hohalzexldr5mdmbmt553tqq3ifqd56mvhifppvyfdc32q",
    "doc": {
        "didDocument": {
            "@context": [
                "https://www.w3.org/ns/did/v1"
            ],
            "id": "did:cid:bagaaieradidcs4hohalzexldr5mdmbmt553tqq3ifqd56mvhifppvyfdc32q",
            "verificationMethod": [
                {
                    "id": "#key-2",
                    "controller": "did:cid:bagaaieradidcs4hohalzexldr5mdmbmt553tqq3ifqd56mvhifppvyfdc32q",
                    "type": "EcdsaSecp256k1VerificationKey2019",
                    "publicKeyJwk": {
                        "kty": "EC",
                        "crv": "secp256k1",
                        "x": "hrpjLquejw7lOE2RVGr1LQ315k0JI1lwlI4WI3t983k",
                        "y": "G2_-Agy95QnIFzW5sa9Ik72vDPeqJ0rqqrxWs3CM49o"
                    }
                }
            ],
            "authentication": [
                "#key-2"
            ],
            "assertionMethod": [
                "#key-2"
            ]
        }
    },
    "proof": {
        "type": "EcdsaSecp256k1Signature2019",
        "created": "2026-01-14T19:29:16.117Z",
        "verificationMethod": "did:cid:bagaaieradidcs4hohalzexldr5mdmbmt553tqq3ifqd56mvhifppvyfdc32q#key-1",
        "proofPurpose": "authentication",
        "proofValue": "LEmM9NGL3b4WBzSUZVy0GOqzZ16KbGydBWfCwRNTmZV-ZRznm9g_09xIszITyB3y2A3DYYYaRp5E_tFegZgBgQ"
    }
}

Upon receiving the operation, the node must:

  1. Verify the proof is valid for the controller of the DID.
  2. Verify the previd is identical to the latest version’s operation CID.
  3. Record the operation on the DID’s specified registry (or forward the request to a trusted node that supports the specified registry).

§ Batch vs. Immediate Registration

For registries such as BTC with non-trivial transaction costs, update operations will be placed in a queue and registered periodically in a batch in order to balance costs and latency. If the registry has trivial transaction costs, the update operation may be distributed individually and immediately. This method defers this tradeoff between cost, speed, and security to the node operators.

sequenceDiagram participant Client participant Node participant IPFS participant Registry Client->>Node: POST update operation Node->>Node: Verify proof and previd Node->>Registry: Record update Registry-->>Node: Confirmation Note right of Registry: Batch or immediate

§ DID Revocation

delete operation

Revoking a DID is a special kind of Update that results in the termination of the DID. Revoked DIDs cannot be updated because they have no controller, therefore they cannot be recovered once revoked. Revoked DIDs can be resolved without error, but resolvers will return a document set with the didMetadata.deactivated property set to true. The resolved didDocument contains only the id field (the DID itself); didDocumentData is returned as an empty object.

§ Revocation Flow

To revoke a DID, the client must sign and submit a delete operation to a node:

Field Required Description
type Yes Must be "delete"
did Yes The DID to be deleted
previd Yes The CID of the previous operation
blockid No Current block ID on registry (if blockchain)

§ Delete Operation Example

{
    "type": "delete",
    "did": "did:cid:bagaaiera7vfnrxrmcvo7prrbmdhpvusroii4y2gir252nzk4jv5nxgkzldha",
    "previd": "bagaaiera7vfnrxrmcvo7prrbmdhpvusroii4y2gir252nzk4jv5nxgkzldha",
    "proof": {
        "type": "EcdsaSecp256k1Signature2019",
        "created": "2026-01-14T19:34:32.170Z",
        "verificationMethod": "did:cid:bagaaieradidcs4hohalzexldr5mdmbmt553tqq3ifqd56mvhifppvyfdc32q#key-1",
        "proofPurpose": "authentication",
        "proofValue": "YUTouPmhHDSudPSJ9iU44HdzBYDm7cqmDmanhgDLa4A3MBNiJpbWL2Db4BbzDYQ4NjJCRDWixYZOT2ojzzBHI3c"
    }
}

Upon receiving the operation, the node must:

  1. Verify the proof is valid for the controller of the DID.
  2. Verify the previd is identical to the latest version’s operation CID.
  3. Record the operation on the DID’s specified registry (or forward the request to a trusted node that supports the specified registry).

§ Post-Revocation Resolution

After revocation is confirmed on the DID’s registry, resolving the DID will return:

{
    "didDocument": {
        "id": "did:cid:bagaaiera7vfnrxrmcvo7prrbmdhpvusroii4y2gir252nzk4jv5nxgkzldha"
    },
    "didDocumentMetadata": {
        "deactivated": true,
        "created": "2026-01-14T19:32:24Z",
        "deleted": "2026-01-14T19:34:33Z",
        "versionId": "bagaaierats6ttxvpx2l3tat25ota7z7335akfd2iup5loajsdlqcwismkgpq",
        "versionSequence": "2",
        "confirmed": true,
        "isOwned": false
    },
    "didDocumentData": {},
    "didDocumentRegistration": {
        "version": 1,
        "type": "asset",
        "registry": "hyperswarm"
    },
    "didResolutionMetadata": {
        "retrieved": "2026-01-14T19:36:09.115Z"
    }
}

The metadata deactivated field is set to true to conform to the DID-CORE specification for DID Document Metadata.

WARNING

Revocation is irreversible. Once a DID is deactivated, there is no controller to sign a recovery operation. Ensure all credentials and references have been migrated before revoking a DID.

§ DID Resolution

resolution

Resolution is the operation of responding to a DID with a DID Document. If you think of the DID as a secure reference or pointer, then resolution is equivalent to dereferencing.

Given a DID and an optional resolution time, the resolver retrieves the associated seed document from IPFS using the DID suffix as the CID, parsing it as plaintext JSON.

§ Resolution Options

The did:cid method supports the following resolution options per DID-CORE:

Option Type Description
versionTime ISO 8601 datetime Resolve the DID document as it existed at or before this point in time
versionSequence integer Resolve at a specific operation sequence number (1-indexed from creation)
versionId CID string Resolve at the operation identified by this specific CID

If no option is specified, the resolver returns the most recent confirmed version.

NOTE

versionId accepts the CID of any operation in the DID’s operation chain, enabling pinpoint resolution at any historical state. This is the most precise resolution mode — versionTime and versionSequence both reduce to a versionId lookup internally once the target operation is identified.


§ Resolution Algorithm

graph TD A["Input: DID, versionTime"] --> B["Extract CID suffix from DID"] B --> C{"Retrieve seed from IPFS?"} C -->|Failure| D["Forward to trusted fallback node"] C -->|Success| E{"Valid seed document?"} E -->|No| F["Return error"] E -->|Yes| G{"Known subject type?"} G -->|No| F G -->|Yes| H{"Registry supported?"} H -->|No| I["Forward to trusted registry node"] H -->|Yes| J["Generate initial document from seed"] J --> K["Retrieve update operations from registry"] K --> L{"For each update lte versionTime"} L -->|Proof valid| M["Apply update to document"] L -->|Invalid| N["Skip update"] M --> O["Return resolved DID document"] N --> O

§ Pseudocode

function resolveDid(did, versionTime=now):
    get suffix from did
    use suffix as CID to retrieve seed document from IPFS
    if fail to retrieve the seed document:
        forward request to a trusted node
        return
    look up did's registry in its seed document
    if did's registry is not supported by this node:
        forward request to a trusted node
        return
    generate initial document from seed
    retrieve all update operations from did's registry
    for all updates until versionTime:
        if proof is valid and update is valid:
            apply update to DID document
    return DID document

§ Fallback and Forwarding

If a node cannot fulfill a resolution request — either because the seed document is unreachable on IPFS or because the DID’s specified registry is not supported — the node must forward the request to a trusted node. The forwarding chain is:

  1. Registry not supported → forward to a trusted node that monitors the specified registry.
  2. No trusted node for registry → forward to a general-purpose fallback node.
  3. IPFS seed unreachable → forward to a node with broader IPFS connectivity.

§ Ordinal Key Ordering

Update records from the registry are ordered by an ordinal key. This ensures deterministic resolution regardless of node synchronization timing.

§ Proof Verification

temporal resolution

When verifying a proof on a credential or other signed object, the verifier must resolve the signer’s DID at the time the proof was created. This is essential for supporting key rotation — credentials signed with an old key must remain verifiable even after the signer rotates to a new key.

The proof.created timestamp serves two purposes:

  1. Records when the proof was made (audit trail).
  2. Anchors verification to the correct historical key state.

§ Verification Algorithm

function verifyProof(object):
    extract signerDid from proof.verificationMethod
    resolve signerDid at versionTime = proof.created
    get publicKey from resolved DID document
    verify signature using publicKey
    return valid or invalid

This temporal resolution ensures that a credential issued in 2020 can still be verified in 2030, even if the issuer has rotated keys multiple times since issuance.

NOTE

While the W3C Data Integrity specification makes proof.created optional, did:cid requires it to support proper verification after key rotation.

§ Verification Method Format

The proof.verificationMethod field identifies which key was used to create the proof. The format depends on the operation type:

Operation verificationMethod Format Example
Agent create Relative reference #key-1 (DID doesn’t exist yet, proof is self-referential) #key-1
Asset create Full DID reference of the controller did:cid:abc123#key-1
Update / Delete Full DID reference of the controller did:cid:abc123#key-N
sequenceDiagram participant Verifier participant Resolver participant IPFS participant Registry Verifier->>Verifier: Extract signer DID and proof.created Verifier->>Resolver: resolveDid(signerDid, versionTime) Resolver->>IPFS: Retrieve seed document IPFS-->>Resolver: Seed document Resolver->>Registry: Get updates up to versionTime Registry-->>Resolver: Update history Resolver->>Resolver: Reconstruct document at versionTime Resolver-->>Verifier: DID document with active key Verifier->>Verifier: Verify signature with historical key

§ DID Recovery

For security reasons, this method provides no support for storing private keys. We recommend that clients use BIP-39 to generate a master seed phrase consisting of at least 12 words, and that users safely store the recovery phrase.

If a user loses a device that contains their wallet, they should be able to install the wallet software on a new device, initialize it with their seed phrase, and recover their DID along with all their credentials. This requires an identity backup — an encrypted backup asset linked from didDocumentData — that stores the agent’s credentials and relationships encrypted with the DID’s current private key.

§ Recovery Process

graph LR A["Lost device"] --> B["Install wallet"] B --> C["Enter BIP-39 seed"] C --> D["Derive HD keys"] D --> E["Recover DID"] E --> F["Resolve DID doc"] F --> G["Key rotation update"] G --> H["DID recovered"]
WARNING

The backup/restore pattern requires the user to retain access to their seed phrase. If both the device and the seed phrase are lost simultaneously, DID recovery is not possible — this is an intentional security property, not a bug.

§ Security Considerations for Recovery

  1. Seed phrase secrecy — The BIP-39 seed phrase is the single point of failure for key recovery. It must never be stored digitally in plaintext.
  2. Backup encryption — The identity backup is encrypted with the DID’s current key. After key rotation, the backup must be re-encrypted with the new key.
  3. No controller delegation — Because an asset DID is controlled by exactly one agent, there is no multi-sig recovery path. The agent’s seed phrase is the sole recovery mechanism.

§ Archon Extensions to DID Core

The did:cid method introduces three structural elements that extend the DID-CORE data model. These extensions are not part of the base DID specification; they are Archon-defined additions that enable the method’s key design goals: subject type distinction, pluggable registry anchoring, and an open application data layer.


§ didDocumentRegistration

didDocumentRegistration

DID-CORE defines no mechanism for a DID method to attach method-specific configuration to a DID document. The did:cid method adds a didDocumentRegistration object to the document set for this purpose. It is present on every did:cid DID and is populated at creation time:

{
  "didDocumentRegistration": {
    "version": 1,
    "type": "agent",
    "registry": "hyperswarm"
  }
}
Field Description
version Protocol version number. Enables forward-compatible evolution of the method.
type DID subject type: "agent" or "asset". Determines the resolution and update rules that apply.
registry The registry used to record update operations. Only one registry is active at a given time.

The registry field is the binding between a DID and its update ledger. Resolvers use it to determine where to look for update operations. The registry may be changed by the controller via a valid signed update operation — only the most recently confirmed registry is active. A change of registry does not invalidate operations previously recorded on the prior registry; those remain part of the verifiable operation chain.


§ DID Subject Types: Agent and Asset

Most DID methods treat all DIDs identically — every DID is a self-controlled, key-bearing entity. The did:cid method introduces a formal distinction between two subject types, reflecting the natural hierarchy found in credential ecosystems.

agent are key-bearing DIDs. An agent:

asset are keyless DIDs. An asset:

This distinction enables a verifiable ownership graph: any observer can resolve an asset DID and determine its current controller, then resolve the controller to verify the controlling agent’s current key state. Transfers are recorded in the operation chain and resolvable at any historical point in time.

NOTE

The agent/asset distinction is expressed in didDocumentRegistration.type. Resolvers use this field to apply the correct creation, update, and resolution rules for the DID.


§ didDocumentData

didDocumentData

DID-CORE defines a service property for attaching typed service endpoint references to a DID document — external URIs pointing to services associated with the DID subject. didDocumentData serves a distinct purpose: it is an inline structured data store for arbitrary JSON state that must be cryptographically bound to the DID itself, versioned alongside it, and resolvable at any point in its history. The did:cid method adds didDocumentData to the document set as an open extension point. Its content is not constrained by the method specification — any Keymaster feature or application layer can read and write properties within it using standard DID update operations.

The general pattern is that each higher-level feature reserves a named property within didDocumentData:

{
  "didDocumentData": {
    "<feature-key>": { ... }
  }
}

Because didDocumentData is updated via the same signed update operations as the rest of the DID document, all changes are anchored to the registry, versioned via the operation chain, and resolvable at any historical point in time via temporal resolution.

§ Known Uses

The following didDocumentData properties are used by the Archon platform as of this writing. This list is illustrative, not exhaustive — new Keymaster features will continue to add properties over time:

Property Feature Description
vault Identity backup DID reference to an encrypted backup asset containing the agent’s credentials and relationships; enables full identity recovery from seed phrase alone
group.vault Encrypted storage vault
manifest DID Manifest Selectively disclosed public credentials (described below)
nostr Nostr integration Agent’s Nostr identity (npub, public key)
contact Identity metadata Human-readable name, Bitcoin address, and other contact fields

Application developers building on did:cid are encouraged to use didDocumentData for any state that must be cryptographically bound to a DID, publicly resolvable, and part of the verifiable update history.

§ DID Manifest

The DID Manifest illustrates the didDocumentData pattern well because it has a clearly defined structure and a two-mode API. It is one specific feature built on didDocumentData — not an architectural primitive in its own right.

The manifest is an object within didDocumentData.manifest where each key is the DID of a held verifiable credential and each value is the manifest entry for that credential. It supports two disclosure levels:

Mode reveal Content Description
Publish false Credential DID reference Announces credential ownership without exposing content
Reveal true Full Verifiable Credential Makes the complete credential publicly verifiable by any resolver

Manifest entries are managed via the Archon Keymaster. Each valid operation generates a DID update that modifies didDocumentData.manifest and is anchored to the registry.

A partial example of a resolved DID with a revealed credential in the manifest:

{
  "didDocumentData": {
    "manifest": {
      "did:cid:bagaaiera...cred1": {
        "@context": ["https://www.w3.org/2018/credentials/v1"],
        "type": ["VerifiableCredential", "ArchonSocialNameCredential"],
        "id": "did:cid:bagaaiera...cred1",
        "issuer": "did:cid:bagaaiera...issuer",
        "issuanceDate": "2026-02-03T00:12:20.000Z",
        "credentialSubject": {
          "id": "did:cid:bagaaiera...agent",
          "name": "@flaxscrip",
          "platform": "archon.social"
        },
        "proof": {
          "type": "EcdsaSecp256k1Signature2019",
          "created": "2026-02-03T00:12:20.000Z",
          "verificationMethod": "did:cid:bagaaiera...issuer#key-1",
          "proofPurpose": "assertionMethod",
          "proofValue": "..."
        }
      }
    }
  }
}

Revealed credentials are independently verifiable by any resolver using the standard temporal resolution algorithm — no interaction with the issuer is required.

NOTE

The manifest pattern is particularly relevant for AI agents operating in multi-agent systems. An agent that reveals a role credential and a principal relationship credential enables any peer agent or service to verify its authorization by resolving its DID — without out-of-band communication or centralized lookup.

WARNING

Revealing a credential in the manifest is permanent at the registry history level. Removing it via unpublish removes it from the current DID document, but it remains visible in any historical version resolvable via temporal resolution.

§ Security Considerations

This section describes the security properties of the did:cid method and addresses the security requirements of DID-CORE Section 10.

§ Threat Model

The did:cid method is designed to resist the following classes of adversary:

The method does not claim security against:


§ Self-Certifying Identifiers

self-certifying identifier

The DID suffix of a did:cid DID is the CID (Content Identifier) of the JSON-canonicalized creation operation. This makes every did:cid DID a self-certifying identifier: a resolver can independently verify that a given creation operation corresponds to a claimed DID by computing its CID and comparing it to the DID suffix. No trusted third party is required to validate the binding between the DID and its initial public key.

Any modification to the creation operation — including the public key, timestamp, or registration metadata — produces a different CID, and therefore a different DID. This property prevents silent substitution of the creation anchor.


§ Operation Chain Integrity

operation chain

All update and delete operations include a previd field containing the CID of the previous operation in the sequence. This forms an operation chain — a tamper-evident hash chain anchored at the self-certifying identifier. An adversary who does not control the signing key cannot:

Node implementations MUST reject any update or delete operation whose previd does not exactly match the CID of the most recently accepted operation for that DID.


§ Registry Security and Finality

The integrity of DID update history depends on the registry specified in the creation operation. Different registries provide different finality and Byzantine fault-tolerance guarantees:

Registry Finality Model Considerations
Bitcoin mainnet Probabilistic; ~6 confirmations (~60 min) Highest economic security; reorganization risk decreases exponentially with block depth
Bitcoin Signet / Testnet Same model; lower economic stake Suitable for development and testing; not appropriate for production identity
Hyperswarm P2P DHT-based ordering Faster settlement; weaker Byzantine fault tolerance; suitable for lower-stakes updates
Ethereum Probabilistic PoS finality (~12 s slots) High economic security; large validator set; EVM-compatible smart contract anchoring
Zcash PoW probabilistic finality Strong privacy properties; shielded transaction support
Solana PoH / Tower BFT; ~400 ms slots Very high throughput; low transaction costs; suitable for high-frequency update patterns
Filecoin EC consensus; ~30 s epochs Storage-native anchoring; aligns with IPFS-based creation layer

The registry field may be changed by the controller via a valid signed update operation — only the most recently confirmed registry is active at any given time. A registry change does not invalidate operations previously recorded on the prior registry; those remain part of the verifiable operation chain and are still consulted during historical resolution. Resolvers MUST follow the current registry for new operations and MUST consult prior registries when replaying the operation history up to any point before the registry change.

NOTE

Node operators SHOULD document the registries they support and their trusted peer node policies. Resolvers that do not support a DID’s specified registry MUST forward the resolution request to a trusted node rather than returning a partial or stale result.


§ Cryptographic Operations

All operations MUST apply the JSON Canonicalization Scheme (JCS) to the operation object before computing its CID and before signing. Failure to apply canonicalization consistently may cause the same logical operation to produce different CIDs depending on JSON serialization order, leading to resolution failures or operation rejection.

The current specification requires EcdsaSecp256k1Signature2019 for all proofs. Implementing nodes MUST:

  1. Verify the proof signature is cryptographically valid before accepting any create, update, or delete operation.
  2. Verify the signing key was the active controller key at the time the operation was submitted.
  3. Reject operations with unknown or unsupported proof.type values.

§ Key Management

The did:cid method makes the following key management design decisions:

After a key rotation (via an update operation), credentials signed with the previous key remain verifiable through temporal resolution — the historical key state is preserved and resolvable at any prior version time.

Clients SHOULD:


§ Proof Verification Requirements

The did:cid method requires the proof.created field in all signed objects. While the W3C Data Integrity specification treats proof.created as optional, did:cid mandates it because temporal resolution requires a creation timestamp to determine which historical key state to use for verification.

Verifiers MUST resolve the signer’s DID at the time the proof was created (versionTime = proof.created) rather than at the current time. Resolving at the current time after a key rotation may produce a different active key, causing valid historical proofs to fail verification.


§ Revocation Finality

DID revocation (via a delete operation) is permanent and irreversible. After a revocation is confirmed on the DID’s registry:

This finality is an intentional security property. It prevents scenarios where an attacker who later recovers an old key attempts to “un-revoke” a DID and take control of its associated credentials and assets.


§ Node Trust Model

DID resolution may involve forwarding requests to trusted peer nodes when a resolver does not directly support the DID’s registry. The following risks apply to this trust model:

Mitigations:


§ Keymaster and Gatekeeper Trust Model

The did:cid method separates key custody from network operations into two distinct components with a well-defined trust boundary.

The Keymaster holds private keys exclusively on the client and signs all operations locally before submission. The Gatekeeper is the network-facing node that submits operations to IPFS and registries and serves DID resolution responses.

Key trust properties of this separation:


§ Availability and Denial of Service

IPFS availability: DID resolution for a newly created DID requires that its creation operation be retrievable from IPFS. Node operators MUST pin creation operations for all DIDs they are responsible for. Operators SHOULD also arrange for redundant pinning (e.g., via Filecoin or a pinning service) to protect against single-node failure.

Registry unavailability: Temporary registry unavailability causes resolution to return the most recently known state rather than failing. This is a graceful degradation, not a hard failure, and is consistent with the method’s design.

Creation spam: DID creation requires only an IPFS pin and a valid signature; there is no on-chain transaction required at creation time. Node operators SHOULD implement rate limiting on creation endpoints to prevent resource exhaustion from spam creation.

Update queue costs: For registries with non-trivial transaction costs (e.g., Bitcoin mainnet), nodes may batch update operations. Operators SHOULD implement queue management policies that prevent unbounded accumulation of pending updates.


§ Cryptographic Algorithm Agility

The proof.type field in all operations specifies the cryptographic algorithm used. The current method version defines EcdsaSecp256k1Signature2019. Future versions of the method specification may introduce additional proof types (e.g., Ed25519Signature2020, BLS12-381 for threshold schemes).

Existing DIDs using EcdsaSecp256k1Signature2019 are unaffected by the introduction of new proof types. Nodes MUST continue to support all historically accepted proof types to preserve backward compatibility of temporal resolution for existing DIDs.


§ Residual Risks

The following risks remain after the mitigations described in this section:

Risk Likelihood Impact Mitigation
Private key compromise Low (with secure local storage) High — attacker can update or revoke the DID Key rotation; monitor for unauthorized update operations
BIP-39 seed phrase exposure Low (with physical security) Critical — unrecoverable if device is also lost Hardware wallet; physically separate offline backup
Blockchain reorganization Very low (mainnet, ≥6 confirmations) Medium — brief resolution inconsistency Await sufficient confirmations before relying on an update
IPFS content unavailability Low (with active pinning) High — resolution failure for affected DIDs Multi-provider pinning; redundant node infrastructure
Trusted node compromise Low Medium — stale or incorrect DID data returned Multi-node resolution; independent registry verification

§ Privacy Considerations

This section addresses the privacy implications of the did:cid method in accordance with DID-CORE Section 10 and applicable W3C privacy guidelines.


§ IPFS Permanence

The creation operation for every did:cid DID is stored on IPFS as a content-addressed object. This has fundamental privacy implications:

Implementations MUST NOT include sensitive personal data in the creation operation beyond what is required for DID function (the public key and registration metadata).


§ Public DID Resolution

All did:cid DID documents are publicly resolvable by any party with access to a node. The full contents of didDocument, didDocumentData, and didDocumentRegistration are visible to any resolver without authentication.

Implementations MUST NOT store unencrypted sensitive personal information in didDocumentData. Data that must be associated with a DID but kept private SHOULD be stored in an encrypted form — either as an identity backup asset or within a vault — so that it is visible as ciphertext to resolvers but inaccessible without the appropriate key.


§ DID Manifest and Selective Disclosure

The DID Manifest provides a mechanism for agents to voluntarily publish verifiable credential publicly. Manifest usage is entirely opt-in — no credentials appear in the manifest unless the controller explicitly adds them.

Agents have two disclosure modes available:

WARNING

Once a credential is revealed in the manifest and the corresponding update operation is confirmed on the registry, the credential becomes part of the permanent update history. Removing the credential from the manifest via unpublish removes it from the current DID document but does not affect historical versions, which remain resolvable via temporal resolution. Agents MUST carefully consider the permanent nature of credential revelation before using the reveal mode.


§ Vaults

A vault is a shared encrypted file store associated with a group DID. Vault items are accessible to group members via individually derived keys. The did:cid method supports two membership configurations with distinct privacy properties:

Standard membership — the member list is stored in plaintext within didDocumentData.group.members. Membership is publicly visible to any resolver.

Secret membership — the member list is encrypted and stored in didDocumentData.group.encryptedMembers. Each member receives their own individually derived access key. The following privacy properties hold:

Secret membership vaults are well suited to use cases that require both shared access and anonymity among participants — such as whistleblower submission systems, blind peer review, and anonymous committees.

NOTE

Identity backup is a separate mechanism from the Vault. An identity backup is an encrypted asset DID referenced from didDocumentData.vault, containing the agent’s credentials and relationships encrypted with the agent’s own private key. It is a single-controller backup/restore mechanism, not a shared store. See DID Recovery for details.

Both the vault and the identity backup store encrypted data that is publicly visible as ciphertext to any resolver. Neither exposes plaintext content without the appropriate key.


§ Pseudonymity

did:cid DIDs are pseudonymous by default. The DID suffix — a CID derived from the creation operation — contains no information about the controller’s real-world identity. A freshly resolved DID reveals only:

Real-world identity is associated with a DID only when the controller explicitly discloses identifying information — for example, by revealing a name credential in the DID Manifest, or by including identifying data in unencrypted didDocumentData. This disclosure is voluntary and controlled entirely by the DID subject.


§ Correlation Risks

DID-level correlation: All activity signed with the same DID is attributable to the same controller by any observer who knows the DID. Controllers who wish to limit correlation across contexts SHOULD use separate DIDs for separate relationships or roles.

Public key correlation: The public key embedded in a did:cid creation operation is visible to IPFS participants from the moment of creation. Reuse of the same cryptographic key material across multiple DIDs — which is not recommended — would enable correlation across those DIDs even if they are otherwise unrelated.

Registry transaction patterns: Update operations recorded on public blockchains are permanently and publicly associated with the DID. The timing, frequency, and size of updates may reveal behavioral patterns even when the content of operations is not sensitive.

Asset DID linkage: Every asset DID contains a controller field referencing its controlling agent DID. This creates a publicly visible, permanent ownership relationship. Applications that create many asset DIDs for a single agent should be aware that the full set of assets controlled by that agent is publicly enumerable by traversing the controller references.


§ Data Minimization

Implementations SHOULD apply the principle of data minimization throughout the DID lifecycle:


§ Third-Party Privacy

When a did:cid node forwards a resolution request to a trusted peer node, the peer node learns the DID being resolved and the network address of the requesting node. This creates a metadata record of resolution activity at the peer node.

Node operators SHOULD:

Controllers who are concerned about resolution metadata leakage SHOULD run their own node directly connected to the relevant registry, eliminating the need for request forwarding.

Table of Contents
,
,