did:webvh — Web DIDs with Verifiable History
What It Is
did:webvh is a DID method that combines the simplicity of hosting a DID document on the web with the security of a cryptographically verifiable history log. It’s specified by the Decentralized Identity Foundation (DIF) and is the primary DID method used throughout the OpenVTC ecosystem.
The “vh” stands for Verifiable History — the key innovation over plain did:web.
The Problem with did:web
Plain did:web is simple: your DID document lives at a URL on your domain. Anyone can resolve it by fetching that URL. But it has serious weaknesses:
- No history — if someone compromises your server and changes your DID document, there’s no way to detect it
- No portability — your DID is tied to your domain; move domains and you lose your identity
- No key rotation proof — you can update your keys, but there’s no verifiable chain proving continuity
- DNS dependency — whoever controls the domain controls the identity
How did:webvh Fixes This
did:webvh adds a verifiable history log — a JSONL file (did.jsonl) hosted alongside the DID document. Each entry in the log is:
- Cryptographically signed by the DID controller
- Hash-chained to the previous entry (like a blockchain, but for one identity)
- Timestamped
- Optionally witnessed by third parties
This gives you:
Self-Certifying Identifier (SCID)
Every did:webvh includes a SCID — a hash derived from the initial DID document. This SCID stays constant even if you move to a new domain, making the identity portable. The DID looks like:
did:webvh:QmSCIDhash:example.com:path
The SCID also generates a did:scid:vh alias that’s completely domain-independent.
Pre-rotation
You can commit to your next public key before you need it, by including its hash in the current log entry. If your current key is compromised, the attacker can’t rotate to a key they control — only the pre-committed key is valid. This is a powerful defense against key compromise.
Witness Proofs
Third parties (witnesses) can sign log entries to attest that they observed the update. This creates multi-party verification — even if someone compromises your server and your keys, the witnesses would need to be compromised too.
Domain Migration
Because of the SCID, you can move your DID to a new domain while maintaining identity continuity. The history log proves the chain of custody across domains.
How It Works in Practice
When a user sets up OpenVTC:
- The VTA generates keys from the user’s BIP-32 seed
- A did:webvh is created with the initial DID document and history log
- The
did.jsonlfile is hosted on the domain of the user’s choice (their own, or via a provider using something like the affinidi-webvh-service) - Key rotations, service endpoint changes, and other updates are appended as new log entries
- Optional witnesses attest to the integrity of updates
When someone resolves the DID:
- The resolver fetches the
did.jsonlfile - It validates every log entry in the chain — checking signatures, hash links, timestamps, and witness proofs
- It returns the current DID document, with confidence in the complete history
What the DTG Spec Says About It (WD02)
Working Draft 02 of the DTG Core Credentials spec switched its examples from did:web to did:webvh for durable identifiers — a community’s, a witnessing VTA’s — because plain did:web has no verifiable history and no successor-key commitment, so a verifier cannot tell a legitimate rotation from an attacker’s substitution, nor establish which key was authoritative when a credential was signed. Its informative DID Method Considerations adds two cautions worth knowing. First, portability is decided at inception: a did:webvh log can be relocated only if portable was set in the first log entry; it defaults to off and a later entry cannot enable it, so an identifier expected to outlive its hosting arrangement must be created with it on. Second, the resolution layer is a correlation channel: several of a person’s narrow-scope ([[correlation-scope|pairwise / directed]]) identifiers resolving through one web origin, one registry or one set of DID-log witnesses can be linked — and their use observed — by that infrastructure regardless of what the credentials disclose, which is why the spec steers such identifiers toward did:peer / did:key and expects deployments to mix methods. (The spec also disambiguates DID log witness — countersigning DID document versions — from the witness of a VWC, which attests to an edge.) See decentralized-identifiers.
Implementation
- didwebvh-rs — the Rust library implementing the did:webvh specification (creation, resolution, update, validation, witness management)
- affinidi-webvh-service — the production service infrastructure for hosting and resolving did:webvh identifiers at scale
As of Eucalyptus (2026-10-07). The library itself was quiet: didwebvh-rs 0.8.0 (#55, 2026-10-01) moves to affinidi-data-integrity 0.8 and affinidi-bbs 0.4 with no behavior change, and the public-hosts-only HostPolicy default dates from 0.7.0 (2026-09-11), not this release. The changes that matter to a did:webvh holder landed around it. A deactivated did:webvh no longer resolves (TDK resolver #907; VTI #1881), and the did-hosting-service refuses a deactivated signer as final (#252) — a retired identifier can no longer sign anything that verifies. The hosting service moved its control plane, edge and witness to Trust Tasks only and removed its REST management surfaces (#218–#238), requires a proof bound to the sender on every task (#213), and gained passkey enrollment and step-up as Trust Tasks (#236, #243, #250) — the same “signed, proof-bound, governed” shape the rest of the release took. On the VTA side, versionTime log updates are serialized so two concurrent updates cannot race the log (VTI #1599), webvh keys rotate in place (#1734), and webvh/dids/create/1.1 reports serverless for a DID hosted without a service (#1886).
See also: decentralized-identifiers, didwebvh-rs, affinidi-webvh-service