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


Abstract

did:me is a self-certifying DID method for individuals, personas, organizations, and groups. A did:me identifier is derived from an immutable genesis commitment, while current DID state is represented by signed, CID-addressed canonical DAG-CBOR core snapshots and projected into a DID Core compatible JSON DID Document. The method is designed for offline-verifiable resolution, key rotation, optional public directory mirroring, classical and post-quantum verification methods, EUDI wallet interoperability, and privacy preserving presentation workflows.


1. Introduction

did:me is a DID method designed for:

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:

1.1 Conformance

The key words MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD, SHOULD NOT, RECOMMENDED, NOT RECOMMENDED, MAY, and OPTIONAL in this document are to be interpreted as described in BCP 14 [RFC 2119] [RFC 8174] when, and only when, they appear in all capitals, as shown here.

Sections explicitly marked as non-normative, along with all examples and notes in this specification, are non-normative. Everything else in this specification is normative. Normative and informative references are listed in Section 19.


2. DID Format

did:me identifiers are not derived from current keys, core CIDs, or any mutable DID state. They are derived from an immutable genesis commitment that includes controller-chosen entropy, allowing stable rekeying while binding the DID string to exactly one genesis control commitment — the initial controllerKeys, updatePolicy, and nonce (Section 2.5). Because the genesis commitment never changes, the identifier never changes — even as keys rotate or services change — and no party can construct a competing, validly-signed chain for the same identifier without controlling the committed genesis material.

2.1 DID Syntax

A did:me identifier MUST conform to:

did:me:<identifier>

Where <identifier> is the full Bech32-encoded string, including:

The encoding is classic Bech32 as defined in BIP-173 (checksum constant 1). It is not Bech32m (BIP-350, checksum constant 0x2bc830a3); decoders MUST verify the BIP-173 checksum and MUST reject identifiers that only validate under Bech32m.

The identifier MUST be entirely lowercase. Although Bech32 permits an all-uppercase variant, DID method-specific identifiers are case-sensitive, and only the lowercase form is a valid did:me identifier. Resolvers and validators MUST reject identifiers containing any uppercase characters.

The 128-bit identifier payload is derived from the genesis commitment defined in Section 2.5:

Formal representation:

