did-btcr2-js

btcr2 resolve

Resolves the DID document of a did:btcr2 identifier and prints the resolution result on stdout. The command is read-only and needs no keystore: it never touches the keystore, never asks for a passphrase, and never reads the session. The identifier encodes the network. So resolve works with no config against the public defaults of each network: the mempool.space Esplora REST endpoints and the public https://trustless-gateway.link IPFS gateway for CAS reads.

The CLI drives the sans-I/O Resolver state machine through @did-btcr2/api. The api fetches the beacon signals from the Bitcoin REST endpoint. With --btc-signal-discovery fullnode, it scans blocks over Bitcoin Core RPC instead. The api fetches a genesis document, a CAS announcement, or a signed update from the configured CAS by hash, if the sidecar data does not supply it. Use -r or -p to pass resolution options (a version pin, sidecar data, a discovery limit).

Synopsis

btcr2 resolve [options] -i <identifier>
btcr2 read [options] -i <identifier>          # 'read' is an alias

btcr2 resolve -i did:btcr2:k1qq...
btcr2 resolve -i did:btcr2:x1qh... -r '<json>'
btcr2 resolve -i did:btcr2:x1qh... -p <path-to-json-file>
btcr2 resolve -i did:btcr2:x1qh... --genesis-document ./genesis.json

There are no subcommands and no arguments. The required -i flag carries the identifier.

Options

Flag Value Default Description
-i, --identifier <identifier> A did:btcr2 identifier string: did:btcr2: and a Bech32m body. The HRP is k (deterministic, a 33-byte compressed secp256k1 public key) or x (external, a 32-byte genesis document hash). The encoded network must be one of bitcoin, testnet3, testnet4, signet, mutinynet, regtest. none (required) The identifier to resolve. The command decodes and validates it before any I/O. A malformed identifier fails at once (for example Invalid did: <value>). An identifier with a reserved network value (6 to 11) or a custom network value (12 to 15) fails at decode with Invalid network (reserved): <n> or Invalid network (custom network not supported): <n> (ADR 107).
-r, --resolution-options <json> An inline JSON string. See “Resolution options JSON” below for the shape. none The resolution options, passed to the resolver as they are. Non-JSON input fails with Invalid resolution options. Must be a valid JSON string. (INVALID_ARGUMENT_ERROR). If both -r and -p are present, -r wins and the command ignores -p without a message.
-p, --resolution-options-path <path> The path of a file with the same JSON shape as -r. none The file form of -r. An unreadable path or non-JSON content fails with Invalid resolution options path. Must be a valid path to a JSON file. (INVALID_ARGUMENT_ERROR).
--min-conf <n> A positive integer (minimum 1). Another value fails at parse time with --min-conf must be a positive integer (minimum 1). 6 (the specification value) The minimum number of block confirmations that a beacon signal needs before resolution applies it (ADR 105). The flag overrides a minConf inside -r or -p. Pass 1 to see a fresh update after one block.
--genesis-document <path> The path of the JSON genesis document of an external (x) identifier, for example the file that btcr2 genesis build wrote. An unreadable path or non-JSON content fails with Invalid genesis document path. Must be a valid path to a JSON file.. A JSON value that is not an object fails with Invalid genesis document. The file must contain a JSON object.. none Fills sidecar.genesisDocument of the resolution options. The flag wins over a sidecar.genesisDocument inside -r or -p. For a k identifier, the command refuses the flag with --genesis-document applies only to external identifiers (x). before it reads the file (ADR 108).
-h, --help none n/a Print the help of the command and exit.

The validation order (from the source): the command decodes the identifier first, then checks --genesis-document against the identifier type, then parses -r, then -p, then reads the genesis document file. An invalid identifier therefore fails before the command looks at a bad options string.

The --help text of resolve matches the source.

Resolution options JSON (the -r or -p value)

The JSON object is the ResolutionOptions type of @did-btcr2/method. Each field is optional. An empty object {} equals no options.

