A canonical, chain-agnostic, wallet-signed envelope that applications validate once off-chain and can reference anywhere via a deterministic qHash anchor.
Identities follow CAIP-10 (did:pkh) and chains follow CAIP-2.
Defines a chain-agnostic, wallet-signed proof object anchored by a SHAKE-256 digest (qHash).
Applications validate once off-chain; the same qHash may be surfaced on-chain for indexing/transport via EIP-7683–compatible vouchers.
Non-goal: CAIP-380 is not an authentication/session protocol. It standardizes a portable, canonical wallet-signed envelope for verifiability; authentication is an application-level choice layered above 380 with additional requirements.
Establish a canonical, deterministic envelope that can be validated once and referenced anywhere (off-chain or on-chain) without binding to any vendor, domain, or transport. Align identities with CAIP-10 and chain context with CAIP-2 to avoid namespace collisions.
qHash anchored to a canonical subset to ensure stable, cross-environment equivalence and idempotency.qHash to enable cross-chain transport without constraining settlement designs.The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" written in uppercase in this document are to be interpreted as described in RFC 2119 and RFC 8174.
qHash (SHAKE-256, 32 bytes) computed over the canonical serialization of the Canonical Subset.verifierId.qHash.The Canonical Subset MUST contain exactly the following top-level properties and MUST NOT contain any others:
did:pkh:<chainRef>:<address>; for EVM, did:pkh:eip155:<chainId>:<address>).Chain binding: If chainId is present, the signer DID MUST map to did:pkh:eip155:<chainId>:<address>. If chain is present, the signer DID MUST map to did:pkh:<chain>:<address> where <chain> is a CAIP-2 reference (e.g., solana:mainnet, eip155:1). Exactly one of (chainId, chain) MUST be present.
For EVM signatures (eip191/eip1271/eip6492), producers MUST use chainId; using chain = "eip155:*" with EVM is NOT RECOMMENDED.
Extensibility. Any additional top-level properties MUST be outside the Canonical Subset and therefore excluded from the Anchor. Producers MAY add such properties (e.g., signature, signedMessage, signatureMethod, options, meta), but validators MUST ignore unknown non-canonical properties when computing/validating the Anchor, while they MAY apply additional local validation policies to them.
eip155:1, solana:mainnet)did:pkh:<chainRef>:<address>signedMessage and signatureMethod are OPTIONAL (see Conformance).Universal signing: The envelope, Canonical Subset, determinism rules, freshness, and qHash are universal across ecosystems; non-EVM (e.g., Solana) follow the same six-line message with profile-specific bindings.
Default freshness window (applies to all profiles): verifiers MUST reject if signedTimestamp is older than 5 minutes from verification time or more than 60 seconds in the future (clock-skew allowance).
Signatures MUST use EIP-191 (personal_sign) over the exact six-line message below.
Signature encoding: signature MUST be 0x-prefixed, lowercase hex (applies to EIP-191, EIP-1271, and EIP-6492).
Six-line signer message (ABNF):
Binding rules.
line2 address MUST equal the lowercased address component of did.line3 MUST equal chainId.line4 MUST equal the ASCII join of verifierIds with "," (no spaces).line5 MUST equal the deterministic (canonical) JSON bytes of data as defined in Deterministic JSON. It MUST be the exact byte sequence used inside the Canonical Subset's data portion.line6 MUST equal signedTimestamp.did.Address Case. Implementations MUST compare addresses case-insensitively. For canonicalization and binding, the address component of did MUST be normalized to lowercase. Examples in this CAIP show lowercase addresses.
Freshness window.
Verifiers MUST reject if signedTimestamp is older than 5 minutes from verification time or more than 60 seconds in the future (clock-skew allowance). Implementations SHOULD make this window configurable, but MUST default to these values.
Verification time is the validator’s local wall clock; implementations SHOULD use a synchronized time source (e.g., NTP).
Deterministic JSON (MUST): See the following normative rules.
All canonicalization in this CAIP follows a JCS-style profile (RFC 8785).
Scope. The rules apply to: (1) the Canonical Subset object, and (2) the "data" object contained within it. Non-canonical, top-level extension properties are not included in the Anchor and MUST NOT be fed into the Canonical Subset digest.
Objects. Keys MUST be UTF-8 and lexicographically sorted by Unicode code point, with no duplicate keys.
Arrays. Order MUST be preserved as provided.
Values.
undefined MUST NOT appear anywhere in the Canonical Subset or the "data" object. Producers MUST omit such keys entirely.null MUST be preserved as a value if present.Whitespace. No insignificant whitespace MUST be present in the serialized canonical form.
Encoding. Canonical byte sequence MUST be UTF-8.
Recommendation. Implementations SHOULD conform to RFC 8785 (JSON Canonicalization Scheme, JCS) or an equivalent deterministic algorithm to produce canonical bytes.
Signer bytes normalization (Normative): Implementations MUST produce the six-line signer message as UTF-8 without BOM, using LF ("\n", 0x0A) line endings only, and strings MUST be NFC-normalized prior to serialization. This requirement prevents cross-environment drift (e.g., differing newline conventions or BOM insertion).
Canonicalization example (informative)
Input (producer view):
Canonical Subset serialized (bytes fed to Anchor):
qHash = "0x" + hex_lower( SHAKE-256_32( canonical_json( CanonicalSubset ) ) ), where CanonicalSubset is exactly { did, verifierIds, data, signedTimestamp, chainId } serialized per Deterministic JSON.
0x.chainId (EVM) or chain (CAIP-2 string) and the Anchor is computed over whichever is present.qHash;Signatures SHOULD use Ed25519 over the same six-line message (non-EVM, e.g., Solana) with the following substitutions:
Signature encoding: signature SHOULD be base58 (64-byte Ed25519).
ABNF differences:
Binding rules:
did MUST be did:pkh:solana:<ref>:<base58Address> and MUST match Wallet: and Chain:.signatureMethod SHOULD be ed25519.Data: line canonicalization is identical to EVM.Validation sketch: verify Ed25519 over the exact six-line bytes. Freshness and qHash rules are unchanged.
Implementations MUST support contract-based accounts (EIP-1271) and MUST detect/verify EIP-6492 signature wrappers.
Verification algorithm:
chainId (or 6492 proves a counterfactual deployment), treat as a smart account; otherwise treat as EOA.did, fail.isValidSignature(<message-bytes>, <signature-bytes>) on the contract at the DID’s address on the chain identified by chainId (or on the counterfactual proven by 6492). The call MUST return magic value 0x1626ba7e; any other result or a revert MUST be treated as invalid. When using 6492, validators MUST validate the deployment proof per EIP-6492.Notes.
380 and 7683 are complementary: 380 provides a portable, off-chain proof keyed by qHash; 7683 provides cross-chain intent/settlement. Two common compositions:
qHash (and optional verifier summary) in a 7683 intent. Settlement looks up and verifies by qHash.data; the 380 signature attests to it; settlement proceeds per 7683.Example (illustrative):
did, qHash, verifierIds, data, signature, signedTimestamp, and exactly one of (chainId, chain).signedMessage (diagnostic);signatureMethod (default eip191).data).did, and did MUST match chainId or chain per profile.chainId and chain are present, or both are absent.signedTimestamp is older than 5 minutes or more than +60 seconds ahead (clock skew).qHash from the Canonical Subset bytes; reject on mismatch.qHash when persisting, indexing, or transporting.Validators MUST enforce the freshness window (5m TTL, +60s skew) and signer determinism.
Baseline (normative):
data bytes).did; did matches asserted chainId or chain).data) per JCS-style rules.qHash to avoid cross-transport replay.Threats → required behavior (normative):
signedTimestamp is older than 5m or > +60s ahead.qHash as an idempotent key and deduplicate on write/relay.data) bytes.did.did and asserted chainId/chain.Use for authentication (informative, out of scope):
CAIP-380 standardizes a portable proof envelope; it is not an auth/session protocol. If an application chooses to use 380 for login/authN, layer these app-level controls:
Client MUST include in data:
audience: the app origin or identifier (e.g., https://app.example or app:myapp).nonce: cryptographically random, single-use.scope: requested app roles/permissions.Server MUST perform:
qHash.audience equals the expected origin/app ID.nonce unused → mark consumed (store (audience, nonce, did, qHash) briefly).sub=did, aud=audience, qhash, iat/exp; rotate/refresh as desired.qHash and by (audience, nonce); apply rate limits by DID/IP/device.Operational notes (guidance):
Data: and qHash.audience to the app ID/bundle, not a web origin.data; 380 attests to it, it does not perform KYC itself.Implementers are encouraged to consider:
data to what is necessary for verification; avoid including sensitive PII.qHash and high-level verifier summaries.Anchor = qHash(canonical-bytes).isValidSignature returns magic value; 6492 proof valid if used.signedTimestamp within [-5m, +60s]).data and ensure it matches the Data: line bytes.Canonical example envelope (must match attached test vector minimal-1.json):
Non-EVM example envelope (informative; see attached vector minimal-solana-1.json):
did:pkh.qHash anchor is stable across environments as long as deterministic JSON and the canonical subset are followed.Copyright and related rights waived via CC0.