did-btcr2-js

ADR 001: Monorepo Structure and Package Boundaries

Status: Accepted

Date: 2025-02-19

Commit: ce4da8d

Context

The TypeScript reference implementation of did:btcr2 composes several distinct concerns: cryptographic primitives, DID-document canonicalization, Bitcoin-transaction construction, Sparse Merkle Tree logic, key management, the protocol layer itself, a high-level SDK facade, and a CLI. Packaging all of those as a single flat library had three obvious problems:

  1. Installable surface. Consumers that only need to resolve a did:btcr2 DID (e.g. a wallet verifying a counterparty’s identity) shouldn’t have to pull in Bitcoin-transaction signing code, a Helia IPFS node, or the CLI’s commander dependency.
  2. Dependency discipline. Without hard package boundaries, lower layers (canonicalization, crypto) would accumulate upward imports from higher layers and become impossible to untangle later.
  3. Parallel iteration. Spec implementers in other languages need to compare against small, focused modules (cryptosuite, identifier codec, SMT). A monolith makes cross-implementation alignment harder than it needs to be.

Options considered

  1. Single flat package. One did-btcr2 package with everything inside. Simplest to publish; worst for consumer surface and dependency discipline.
  2. Two packages (library + CLI). Better than flat but still conflates crypto, canonicalization, Bitcoin logic, and protocol in one.
  3. Layered monorepo with one package per architectural layer.

Decision

Option 3. The repo is a pnpm workspace with nine packages under the @did-btcr2/ npm scope, organized by architectural layer:

common      : types, canonicalization, hashing, JSON patch, errors
├── keypair     : secp256k1 key pairs, BIP340 Schnorr signatures
│   ├── cryptosuite : Data Integrity BIP340 proof creation/verification
│   ├── bitcoin     : Bitcoin Core RPC/REST, sans-I/O protocol layers
│   └── kms         : key management (generate/import/sign/verify, URN IDs)
├── smt         : Optimized Sparse Merkle Tree (no workspace deps)
│
└── method      : core did:btcr2: create, resolve, update, beacons
    └── api         : high-level SDK facade
        └── cli         : commander.js CLI binary

Inter-package references use the workspace protocol (workspace:^). Dependencies flow strictly downward in the layer graph: no upward imports are permitted.

Consequences

Positive

Negative

Explicitly accepted trade-offs

References