did-btcr2-js

ADR 072: CLI Writable-CAS Configuration and an Opt-In –publish-to-cas Flag

Status: Accepted

Date: 2026-07-07

Branch / PR: feat/cli-cas-rpc-url

References: ADR 070, ADR 071, ADR 073

Context

ADR 071 wired CAS publication into the api update path behind a publishToCas: 'auto' | 'always' | 'never' policy and gave CasConfig a writable rpcUrl executor. The cli, however, could only configure a read-only CAS: its --cas-gateway flag, BTCR2_CAS_GATEWAY env var, and profiles.<n>.cas.gateway config key all map to a read-only IPFS HTTP gateway, and resolveConnectionConfig hard-typed the CAS it built as { gateway: string }. ADR 071 §7 had the cli pass publishToCas: 'never' explicitly, with a note that a follow-up would “add writable-CAS configuration and a --publish-to-cas flag, at which point the explicit 'never' is replaced by the exposed knob.” This ADR is that follow-up.

ADR 073 lands on this same branch and corrects the api policy so CAS publication is opt-in: the default is 'never' and 'auto' is best-effort (never blocks an update). This cli work assumes that corrected policy.

A separate config-surface gap: btcr2 config set profiles.x.cas.rpcUrl <url> already wrote the key to disk (the config set command is a generic dotted-path writer), but profileToOverrides never read it back, so the value was silently dropped.

The governing principle, from the method’s design and the did:btcr2 spec, is that CAS publication is optional and never required. Every beacon update, including a CAS beacon’s, can be completed and distributed entirely via sidecar. Publishing update artifacts to a content-addressed store is a convenience that makes OP_RETURN update hashes fetchable at resolution time without sidecar data; it is something a user opts into, not a precondition for updating.

Decision

  1. A writable CAS is configurable through the same three-layer override chain as every other endpoint. New --cas-rpc-url <url> global flag, BTCR2_CAS_RPC_URL environment variable, and profiles.<n>.cas.rpcUrl config key, merged in the standard precedence (CLI flag > env var > config file). resolveConnectionConfig now returns cas?: CasConfig (widened from { gateway: string }) and passes both gateway and rpcUrl through when set; the api’s CasConfig priority (rpcUrl > gateway) selects the writable RPC executor when both are present. profileToOverrides now reads cas.rpcUrl, closing the silently-dropped-key gap.

  2. update and deactivate gain --publish-to-cas <auto|always|never>, defaulting to 'never'. The value is validated at parse time (an invalid value errors before any signing or spending) and forwarded verbatim to the api’s publishToCas. This replaces the hardcoded 'never' from ADR 071 §7.

  3. The cli default is 'never', matching the corrected api default (ADR 073). CAS publication stays strictly opt-in. A user who wants it passes --publish-to-cas auto (or 'always') and configures a writable CAS via --cas-rpc-url. With the default, update/deactivate complete sidecar-only and print every artifact a resolver needs (signed update, txid, announcement for CAS beacons, SMT proof for SMT beacons) for the user to distribute.

Consequences

Rejected alternatives