did-btcr2-js

ADR 003: Bech32m Encoding for did:btcr2 Identifiers

Status: Accepted

Date: 2025-03-14

Commit: ce4da8d

Context

did:btcr2 DIDs need a compact, unambiguous, human-readable-ish identifier encoding that carries three pieces of information in a single string:

A DID like did:btcr2:k1q5pvrqxrtmu8d... must encode all three plus the 33-byte compressed pubkey (KEY case) or 32-byte SHA-256 hash (EXTERNAL case), with strong error detection so a typo’d DID fails loud instead of dereferencing the wrong entity.

Options considered

  1. Base58check (Bitcoin legacy addresses). Familiar, but weaker error detection than BCH codes and no HRP concept.
  2. Base64url. Compact, but zero error detection and visually ambiguous characters (1/l, 0/O).
  3. Bech32 (BIP173). BCH-code error detection, HRP convention, battle-tested via SegWit addresses. Has a known *q* mutation weakness.
  4. Bech32m (BIP350). Bech32 with the constant changed to eliminate the mutation weakness. Native format for Taproot addresses.

The did:btcr2 spec itself mandates Bech32m for the identifier portion (see the did:btcr2 spec §3.2). This ADR captures our implementation’s adherence to that choice and the two HRP values we assign.

Decision

Bech32m encoding for the entire identifier payload, with two HRPs:

The full DID is did:btcr2:<bech32m-string>. Implementation lives in the Identifier.encode() and Identifier.decode() methods of the @did-btcr2/method package, using the Bech32m codec from @scure/base.

Consequences

Positive

Negative

Explicitly accepted trade-offs

References