Introduction
Many DID methods make resolution a public operation: any party with a DID string can get the DID document. For many use cases, this is acceptable. For other use cases, it is a privacy problem. If any party can get your DID document, any party can use the DID and the document contents to link your interactions. did:btcr2 does not require DID documents or updates to be public. The controller can give them directly to a verifier as sidecar data (did:btcr2 spec, Privacy Considerations).
The Problem with Public Resolution
These are the steps for a typical public DID:
- You give your DID to a verifier.
- The verifier resolves the DID through a public network or service, for example ION nodes or a Universal Resolver instance.
- The verifier sees your full DID document: all public keys, service endpoints, and metadata.
This model works for organizational DIDs that must be public, for example the DID of a company or of a government credential issuer. For individuals, public resolution causes these problems:
- Correlation: If you use one DID with many verifiers, the verifiers can correlate your activities. An observer of the resolution network can also correlate them.
- Metadata leakage: Your DID document shows your services, your key types, and the time of your last update. Together, these values can identify you.
- Surveillance: If a resolution network logs queries, it knows which party resolves which DID.
The btcr2 Sidecar Model
In btcr2, the controller does not have to publish the DID document or its update history to a public registry. The controller can give this data directly to the verifier as sidecar data. The spec defines a sidecar as data for resolution that comes with the DID, not from open retrieval such as IPFS.
How It Works
-
Initial document: For a key-based DID (
kprefix), the resolver makes the initial DID document from the DID itself. The DID contains the public key. For an external DID (xprefix), the DID contains only the SHA-256 hash of a Genesis Document. The resolver must get that document from the controller as sidecar data, or from Content Addressable Storage (CAS) (did:btcr2 spec, Resolve). -
Update history: Each update is a signed BTCR2 Update that contains a JSON Patch. A Beacon Signal on Bitcoin announces the hash of each update. The controller keeps all updates. For aggregate beacons, the sidecar data also contains a Beacon Announcement Map or an SMT proof for each signal (Sidecar Data).
-
Presentation: The controller gives the DID and the sidecar data to the verifier. The resolver of the verifier then does these checks (did:btcr2 spec, Resolve):
- Find each Beacon Signal that spends from a beacon address of the DID
- Check the BIP340 Schnorr signature of each update
- Make sure that the hash of each update matches its Beacon Signal
- Apply the JSON Patch operations in sequence to build the current document
-
Resolution without a registry: The verifier uses only the sidecar data and Bitcoin data. The verifier does not send the DID to a resolution service. But the verifier must still read Bitcoin data for the beacon addresses of the DID. The spec RECOMMENDS an indexed Bitcoin RPC service, for example electrs or Esplora. It also permits a scan of all blocks from the genesis block (Find Beacon Signals).
-
Trust in the Bitcoin data source: If the verifier uses a third-party indexer, the indexer sees which addresses the verifier queries. The indexer can also withhold a Beacon Signal. If the verifier runs its own indexed node, this risk does not apply (A Bitcoin Node for Identity Infrastructure).
What Goes On-Chain
Only hashes go on Bitcoin. Each Beacon Signal carries 32 Signal Bytes in an OP_RETURN output, the last output of the transaction:
- Singleton Beacon: the SHA-256 hash of one signed update
- CAS Beacon: the SHA-256 hash of the Beacon Announcement Map
- SMT Beacon: the root of the Sparse Merkle Tree
An observer of the Bitcoin blockchain sees transaction outputs with hash data. From the hashes alone, the observer cannot find which DIDs changed or what the updates contain. Aggregate beacons (CAS and SMT) also hide the number of DIDs that one transaction affects, because one Beacon Signal commits to a batch of updates. A Singleton Beacon commits to one update for one DID document. If an observer knows that a transaction comes from a Singleton Beacon, the observer knows that it affects one DID. From the hash alone, the observer cannot find which DID (did:btcr2 spec, Beacons).
An observer who knows a DID can find its Beacon Signals, because the DID document lists the beacon addresses. For a key-based DID, the initial document lists three Singleton Beacon addresses that come from the public key in the DID (did:btcr2 spec, Resolve). Thus, a party that knows the DID can see when the DID gets an update. If the update is sidecar data only, that party cannot see the content of the update.
Privacy Properties
No Shared Resolution Service
The spec defines no public resolution endpoint for a did:btcr2 DID. Thus, two verifiers that get the same DID do not learn this fact from a shared registry. Each verifier gets the sidecar data separately. If the two verifiers do not collude and share data, they do not know that they interact with the same DID. This property has a limit. If both verifiers query the same third-party indexer for the same beacon addresses, the indexer can see the relation.
Pairwise DIDs at No Extra Creation Cost
A controller can make a did:btcr2 identifier offline, with no Bitcoin transaction (Offline DID Creation; also see Zero-Cost DID Creation). Thus, a controller can make a different DID for each relationship. With private resolution, the result is as follows:
- Your employer knows you as
did:btcr2:k1abc... - Your bank knows you as
did:btcr2:k1def... - The employer and the bank cannot link the two identifiers from the DIDs alone. This is true only if the two DIDs share no key, no beacon address, and no service endpoint.
- Each party can verify a credential for either DID, if you present that credential. If you show the bank a credential for your employer DID, the bank can link the two DIDs.
Updates are not free. Each update needs a Beacon Signal, and each Beacon Signal is a Bitcoin transaction.
Who Gets the History
The controller decides which parties get the history. The controller cannot give a verifier only a part of the history. To build the current document, the resolver needs each update that a Beacon Signal of the DID announces. If an update is not in the sidecar data or in CAS, resolution stops with a MISSING_UPDATE_DATA error (Find Beacon Signals). The spec also notes that a verifier sees all prior updates of the DID document (Non-Repudiation Reveals DID Document History). If you want a verifier to see less history, use a new DID for that relationship.
Resolution Modes
btcr2 permits different levels of resolution privacy. The spec defines two ways to deliver update data: sidecar and CAS (BTCR2 Update Data Distribution). The trusted resolver mode below is a deployment pattern, not a feature of the spec.
Private (Sidecar Only)
- The controller gives the sidecar data directly to the verifier.
- The verifier sends no resolution query to a registry. The verifier still reads Bitcoin data.
- Of these three modes, this mode shows the least data to third parties. The tradeoff is that the controller must deliver the data to each verifier.
Semi-Private (Trusted Resolver)
- The controller stores the sidecar data with a service that it trusts.
- The verifier sends the DID to the service.
- The service sees each resolution request. The service gives the data only to the verifiers that the controller authorizes.
Public (Open Resolution)
- The controller puts the update data in CAS, for example IPFS. Any resolver can make the CID from the hash of a document and get the document.
- Any party can get the full history, not only the parties that the controller selects. The spec tells implementers to treat data on CAS as public (CAS Systems Lack Privacy).
- This mode is suitable for organizational DIDs that must be public.
The controller chooses the mode. The controller can use the sidecar mode or the trusted resolver mode differently for each relationship. After the controller puts update data on CAS, that data is public for all parties. The spec says that a mix of the two delivery mechanisms for one DID is NOT RECOMMENDED. It RECOMMENDS one mechanism for the full life of the DID (BTCR2 Update Data Distribution).
Tradeoffs
Private resolution has these tradeoffs:
- Sidecar management: The controller must keep the update history and give it to each verifier. If the controller loses an update after a Beacon Signal announces it, no party can resolve the DID. The resolver stops with a
MISSING_UPDATE_DATAerror. For more, see Sidecar Custody. - Larger payloads: A DID with its full sidecar data is larger than a DID string alone. The history grows with each update. For an SMT Beacon, it also grows with each signal, because the sidecar data needs one SMT proof for each signal (Sidecar Data).
- Availability of data: The verifier needs the sidecar data at verification time. If the controller is not present, the verifier needs a cached copy or a copy that it got before.
- Bitcoin data source: The verifier needs Bitcoin data for the beacon addresses. A third-party indexer sees the queried addresses. A local indexer costs hardware and operation time.
Conclusion
Privacy in decentralized identity is not only about the credentials that you present. It is also about the data that the identity infrastructure shows. With the btcr2 sidecar model, the verifier sends no DID to a resolution registry, and on-chain anchors are only hashes. The verifier still reads Bitcoin data, and the choice of data source is a privacy decision. Pairwise DIDs that you make offline add to these properties. In btcr2, privacy is part of the design of the method, not an addition after the design.
