§ 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:
- agent
- asset
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
- Decentralized creation — DIDs are anchored to IPFS prior to any declaration on a registry, enabling immediate use without on-chain transactions or fees.
- Decentralized updates — DID mutations are registered on a pluggable registry specified at creation time, preserving the decentralization requirement across the entire lifecycle.
- Temporal resolution — DIDs can be resolved at any historical point in time, enabling verification of credentials signed with keys that have since been rotated.
- 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.
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.
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.
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:
-
Generate a new private key
- We recommend deriving a new private key from a Hierarchical Deterministic (HD) wallet (BIP-32).
-
Generate a public key from the private key.
-
Convert the public key to JWK format.
-
Create an operation object with these fields in any order:
Field Required Description typeYes Must be "create"registration.versionYes Version number, e.g. 1registration.typeYes Must be "agent"registration.registryYes A valid registry identifier, e.g. "BTC","hyperswarm"publicJwkYes The public key in JWK format createdYes ISO 8601 timestamp blockidNo Current block ID on registry (if registry is a blockchain) -
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.verificationMethodmust be set to#key-1(a relative reference) since the DID does not yet exist.
- The
-
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:
- Verify the proof.
- Apply JCS to the operation object.
- 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.
-
Create an operation object with these fields in any order:
Field Required Description typeYes Must be "create"registration.versionYes Version number, e.g. 1registration.typeYes Must be "asset"registration.registryYes A valid registry identifier controllerYes The DID of the owner/controller agent dataYes Any non-empty JSON data payload createdYes ISO 8601 timestamp blockidNo Current block ID on registry (if blockchain) -
Sign the JSON with the private key of the controller.
- The
proof.verificationMethodmust be the full DID reference of the controller (e.g.,did:cid:abc123#key-1).
- The
-
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:
- Verify the proof is valid for the specified controller.
- Apply JCS to the operation object.
- 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
- Create an update operation object with the fields above.
- Sign the JSON with the private key of the controller of the DID.
- Submit the operation to a node (e.g.,
POST /api/v1/did/).
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:
- Verify the proof is valid for the controller of the DID.
- Verify the
previdis identical to the latest version’s operation CID. - 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.
§ 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:
- Verify the proof is valid for the controller of the DID.
- Verify the
previdis identical to the latest version’s operation CID. - 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.
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.
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
§ 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:
- Registry not supported → forward to a trusted node that monitors the specified registry.
- No trusted node for registry → forward to a general-purpose fallback node.
- 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:
- Records when the proof was made (audit trail).
- 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.
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 |
§ 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
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
- 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.
- 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.
- 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:
- Possesses a cryptographic key pair
- Controls its own DID document via signed update operations
- Can act as the controller of one or more asset DIDs
- Represents entities that take action: users, issuers, verifiers, nodes, and AI agents
asset are keyless DIDs. An asset:
- Has no cryptographic keys of its own
- Is controlled by exactly one agent DID at any given time (specified in the
controllerfield) - Can be transferred to a new controller via a valid update operation signed by the current controller
- Holds application data in
didDocumentData - Represents entities that are acted upon: verifiable credentials, schemas, presentations, challenges, and responses
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.
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 | |
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.
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.
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:
- Passive observers — parties who can read all IPFS data, registry contents, and network traffic but cannot forge cryptographic signatures
- Active network adversaries — parties who can intercept and modify messages between a client and a node
- Malicious or compromised nodes — nodes that may serve stale, incorrect, or selectively withheld DID data
- Minority registry attackers — a minority coalition of registry validators acting in bad faith
The method does not claim security against:
- An adversary who obtains the controller’s private key
- An adversary who controls a majority of the DID’s specified registry (e.g., a blockchain majority attack)
- A global IPFS outage in which no node retains a copy of the creation operation
§ 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:
- Reorder operations without invalidating the
previdchain - Insert a forged operation without producing a valid signature and a known
previd - Replay a prior update operation, as the current
previdwill have advanced beyond the replayed operation’s target
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.
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:
- Verify the proof signature is cryptographically valid before accepting any create, update, or delete operation.
- Verify the signing key was the active controller key at the time the operation was submitted.
- Reject operations with unknown or unsupported
proof.typevalues.
§ Key Management
The did:cid method makes the following key management design decisions:
- Client-side signing only — Private keys are never transmitted to nodes. Clients sign operations locally and submit only the signed operation and the corresponding public key.
- HD key derivation — Clients SHOULD derive keys using BIP-32 hierarchical deterministic derivation from a BIP-39 seed phrase. Implementations SHOULD use hardened derivation paths to prevent child key exposure from compromising the parent key.
- No server-side key custody — Nodes have no access to private keys and cannot sign on behalf of DID controllers.
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:
- Encrypt private key material at rest using a passphrase-derived key.
- Implement key rotation promptly when a key may have been exposed.
- Store the BIP-39 seed phrase in a physically secure, offline location.
§ 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:
- The DID resolves with
didDocumentMetadata.deactivated: true. didDocumentanddidDocumentDataare returned as empty objects.- No further update or delete operations are accepted for that DID.
- Possession of the original BIP-39 seed phrase does not enable recovery.
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:
- Stale data: A peer node that lags behind the registry may return outdated DID documents, causing verifiers to use superseded keys.
- Malicious forwarding: A compromised node may return incorrect or fabricated DID data to the requesting client.
- Availability dependency: If no reachable node supports a given registry, resolution fails for DIDs using that registry.
Mitigations:
- Resolvers in high-security contexts SHOULD run their own node directly connected to the DID’s specified registry.
- Clients SHOULD validate that the returned document’s
versionIdis consistent with the expected operation chain. - Node operators SHOULD monitor registry sync status and alert on significant lag.
§ 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:
- Keymaster users must trust their Gatekeeper — The Gatekeeper is responsible for faithfully submitting operations to IPFS and registries and for returning accurate resolution results. A compromised Gatekeeper could delay or drop operations, or return stale DID documents. It cannot, however, forge operations, alter signed content, or access private keys.
- Gatekeepers are interchangeable — Because the Keymaster signs all operations locally with keys that never leave the client, a controller can switch to a different Gatekeeper at any time — for better availability, geographic proximity, or greater institutional trust — without any change to their DID or credentials.
- Self-sovereign deployment — Controllers who require maximum trust and control can operate their own Gatekeeper node. This eliminates reliance on any third party while maintaining full compatibility with the network.
- SaaS node assurance — Controllers using a third-party hosted Gatekeeper can do so with the assurance that the node operator cannot access their private keys, cannot sign operations on their behalf, and cannot update or revoke their DID without a valid signature from the controller’s Keymaster.
§ 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:
- Permanent record: Once pinned, the creation operation — including the DID’s initial public key and registration metadata — is permanently accessible to any party with IPFS access.
- No practical erasure: Content addressing makes deletion impossible. Unpinning reduces availability but does not remove cached copies from other nodes that have retrieved the content.
- Public by design: DID creation is an intentionally public act. Controllers should understand that creating a
did:cidDID results in a permanent, globally accessible record.
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:
- Publish (existence-only): Announces that the agent holds a credential without revealing its content. This supports selective disclosure at the presentation layer — a verifier can request presentation of the credential without being able to read it from the DID document.
- Reveal (full data): Makes the complete credential, including all claims and the cryptographic proof, publicly readable by any resolver.
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:
- Resolvers cannot determine who the members are
- Members cannot prove or disprove the membership of other participants
- Items added to the vault do not reveal the identity of the contributor
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.
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:
- That the controller possesses the private key corresponding to the embedded public key
- The controller’s chosen registry
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:
- Include only the minimum necessary data in DID creation operations (public key and registry selection).
- Prefer encrypted storage (identity backup or vault) over plaintext
didDocumentDatafor non-public information. - Use DID Manifest reveal mode only for credentials explicitly intended for permanent public disclosure.
- Use DID Manifest publish mode (existence-only) as a privacy-preserving alternative when full credential data need not be public.
- Consider using separate DIDs for contexts where cross-context correlation is undesirable.
§ 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:
- Minimize logging of resolution requests and retain logs only for the minimum period required for operational purposes.
- Disclose their data retention and forwarding policies to users.
- Consider supporting transport-layer anonymization (e.g., Tor hidden services) for resolution endpoints used in privacy-sensitive contexts.
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.