DID lifecycle
A did:webvh identifier is only trustworthy if every version of its log is signed by the right key and served unchanged from a stable URL, from creation through to deletion.
In WebVH Hosting, the DID’s controller signs every log entry. The control plane validates and stores each version as the single source of record, and edge servers serve copies synced from it. Create and manage DIDs →
Who owns and signs a DID
Every hosted DID has an owner: the DID that reserved or registered it. When your VTA creates a DID, it authenticates to the hosting server as itself, so the VTA’s DID is the owner. Only the owner or an admin can read, update, disable, roll back, transfer, or delete a DID.
Signing stays with your VTA:
- Update keys: your VTA holds them and signs each new log entry before sending it.
- Pre-rotation: when an entry commits pre-rotation key hashes, the next entry must be signed with one of the pre-committed keys.
- Lost records: a DID left on the host after your VTA has lost its record stays frozen at its last version.
pnm did-mgmt dids reconcile --server <id>reports such DIDs.
How a DID is created
Creation happens in two steps, so that a resolver only ever finds a published log.
- Path reservation: the caller claims a path on the server, such as
my-service. If it names none, the server assigns a random two-word path. A path that is already taken is refused. The root path,.well-known, can be reserved only by an admin. - First publish: the caller signs the genesis log entry and publishes it to the reserved path. The server can also claim and publish in one atomic step.
Your VTA runs both steps for you when you run pnm did-mgmt dids create.
An unpublished reservation expires. On a standalone edge server and the unified daemon, reservations older than cleanup_ttl_minutes under [auth], 60 minutes by default, are deleted. A standalone control plane keeps unpublished reservations until they are published or deleted.
What the server checks when a version is published
Each version is a complete, signed did.jsonl log. Before storing it, the control plane validates the whole log: every entry’s signature, the hash chain between entries, the rules for changing DID parameters, pre-rotation key authorisation, and that nothing follows a deactivation. A log that fails is refused as invalid.
The server also checks that the DID’s host is an active hosting domain that the caller may use, and that each path keeps the same DID method. Witness proofs are uploaded separately and are outside this check. See Hosting domains.
An update is simply a new, longer log published to the same path. Agent names and service endpoints are taken from the latest entry each time.
How changes reach edge servers
The control plane writes every change to a durable outbox, then sends each registered edge server either the full updated log or a deletion.
- Reliable: delivery is at least once, in order for each server, and survives restarts.
- Retried: failed deliveries are retried with a growing delay for up to 7 days.
- Transport: TSP when the edge server supports it, and DIDComm otherwise.
- Efficient catch-up: a newly registered edge server receives only the DIDs it lacks at their current version.
In the unified daemon, the embedded edge server reads the same store as the control plane, so no sync is needed.
Where a DID resolves
The hosting service serves each DID at these public URLs:
| URL | What it returns |
|---|---|
/{path}/did.jsonl | The DID’s signed log. The root DID uses /.well-known/did.jsonl. |
/{path}/did-witness.json | The DID’s witness file, when one has been uploaded. |
/@{name} | A 302 redirect to the DID bound to that agent name, or to its did.jsonl for browsers. |
Responses for did.jsonl and /@{name} can be cached for up to 5 minutes. A self-hosted server also serves a did:web compatible document at /{path}/did.json by default, sent with Cache-Control: no-store. A request whose host does not match the DID’s host returns 404, and a request on a disabled domain returns 503.
Disabling, rolling back, and transferring a DID (self-hosted)
PNM covers creating, updating, and deleting DIDs. Disabling, rolling back, and transferring are hosting actions that a self-hosted server offers through its management UI and API.
- Disable and enable: a disabled DID stops resolving, and every URL above returns
404until it is enabled again. On a standalone deployment the disabled state is not synced to edge servers, so it takes effect only where the control plane’s store is read directly, such as the unified daemon. - Roll back: removes the most recent log entry and any witness file, returning the DID to its previous version. A DID must have at least two entries to roll back, and each rollback removes one entry.
- Transfer: the owner or an admin can hand a DID to another DID that already has an ACL entry. The new owner then controls the hosting record. Updating the DID itself still needs its update keys.
What deleting a DID removes
Deleting a DID through your VTA or the control plane is permanent: the record, its log, its witness file, and its ownership entry are removed, and a deletion is sent to every edge server. There is no undo. If you need the log for audit, export it first with pnm did-mgmt dids get-log.
When your VTA deletes a DID:
- It first revokes any credentials it issued to that DID, then deletes the DID on the host.
- It refuses to delete a DID that something else still depends on.
- If the host cannot be reached, it still removes its own record, so the DID can be left behind on the host until you find it with
pnm did-mgmt dids reconcile.
A standalone edge server that manages DIDs itself, without a control plane, soft-deletes instead: the DID stops resolving, did-hosting-server recover-did --path <path> can restore it, and it is purged after 30 days.
Related
Glad to hear it! Please tell us how we can improve more.
Sorry to hear that. Please tell us how we can improve.
Thank you for sharing your feedback so we can improve your experience.