identifier = Bech32.encode(hrp="me", data=convertBits(payload[16], 8-to-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.) For did:me v1, the data portion after "me1" MUST contain exactly 32 characters: 26 data characters derived from the 128-bit identifier payload and 6 Bech32 checksum characters.

2.3 Length

Given a 16-byte identifier payload:

2.4 Example

did:me:me1072e0a7myhee5da50wt3tu2a9sds44xv

This example is taken from the self-contained genesis binding vector in Section 17.6 (published with this specification at https://did-me.org/spec/v1/vectors/), and can be reproduced exactly using the algorithm in Section 2.5.

2.5 Identifier Derivation (Genesis Binding)

The 128-bit identifier payload is a cryptographic commitment to the DID’s initial control material. It is computed from a GenesisBinding object:

GenesisBinding = {
  nonce: bytes(16),                 // controller-chosen 128-bit entropy
  updatePolicy: CoreUpdatePolicy,   // exactly as in the genesis core (Section 5.4)
  controllerKeys: [CoreKey]         // exactly as in the genesis core (Section 5.3)
}

Derivation steps:

  1. Generate a 16-byte nonce, either from a cryptographically secure random number generator, or deterministically from a controller-held secret via a cryptographically strong KDF (e.g., HKDF with a per-DID info string), provided the result carries at least 128 bits of entropy and is unique per DID. The nonce is a public salt, not a secret, so deterministic seed-based derivation — which lets a wallet regenerate its identifier from a recovery seed — does not weaken the binding.
  2. Construct the controllerKeys array and updatePolicy object that will appear in the genesis core (Section 5).
  3. Encode the GenesisBinding object — a map containing exactly the three fields above and no others — using canonical DAG-CBOR as defined in Section 5.6.
  4. Compute:
payload = SHA-256( UTF8("did:me:v1:genesis") || bindingBytes )[0..15]

i.e., the first 16 bytes of the SHA-256 digest of the domain-separation tag concatenated with the canonical GenesisBinding bytes.

  1. Encode payload via Bech32 with HRP "me" as described in Section 2.1.

The controllerKeys and updatePolicy values in the GenesisBinding MUST be identical — element for element and field for field, including array element order — to the controllerKeys and updatePolicy values in the genesis core (sequence = 1). The GenesisBinding deliberately excludes id (which would be circular), controller, services, the relationship arrays, and all signatures, so that the identifier commits to the initial cryptographic control material and nothing else.

Because the identifier is a commitment over these values, every array that participates in the commitment MUST use a deterministic order, so that the reconstruction in Section 2.6 reproduces the exact bytes regardless of how the core was authored:

A GenesisBinding whose controllerKeys order differs from the genesis core — even when the set of keys is identical — produces a different identifier and MUST be treated as non-conformant. (An earlier draft did not pin this order; implementations MUST use the ascending-id order defined here.)

The identifier therefore binds the DID to exactly one genesis control commitment, not to every field of the genesis core: genesis cores that differ only in fields outside the commitment (e.g., controller, services, or relationship arrays) derive the same identifier. Since only the holder of the committed key material can validly sign such variants, this is a self-equivocation case, not a takeover vector; resolvers disambiguate it with the same fork-rejection and first-seen pinning rules that apply to any conflicting snapshots (Sections 11.2 and 11.3).

The nonce prevents the identifier from being a naked fingerprint of public keys (two DIDs created with the same key material still receive unlinkable identifiers), while the hash binding prevents any party who does not control the committed genesis material from presenting a competing genesis for an existing identifier.

2.6 Identifier Verification

Given a did:me identifier and a candidate genesis core, verifiers MUST:

  1. Extract nonce, updatePolicy, and controllerKeys from the genesis core, taking controllerKeys in the core’s canonical ascending-id order (Section 2.5).
  2. Reconstruct the GenesisBinding object — the map { nonce, updatePolicy, controllerKeys } — and encode it using canonical DAG-CBOR (Section 5.6).
  3. Recompute the identifier payload per Section 2.5 and Bech32-encode it.
  4. Confirm the result is byte-identical to the identifier in the DID.

Because the ordering of every committed array is fixed by Section 2.5, this reconstruction is fully deterministic: any conformant verifier derives the same identifier from the same genesis core.

A genesis core whose recomputed identifier does not match MUST be rejected, and any chain built on it MUST be rejected. Section 3.4 defines the assurance levels available when a resolver holds only the current core rather than the full chain.


3. DID Resolution

3.1 Optional Directory-Based Resolution

Controllers MAY publish their DID Document to one or more public directories (e.g., directory.example). If published, a DID Document can be retrieved via:

GET https://directory.example/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.

Directories SHOULD serve DID Documents with the media type application/did+ld+json. Directories MAY additionally serve prior core snapshots by CID, enabling full-chain verification (Section 3.4):

GET https://directory.example/cores/<cid>

When served, historical core snapshots MUST be returned as the raw canonical DAG-CBOR bytes (media type application/octet-stream) or as a base64url-encoded JSON wrapper; in either case the bytes MUST hash to the requested CID.

3.2 HTTP Status Codes

Directories MUST NOT return 404 Not Found for a DID they know to be deactivated; deactivation and absence are distinct states, and conflating them enables a malicious or stale mirror to silently serve a pre-deactivation document (see Section 11.6).

3.3 DID URL Parameters

Resolvers that maintain or can retrieve the core chain SHOULD support the following DID Core resolution parameters:

Resolution results SHOULD populate didDocumentMetadata with versionId (the currentCore CID), and deactivated: true where applicable.

3.4 Verification Assurance Levels

did:me resolution supports two assurance levels:

  1. Attestation-verified (self-contained). Using only the DID Document (which carries coreCbor in the default profile), a verifier confirms that coreCbor hashes to currentCore and that at least the required attestations verify over the core bytes (Section 7.1). This proves the document reflects a core signed by keys listed in that core, but does not by itself prove the chain back to genesis.
  2. Chain-verified (genesis-bound). The verifier retrieves each prior core by CID (via prev links), validates every transition per Section 8.2, and verifies the identifier binding of the genesis core per Section 2.6. This provides the full method-level guarantee: the DID string is cryptographically bound to this exact chain.

For a genesis-state DID (sequence = 1), the two levels coincide: the DID Document is fully self-verifying, including the identifier binding. Verifiers SHOULD perform chain verification when the sequence is greater than 1 and the trust context warrants it (e.g., first encounter with a DID, high-value transactions, issuer onboarding); they MAY cache the verified (genesis CID, currentCore, sequence) tuple and subsequently verify only new transitions.


4. Data Model Overview

A did:me DID Document is a standard DID Document projected from a signed core object and:

4.1 Method-Specific Properties

did:me supports 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:

Method-specific terms fall into two categories:

Generic DID processors that do not understand optional extension metadata MUST ignore that metadata, per DID Core rules. did:me processors MUST NOT ignore core-derived did:me terms or attestations when validating a did:me DID Document.

These properties allow did:me to support richer metadata while preserving DID Core compatibility and forward extensibility.

4.2 Encoding Preferences

did:me uses DID Core-compatible key representations. Public verification keys use the representation required by their verification method, such as publicKeyMultibase; signature values and binary proof payloads such as attestations.sig, proof.jws, and coreCbor use base64url where this specification requires it.

4.3 Protocol buffers

Due to the size of post-quantum keys, implementations MAY use a Protocol Buffers serialization as an implementation detail for transport or storage. This specification does not define a normative Protocol Buffers mapping.

The canonical authoritative state is always the signed canonical DAG-CBOR core:

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:

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:

This design provides:

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,                        // CID of previous core.  Present iff sequence > 1.
  nonce: bytes(16),                 // identifier-binding entropy.  Present iff sequence == 1.
  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.

Field presence rules are strict, because any variation changes the canonical bytes and therefore the CID:

A did:me DID MAY have one or more controllers. If only one controller is present, it is typically the DID subject itself. All keys in controllerKeys are controlled by the DID controller set unless a future profile defines per-key controller ownership semantics.

5.3 CoreKey Schema

All of the following are required values:

CoreKey = {
  id: string,                     // e.g., "#ed25519"
  type: string,                   // "Multikey" for all did:me v1 keys (Section 5.3.1)
  algorithm: string,              // key-type identifier, e.g., "Ed25519", "P-256", "ML-DSA-87", "X25519", "ML-KEM-768"
  publicKeyMultibase: string
}

Keys listed here define:

All key relationship references (authenticationKeys, assertionKeys, keyAgreementKeys, and allowedVerificationMethods in updatePolicy) MUST refer to a CoreKey.id present in controllerKeys. Section 15 defines a stricter, normative default profile for public did:me v1 interoperability; those profile requirements may constrain this base model further.

5.3.1 Public Key Encoding (Normative)

publicKeyMultibase values MUST be encoded as Multikey: the multicodec varint prefix for the key type, followed by the raw public key bytes, encoded with multibase base58btc (prefix z). The following multicodec codes, as registered in the multicodec table, are normative for did:me v1:

Algorithm Multicodec name Code Raw key bytes
Ed25519 ed25519-pub 0xed 32
X25519 x25519-pub 0xec 32
P-256 (ES256) p256-pub 0x1200 33 (SEC1 compressed)
secp256k1 secp256k1-pub 0xe7 33 (SEC1 compressed)
ML-DSA-44 mldsa-44-pub 0x1210 1312 (FIPS 204)
ML-DSA-65 mldsa-65-pub 0x1211 1952 (FIPS 204)
ML-DSA-87 mldsa-87-pub 0x1212 2592 (FIPS 204)
ML-KEM-768 mlkem-768-pub 0x120c 1184 (FIPS 203)
ML-KEM-1024 mlkem-1024-pub 0x120d 1568 (FIPS 203)

Elliptic-curve public keys (P-256, secp256k1) MUST use the SEC1 compressed point form. Implementations MUST reject a publicKeyMultibase value whose multicodec prefix does not match the declared algorithm, or whose decoded key length does not match the table above. The genesis conformance vector (Section 17.6) carries a worked publicKeyMultibase (Multikey) encoding for each supported algorithm.

Some of these multicodec entries (notably the ML-DSA and ML-KEM codes) currently carry draft status in the upstream multicodec table. did:me v1 pins the code values above normatively: they remain the did:me v1 encodings even if the upstream table later changes an entry’s status, name, or value. Any future change would require a new did:me method version or profile.

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.
  threshold?: uint                      // minimum number of valid attestations required to authorize an update.
                                        // OPTIONAL; when absent, the threshold is 1.
}

When present, threshold MUST be an integer greater than or equal to 1 and less than or equal to the number of entries in allowedVerificationMethods; cores violating this constraint are invalid and MUST be rejected. When the threshold key is absent it MUST be entirely absent from the encoded CBOR map, and the effective threshold is 1. Threshold semantics for update validation are defined in Section 8.2.1.

allowedVerificationMethods MUST be non-empty in every active core. An empty allowedVerificationMethods array (with the threshold key absent) is permitted only in a terminal deactivation core (Section 8.3), where it means that no verification method can ever authorize a subsequent update.

A threshold of 2 or more provides true hybrid AND-control: for example, listing an ML-DSA-87 and an Ed25519 verification method with threshold: 2 requires both a post-quantum and a classical signature on every update, so an attacker must break both schemes (or compromise both keys) to forge a state transition.

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
}

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 DAG-CBOR:

For example, the top-level core map keys are encoded in the order: id, nonce (genesis) or prev (updates), sequence, services, controller, updatePolicy, assertionKeys, controllerKeys, keyAgreementKeys, authenticationKeys.

This ensures:

Implementations MUST reject cores containing indefinite-length items or map keys not in the canonical order defined above. Note that a plain lexicographic sort of the raw key strings (ignoring length) produces a different ordering and therefore different CIDs; implementations MUST NOT use it.

5.7 CID Derivation

The CID is computed as follows:

This CID is published as:

"currentCore": "<cid>"

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:

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:

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.9.4 Relationship to X.509 Trust Chains and SD-JWT VC / VP (Non-Normative)

The eIDAS 2.0 / EUDI architecture does not require DIDs: its primary trust rails are X.509 certificate chains anchored in trusted lists, with SD-JWT VC and ISO mdoc as credential formats and OpenID4VCI / OpenID4VP as protocols. did:me is designed to complement these rails rather than replace them, in three ways:

Holder and subject binding. A did:me identifier can serve as the stable credential subject across wallet migrations and key rotations. SD-JWT VC holder binding (the cnf claim) can carry a JWK corresponding to a key published in the did:me document — the #p256 assertion key is a P-256 key that can produce ES256 signatures, so the same WSCD- or Secure Enclave-resident key can back both the SD-JWT key-binding JWT and the DID verification method. During presentation, the Key Binding JWT is signed with that key, and a verifier can confirm the key against the DID Document (including fully offline, via the embedded coreCbor).

Issuer identity and X.509 bridging. An issuer can anchor its did:me identity to its eIDAS-recognized legal identity by dual binding: an X.509 end-entity certificate (issued under a trusted-list chain) that certifies the same P-256 key listed in controllerKeys, and/or carries the DID in a subjectAltName URI entry, while the DID Document carries the corresponding domainVerification entry in the other direction. In this pattern the X.509 chain provides the eIDAS-recognized trust path, and the did:me chain provides what certificates alone do not: key continuity across certificate renewals, a stable issuer identifier independent of any single CA relationship, and post-quantum attestations over the issuer’s key state. The DID persists as the root of continuity; the certificate chain supplies the root of legal recognition.

Post-quantum migration bridge. The EUDI ecosystem’s deployed rails are classical (ES256 throughout). A did:me identity carries an ML-DSA-87-anchored control plane underneath those classical interfaces today, so issuers and holders gain post-quantum key-state continuity now, and can adopt future eIDAS PQC credential profiles without changing identifiers, subject bindings, or trust relationships.

5.10 Wallet Binding and Domain Verification

DID Documents MAY include domainVerification entries used for:

These domain bindings are optional metadata and do not alter core state.

5.11 Secure Messaging Pre-Key Discovery

did:me supports asynchronous secure-messaging bootstrap by publishing medium-term key-agreement public keys — messaging pre-keys — in the DID Document, discovered through a dedicated service entry. This replaces server-held pre-key distribution: the pre-key a sender uses is signed into the controller’s core chain, so every sender resolves the same key material, and a directory or delivery server cannot substitute per-sender keys.

This section does not define a messaging protocol. It defines authenticated discovery of controller-published hybrid key-agreement material that can be consumed by direct asynchronous messaging protocols, MLS-based messaging protocols, or future messaging profiles.

A DID Document MAY include a messaging service entry:

{
  "id": "#messaging",
  "type": "MessagingService",
  "serviceEndpoint": {
    "uri": "https://relay.example.com/inbox/7f3a",
    "preKeys": ["#x25519-msg", "#mlkem768-msg"]
  }
}

Infrastructure authority. Messaging infrastructure MAY relay, cache, advertise, or transport protocol-specific bootstrap artifacts. It MUST NOT be authoritative for cryptographic identity or key-agreement public keys. A sender MUST treat X25519 and ML-KEM messaging keys as authoritative only when they are bound to the signed did:me core snapshot it resolved.

Hybrid combiner selection. The choice between ML-KEM-768 and ML-KEM-1024 belongs to the controller; both satisfy the pre-key set requirement above. When the selected pre-keys are an X25519 key and an ML-KEM-768 key, initiating protocols SHOULD combine them with the X-Wing hybrid KEM [XWING] rather than an ad hoc construction: X-Wing is specifically defined for X25519 and ML-KEM-768, and its combiner and domain-separation label are designed and security-analyzed for exactly this pair. When ML-KEM-1024 is selected for a higher post-quantum security category, protocols MUST NOT describe the resulting construction as X-Wing unless a compatible standardized X-Wing variant exists; they MUST use a KDF-based combiner that binds both shared secrets, both ciphertexts or public keys, and a protocol-specific domain-separation label, and SHOULD migrate to a standardized combiner when one becomes available.

Rotation and retention.

Transcript binding. Initiating protocols SHOULD bind the DID, the core sequence, and the core CID of the snapshot that supplied the pre-keys into the handshake transcript (for example, as associated data). Both parties then cryptographically agree on which published key state was used, and stale-snapshot or downgrade conditions become detectable.

Deniability. Transcript binding is associated data, not an identity signature. Where conversation deniability is a goal, initiating protocols SHOULD authenticate the handshake implicitly through the key agreement itself rather than by signing handshake transcripts with authentication or assertionMethod keys, since an identity-key signature over a conversation transcript is transferable cryptographic evidence that the conversation took place.

Direct and MLS messaging. Pre-key discovery as defined here can bootstrap direct asynchronous messaging sessions or MLS-based conversations. Direct messaging profiles consume the DID-published pre-keys directly. MLS profiles bind MLS credentials and KeyPackages to did:me identifiers — for example, an MLS credential carrying the DID whose published authentication key signs the member’s leaf node. MLS KeyPackages are one-time, consumable objects; they are delivered through the messaging delivery layer, not published in the DID Document. The DID Document’s role in MLS is to authenticate the identity and key state behind each KeyPackage, not to carry MLS group state.

Like other service entries, a MessagingService is part of the core snapshot (Section 5.5): it is immutable within a core version, and changing the delivery endpoint or the pre-key designation requires a signed core update.


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:

These proofs are non-authoritative: the authoritative cryptographic rail is the signature over the canonical DAG-CBOR core snapshot.

DI proofs:

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:

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:

Verification Steps:

  1. Parse the compact JWS into header, payload, signature.
  2. Confirm the protected header contains exactly the single member {"alg":"ES256"}.
  3. Confirm the verificationMethod referenced by the proof is listed in the DID Document’s assertionMethod relationship, per Section 6.2.
  4. Decode payload and confirm it equals currentCore.
  5. Recompute signingInput.
  6. Verify ECDSA P-256 over SHA-256(signingInput) using the referenced P-256 key.

The created timestamp is informational: verifiers MUST NOT treat a proof as invalid solely because of its created value, though trust frameworks MAY layer their own freshness policies on top.

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:

This rule ensures full interoperability across platforms such as Secure Enclave, WebCrypto, and noble-curves.

6.5 Interoperability and Usage Notes


7. Signatures and Attestations

7.1 Core Signature

Each core snapshot MUST be signed by the DID controller using verification methods listed in:

updatePolicy.allowedVerificationMethods

with at least updatePolicy.threshold (default 1) distinct methods contributing valid signatures (Section 8.2.1).

For updates, the signature authorizing the transition is verified against the previously active updatePolicy and controllerKeys. After the update is accepted, the new core snapshot’s updatePolicy governs subsequent updates.

The signature covers a domain-separated signing input constructed from the canonical DAG-CBOR bytes of the core object. The domain-separation tag is the 14-byte UTF-8 string:

DOMAIN_TAG = UTF8("did:me:v1:core")

The signing input is:

coreSigningInput = DOMAIN_TAG || coreBytes

Per-algorithm signing input:

Domain separation ensures a did:me core signature cannot be replayed as a valid signature in any other protocol that signs raw DAG-CBOR content, and vice versa. Signatures computed over raw coreBytes without the domain tag are invalid and MUST be rejected.

These signatures are the authoritative root of trust for did:me and determine:

Core signatures appear in:

"attestations": [
  {
    "alg": "ML-DSA-87",
    "vm": "#mldsa87-root",
    "sig": "<base64url(signature-over-domain-separated-core-signing-input)>"
  }
]

Multiple signatures MAY be present (e.g., Ed25519 and ML-DSA-87) to support multi-suite cryptography, and updatePolicy.threshold (Section 5.4) determines how many of them are required. For attestations.sig, implementations MUST use base64url-encoded signature bytes. Every attestation signature MUST be computed with the domain-separated signing input defined above. The alg value of each attestation MUST equal the algorithm value of the controllerKeys entry referenced by vm — the key’s declared key-type identifier (e.g., "Ed25519", "ML-DSA-87", "P-256"), not a JOSE signature-algorithm name. did:me attestations are domain-separated and are not JWS objects; the JOSE alg register (EdDSA, ES256, …) is used only inside genuine JWS structures such as the DataIntegrity proof of Section 6.

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 the following core state:

sequence = 1
prev     absent
nonce    present (16 bytes)

Creation steps:

  1. Generate the 16-byte nonce (randomly or seed-derived, per Section 2.5)
  2. Construct the genesis controllerKeys and updatePolicy
  3. Derive the identifier from the GenesisBinding per Section 2.5
  4. Construct the initial core object, including id, nonce, and the material from step 2
  5. Encode the core using canonical DAG-CBOR (Section 5.6)
  6. Compute currentCore as CIDv1 (dag-cbor, sha2-256)
  7. Sign the core bytes (with domain separation, Section 7.1) using enough allowed verification methods to satisfy updatePolicy.threshold
  8. Publish the resulting DID Document to an optional directory endpoint, or retain it in a wallet, device, or agent for local resolution

8.2 Update

To update a DID, the update processor MUST enforce all of the following:

new.id == old.id
new.sequence == old.sequence + 1
new.prev == old.currentCore
at least old.updatePolicy.threshold (default 1) attestations over the
  domain-separated core signing input from Section 7.1 verify,
  each under a DISTINCT verification method listed in
  old.updatePolicy.allowedVerificationMethods, using key material from old.controllerKeys

If any check fails, the update MUST be rejected. The new updatePolicy becomes authoritative only after the update is accepted.

A successful update produces:

8.2.1 Threshold and Multiple Controllers

A did:me DID MAY list one or more controllers.

An update is valid if and only if at least threshold attestations (where threshold is the previously active updatePolicy.threshold, defaulting to 1 when absent) each verify under a verification method that:

  1. is listed in the previously active updatePolicy.allowedVerificationMethods,
  2. belongs to at least one currently active controller, and
  3. is distinct from the verification methods satisfying the other counted attestations (multiple attestations by the same verification method count once).

When threshold is 1 (or absent), any single such verification method is sufficient. Deployments that require hybrid AND-control — for example, a post-quantum signature AND a classical signature on every update — set threshold: 2 with both methods listed in allowedVerificationMethods, which makes the dual-signature requirement enforceable at the method layer rather than by application policy. Additional attestations beyond the threshold MAY be included as extra evidence.

8.3 Deactivate

Deactivation is performed by publishing a terminal core: an ordinary core update (Section 8.2) whose content is emptied. A terminal core MUST have exactly the following shape:

The empty allowedVerificationMethods is what makes the core terminal: no verification method can ever satisfy the update rule again, so no subsequent core can be valid. Processors MUST treat any core whose updatePolicy.allowedVerificationMethods is empty as a terminal deactivation record and MUST reject any core claiming a higher sequence for that DID. The internal-reference rules of Sections 5.3 and 5.4 (references resolving into controllerKeys, non-empty policy) apply to active cores; the terminal core is the single defined exception, and a core that empties controllerKeys or allowedVerificationMethods in any other configuration (e.g., empty keys with a non-empty policy) is invalid.

The terminal core MUST carry attestations satisfying the previously active updatePolicy (including its threshold), signed with key material from the previous core’s controllerKeys, exactly as for any update (Section 8.2).

After deactivation:

Deactivation records are terminal records and are not valid active did:me v1 default-profile DID Documents.


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:

These fields MUST reflect the core (or, for keyHistory, the verified core chain) exactly, and MUST NOT introduce information not present in the signed core snapshots. Note that the projected keyHistory array is itself unsigned convenience metadata: validators reconstruct and verify history by walking the prev chain of signed cores, not by trusting the projected array. The consistency checks in Section 15.1 (sequence == keyHistory.count + 1, prev == last keyHistory entry) bind the projection’s shape to the core, but the array’s earlier entries are only proven by chain verification (Section 3.4).

9.2 Method-Specific Extensions (Non-Core Metadata)

The DID Document MAY include information that is not itself part of the core object:

