did:me Method Specification (v1)
Status: Beta
Specification URI: https://did-me.org/spec/v1/
JSON-LD Context:
https://did-me.org/ns/did-me/v1
1. Introduction
did:me is a DID method designed for:
- Stable identifiers for individuals, pseudonymous personas, organizations, and groups
- Identifiers not derived from cryptographic keys, ensuring DID stability across rekeying
- CID-versioned core snapshots that define canonical DID
state
- Classical and post-quantum verification methods
- Compatibility with EU Digital Identity Wallets, OpenID4VCI / OpenID4VP, and ZK-proof circuits
The authoritative DID state is represented by a core object, encoded using canonical DAG-CBOR, hashed to a CID, and signed by the DID controller. The public DID Document is a JSON projection derived from this signed core.
did:me identifiers are fundamentally self-hosted
DIDs whose authoritative state lives on the controller’s wallet,
device, or agent. No central registry is required for validity or
resolution. Decentralization is enabled by the method’s
cryptographic update structure, including:
- Monotonic
sequencecounters for update ordering
prevlinks referencing the CID of the previous core- A verifiable, tamper-evident chain of signed core snapshots
2. DID Format
did:me identifiers are not derived from keys
or core CIDs.
This ensures DID stability even as keys rotate or services
change.
2.1 DID Syntax
A did:me identifier MUST conform to:
did:me:<identifier>
Where <identifier> is the full
Bech32-encoded string, including: - the HRP
"me"
- the separator "1"
- the 5-bit data payload
- the Bech32 checksum
Identifiers are generated by:
- Generating 16 bytes (128 bits) of
cryptographically secure entropy
- Converting the entropy into 5-bit groups (8→5
conversion, padded)
- Encoding the result via Bech32 with HRP
"me"
Formal representation:
identifier = Bech32.encode(hrp="me", data=convertBits(entropy[16], 8→5))
The checksum included in the Bech32 output MUST be preserved, as it provides error-detection and guards against accidental corruption.
2.2 Allowed characters
The data portion of the Bech32 identifier (after “me1”) MUST match Bech32’s lowercase character set:
^[023456789acdefghjklmnpqrstuvwxyz]+$
(Excludes: 1, b, i,
o.)
2.3 Length
Given 16 bytes of entropy: - 16 bytes × 8 bits = 128 bits - Converted to ~26 five-bit groups - Bech32 encoding produces ~26–35 characters, including checksum
2.4 Example
did:me:me1q90w7wd0qpsl6rh6tsg4y0e0a60p3f60g
3. DID Resolution
3.1 Optional Directory-Based Resolution
Controllers MAY publish their DID Document to one or more public directories (e.g., reallyme.directory). If published, a DID Document can be retrieved via:
GET https://reallyme.directory/dids/<id>
This endpoint is optional and not required for DID validity or resolution. Local resolvers MAY reconstruct DID state using only the signed core chain.
3.2 HTTP Status Codes
200 OK— DID Document returned
404 Not Found— DID not registered
4. Data Model Overview
A did:me DID Document is a standard DID Document
projected from a signed core object and: - Uses
verificationMethod, authentication,
assertionMethod, capabilityInvocation,
keyAgreement, and service as defined in
DID Core. - Adds the following method-specific fields: -
sequence — monotonic per-DID update counter
- prev — CID of the previous core (or
null)
- currentCore — CID of the current DAG-CBOR core
object
- keyHistory — ordered list of prior core CIDs -
updatePolicy — rules defining which verification
methods may authorize updates
- attestations — signatures over the core object by the
DID controller
- proof — optional Data Integrity Proof over
currentCore
- domainVerification — optional DNS/HTTP binding the
DID to a domain
4.1 Method-Specific Extension Properties
did:me supports optional, method-specific properties
that extend the base DID Document data model. These properties are
defined in the did:me JSON-LD context:
https://did-me.org/ns/did-me/v1
The context defines terms such as: - currentCore - keyHistory - attestations - updatePolicy - domainVerification - and additional optional metadata fields (e.g., hardwareBound, biometricProtected, deviceModel)
All method-specific terms: - MAY appear in a DID Document - MUST NOT affect the validity of the core snapshot or signature - MUST be ignored by processors that do not understand them, per DID Core rules - MUST NOT change the canonical core object unless explicitly listed in the core schema
These extensions allow did:me to support richer metadata while preserving DID Core compatibility and forward extensibility.
4.2 Encoding Preferences
Did:me should use base64url encoding for keys.
4.3 Protocol buffers
Due to the size of post-quantum keys, did:me documents MAY use Protocol Buffers as an optional (and preferred) transport/storage encoding.
The canonical authoritative state is always the signed canonical DAG-CBOR core: - CID derivation MUST be performed from canonical DAG-CBOR core bytes - authoritative signature verification MUST be performed over canonical DAG-CBOR core bytes
The DID Document JSON is a projection output derived from the core. Protocol Buffers bytes MUST NOT be used for CID derivation or authoritative signature verification.
5. Core Object
5.1 Purpose
The core object is the canonical representation of DID state. It is:
- encoded using canonical DAG-CBOR
- hashed to a CID (CIDv1, SHA-256, base32)
- signed by the DID controller
- used to derive the public JSON DID Document (“projection”)
Although the DID string never changes, all mutable cryptographic state is reflected through successive, signed core snapshots.
currentCore contains the CID of the latest snapshot.
keyHistory contains only the CIDs of prior core
snapshots, in ascending sequence order. prev references
the immediately preceding core CID and enforces canonical update
ordering.
prev enables: - rollback prevention (reject older
snapshots) - replay protection (reject repeated snapshots) - fork
detection (reject alternative update branches)
This design provides: - deterministic state integrity - tamper-evident version history - key rotation without changing the DID - future support for decentralized or mirrored resolvers
The keyHistory array MUST contain only the CIDs of
all prior core snapshots and MUST NOT include the
currentCore CID. The CIDs in keyHistory
MUST be ordered in strict ascending sequence order, representing the
canonical update history from the first snapshot to the most recent
prior snapshot.
5.2 Core Schema (Conceptual)
Core = {
id: string, // "did:me:<id>"
sequence: uint64, // monotonic update counter. This always starts at 1.
prev: CID | null, // CID of previous core. If null, then prev is omitted.
controller: string | string[], // one or more controllers
controllerKeys: [CoreKey], // declared keys
authenticationKeys: [string], // references into controllerKeys
assertionKeys: [string],
keyAgreementKeys: [string],
services: [CoreService], // service endpoints
updatePolicy: CoreUpdatePolicy, // defines which verification methods may authorize updates
}
This schema represents exactly the content protected by the signature. Anything not included here (e.g., proof, domainVerification) is not part of the core and does not affect DID validity.
A did:me DID MAY have one or more controllers. If only one controller is present, it is typically the DID subject itself.
5.3 CoreKey Schema
All of the following are required values:
CoreKey = {
id: string, // e.g., "#ed25519"
type: string, // e.g., "Multikey", "MLDSA87Key2024"
algorithm: string, // e.g., "ES256", "Ed25519", "ML-DSA-87"
publicKeyMultibase: string
}
Keys listed here define: - which verification methods exist - which methods can sign updates (via updatePolicy) - which methods serve authentication, assertion, invocation, or key agreement
All key relationship references (authenticationKeys, assertionKeys, keyAgreementKeys, and allowedVerificationMethods in updatePolicy) MUST refer to a CoreKey.id present in controllerKeys. Section 14 defines a stricter, normative default profile for public did:me v1 interoperability; those profile requirements may constrain this base model further.
5.4 CoreUpdatePolicy
The update policy is part of the canonical core object and defines which verification methods are authorized to sign core updates.
CoreUpdatePolicy = {
allowedVerificationMethods: [string] // references into controllerKeys by id. These strings MUST match a CoreKey.id.
}
5.5 CoreService Schema
CoreService = {
id: string, // "#hub", "#openid", "#wallet"
type: string, // DID Core service type or did:me extension
serviceEndpoint: any, // URI or structured JSON
version?: string
}
Service endpoints are part of the core snapshot and therefore immutable within each version.
5.6 Canonical CBOR Encoding
The core object MUST be encoded using canonical CBOR: - Map keys sorted lexicographically by UTF-8 bytes - Only definite-length arrays/maps - UTF-8 text keys - No undefined or extraneous fields - Deterministic encoding per RFC 8949 / DAG-CBOR restrictions
This ensures: - identical encoding across languages - identical CIDs across implementations - provable state integrity
Implementations MUST reject cores containing indefinite-length items or map keys not in strictly sorted order.
5.7 CID Derivation
The CID is computed as follows:
- Codec:
dag-cbor
- Multihash:
SHA-256
- CID version: v1
- Multibase: base32 (lowercase)
This CID is published as: ~ “currentCore”: “
and used to verify signatures over the core.
5.8 Payment and Crypto Address Services
did:me supports chain-agnostic crypto payment addresses through standard DID Core service entries. These addresses are non-authoritative metadata and are not part of the core update authorization model unless the corresponding public keys also appear in controllerKeys.
A DID MAY include one or more payment-related services such as: • Bitcoin (bech32) • Ethereum and EVM chains (0x-prefixed addresses) • Solana (base58) • Avalanche (X-Chain, C-Chain, or P-Chain formats) • Any other blockchain or digital payment network
Payment addresses MUST be expressed as a structured JSON serviceEndpoint object, for example:
{
"id": "#wallet",
"type": "PaymentService",
"serviceEndpoint": {
"btc": "bc1q...",
"eth": "0x1234...",
"sol": "So1111...",
"avax": "0xabc..."
}
}
5.9 EU Digital Identity Wallet Interoperability
did:me is designed to interoperate cleanly with the European Digital Identity Wallet (EUDI Wallet) architecture, including PID, ARF, and EBSI profiles. DID Documents MAY include service endpoints and proof formats that support credential issuance, presentation, and trust-chain interoperability.
5.9.1 EUDI Wallet Subject Compatibility
did:me identifiers: • are stable, non-key-derived identifiers • support rekeying without subject identifier changes • are fully compatible as subject_id values in PID / ARF data models • remain resolvable even when controller cryptographic material rotates
5.9.2 OIDC4VCI and OIDC4VP Service Endpoints
did:me supports EUDI wallet credential flows via standard DID Core service entries:
{
"id": "#openid-credential-offer",
"type": "OpenID4VCI",
"serviceEndpoint": "<issuer-endpoint>"
}
{
"id": "#oid4vp",
"type": "OpenID4VP",
"serviceEndpoint": "<presentation-endpoint>"
}
Resolvers MAY treat these endpoints as wallet-compatible credential interfaces.
5.9.3 Optional Service Type for EUDI Integration
Implementations MAY define a dedicated service entry for EUDI wallet interactions:
{
"id": "#eudi",
"type": "EudiCredentialService",
"serviceEndpoint": "<eudi-service-url>"
}
This is functionally equivalent to using OpenID4VCI and OpenID4VP, but provides clearer semantic typing for wallets that recognize EUDI-specific services.
5.10 Wallet Binding and Domain Verification
DID Documents MAY include domainVerification entries used for: • binding a DID to a legally recognized domain • proving issuer authenticity • supporting EUDI trust chain requirements
These domain bindings are optional metadata and do not alter core state.
6. Data Integrity Proofs (Optional P-256 Anchor)
6.1 Purpose and Motivation
did:me supports optional
DataIntegrityProof objects using ES256 (P-256).
These proofs provide a classical-cryptography signature anchor suitable for:
- EU Digital Identity Wallet trust infrastructure
- zero-knowledge proof circuits
- verifiers that require standardized P-256 semantics
- environments that cannot process canonical DAG-CBOR
These proofs are non-authoritative: the authoritative cryptographic rail is the signature over the canonical DAG-CBOR core snapshot.
DI proofs: - MAY appear in a DID Document - MUST NOT influence update authorization or determine DID validity
6.2 Proof Object Schema
A did:me DataIntegrityProof using the
es256-jws-cid-2025 cryptosuite appears as follows:
"proof": {
"type": "DataIntegrityProof",
"cryptosuite": "es256-jws-cid-2025",
"proofPurpose": "assertionMethod",
"verificationMethod": "#p256",
"created": "<ISO8601Z>",
"jws": "<compact-jws-with-currentCore-as-payload>"
}
es256-jws-cid-2025 is a did:me-defined custom Data
Integrity cryptosuite profile. Implementations MUST apply the
verification algorithm defined in this specification for this
cryptosuite value.
The proof MUST:
- sign the UTF-8 byte sequence of the currentCore CID string
- NOT sign or canonicalize any other part of the DID Document
- use a verificationMethod listed in assertionMethod
6.3 Cryptosuite: es256-jws-cid-2025
The cryptosuite identifier:
"cryptosuite": "es256-jws-cid-2025"
The es256-jws-cid-2025 suite defines a compact ES256 JWS signature over the currentCore CID string. This cryptosuite is a did:me custom profile and is not required to be supported by generic Data Integrity processors unless they implement this suite definition.
Components:
- Signature type: ECDSA secp256r1 (P-256)
- Representation: compact JWS (header.payload.signature)
- Protected header: The protected header MUST contain exactly the single member {“alg”:“ES256”}.
- Payload: the UTF-8 byte sequence of the currentCore CID string
- Signing input: BASE64URL(header) + “.” + BASE64URL(payload)
- Signature format: JWS ES256 signature bytes in
fixed-length
R || Sform (64 bytes total, base64url-encoded in the JWS signature segment)
Verification Steps: 1. Parse the compact JWS into header, payload, signature. 2. Confirm header.alg === “ES256”. 3. Decode payload and confirm it equals currentCore. 4. Recompute signingInput. 5. Verify ECDSA P-256 over SHA-256(signingInput) using the referenced P-256 key.
This proof MUST NOT be used to authorize DID updates. If a
verifier does not support es256-jws-cid-2025, it MUST
treat this proof as unsupported and MUST NOT treat it as valid.
6.4 ECDSA S-Value Canonicality
ES256 signatures MAY use either the “low-S” or “high-S” form. Both are cryptographically valid and MUST be accepted.
Implementations: - MAY normalize to low-S (s ≤ n/2) - MUST NOT reject high-S signatures - MAY apply normalization internally (s := min(s, n − s))
This rule ensures full interoperability across platforms such as Secure Enclave, WebCrypto, and noble-curves.
6.5 Interoperability and Usage Notes
- DI proofs are optional metadata and do not affect DID validity or update logic.
- Resolvers MUST ignore DI proofs when determining the authoritative core chain.
- Relying parties MAY use DI proofs for assurance or trust-framework requirements.
- DI proofs MAY be safely added, omitted, or mirrored without affecting core state.
- The suite is designed to interoperate cleanly with EUDI Wallets, OpenID4VCI/VP, and ZK-proof circuits.
7. Signatures and Attestations
7.1 Core Signature
Each core snapshot MUST be signed by the DID controller using one of the verification methods listed in:
updatePolicy.allowedVerificationMethods
The signature covers the raw canonical DAG-CBOR bytes of the core object.
These signatures are the authoritative root of trust for did:me and determine: - whether an update is valid - whether rollback, replay, or fork attempts are rejected - whether the currentCore CID matches its signed content
Core signatures appear in:
"attestations": [
{
"alg": "ML-DSA-87",
"vm": "#mldsa87-root",
"sig": "<base64url(signature-over-core-cbor)>"
}
]
Multiple signatures MAY be present (e.g., Ed25519 and ML-DSA-87)
to support multi-suite cryptography. For
attestations.sig, implementations MUST use
base64url-encoded signature bytes.
7.2 Additional Signatures
A DID Document MAY include an optional W3C DataIntegrityProof using ES256 (P-256). This proof is not authoritative for update validation. See Section 6 for the full definition of the DataIntegrityProof and the es256-jws-cid-2025 cryptosuite.
8. Operations
8.1 Create
A new did:me identifier starts with:
sequence = 1
prev = null
Creation steps:
- Construct the initial core object
- Encode the core using canonical DAG-CBOR
- Compute currentCore as CIDv1 (dag-cbor, sha2-256)
- Sign the core bytes using an allowed verification method
- Publish the resulting DID Document at the registry endpoint
8.2 Update
To update a DID, the registry MUST enforce all of the following:
new.id == old.id
new.sequence == old.sequence + 1
new.prev == old.currentCore
signature(new-core-cbor) verifies under new.updatePolicy.allowedVerificationMethods
If any check fails, the update MUST be rejected.
A successful update produces: - a new core snapshot - a new currentCore CID - an appended entry in keyHistory - an updated DID Document projection
8.2.1 Multiple Controllers
A did:me DID MAY list one or more controllers.
An update is valid if and only if its signature verifies under a verification method that:
- is listed in updatePolicy.allowedVerificationMethods, and
- belongs to at least one controller.
Any single such verification method is sufficient.
8.3 Deactivate
To deactivate: - Publish a DID Document with completely empty verification relationships (e.g., empty verificationMethod, authentication, assertionMethod, and capabilityInvocation arrays). - Include a valid core signature from the controller or authorized recovery key.
After deactivation: - The resolver returns 404 Not Found for the DID. - A stored deactivated DID Document MAY still be published in a separate archival namespace for audit or compliance, but does not participate in resolution.
9. DID Document Projection
The DID Document is a JSON projection derived from the signed core. Validation MUST be performed against the core and its signatures, not the DID Document.
9.1 Field Mapping from Core
The following DID Document fields MUST be derived directly from
the core: - id → core.id - controller →
core.controller - sequence → core.sequence -
prev → core.prev - currentCore → CID of
the canonical DAG-CBOR core snapshot - coreCbor →
base64url-encoded canonical DAG-CBOR bytes of the current core
snapshot - verificationMethod → derived from
core.controllerKeys - authentication → derived from
core.authenticationKeys - assertionMethod → derived
from core.assertionKeys - updatePolicy →
core.updatePolicy - capabilityInvocation → derived from
the appropriate core keys - keyAgreement → derived from
core.keyAgreementKeys - service → derived from
core.services. If the core has no services, the service property
MUST be omitted from the DID Document. - keyHistory →
ordered list of prior core CIDs
These fields MUST reflect the core exactly, and MUST NOT introduce information not present in the core snapshot.
9.2 Method-Specific Extensions (Non-Core Metadata)
The DID Document MAY include additional non-authoritative
metadata that does not affect the core state: -
attestations — signatures over the core object -
proof — optional Data Integrity Proof (P-256) over
currentCore - domainVerification — optional DNS/HTTP
domain-binding - any other optional terms defined in the JSON-LD
context at: https://did-me.org/ns/did-me/v1
Processors that do not recognize these fields MUST ignore them,
per DID Core rules. When projecting from core to DID Document JSON,
prev MUST be omitted when core.prev is
null (genesis state) and MUST be present as a non-null CID value
when sequence > 1. proof remains
optional metadata in v1; however, profiles that require ZK anchoring
SHOULD include it.
9.3 Domain Verification Objects
Each optional domainVerification entry MUST include:
- type (string) — the verification object type (“DnsTxtVerification” or “HttpsWellKnownVerification”)
- domain (string)
- method (string)
- binding or proofUrl (depending on method)
Resolvers MUST accept at least one valid domain-verification method. Support for both is RECOMMENDED.
9.4 Requirements
- Reconstructing DID state MUST be possible using only the core snapshot and its signatures.
- The DID Document MUST NOT override or conflict with the contents of the signed core.
- Optional fields MUST NOT influence update authorization or core validity.
- The resolver MUST treat any mismatch between the core and projection as an error.
- If
coreCboris present, decoding it MUST produce canonical DAG-CBOR bytes whose CID equalscurrentCore. - In the did:me v1 default profile (Section 14),
coreCboris required to enable self-contained attestation verification.
10. JSON-LD Context
did:me JSON-LD context provides optional metadata
extensions. Implementations MUST ignore any unrecognized terms,
consistent with DID Core processing rules.
The did:me JSON-LD context is published at https://did-me.org/ns/did-me/v1.
This context defines: - method-specific terms (sequence, prev, currentCore, coreCbor, keyHistory) - metadata terms (updatePolicy, attestations, domainVerification) - service types (e.g., ReallyMeDirectoryService) - optional device- or persona-related extensions (e.g., hardwareBound, biometricProtected, deviceModel)
All terms defined in the context: - MAY appear in DID Documents - MUST NOT influence the authoritative core state unless explicitly part of the core schema - MUST be ignored by processors that do not understand them, per DID Core rules
The JSON-LD context MUST be included in all did:me DID Documents
to guarantee consistent interpretation of method-specific terms. The
first three @context entries MUST be the canonical
did:me context tuple, in this exact order: 1.
https://www.w3.org/ns/did/v1 2.
https://w3id.org/security/multikey/v1 3.
https://did-me.org/ns/did-me/v1 Additional contexts MAY
follow and MUST NOT redefine terms from the canonical tuple.
Conformance checkers and validators SHOULD reject DID Documents
whose additional contexts redefine terms from the canonical
tuple.
10.1 alsoKnownAs (Optional Correlation Identifiers)
alsoKnownAs is an optional DID Core property that
MAY appear in a did:me DID Document. It provides human-readable or
system-assigned alternate identifiers, such as short profile URLs,
usernames, handles, or legacy identifiers. These values: • do not
participate in update authorization • do not affect core validity •
do not alter the canonical core snapshot • MAY be ignored by
resolvers or relying parties
"alsoKnownAs": [
"https://domain/<short-id>"
]
alsoKnownAs is strictly non-authoritative metadata and serves only as an optional correlation convenience.
10.2 Hardware Bound and Biometric Protected
hardwareBound and biometricProtected
are optional metadata fields that describe protection
characteristics of the controller’s authentication environment.
These fields are not authoritative, do not affect core state, and
MAY be ignored by relying parties.
10.3 User Verification Method
userVerificationMethod is optional metadata
describing how a user authenticates locally (face, fingerprint, pin,
passcode, password, iris, voice, pattern, none). This field is
non-authoritative and MUST NOT influence the validity of a core
snapshot.
10.4 Device and Model
deviceModel is optional metadata identifying the
user’s device model or hardware platform. It is purely informational
and does not affect DID state.
11. Security Considerations
- Rollback protection: enforced through monotonic sequence values
- Fork prevention: enforced through prev linking the previous core snapshot
- Tamper-evidence: achieved via CIDv1 (SHA-256, DAG-CBOR) and core signatures
- Authorized updates: restricted to verification methods listed in updatePolicy
- Canonical encoding: core signatures MUST cover the canonical DAG-CBOR bytes
- Projection safety: DID Documents are non-authoritative projections; validators must rely on core + signatures
- Key rotation: does not change the DID; state changes only via new core snapshots
- Backup practices: operators SHOULD maintain redundant storage of core snapshots and DID Documents
- Cryptographic agility: coexistence of classical (Ed25519, ES256) and post-quantum (ML-DSA-87, ML-KEM-768, ML-KEM-1024) suites ensures long-term security
- ECDSA S-Value Forms: ES256 signatures may use either low-S or high-S encoding. Both forms are secure and mathematically equivalent. Verifiers MUST accept both and MAY normalize signatures to low-S form internally for canonicality.
12. Decentralization and Federation
did:me identifiers are fundamentally self-hosted and do not rely on any central registry. Optional public directories may mirror published DID Documents for discovery. The method’s structure supports decentralized and federated deployments without breaking existing identifiers.
The method supports: - Multi-directory federation – multiple independent operators may host equivalent projections of the same DID state for discovery. - Verifiable replay – any resolver can reconstruct canonical DID state by validating the chain of signed core snapshots. - Independent resolution – resolvers do not need to trust any directory, host, or service; all validation derives from the signed core chain.
This is enabled by: - sequence – enforces strict
update ordering
- prev – links each core snapshot to the previous
CID
- currentCore – cryptographic addressing of core
snapshots
- signed canonical cores – guarantee tamper-evident integrity
These features allow did:me to evolve toward decentralized, distributed, or mirrored resolution models in the future.
13. Illustrative did:me JSON member order (non-normative)
JSON member order is non-normative. Producers MAY emit members in any order, and processors MUST NOT rely on JSON key order for validity. ~ @context id controller alsoKnownAs sequence prev
hardwareBound biometricProtected userVerificationMethod deviceModel
coreCbor currentCore keyHistory
verificationMethod authentication assertionMethod capabilityInvocation keyAgreement
service(s) updatePolicy attestations proof ~
14. Default Profile Requirements Matrix (did:me v1)
This section is normative for the did:me v1 default interoperability profile. The base method model is defined in Sections 5 through 9; this profile adds stricter required key sets and relationship requirements for public did:me v1 deployments.
This table defines for every DID Document field:
- Required?: MUST appear for a valid did:me v1
default-profile DID Document
- Nullable?: Whether the field may be
null
- Emit When Empty?: Whether to include the JSON
key when the value is empty
- Notes: Method-specific behavior
14.1 DID Document Field Matrix
| Field | Required | Nullable | Emit When Empty | Notes |
|---|---|---|---|---|
| @context | Yes | No | N/A | The first 3 entries MUST be, in order:
https://www.w3.org/ns/did/v1,
https://w3id.org/security/multikey/v1,
https://did-me.org/ns/did-me/v1. Additional contexts
MAY follow and MUST NOT redefine terms from the canonical
tuple. |
| id | Yes | No | N/A | MUST be a valid did:me: identifier. |
| controller | Yes | No | N/A | MUST be either: (a) a did:me: string, or (b) a
non-empty array of did:me: strings. |
| alsoKnownAs | No | No | Omit if empty | Optional. MUST be an array when present. |
| sequence | Yes | No | N/A | MUST equal keyHistory.count + 1. |
| prev | Conditional | No | Omit when sequence=1 | MUST be omitted when sequence=1; otherwise MUST equal last keyHistory entry. |
| hardwareBound | No | Yes | Omit if null/false | Optional metadata. False treated as absent. |
| biometricProtected | No | Yes | Omit if null/false | Optional metadata. False treated as absent. |
| userVerificationMethod | No | Yes | Omit if null | Optional (“face”, “pin”, etc.). |
| deviceModel | No | Yes | Omit if null/empty | Optional metadata. |
| coreCbor | Yes | No | N/A | Base64url-encoded canonical DAG-CBOR bytes of the current core
snapshot; decoded bytes MUST hash to currentCore. |
| currentCore | Yes | No | N/A | MUST be CIDv1 of the core snapshot. |
| keyHistory | Yes | No | Emit empty array | MUST contain only past CIDs; MUST NOT include currentCore. |
| verificationMethod | Yes | No | N/A | MUST contain all required key types. |
| authentication | Yes | No | N/A | MUST contain at least #ed25519 and
#mldsa87-auth. |
| assertionMethod | Yes | No | N/A | MUST include #p256. |
| capabilityInvocation | Yes | No | N/A | MUST include #mldsa87-root. |
| keyAgreement | Yes | No | N/A | MUST include #x25519 and at least one of
#mlkem768 or #mlkem1024. |
| service | No | No | Omit if empty | Include only when one or more services exist. |
| updatePolicy | Yes | No | N/A | MUST include allowedVerificationMethods containing
#mldsa87-root. |
| attestations | Yes | No | N/A | MUST have ≥1 ML-DSA-87 core signature. |
| proof | No | No | Omit if absent | Optional in v1; strongly recommended for profiles requiring ZK anchoring. |
| domainVerification | No | No | Omit if empty | Optional DNS/HTTP domain-bindings. |
14.2 Null vs Omission Rules Summary
NEVER emit explicit null in the JSON DID
Document.
- Optional fields MUST be omitted entirely when not present.
- Arrays MUST either contain values or be omitted (except
keyHistory, which MUST be an empty array when no entries exist). - Optional booleans (
hardwareBound,biometricProtected) SHOULD be omitted when false unless explicitly set.
14.3 Required Arrays (non-null)
These MUST always be present in the JSON and MUST NOT be null:
- verificationMethod
- authentication
- assertionMethod
- capabilityInvocation
- keyAgreement
- attestations
- keyHistory (may be empty but MUST be present)
14.4 Optional Arrays
These MAY be omitted entirely:
- alsoKnownAs
- service
- domainVerification
{
"domainVerification": [
{
"method": "dns",
"domain": "example.com",
"dns": {
"recordName": "_did",
"txtValue": "did=me:123..."
}
}
]
}
or
{
"domainVerification": [
{
"method": "wellknown",
"domain": "example.com",
"wellknown": {
"uri": "/.well-known/did-configuration.json",
"content": "eyAidHlwZSI6ICJkaWQtY29uZmlnIiB9"
}
}
]
}
domainVerification entries are self-asserted claims added by the DID controller. Resolvers and relying parties MUST NOT assume these claims are accurate. Independent verification MUST be performed by the verifier.
DID Document Relationship Definitions
verificationMethod
Definition:
A list of all public keys the DID controls.
Meaning:
These keys exist and can be referenced by other DID Document
sections.
This list does not define how the keys are used; roles
appear below.
Used for:
- Declaring available keys
- Referencing keys in authentication, assertionMethod, etc.
- Interoperability with DID Core processors
authentication
Definition:
Keys used to prove control of the DID in interactive protocols.
Used for:
- Logging in
- Pairwise authentication
- Messaging identity proofs
- Presentations that require DID control verification
Key types:
- Ed25519
- ML-DSA-87
- P-256
(Never X25519 or ML-KEM; they cannot sign.)
assertionMethod
Definition:
Keys used to sign statements made by the DID, including
VCs, public claims, and ZK-friendly proofs.
Used for:
- Verifiable Credential issuance
- Identity claims
- Public profile statements
- ZK-friendly attestations (P-256)
Key types:
- Ed25519
- ML-DSA-87
- P-256
(Never X25519 or ML-KEM.)
keyAgreement
Definition:
Keys used to derive shared secrets for encrypted channels.
Used for:
- Secure messaging
- Encrypted VC presentation
- Session establishment
- Hybrid encryption (X25519 + ML-KEM-768 + ML-KEM-1024)
Key types:
- X25519
- ML-KEM-768
- ML-KEM-1024
(Never Ed25519 or P-256; those are signing keys.)
capabilityInvocation
Definition:
Keys authorized to change or update DID state. These are the
root control keys.
Used for:
- DID Document updates
- Key rotation
- Recovery operations
- Deactivation
Key types:
- ML-DSA-87 (recommended primary root)
- Ed25519 (optional second root for hybrid AND-control)
(Never X25519 or ML-KEM. P-256 should only be used if Secure Enclave
must sign updates.)
attestations
Definition:
Signatures over the canonical core snapshot (DAG-CBOR) that validate
DID state.
Used for hybrid AND-security.
Used for:
- Proving core snapshot authenticity
- True hybrid root control (Ed25519 AND ML-DSA-87)
- Long-term survivability across cryptographic eras
Key types:
- Ed25519
- ML-DSA-87
proof
Definition:
A non-authoritative P-256 Data Integrity Proof for
compatibility with ZK systems, Apple Secp256r1 flows, and DI
verifiers.
Used for:
- ZK proof integrations
- Apple Secure Enclave-backed identities
- EUDI Wallet compatibility
- JWS-based verifiers
Key types:
- P-256 only
(Does not control DID updates.)
| DID Type | Authentication | Assertion / Proofs | Verification Methods (VM Set) | Key Agreement | Capability Invocation (Root Control) | Attestations (Core Update) |
|---|---|---|---|---|---|---|
| Core Identity (private wallet, root identity) | Ed25519 + ML-DSA-87 + P-256 | ML-DSA-87 + Ed25519 + P-256 (ZK-friendly) | Ed25519, ML-DSA-87, P-256, X25519, ML-KEM-768, ML-KEM-1024 | X25519 + ML-KEM-768 and/or ML-KEM-1024 | ML-DSA-87 AND Ed25519 (dual required) | Required dual (ML-DSA + Ed25519) |
| Public Profile (persona; receives ZK proofs) | Ed25519 + P-256 (+ optional ML-DSA-87) | Ed25519 + P-256 (+ optional ML-DSA-87) | Ed25519, ML-DSA-87, P-256, X25519, ML-KEM-768, ML-KEM-1024 | X25519 + ML-KEM-768 and/or ML-KEM-1024 | ML-DSA-87 only | Optional (P-256 DI proof recommended) |
| Public Profile + Issuer (VC issuer persona) | Ed25519 + ML-DSA-87 + P-256 | ML-DSA-87 (primary) + Ed25519 + P-256 | Ed25519, ML-DSA-87, P-256, X25519, ML-KEM-768, ML-KEM-1024 | X25519 + ML-KEM-768 and/or ML-KEM-1024 | ML-DSA-87 AND Ed25519 | Optional dual or PQ-only |
| Issuer DID (Harvard, banks, orgs) | Ed25519 + ML-DSA-87 + P-256 | ML-DSA-87 (primary) + Ed25519 + P-256 | Ed25519, ML-DSA-87, P-256 | Optional | ML-DSA-87 AND Ed25519 | Optional dual or PQ-only |
| Messaging DID (fast, throwaway, persona-tied) | Ed25519 (+ optional P-256) | None or P-256 if ZK required | Ed25519, P-256, X25519, ML-KEM-768, ML-KEM-1024 | X25519 + ML-KEM-768 and/or ML-KEM-1024 | Ed25519 | None |
| Payment / Wallet DID (optional specialization) | secp256k1 + optional P-256 | Optional | secp256k1, P-256 | Rare | secp256k1 | Optional |
15. Conformance Test Vectors (Informative)
This section provides implementation-oriented conformance vectors. It is informative and does not replace normative requirements in Sections 5 through 14.
15.1 @context Canonical Prefix
15.1.1 Valid
The first three @context entries match the canonical
tuple in order:
"@context": [
"https://www.w3.org/ns/did/v1",
"https://w3id.org/security/multikey/v1",
"https://did-me.org/ns/did-me/v1",
"https://example.org/extra-context"
]
Expected result: ACCEPT
15.1.2 Invalid
Wrong order in first three entries:
"@context": [
"https://w3id.org/security/multikey/v1",
"https://www.w3.org/ns/did/v1",
"https://did-me.org/ns/did-me/v1"
]
Expected result: REJECT
15.1.3 Invalid (Term Redefinition in Additional Context)
Additional context attempts to redefine a canonical term:
"@context": [
"https://www.w3.org/ns/did/v1",
"https://w3id.org/security/multikey/v1",
"https://did-me.org/ns/did-me/v1",
{
"currentCore": "https://example.org/redefinedCurrentCore"
}
]
Expected result: REJECT
15.2
coreCbor and currentCore Consistency
15.2.1 Valid
coreCbordecodes as canonical DAG-CBOR core bytes- CID(core bytes) equals
currentCore
Expected result: ACCEPT
15.2.2 Invalid
coreCbordecodes, but CID(core bytes) does not equalcurrentCore
Expected result: REJECT
15.3 sequence,
prev, and keyHistory
15.3.1 Valid Genesis
"sequence": 1,
"keyHistory": []
prev is omitted in the DID Document projection for
genesis.
Expected result: ACCEPT
15.3.2 Invalid Non-Genesis
"sequence": 3,
"keyHistory": ["cidA", "cidB"],
"prev": "cidA"
Expected result: REJECT (prev MUST
equal last keyHistory entry for
sequence > 1)
15.4 Core Attestation Validation
15.4.1 Valid
"attestations": [
{
"alg": "ML-DSA-87",
"vm": "#mldsa87-root",
"sig": "<valid-base64url-signature-over-coreCbor-bytes>"
}
]
Expected result: ACCEPT when: - vm
exists in verificationMethod - alg matches
verification method algorithm - decoded sig verifies
over decoded coreCbor bytes
15.4.2 Invalid
"attestations": [
{
"alg": "ML-DSA-87",
"vm": "#mldsa87-root",
"sig": "!!!not-base64url!!!"
}
]
Expected result: REJECT
15.5 Data Integrity Proof Behavior
15.5.1 Valid (Supported Suite)
"proof": {
"type": "DataIntegrityProof",
"cryptosuite": "es256-jws-cid-2025",
"proofPurpose": "assertionMethod",
"verificationMethod": "#p256",
"created": "2026-01-01T00:00:00Z",
"jws": "<valid-compact-jws>"
}
Expected result: - ACCEPT only if all
cryptosuite checks pass, including: - type is
DataIntegrityProof - cryptosuite is
es256-jws-cid-2025 - protected JWS header is exactly
{"alg":"ES256"} - payload equals
currentCore - ES256 signature verifies with the
referenced P-256 verification method - proof remains
non-authoritative for update validity
15.5.2 Unsupported Suite Handling
"proof": {
"type": "DataIntegrityProof",
"cryptosuite": "unsupported-suite",
"jws": "header.payload.signature"
}
Expected result: - proof treated as unsupported - proof MUST NOT be treated as valid - core-based DID validity processing continues