Host a DID Document
A did:web DID is only useful if the document is actually hosted at the URL implied by the DID. In PDTF, organisations, transactions, and adapters use did:web because they need discoverable keys and service endpoints.
The two important rules are:
- the DID must resolve to HTTPS
- the document
idmust exactly match the DID being resolved
1. Understand the URL mapping
Section titled “1. Understand the URL mapping”@pdtf/core exposes the same mapping the verifier uses via didWebToUrl.
import { didWebToUrl } from '@pdtf/core';
console.log(didWebToUrl('did:web:smithandjones.co.uk'));// https://smithandjones.co.uk/.well-known/did.json
console.log(didWebToUrl('did:web:adapters.propdata.org.uk:epc'));// https://adapters.propdata.org.uk/epc/did.jsonuse pdtf_core::did::resolver::did_web_to_url;
let url = did_web_to_url("did:web:smithandjones.co.uk")?;println!("{url}");// https://smithandjones.co.uk/.well-known/did.json
let url = did_web_to_url("did:web:adapters.propdata.org.uk:epc")?;println!("{url}");// https://adapters.propdata.org.uk/epc/did.jsonfrom pdtf_core import did_web_to_url
print(did_web_to_url("did:web:smithandjones.co.uk"))# https://smithandjones.co.uk/.well-known/did.json
print(did_web_to_url("did:web:adapters.propdata.org.uk:epc"))# https://adapters.propdata.org.uk/epc/did.jsonusing Pdtf.Core;
Console.WriteLine(DidResolver.DidWebToUrl("did:web:smithandjones.co.uk"));// https://smithandjones.co.uk/.well-known/did.json
Console.WriteLine(DidResolver.DidWebToUrl("did:web:adapters.propdata.org.uk:epc"));// https://adapters.propdata.org.uk/epc/did.jsonFor a root domain DID, host /.well-known/did.json. For a path-based DID, host /path/.../did.json.
2. Generate a starter DID document
Section titled “2. Generate a starter DID document”The CLI in @pdtf/core can generate a simple organisation DID document and local private key:
pdtf org init --domain smithandjones.co.uk --output ./didThat writes:
did.jsonprivate-key.jwk
The generated DID document uses did:web:smithandjones.co.uk with #key-1 and includes the key in both authentication and assertionMethod.
3. Example DID document
Section titled “3. Example DID document”A minimal PDTF-compatible organisation document looks like this:
{ "@context": [ "https://www.w3.org/ns/did/v1", "https://w3id.org/security/suites/ed25519-2020/v1" ], "id": "did:web:smithandjones.co.uk", "verificationMethod": [ { "id": "did:web:smithandjones.co.uk#key-1", "type": "Ed25519VerificationKey2020", "controller": "did:web:smithandjones.co.uk", "publicKeyMultibase": "z6Mkr7JAFsC4K5Zmq3RqtEZjTNz9e3o8yBPyqGMpKVqZv2R" } ], "authentication": ["did:web:smithandjones.co.uk#key-1"], "assertionMethod": ["did:web:smithandjones.co.uk#key-1"]}If you plan to issue credentials, assertionMethod is mandatory in practice because PDTF verifiers check that the proof key is authorised for assertions.
4. Add service endpoints where useful
Section titled “4. Add service endpoints where useful”Adapters and transaction DIDs usually expose services.
{ "service": [ { "id": "did:web:adapters.propdata.org.uk:epc#vc-issuance", "type": "VcIssuanceEndpoint", "serviceEndpoint": "https://adapters.propdata.org.uk/epc/credentials/issue" }, { "id": "did:web:adapters.propdata.org.uk:epc#status", "type": "BitstringStatusListEndpoint", "serviceEndpoint": "https://adapters.propdata.org.uk/status/epc" } ]}These endpoints are not decorative. They let clients discover where to request credentials and where revocation data lives.
5. Verify hosting end to end
Section titled “5. Verify hosting end to end”Once the file is live, test it with the same resolver that PDTF verifiers use.
import { DidResolver } from '@pdtf/core';
const resolver = new DidResolver();const doc = await resolver.resolve('did:web:smithandjones.co.uk');
console.log(doc.id);console.log(doc.assertionMethod);use pdtf_core::did::resolver::DidResolver;
let resolver = DidResolver::new()?;let doc = resolver.resolve("did:web:smithandjones.co.uk").await?;
println!("ID: {}", doc.id);println!("assertionMethod: {:?}", doc.assertion_method);from pdtf_core import DidResolver
resolver = DidResolver()doc = resolver.resolve("did:web:smithandjones.co.uk")
print(f"ID: {doc['id']}")print(f"assertionMethod: {doc['assertionMethod']}")using Pdtf.Core;
var resolver = new DidResolver();var doc = await resolver.Resolve("did:web:smithandjones.co.uk");
Console.WriteLine($"ID: {doc.Id}");Console.WriteLine($"assertionMethod: {string.Join(", ", doc.AssertionMethod)}");If the hosted JSON does not match the DID exactly, resolveDidWeb will reject it.
6. Production checklist
Section titled “6. Production checklist”Before you rely on the DID in live issuance:
- serve the document over HTTPS only
- make sure
idexactly matches the DID string - include the signing key in
verificationMethod - include that same key in
assertionMethod - keep the key fragment stable, typically
#key-1 - update the DID document during key rotation before using the new key
- register the issuer DID in the TIR
7. Common failure modes
Section titled “7. Common failure modes”The most common problems are simple:
- hosting the file at the wrong path
idmismatch between document and DID- missing
assertionMethod - rotating the signing key without updating the DID document
- using HTTP or a redirect chain that breaks fetches
If a credential signed by your DID is failing verification, the DID document is one of the first things to check. PDTF trust depends on the resolver being able to fetch the right key from the right HTTPS location.