Generic DID processors that do not recognize optional extension metadata MUST ignore that metadata, per DID Core rules. did:me processors MUST evaluate attestations when validating a did:me DID Document. When projecting from core to DID Document JSON, prev MUST be omitted when the core has no prev field (genesis state) and MUST be present as a non-null CID value when sequence > 1. Conversely, nonce MUST be present (base64url-encoded) only for genesis state. attestations are not part of the canonical core object, but they are authoritative evidence for validating the core snapshot. proof remains optional metadata in v1; however, profiles that require a JWS/P-256 proof over the compact currentCore commitment SHOULD include it.

9.3 Domain Verification Objects

Each optional domainVerification entry MUST include:

Resolvers MUST accept at least one valid domain-verification method. Support for both is RECOMMENDED.

9.4 Requirements


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:

All terms defined in the context:

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:


  "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

This section addresses the attack classes identified in RFC 3552 (eavesdropping, replay, message insertion, deletion, modification, and denial of service) as they apply to did:me, along with method-specific risks.

11.1 Chain Integrity and Update Authorization

11.2 Identifier–Genesis Binding

The identifier is a truncated SHA-256 commitment to the genesis control material (Section 2.5), so a party who does not control the original genesis material cannot construct a competing, validly bound chain for an existing did:me identifier. Verifiers obtain this guarantee only at the chain-verified assurance level (Section 3.4); attestation-only verification of a non-genesis document proves internal consistency but not the identifier binding, and verifiers SHOULD perform chain verification on first encounter with a DID.

The commitment covers nonce, controllerKeys, and updatePolicy only. Genesis cores that differ solely in non-committed fields (controller, services, relationship arrays) share the same identifier; because such variants can only be signed by the holder of the committed keys, this is self-equivocation by the controller rather than a takeover vector. Resolvers SHOULD pin the first-seen genesis core CID per DID and treat any conflicting genesis core — whether it differs in committed or non-committed fields — as a fork to be rejected.

Truncating SHA-256 to 128 bits preserves ~2^128 second-preimage resistance against attacks on a specific existing DID. Collision resistance of the truncated hash is ~2^64, but a collision requires the attacker to control both colliding genesis states; it therefore allows an attacker at most to equivocate between two genesis states for a DID they themselves created, which the fork-rejection rules already treat as invalid. It does not enable takeover of any honestly generated DID.

11.3 Freshness and Rollback by Omission

A directory, cache, or attacker replaying old documents can serve a stale-but-validly-signed core — for example, one listing a key that a later rotation retired after compromise. The sequence and prev rules only protect resolvers that have seen the newer state. Resolvers SHOULD cache the highest verified (sequence, currentCore) pair per DID and treat any regression as an error; relying parties with high assurance requirements SHOULD cross-check more than one directory or obtain the latest core directly from the controller (e.g., during a live protocol exchange). Deployments should treat freshness as a residual risk proportional to how long they are willing to accept a cached state.

11.4 Threshold Policy and Post-Quantum Weakest-Link

Update authorization with threshold: 1 is weakest-link: compromise of any single verification method listed in allowedVerificationMethods is sufficient to take over the DID. In particular, listing a classical method (e.g., Ed25519) alongside ML-DSA-87 at threshold 1 reduces the control plane’s post-quantum security to the classical method. Deployments requiring post-quantum control security SHOULD either list only post-quantum methods in allowedVerificationMethods, or list hybrid methods with a threshold of 2 or more so that both a post-quantum and a classical signature are required (Section 5.4). The default profile (Section 15) requires #mldsa87-root to be present; it deliberately does not forbid additional methods, so profile users make this trade-off explicitly.

11.5 Domain Separation and Cross-Protocol Replay

All core attestations are domain-separated with the tag did:me:v1:core (Section 7.1), and the identifier derivation is domain-separated with did:me:v1:genesis (Section 2.5). This prevents a signature or hash computed for another protocol over the same bytes from being replayed into did:me, and vice versa. Future did:me versions or profiles MUST use distinct tags.

11.6 Directory Trust, Deactivation, and Denial of Service

Directories are untrusted conveniences: all authority derives from the signed core chain. Directory connections MUST use authenticated transport (HTTPS) to prevent in-path modification, but transport security does not make a directory’s content authoritative. Two directory behaviors deserve attention: withholding (serving stale state, Section 11.3) and deactivation stripping — continuing to serve the pre-deactivation document after the controller has deactivated. The distinct 410 Gone deactivation state (Sections 3.2, 8.3) makes honest directories unambiguous, but a malicious directory can still misreport; relying parties handling high-value interactions SHOULD confirm liveness with the controller or multiple directories. Directories are also a denial-of-service surface: their unavailability affects discoverability but never DID validity, and controllers SHOULD retain locally resolvable signed cores.

Directories SHOULD additionally publish an append-only, third-party-auditable log of the snapshots they serve (a transparency log). First-seen pinning (Section 11.2) then becomes globally auditable: equivocation between what a directory served to different resolvers is detectable by anyone comparing log inclusion proofs, rather than only by the parties who happened to receive conflicting snapshots. This matters most for high-value key material discovered through directories, such as messaging pre-keys (Section 5.11).

11.7 Key Compromise and Recovery

did:me v1 has no recovery mechanism separate from the update policy itself: if an attacker obtains keys satisfying the active update threshold, they can rotate the update policy and permanently seize the DID; if the controller loses all update-authorized keys, the DID is permanently frozen. Thresholds (Section 5.4) mitigate single-key compromise. Controllers SHOULD protect update-authorized keys with hardware-backed storage where available. A future profile may add pre-rotation commitments (committing to the hash of the next key set inside the current core) to further constrain post-compromise takeover.

Recovery patterns (non-normative). The update-policy machinery already supports device-loss recovery; deployments should choose a tier deliberately at DID creation, since the genesis updatePolicy is part of the identifier commitment:

  1. Seed-derived recovery. Because the nonce MAY be KDF-derived (Section 2.5) and ML-DSA, ML-KEM, Ed25519, and X25519 key generation are deterministic from a seed (FIPS 204/203), a wallet can derive its entire genesis state from a single recovery seed. The seed phrase alone then regenerates the identifier, all keys, and full update capability after total device loss.
  2. Backup key. Listing an additional verification method in controllerKeys and allowedVerificationMethods at creation — held offline or on a second device — allows a single update signed by the backup key to rotate to replacement device keys. This protects against loss but not compromise: at threshold 1, an attacker holding any allowed key can race the controller.
  3. Multi-key threshold (e.g., 2-of-3). Distributing three allowed verification methods across device, offline backup, and a second device or custodian, with threshold: 2, protects against both the loss and the compromise of any single key: losing one key does not freeze the DID, and stealing one key does not permit takeover.

11.8 Cryptographic Agility

11.9 Messaging Pre-Key Forward Secrecy and Replay

