didwebvh-ts provides developers with a comprehensive library for working with Decentralized Identifiers (DIDs) following the did:webvh method specification. This Typescript-based toolkit is designed to facilitate the integration and management of DIDs within web applications, enabling secure identity verification and authentication processes. It includes functions for creating, resolving, updating and deactivating DIDs by managing DID documents. The package is built to ensure compatibility with the latest web development standards, offering a straightforward API that makes it easy to implement DID-based features in a variety of projects.
The didwebvh-ts implementation of the did:webvh specification aims to be compatible with the did:webvh v1.0 specification.
The examples directory contains sample code demonstrating how to use the library:
- Resolver Examples: The
examplesdirectory includes two resolver implementations:elysia-resolver.ts: (bun run example:resolver) A resolver built with the Elysia web frameworkexpress-resolver.ts: A resolver built with Express.js Both examples demonstrate how to implement a DID resolver with different web frameworks. See the Examples README for more information.
- Signer Example: The
examples/signer.ts(bun run example:signer) file demonstrates how to implement a custom signer usingAbstractCrypto.
Install bun.sh
curl -fsSL https://bun.sh/install | bashbun installWhen running the examples from the source checkout, Bun needs to resolve the didwebvh-ts package name to your local code. Run the following once per clone:
bun run build # generate the dist/ artifacts
bun link # register the local package globally
bun link didwebvh-ts # create a symlinked dependency in node_modulesAfter linking, you can start the resolver example:
bun run serverIf you ever need to refresh the build (for example after local code changes), rerun bun run build. The bun link commands only need to be repeated if you remove the symlink or clone the repo again.
The following commands are defined in the package.json file:
-
dev: Run the Elysia resolver example in development mode with debugging enabled.bun run dev
This command runs: bun --watch --inspect-wait ./examples/elysia-resolver.ts
-
server: Run the Elysia resolver example in watch mode for development.bun run server
This command runs: bun --watch ./examples/elysia-resolver.ts
-
test: Run all tests.bun run test -
test:watch: Run tests in watch mode.bun run test:watch
-
test:bail: Run tests in watch mode with bail and verbose options.bun run test:bail
-
test:log: Run tests and save logs to a file.bun run test:log
-
cli: Run the CLI tool.bun run cli
The CLI accepts a
--watcheroption during create and update operations to specify one or more watcher URLs. -
build: Build the package.bun run build
-
build:clean: Clean the build directory.bun run build:clean
Publishing is fully automated and happens only when a maintainer publishes a GitHub Release.
- Who can publish: GitHub users with write, maintain, or admin permission on this repo.
- Required tag format:
vMAJOR.MINOR.PATCH(for examplev2.7.5). - Required semver bump: the tag must be a single major/minor/patch increment over the latest existing
v*tag.
- In GitHub, go to Releases → Draft a new release
- Set Tag to the next version, e.g.
v2.7.5 - Choose the target branch/commit (typically
main) - Click Publish release
That will trigger the publish workflow, which will:
- validate the tag + your repo permission
- set
package.jsonversion from the tag (without the leadingv) - run
bun testandbun run build - publish to npm
Publishing uses npm OIDC trusted publishing — the workflow exchanges its GitHub Actions OIDC token for a short-lived npm publish token at publish time. No static NPM_TOKEN is required.
For this to work, the didwebvh-ts package on npmjs.com must have a Trusted Publisher configured pointing at this repository and the .github/workflows/publish.yml workflow.
- Tag rejected: make sure it matches
vX.Y.Zand is exactly one major/minor/patch bump over the latestv*tag. - Permission rejected: ensure the releasing user has write/maintain/admin permission on the GitHub repo.
EOTP/ OTP required at publish: the npm token path is being used instead of OIDC. Make sure noNODE_AUTH_TOKENis set on the publish step and that the workflow hasid-token: writepermission.- OIDC exchange failed: confirm the Trusted Publisher config on npmjs.com matches this repo's owner, name, and workflow file path (
.github/workflows/publish.yml).
The didwebvh-ts library provides the core functionality for resolving DIDs, but it does not include a built-in HTTP resolver. You can create your own resolver using your preferred web framework by following these steps:
-
Import the
resolveDIDfunction from thedidwebvh-tslibrary:import { resolveDID } from 'didwebvh-ts';
-
Create endpoints for resolving DIDs:
// Example using Express app.get('/resolve/:id', async (req, res) => { try { const result = await resolveDID(req.params.id); res.json(result); } catch (error) { res.status(400).json({ error: 'Resolution failed', details: error.message }); } });
-
Implement file retrieval logic for DID documents and associated resources.
For complete examples, see the examples directory.
For did:webvh:1.0 resolution flows, resolver failures that invalidate the DID are surfaced using:
meta.error = "invalidDid"meta.problemDetailspopulated with RFC9457-style fields (type,title,detail)
Absence cases (for example missing DID log or missing DID URL resource) use:
meta.error = "notFound"
When resolving a requested earlier version (for example with versionId, versionNumber, or versionTime), the resolver may return a valid earlier document while still reporting meta.error = "invalidDid" if a later log entry fails verification.
-
resolveDID(did: string, options?: ResolutionOptions): Promise<{did: string, doc: any, meta: DIDResolutionMeta, controlled: boolean}>Resolves a DID to its DID document. Forv1.0,options.fastResolveis an opt-in mode defaulting tofalsefor full log parsing. -
resolveDIDFromLog(log: DIDLog, options?: ResolutionOptions & { witnessProofs?: WitnessProofFileEntry[] }): Promise<{did: string, doc: any, meta: DIDResolutionMeta}>Resolves directly from an in-memory DID log. Forv1.0,options.fastResolveis an opt-in mode defaulting tofalsefor full log parsing. -
createDID(options: CreateDIDInterface): Promise<{did: string, doc: any, meta: DIDResolutionMeta, log: DIDLog, webDoc?: DIDDoc}>Creates a new DID. Acceptsaddress(host,host:port,https://..., ordid:webvh:...) or legacydomain. Resolver URL mapping useshttp://localhostfor local testing andhttps://for non-local hosts. IfalsoKnownAsWeb: trueis supplied, the result also includeswebDoc, the paralleldid:webDID document to publish asdid.json. -
updateDID(options: UpdateDIDInterface): Promise<{did: string, doc: any, meta: DIDResolutionMeta, log: DIDLog, webDoc?: DIDDoc}>Updates an existing DID. ReturnswebDocwhen the updated DID document carries adid:web:alias inalsoKnownAs. -
deactivateDID(options: DeactivateDIDInterface): Promise<{did: string, doc: any, meta: DIDResolutionMeta, log: DIDLog}>Deactivates an existing DID. -
generateParallelDidWeb(didwebvhDid: string, didwebvhDoc: DIDDoc): DIDDocGenerates the paralleldid:webdocument defined by did:webvh v1.0 §3.7.10.
-
createWitnessProof(signer, versionId, verificationMethod, created?): Promise<DataIntegrityProof>Creates and signs one witness proof for a specificversionId. -
signWitnessProofEntry(options: WitnessSigningOptions): Promise<WitnessSigningResult>Signs one did-witness proof entry ({ versionId, proof[] }) for a single target version. -
signWitnessProofEntries(versionIds: string[], witnesses: WitnessEntry[], witnessSignersByDid: Record<string, WitnessSigner>, created?: string): Promise<WitnessSigningResult[]>Signs did-witness proof entries for multiple target versions.
-
createDocumentSigner(options: SignerOptions): SignerCreates a signer for signing DID documents. -
prepareDataForSigning(data: any): Uint8ArrayPrepares data for signing. -
createProof(options: SigningInput): Promise<SigningOutput>Creates a proof for a DID document. -
createSigner(options: SignerOptions): SignerCreates a signer for signing data. -
AbstractCryptoAn abstract class for implementing custom signers.
This project is licensed under the MIT License.