Field Type Meaning
versionId string The version of the DID document to resolve, as an ASCII string of an integer. The versions start at "1", the genesis document. The resolver stops before it applies the update that yields the next version. A version that the history does not reach fails with NOT_FOUND. Mutually exclusive with versionTime: a request with both fails with INVALID_OPTIONS.
versionTime string An XML datetime in UTC with the Z designator and no fraction (for example '2026-07-01T00:00:00Z'). The resolver applies each update whose block mediantime (median time past) is at or before that instant, and stops at the first update whose block mediantime is after it. A value in another form fails with INVALID_OPTIONS.
maxDiscoveryRounds number An opt-in upper bound on the number of beacon discovery rounds. Unset, absent, or not positive means no limit. The resolver always stops, because it does not query a beacon address twice. A positive value is a resource guard. A run over the limit fails with INTERNAL_ERROR.
sidecar object The off-chain data bundle. See below.

The sidecar fields:

Field Type Meaning
@context string The optional context string https://btcr2.dev/context/v1.
genesisDocument object The genesis document. An x identifier needs it, unless the api can fetch the document from the configured CAS by its hash.
updates array of SignedBTCR2Update The signed updates. Necessary if the identifier has published updates that the api cannot fetch from the CAS.
casUpdates array of CASAnnouncement The CAS announcements (maps of identifier to signed update hash). Necessary for CAS beacon updates that the api cannot fetch from the CAS.
smtProofs array of SMTProof The SMT proofs (id, collapsed, hashes, optional nonce and updateId, all base64url without padding). Sidecar data is the only channel for an SMT proof. A proof has no content address on chain, so the api cannot fetch it from a CAS. A missing proof fails resolution with MISSING_UPDATE_DATA: SMT proof required but not in sidecar (root hash: ...).

How the @did-btcr2/api layer satisfies each data need:

Output

Exit codes: 0 on success, 1 on an error. Errors go to stderr. A CLI-typed error (an invalid identifier network, bad -r or -p input, a config problem) prints the message only, unless --verbose is set. Then it prints the full structured error. A resolution failure from the api layer (a network failure, missing sidecar data, an unreachable endpoint) is a plain Error with a cause chain. It prints with its stack, with or without --verbose. An identifier that does not decode, also one with a correct prefix but an invalid Bech32m body, fails as a method error of type INVALID_DID and prints as one line (Invalid did: ... or Invalid method-specific id (Bech32m decoding failed: ...)).

Environment and configuration

resolve reads the network from the identifier. Then it resolves the Bitcoin and CAS endpoints of that network through the standard CLI precedence chain:

flag  >  environment variable  >  profile in config.json  >  built-in default of the network

A blank value at one layer defers to the next layer. It does not mask the next layer.

Profile selection: the --profile <name> flag, else defaults.profile of the config file, else the profile with the name of the network of the identifier. A mutinynet identifier selects profiles.mutinynet. resolve does not read defaults.network of the config file. That key steers a command without an identifier, such as create, init, and quickstart. The identifier always fixes the network.

The settings that feed this command:

Setting Flag Env var config.json key Built-in default
Home directory --home <dir> BTCR2_HOME n/a ~/.btcr2 (Linux and macOS). On Windows %LOCALAPPDATA%\btcr2, else %APPDATA%\btcr2
Config file -c, --config <path> none n/a <home>/config.json
Active profile --profile <name> none defaults.profile the network name of the identifier
Output format -o, --output <format> (json | text) BTCR2_OUTPUT defaults.output text
Bitcoin REST endpoint --btc-rest <url> BTCR2_BTC_REST profiles.<name>.btc.rest per network, see below
Bitcoin Core RPC URL --btc-rpc-url <url> BTCR2_BTC_RPC_URL profiles.<name>.btc.rpcUrl http://localhost:18443 (regtest only), none elsewhere
RPC username --btc-rpc-user <user> BTCR2_BTC_RPC_USER profiles.<name>.btc.rpcUser none
RPC password none (never argv) BTCR2_BTC_RPC_PASS profiles.<name>.btc.rpcPass none
RPC password file none BTCR2_BTC_RPC_PASS_FILE none none
RPC wallet --btc-rpc-wallet <name> none profiles.<name>.btc.wallet none
Extra REST headers --btc-rest-header <header> (repeatable, 'Key: Value') none profiles.<name>.btc.headers none
Extra RPC headers --btc-rpc-header <header> (repeatable, 'Key: Value') none profiles.<name>.btc.rpcHeaders none
Beacon signal discovery --btc-signal-discovery <mode> (indexer | fullnode) BTCR2_BTC_SIGNAL_DISCOVERY profiles.<name>.btc.signalDiscovery indexer
Bitcoin timeout (ms) --btc-timeout <ms> (finite number, 1 or more) BTCR2_BTC_TIMEOUT profiles.<name>.btc.timeoutMs no limit
CAS gateway (read-only) --cas-gateway <url> BTCR2_CAS_GATEWAY profiles.<name>.cas.gateway https://trustless-gateway.link
CAS RPC endpoint (writable) --cas-rpc-url <url> BTCR2_CAS_RPC_URL profiles.<name>.cas.rpcUrl none
CAS timeout (ms) --cas-timeout <ms> (finite number, 0 or more. 0 disables the timeout) BTCR2_CAS_TIMEOUT profiles.<name>.cas.timeoutMs 30000

The built-in Bitcoin REST defaults per network (from @did-btcr2/api):

Network REST default
bitcoin https://mempool.space/api
testnet3 https://mempool.space/testnet/api
testnet4 https://mempool.space/testnet4/api
signet https://mempool.space/signet/api
mutinynet https://mutinynet.com/api
regtest http://localhost:3000 (REST), http://localhost:18443 (RPC, no default credentials)

Behavior details, all checked against the source:

Global flags

See the docs README for the shared global flags. resolve uses: -o, --output (text or the JSON envelope), --verbose (the full structured error for a CLI-typed error), the connection overrides (--btc-rest, --btc-rpc-url, --btc-rpc-user, --btc-rpc-wallet, --btc-rest-header, --btc-rpc-header, --btc-signal-discovery, --btc-timeout, --cas-gateway, --cas-rpc-url, --cas-timeout), and the state location flags (--home, -c, --config, --profile). The command accepts --quiet, --keystore, and --passphrase-file, but they have no effect on it.

Examples

# No config: the identifier encodes mainnet ('bitcoin'), so the CLI uses https://mempool.space/api
btcr2 resolve -i did:btcr2:k1qqpyerymt5aaxm2jyh7za2594hgrq24uhqanxe5h94rf42flxkwhvmqd03t47

# The same, through the alias
btcr2 read -i did:btcr2:k1qqpyerymt5aaxm2jyh7za2594hgrq24uhqanxe5h94rf42flxkwhvmqd03t47

# The JSON envelope ({ "action": "resolve", "data": ... })
btcr2 -o json resolve -i did:btcr2:k1qqpyerymt5aaxm2jyh7za2594hgrq24uhqanxe5h94rf42flxkwhvmqd03t47

# Pin one version of the document
btcr2 resolve -i did:btcr2:k1qq... -r '{"versionId":"2"}'

# Resolve the document as it was at a point in time (UTC, no sub-second precision)
btcr2 resolve -i did:btcr2:k1qq... -r '{"versionTime":"2026-07-01T00:00:00Z"}'

# An external (x) identifier with sidecar data from a file
btcr2 resolve -i did:btcr2:x1qh... -p ./resolution-options.json

# An external (x) identifier with the genesis document that genesis build wrote
btcr2 resolve -i did:btcr2:x1qh... --genesis-document ./genesis.json

# Limit the beacon discovery rounds as a resource guard
btcr2 resolve -i did:btcr2:k1qq... -r '{"maxDiscoveryRounds":3}'

# Override the Bitcoin REST endpoint and limit the request time
btcr2 --btc-rest 'https://mutinynet.com/api' --btc-timeout 15000 resolve -i did:btcr2:k1qq...

# Use your own IPFS gateway for the CAS reads
btcr2 --cas-gateway 'http://127.0.0.1:8080' resolve -i did:btcr2:x1qh...

A resolution-options.json for an external identifier with sidecar updates:

{
  "sidecar": {
    "genesisDocument": { "id": "did:btcr2:_", "@context": ["..."] },
    "updates": [ { "patch": [ ... ], "proof": { ... }, "targetVersionId": 2 } ],
    "smtProofs": [ { "id": "...", "collapsed": "...", "hashes": [ "..." ] } ]
  }
}

See also