Publishing messaging pre-keys in the DID Document (Section 5.11) is a signed-pre-key-only asynchronous handshake model: there are no server-consumed one-time pre-keys. The security consequences are bounded and deployments should understand them:


12. Privacy Considerations

did:me DIDs can represent individuals, pseudonymous personas, organizations, groups, and device- or wallet-mediated identities. Implementations and relying parties MUST treat DID Documents and directory records as potentially privacy-sensitive, even when the values are public or self-asserted.

12.1 Data Minimization

Controllers SHOULD publish only the fields required for resolution, verification, and the intended relying-party interaction. Optional fields such as alsoKnownAs, domainVerification, hardwareBound, biometricProtected, userVerificationMethod, deviceModel, service endpoints, payment addresses, and other metadata can increase linkability or reveal operational details. These fields MUST NOT be required by generic resolvers for DID validity unless a separate application profile explicitly requires them.

Generic did:me DID Documents and core snapshots MUST NOT contain raw biometric templates, private keys, recovery secrets, seed material, or unnecessary personal data. Application profiles that require personal data in DID-related records MUST define separate consent, disclosure, retention, and minimization requirements.

12.2 Correlation and Persona Separation

Stable identifiers and alsoKnownAs values can enable correlation across contexts. Controllers SHOULD use separate did:me identifiers for separate personas, roles, devices, wallets, or unlinkable interaction contexts. Relying parties MUST NOT assume that two did:me identifiers refer to the same subject unless that relationship is explicitly disclosed and independently verified.

Directory operators and resolvers SHOULD avoid creating cross-context indexes that correlate DID identifiers, alsoKnownAs values, service endpoints, device metadata, payment addresses, or domain bindings unless the controller has intentionally published that relationship for the applicable context.

Controllers and implementers SHOULD avoid reusing verification methods, infrastructure accounts, service endpoints, payment addresses, or directory operator accounts across personas or across identity and infrastructure layers unless correlation across those contexts is intentional. When a deployment uses public infrastructure or ledger-facing accounts, those accounts SHOULD be separated from DID controller keys and identity payload signing keys.

12.3 Biometric, Device, and Local Authentication Metadata

Fields such as biometricProtected, hardwareBound, userVerificationMethod, and deviceModel describe authentication environment characteristics. They MUST be treated as non-authoritative metadata, not as proof that a biometric, secure element, hardware model, or local unlock method was actually used. Implementations SHOULD avoid publishing precise device model, biometric modality, or local authentication details unless needed for a specific trust framework, because such details can aid fingerprinting or targeted attacks.

12.4 Directory Publication and Retention

Publishing a DID Document to an optional directory can disclose service endpoints, update timing, key history length, domain relationships, and other metadata. Directory publication SHOULD be opt-in by the controller. Directory operators SHOULD provide deletion or de-listing mechanisms for discoverability records where compatible with the deployment model, SHOULD minimize access logs, and SHOULD avoid retaining resolver query data longer than necessary for abuse prevention and operational integrity.

Resolvers SHOULD support local or wallet-provided resolution from signed core chains so that controllers are not forced to disclose DID usage to a public directory for every verification event.

12.5 Resolver and Presentation Tracking

DID resolution, service endpoint retrieval, credential status checks, revocation checks, or other DID-adjacent resource fetches can reveal when and where a DID is being used. Implementations SHOULD avoid resolution flows that require a controller, issuer, directory operator, or service provider to be contacted during every presentation or verification event when local verification from signed core data is sufficient.

Credential and presentation protocols that use did:me identifiers SHOULD prefer privacy-preserving status, revocation, and presentation mechanisms that do not allow issuers or infrastructure operators to track relying-party interactions. Resolvers MAY use privacy-enhancing transport or trusted resolver services where appropriate, but relying parties SHOULD NOT treat network privacy controls as a substitute for minimizing published metadata and avoiding unnecessary callbacks.

12.6 Key History and Update Metadata

The sequence, prev, currentCore, coreCbor, and keyHistory fields support integrity, replay protection, and auditability, but they can also reveal update cadence and operational events. Implementations SHOULD avoid embedding unrelated personal data in signed core snapshots and SHOULD avoid publishing historical snapshots beyond what is necessary for verifiable state reconstruction and the applicable retention policy. Messaging pre-key rotations (Section 5.11) are ordinary core updates and are therefore publicly visible; rotating on a fixed cadence avoids turning rekey timing into an observable operational signal.

12.7 Service Endpoints and Payment Addresses

Service endpoints, wallet addresses, and domain bindings can identify providers, infrastructure, organizations, or payment activity. Controllers SHOULD use pairwise or context-specific service endpoints when unlinkability is required. Payment address services SHOULD be omitted unless the DID is intentionally used for payment discovery, because public wallet addresses can expose transaction graph information outside the DID system.

Messaging delivery endpoints (Section 5.11) reveal a controller’s relay choice to every resolver. Deployments that need sender or relationship unlinkability SHOULD use shared or oblivious relay endpoints and SHOULD use delivery protocols that authenticate the sender to the recipient inside the encrypted payload rather than to the relay (sealed-sender designs), so that the relay learns at most the recipient of a deposit, not the social-graph edge between two identities.

12.8 Privacy-Preserving Verification

The optional Data Integrity proof over currentCore is intended to support compact commitments and zero-knowledge-compatible workflows. Verifiers SHOULD prefer presentation protocols that disclose the minimum necessary claims and SHOULD NOT require publication of optional metadata merely to satisfy DID resolution. Application profiles SHOULD document any additional metadata they require and the privacy impact of that requirement.


13. 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:

This is enabled by:

These features allow did:me to evolve toward decentralized, distributed, or mirrored resolution models in the future.


14. 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
nonce

hardwareBound
biometricProtected
userVerificationMethod
deviceModel

coreCbor
currentCore
keyHistory

verificationMethod
authentication
assertionMethod
capabilityInvocation
keyAgreement

service(s)
updatePolicy
attestations
proof

15. 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:

15.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.
nonce Conditional No Omit when sequence>1 MUST be present (base64url of the 16-byte genesis nonce) when sequence=1; MUST be omitted otherwise.
hardwareBound No No Omit if null/false Optional metadata. False treated as absent.
biometricProtected No No Omit if null/false Optional metadata. False treated as absent.
userVerificationMethod No No Omit if null Optional (“face”, “pin”, etc.).
deviceModel No No 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. MAY include threshold (Section 5.4); see Section 11.4 before adding classical methods.
attestations Yes No N/A MUST have ≥1 ML-DSA-87 core signature, and at least threshold valid signatures overall.
proof No No Omit if absent Optional in v1; strongly recommended for profiles requiring a JWS/P-256 proof over currentCore.
domainVerification No No Omit if empty Optional DNS/HTTP domain-bindings.

