Create and manage DIDs
Create, inspect, update, and delete did:webvh identifiers on your WebVH Hosting appliance using the PNM CLI (pnm did-mgmt).
PNM connects to your VTA, which signs every version of a DID and publishes it to the appliance. For how a hosted DID is reserved, published, and deleted, see DID lifecycle.
A DID’s keys, service endpoints, and watchers stay as they were at creation until you publish a new version, and a retired service stays in the DID document until you publish a version without it or delete the DID.
Use this guide when:
- You want to create
did:webvhidentifiers for applications or services. - You need to inspect, update, or delete existing DIDs.
Prerequisites
- PNM CLI installed and configured against your VTA. The examples use your active VTA, shown by
pnm vta info. Add--vta <vta-slug>to target a different one. - Your appliance registered with your VTA under a server ID,
my-webvhin these examples. See Create your first hosted DID. - A VTA context for the application,
my-appin these examples, in which your DID has theadminrole. See the VTA guides Manage the contexts on a VTA and Grant and revoke access.
Every DID you create uses your appliance’s own domain, the host of its WebVH URL in Affinidi Portal, so leave out --domain.
Create a DID
pnm did-mgmt dids create \
--context my-app \
--server my-webvh \
--path my-service \
--label "My service identity"| Flag | Description |
|---|---|
--context | VTA context that owns the DID. |
--server | Server ID of your registered appliance, from pnm did-mgmt servers list. |
--path | Path for the DID on the appliance. A two-word path is generated when omitted. See the path rules below. |
--label | Optional human-readable label. |
--mediator-service | Adds your mediator as a DIDComm service endpoint, so the DID can receive DIDComm messages. |
--pre-rotation | Number of pre-rotation keys to generate. Default: 0. |
Path rules:
- Each segment is 2 to 63 lowercase letters, digits, or hyphens, starting and ending with a letter or digit.
- The first segment cannot be a reserved word such as
api,auth,dids,stats,acl, orhealth. .well-knownselects the appliance’s root DID slot, which is usually already taken by the appliance’s own DID.
On success, PNM prints WebVH DID created: followed by the new identifier on the DID: line, its context, server, mnemonic, SCID, portability, signing and key-agreement key IDs, and pre-rotation key count.
A self-hosted server can serve several domains. Run pnm did-mgmt dids list-domains --server <id> to see the domains your VTA may use, and pass --domain to choose one. See Hosting domains.
List DIDs
# Every DID known to your VTA
pnm did-mgmt dids list
# DIDs in one context, or on one appliance
pnm did-mgmt dids list --context my-app
pnm did-mgmt dids list --server my-webvhInspect a DID
Show a DID’s record, including its context, server, SCID, and log entry count:
pnm did-mgmt dids get did:webvh:Q1abc…:did.example.com:my-serviceRetrieve the DID’s did.jsonl log, including its current DID document. Add --out to write it to a file:
pnm did-mgmt dids get-log did:webvh:Q1abc…:did.example.com:my-service \
--out my-service.jsonlUpdate a DID
Each update publishes a new signed version of the DID, for example to change service endpoints, the pre-rotation count, watchers, or the TTL.
Edit interactively in your editor:
pnm did-mgmt dids edit --did did:webvh:Q1abc…:did.example.com:my-serviceAfter you save, PNM shows the document diff and asks whether to change each WebVH parameter: pre-rotation count, watchers, TTL, and audit label. It then asks you to confirm before it publishes the new version.
Or update from the command line:
# From a prepared DID document
pnm did-mgmt dids edit \
--did did:webvh:Q1abc…:did.example.com:my-service \
--document updated-document.json
# Change the TTL and the watcher set
pnm did-mgmt dids edit \
--did did:webvh:Q1abc…:did.example.com:my-service \
--ttl 86400 \
--watcher https://watcher.example.com| Flag | Effect |
|---|---|
--watcher | Replaces the whole watcher set. Repeat it for several watchers. |
--no-watchers | Turns watchers off. |
--options-file | Sets every parameter at once from a full update body, including witnesses. Use it instead of the per-field flags. |
--no-confirm | Skips the final publish prompt, for scripted runs. |
Delete a DID
pnm did-mgmt dids delete did:webvh:Q1abc…:did.example.com:old-servicePNM prints WebVH DID deleted: <did>.
- Effect: permanent. The appliance deletes the DID’s record, log, and any witness file. Export the log first with
get-logif you need an audit copy. - Dependencies: the VTA refuses to delete a DID it still relies on, such as its own DID or a context’s DID, and lists what to change first.
- Unreachable appliance: the VTA still removes its own record, so the DID can be left behind on the appliance. Contact Affinidi support with the DID to have it removed.
Confirm
Test 1: dids list shows the new DID
pnm did-mgmt dids list --context my-appExpected output: a table row with the DID, context my-app, and server my-webvh.
Test 2: request for the DID’s did.jsonl returns its signed log
The URL follows from the DID: https://<domain>/<path>/did.jsonl, where <domain> is the host of your appliance’s WebVH URL.
curl -s "https://did.example.com/my-service/did.jsonl"Expected output: the DID’s signed log, one JSON entry per line.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
✗ Not found: webvh server not found: <id> | The --server value is not a registered server ID. | Run pnm did-mgmt servers list and use the registered ID. |
✗ Forbidden: … on dids create | Your DID is not an admin of the context, or --domain names a domain other than the appliance’s. | Run pnm acl list --context my-app and confirm your DID has the admin role. Leave out --domain. |
webvh path already taken on the hosting server | Another DID already uses that path on the appliance. | Choose a different --path, or omit it to have one generated. |
| DID not resolving right after creation | The request reached the appliance before the publish completed, or went to a different host or path. | Wait a few seconds and retry. Contact Affinidi support if it still fails. |
edit rejected with the edited document changed the DID identifier | The id field was changed in the edited document. | Keep the id unchanged: it is fixed from the DID’s first log entry. |
Next steps
- Give a DID a human-readable name: make the DID reachable at
/@name. - Make a DID reachable over DIDComm: add your mediator as a service endpoint.
- Troubleshoot DID resolution: fix a DID that does not resolve.
Related
- Hosting domains: how a DID’s domain is chosen.
- Manage the contexts on a VTA: create and manage the contexts that own your DIDs.
- Grant and revoke access: control who can create DIDs in a context.
Glad to hear it. Thanks for letting us know.
Sorry to hear that. Thanks for letting us know.