15.2 Null vs Omission Rules Summary

NEVER emit explicit null in the JSON DID Document.

15.3 Required Arrays (non-null)

These MUST always be present in the JSON and MUST NOT be null:

15.4 Optional Arrays

These MAY be omitted entirely:

{
  "domainVerification": [
    {
      "type": "DnsTxtVerification",
      "method": "dns",
      "domain": "example.com",
      "dns": {
        "recordName": "_did",
        "txtValue": "did=me:123..."
      }
    }
  ]
}

or

{
  "domainVerification": [
    {
      "type": "HttpsWellKnownVerification",
      "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.

16. 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:


authentication

Definition:
Keys used to prove control of the DID in interactive protocols.

Used for:

Key types:


assertionMethod

Definition:
Keys used to sign statements made by the DID, including VCs, public claims, and proofs over compact commitments.

Used for:

Key types:


keyAgreement

Definition:
Keys used to derive shared secrets for encrypted channels.

Used for:

Key types:

This specification defines how key-agreement public keys are published, not how they are used: the concrete encryption protocol — e.g., HPKE, X-Wing for X25519 plus ML-KEM-768, another domain-separated hybrid X25519/ML-KEM combiner, direct messaging, or an MLS-based messaging profile — is defined by the application or messaging protocol that consumes these keys. Two parties MUST agree on that protocol out of band or via protocol negotiation; publishing both classical and post-quantum key-agreement keys is what enables hybrid schemes, but does not by itself define one.

Section 5.11 defines how a controller designates specific key-agreement keys as messaging pre-keys, how senders discover them through a MessagingService entry, and the rotation, retention, and hybrid requirements that apply to that use.


capabilityInvocation

Definition:
Keys authorized to change or update DID state. These are the root control keys.

Used for:

Key types:


attestations

Definition:
Signatures over the canonical core snapshot (DAG-CBOR) that validate DID state.
Used for hybrid AND-security.

Used for:

Key types:


proof

Definition:
A non-authoritative P-256 Data Integrity Proof over the compact currentCore commitment, for compatibility with Apple Secp256r1 flows, DI verifiers, and proof systems that consume CID commitments.

Used for:

Key types:

The following table is non-normative policy guidance. References to dual or hybrid control (“AND”) describe deployments that list both verification methods in updatePolicy.allowedVerificationMethods with threshold: 2, which makes the dual-signature requirement enforceable at the method layer (Sections 5.4 and 8.2.1). Rows that omit required default-profile elements describe specialized or private profiles and are not valid did:me v1 default-profile DID Documents unless the requirements in Section 15 are also satisfied.

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 over compact commitments 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 selective-disclosure 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 when a hardware-backed JWS proof is required Ed25519, P-256, X25519, ML-KEM-768, ML-KEM-1024 X25519 + ML-KEM-768 and/or ML-KEM-1024 Ed25519 Single Ed25519 (minimum required signature)
Payment / Wallet DID (optional specialization) secp256k1 + optional P-256 Optional secp256k1, P-256 Rare secp256k1 Single secp256k1 (minimum required signature)

17. Conformance Test Vectors (Informative)

This section provides implementation-oriented conformance vectors. It is informative and does not replace normative requirements elsewhere in this specification.

17.1 @context Canonical Prefix

17.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

17.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

17.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

17.2 coreCbor and currentCore Consistency

17.2.1 Valid

Expected result: ACCEPT

17.2.2 Invalid

Expected result: REJECT

17.3 sequence, prev, and keyHistory

17.3.1 Valid Genesis

"sequence": 1,
"keyHistory": []

prev is omitted in the DID Document projection for genesis.

Expected result: ACCEPT

17.3.2 Invalid Non-Genesis

"sequence": 3,
"keyHistory": ["cidA", "cidB"],
"prev": "cidA"

Expected result: REJECT (prev MUST equal last keyHistory entry for sequence > 1)

17.4 Core Attestation Validation

17.4.1 Valid

"attestations": [
  {
    "alg": "ML-DSA-87",
    "vm": "#mldsa87-root",
    "sig": "<valid-base64url-signature-over-domain-separated-core-signing-input>"
  }
]

Expected result: ACCEPT when:

17.4.2 Invalid

"attestations": [
  {
    "alg": "ML-DSA-87",
    "vm": "#mldsa87-root",
    "sig": "!!!not-base64url!!!"
  }
]

Expected result: REJECT

17.5 Data Integrity Proof Behavior

17.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:

17.5.2 Unsupported Suite Handling

"proof": {
  "type": "DataIntegrityProof",
  "cryptosuite": "unsupported-suite",
  "jws": "header.payload.signature"
}

Expected result:

17.6 Identifier–Genesis Binding

These values are computed from the conformance key vectors using the algorithm in Section 2.5, with the fixed test nonce a0a1a2a3a4a5a6a7a8a9aaabacadaeaf (hex). The full GenesisBinding CBOR bytes and genesis core bytes are published with this specification in didme-genesis.json at https://did-me.org/spec/v1/vectors/ (also in the vectors/ directory of the specification repository).

17.6.1 Valid

nonce (hex):        a0a1a2a3a4a5a6a7a8a9aaabacadaeaf
payload (hex):      7f9597f7db25f39a37b47b9715f15d2c
id:                 did:me:me1072e0a7myhee5da50wt3tu2a9sds44xv
currentCore (CID):  bafyreiflaivklbly4ad6kdgmufu26vfnls2hvvusutlpqrsti2dy7cntdi

A genesis core carrying this nonce, controllerKeys, and updatePolicy, presented for the DID above:

Expected result: ACCEPT (recomputed identifier matches)

17.6.2 Invalid (Wrong Genesis)

The same genesis core presented for any other did:me identifier, or a genesis core whose nonce, controllerKeys, or updatePolicy differ in any byte from the values committed by the identifier:

Expected result: REJECT (recomputed identifier does not match; the entire chain built on this genesis MUST be rejected)

17.6.3 Invalid (Case)

did:me:ME1APVVV5K5A7Y5QA8HSQZWRK9MMVN6NHYT

Expected result: REJECT (uppercase; only the lowercase Bech32 form is a valid did:me identifier)

17.7 Update Threshold

17.7.1 Valid

Previous core has updatePolicy = { allowedVerificationMethods: ["#mldsa87-root", "#ed25519"], threshold: 2 }. The new core snapshot carries one valid ML-DSA-87 attestation under #mldsa87-root and one valid Ed25519 attestation under #ed25519.

Expected result: ACCEPT

17.7.2 Invalid (Below Threshold)

Same previous policy; the new core snapshot carries only the ML-DSA-87 attestation.

Expected result: REJECT (1 < threshold 2)

17.7.3 Invalid (Duplicate Method)

Same previous policy; the new core snapshot carries two attestations both verifying under #mldsa87-root.

Expected result: REJECT (attestations under the same verification method count once)

17.8 MessagingService Entries

17.8.1 Valid

A DID Document lists #x25519-msg (X25519) and #mlkem768-msg (ML-KEM-768) under keyAgreement, and includes:

{
  "id": "#messaging",
  "type": "MessagingService",
  "serviceEndpoint": {
    "uri": "https://relay.example.com/inbox/7f3a",
    "preKeys": ["#x25519-msg", "#mlkem768-msg"]
  }
}

Expected result: ACCEPT

17.8.2 Invalid (Pre-Key Not a keyAgreement Method)

The same service entry, but #x25519-msg appears only under authentication and not under keyAgreement.

Expected result: REJECT (Section 5.11: every preKeys entry MUST reference a verification method in the keyAgreement relationship)

17.8.3 Invalid (Classical-Only Pre-Key Set)

The same service entry, but preKeys lists only #x25519-msg.

Expected result: REJECT for asynchronous session establishment (Section 5.11: a pre-key set MUST reference at least one X25519 key and at least one ML-KEM-768 or ML-KEM-1024 key)

Machine-readable versions of these cases (plus post-quantum-only and dangling-reference variants) are published in the repository at vectors/v1/messaging-service.json.


18. Intellectual Property and Licensing

This specification, the did:me JSON-LD context, and other non-software documents in the did:me specification repository are licensed under the W3C Software and Document License, except where otherwise indicated.

Sample implementations, tooling, and other software contributions in the did:me specification repository are licensed under the Apache License 2.0, except where otherwise indicated.

The did:me method name and related project marks are subject to the trademark notices published with the specification repository. Registration of the did:me DID method in the W3C DID Extensions registry is not a W3C endorsement of the method, its implementations, or any related marks.


19. References

19.1 Normative References

19.2 Informative References


Appendix A. Example DID Document (Non-Normative, Abridged)

The following genesis-state (sequence = 1) DID Document conforms to the did:me v1 default profile (Section 15). It is an abridged rendering: the long post-quantum key values, coreCbor, and signature values are shortened with for readability. The identifier, CID, nonce, key material, and attestations are real; every elided key, CBOR value, and signature is available in complete, machine-readable form in the conformance vectors at https://did-me.org/spec/v1/vectors/ (see Section 17.6).

{
  "@context": [
    "https://www.w3.org/ns/did/v1",
    "https://w3id.org/security/multikey/v1",
    "https://did-me.org/ns/did-me/v1"
  ],
  "id": "did:me:me1072e0a7myhee5da50wt3tu2a9sds44xv",
  "controller": "did:me:me1072e0a7myhee5da50wt3tu2a9sds44xv",
  "sequence": 1,
  "nonce": "oKGio6SlpqeoqaqrrK2urw",
  "coreCbor": "qmJpZHgqZGlkOm1lOm1lMTA3MmUwYTdteWhlZTVkYTUwd3QzdHUyYTlzZHM0NHh2ZW5vbmNlUKChoqOk…",
  "currentCore": "bafyreiflaivklbly4ad6kdgmufu26vfnls2hvvusutlpqrsti2dy7cntdi",
  "keyHistory": [],
  "verificationMethod": [
    {
      "id": "#ed25519",
      "type": "Multikey",
      "controller": "did:me:me1072e0a7myhee5da50wt3tu2a9sds44xv",
      "algorithm": "Ed25519",
      "publicKeyMultibase": "z6Mkf68ponjAdBLZ1rAhxkpiNCqKdVY1…"
    },
    {
      "id": "#mldsa87-auth",
      "type": "Multikey",
      "controller": "did:me:me1072e0a7myhee5da50wt3tu2a9sds44xv",
      "algorithm": "ML-DSA-87",
      "publicKeyMultibase": "z5fhCbCK3ai95oafzBwyf8vKDVhKqVXJ…"
    },
    {
      "id": "#mldsa87-root",
      "type": "Multikey",
      "controller": "did:me:me1072e0a7myhee5da50wt3tu2a9sds44xv",
      "algorithm": "ML-DSA-87",
      "publicKeyMultibase": "z5fhRsyrZ4RFeXUyiJujSc6t64Y9q3ER…"
    },
    {
      "id": "#mlkem1024",
      "type": "Multikey",
      "controller": "did:me:me1072e0a7myhee5da50wt3tu2a9sds44xv",
      "algorithm": "ML-KEM-1024",
      "publicKeyMultibase": "zmsZT6URCA4WuMsV1FJTSVGxEavnq5q3…"
    },
    {
      "id": "#mlkem768",
      "type": "Multikey",
      "controller": "did:me:me1072e0a7myhee5da50wt3tu2a9sds44xv",
      "algorithm": "ML-KEM-768",
      "publicKeyMultibase": "z9LYfmneXVRdaGXEd8DJxofvuTok5kDV…"
    },
    {
      "id": "#p256",
      "type": "Multikey",
      "controller": "did:me:me1072e0a7myhee5da50wt3tu2a9sds44xv",
      "algorithm": "P-256",
      "publicKeyMultibase": "zDnaepCUWe4u5bpxSunUZfScPkVXTWYV…"
    },
    {
      "id": "#x25519",
      "type": "Multikey",
      "controller": "did:me:me1072e0a7myhee5da50wt3tu2a9sds44xv",
      "algorithm": "X25519",
      "publicKeyMultibase": "z6LSfnA88DsBL74TPv7Xate2wQE6rwmk…"
    }
  ],
  "authentication": ["#ed25519", "#mldsa87-auth", "#p256"],
  "assertionMethod": ["#ed25519", "#p256", "#mldsa87-auth"],
  "capabilityInvocation": ["#mldsa87-root", "#ed25519"],
  "keyAgreement": ["#x25519", "#mlkem768", "#mlkem1024"],
  "updatePolicy": {
    "allowedVerificationMethods": ["#mldsa87-root", "#ed25519"],
    "threshold": 2
  },
  "attestations": [
    {
      "alg": "Ed25519",
      "vm": "#ed25519",
      "sig": "V8ZpzyLzvIBa-QPneePA7CQMMKWrz2JaPkK3IwZ_7crmnTzxN0hEyE-BpJOHls6y…"
    },
    {
      "alg": "ML-DSA-87",
      "vm": "#mldsa87-root",
      "sig": "z50-VAN4JkOqGVSQhwt6tff5yee9qskdJe4tjqlM_xPOY1GuA2bKz_CRKRVCte8r…"
    }
  ]
}

Notes: