Skip to content

Repository files navigation

Important

Repository Moved / Donated

This project has been donated to the Decentralized Identity Foundation (DIF) and is now maintained at:

https://github.com/decentralized-identity/didwebvh-java

Please follow, open issues, and contribute at the new repository going forward. This repository is no longer the primary upstream source.

didwebvh-java

Java CIcodecovMaven CentralJava VersionLicenseQuality Gate Status

A Java 11+ library for the did:webvh (DID Web + Verifiable History) DID Method, v1.0.

Create, resolve, update, migrate, deactivate, and publish a parallel did:web document for did:webvh DIDs with pluggable key management (local keys, AWS KMS, external APIs, and more).

Features

  • Full did:webvh v1.0 specification support
  • Create DIDs with SCID generation, authorization keys, and Data Integrity proofs
  • Resolve DIDs from HTTPS URLs or local files with full log chain verification
  • Update DID documents, rotate keys, change parameters, migrate to new domains, and deactivate
  • Witness support with threshold-based approval (did-witness.json)
  • Pre-rotation key commitment (nextKeyHashes) for forward security
  • DID portability across domains while preserving verifiable history
  • Parallel did:web document publishing for backward compatibility
  • Pluggable signing via Signer interface (local Ed25519, AWS KMS, HSM, external API)
  • Interactive wizard CLI for guided DID management
  • Java 11+ compatible, tested on Java 11, 17, 21, and 25

Quick Start

Maven

<dependency>
<groupId>io.github.decentralized-identity</groupId>
<artifactId>didwebvh-java</artifactId>
<version>0.3.0</version>
</dependency>

This aggregate artifact depends on didwebvh-core and didwebvh-signing-local. Depend on didwebvh-core directly if you plan to supply your own Signer implementation and do not need the local-key adapter.

Gradle

implementation 'io.github.decentralized-identity:didwebvh-java:0.3.0'

The old io.github.ivir3zam:*:0.2.x coordinates remain on Maven Central as relocation stubs that automatically redirect to the new DIF coordinates, so existing builds keep resolving — but please update to the coordinates above.

Library Usage

All DID operations are exposed as static entry points on io.github.ivir3zam.didwebvh.core.DidWebVh. The examples below use LocalKeySigner from didwebvh-signing-local; see Pluggable Key Management for custom Signer implementations.

Create a DID

importio.github.ivir3zam.didwebvh.core.DidWebVh;
importio.github.ivir3zam.didwebvh.core.create.CreateDidResult;
importio.github.ivir3zam.didwebvh.signing.local.LocalKeySigner;
LocalKeySignersigner = LocalKeySigner.generate();
CreateDidResultresult = DidWebVh.create("example.com", signer)
.path("dids:alice") // optional URL path segment
.portable(true) // allow future domain migration
.ttl(3600)
.alsoKnownAs(List.of("did:key:z6Mk...")) // optional
.execute();
Stringdid = result.getDid(); // e.g. did:webvh:QmSCID:example.com:dids:aliceStringlogLine = result.getLogLine();// write this as the first line of did.jsonl

Save the signer material safely — you need it to sign every subsequent update:

Files.writeString(Paths.get("did-secrets.json"), signer.toJson());

Resolve a DID

importio.github.ivir3zam.didwebvh.core.DidWebVh;
importio.github.ivir3zam.didwebvh.core.model.ResolveResult;
importio.github.ivir3zam.didwebvh.core.resolve.DidResolver;
importio.github.ivir3zam.didwebvh.core.resolve.ResolveOptions;
// Remote resolution over HTTPS:ResolveResultremote = DidWebVh.resolve("did:webvh:QmSCID:example.com");
// Offline resolution from an already-downloaded did.jsonl:Stringjsonl = Files.readString(Paths.get("did.jsonl"));
ResolveResultoffline = newDidResolver().resolveFromLog(jsonl, did);
// Time-travel / version filtering:ResolveOptionsopts = ResolveOptions.builder().versionNumber(3).build();
ResolveResultolder = newDidResolver().resolveFromLog(jsonl, did, opts);
System.out.println(offline.getDidDocument().asJsonObject());

Update, Migrate, or Deactivate

DidWebVhState holds the validated log state and is the input to every update:

importio.github.ivir3zam.didwebvh.core.DidWebVh;
importio.github.ivir3zam.didwebvh.core.DidWebVhState;
importio.github.ivir3zam.didwebvh.core.model.Parameters;
importio.github.ivir3zam.didwebvh.core.update.UpdateDidResult;
DidWebVhStatestate = DidWebVhState.fromDidLog(did, jsonl);
// Replace the DID Document (same SCID, same domain):UpdateDidResultrotated = DidWebVh.update(state, signer)
.newDocument(updatedDocJsonObject)
.execute();
// Or change only parameters (e.g. TTL and watchers):Parametersdelta = newParameters();
delta.setTtl(120);
delta.setWatchers(List.of("https://watch.example.com"));
DidWebVh.update(state, signer).changedParameters(delta).execute();
// Migrate a portable DID to a new domain:DidWebVh.migrate(state, signer, "new.example.com")
.newPath("dids:alice")
.execute();
// Deactivate (permanent):DidWebVh.deactivate(state, signer).execute();
// Each result carries the new entry / entries to append to did.jsonl:for (varentry : rotated.getNewEntries()) {
Files.writeString(
Paths.get("did.jsonl"),
entry.toJsonLine() + "\n",
StandardOpenOption.APPEND);
}

Pre-rotation (forward security)

Publish a hash of the next authorization key; rotation must reveal that key or the DID becomes unrecoverable:

importio.github.ivir3zam.didwebvh.core.crypto.PreRotationHashGenerator;
LocalKeySignernextSigner = LocalKeySigner.generate();
StringnextHash = PreRotationHashGenerator.generateHash(nextSigner.getPublicKeyMultikey());
DidWebVh.create("example.com", signer)
.nextKeyHashes(List.of(nextHash))
.execute();

On the next update, pass the previously-committed key as the current signer and supply a new next-key hash the same way.

Witness configuration

Witnesses co-sign each new log entry; their proofs live in did-witness.json next to did.jsonl and MUST be published first (spec §3.7.8).

importio.github.ivir3zam.didwebvh.core.witness.WitnessConfig;
importio.github.ivir3zam.didwebvh.core.witness.WitnessEntry;
WitnessConfigwitness = newWitnessConfig(
2, // thresholdList.of(
newWitnessEntry("did:key:z6MkWitness1..."),
newWitnessEntry("did:key:z6MkWitness2...")));
DidWebVh.create("example.com", signer).witness(witness).execute();

Collecting witness proofs (sign {"versionId":"<id>"} with each authorized witness key and write them to did-witness.json) is done outside DidWebVh.update; see WizardWitnessProofs for a reference implementation that uses ProofGenerator and WitnessProofEntry.

Publish a parallel did:web document

The spec (§3.7.10) lets a did:webvh publisher also serve a plain did:web document at the same URL, so clients that do not understand did:webvh can still resolve the DID.

importio.github.ivir3zam.didwebvh.core.didweb.DidWebPublisher;
importio.github.ivir3zam.didwebvh.core.model.DidDocument;
DidDocumentresolved = newDidResolver().resolveFromLog(jsonl, did).getDidDocument();
DidDocumentwebDoc = DidWebPublisher.toDidWeb(resolved);
StringdidWebUrl = DidWebPublisher.toDidWebUrl(did); // did:webvh:... → did:web:...Files.writeString(
Paths.get("did.json"),
newGsonBuilder().setPrettyPrinting().create().toJson(webDoc.asJsonObject()));

Publish did.json alongside did.jsonl (and did-witness.json if used).

Pluggable Key Management

All signing goes through a single interface:

publicinterfaceSigner {
StringkeyType(); // e.g. "Ed25519VerificationKey2020"StringverificationMethod(); // did:key:... identifier of the public keybyte[] sign(byte[] data) throwsSigningException;
}

Built-in implementations:

  • LocalKeySigner (module didwebvh-signing-local) — Ed25519 keys from JSON key files. LocalKeySigner.generate(), LocalKeySigner.fromJson(json), signer.toJson().

A custom Signer only needs to return the DID-key verification method for its public key and sign the bytes handed to it. Example skeleton for an HSM / cloud-KMS adapter:

publicfinalclassKmsSignerimplementsSigner {
privatefinalStringverificationMethod; // pre-computed did:key from the HSM pubkeyprivatefinalKmsClientclient;
@OverridepublicStringkeyType() { return"Ed25519VerificationKey2020"; }
@OverridepublicStringverificationMethod() { returnverificationMethod; }
@Overridepublicbyte[] sign(byte[] data) {
returnclient.signEdDsa(data); // must be a raw 64-byte Ed25519 signature
}
}

Drop it into any DidWebVh.create/update/migrate/deactivate call — the library never touches raw key material.

Wizard CLI

Build the shaded (uber) jar, then run the interactive wizard:

./mvnw -pl didwebvh-wizard -am package
java -jar didwebvh-wizard/target/didwebvh-wizard.jar

(-am builds the didwebvh-core and didwebvh-signing-local dependencies first; the jar is a self-contained uber-jar produced by maven-shade-plugin and is not published to Maven Central.)

The wizard supports:

  1. Create a new did:webvh DID (keys, pre-rotation, witnesses, watchers, TTL)
  2. Update an existing DID — modify the document, change any parameter, migrate to a new domain (portable DIDs only), or deactivate; witness proofs are collected automatically when the active configuration requires them
  3. Resolve a did:webvh DID (HTTPS or local file, with optional version filtering)
  4. Export the parallel did:web document (did.json) for the current DID
  5. Exit

Single-shot mode (skip the menu):

java -jar didwebvh-wizard.jar --action export --dir /srv/dids/alice

Valid --action values: create, update, resolve, export.

Project Structure

didwebvh-java/
didwebvh-core/ # Core library (model, create, resolve, update, didweb, validate)
didwebvh-signing-local/ # Local key file signer adapter
didwebvh-wizard/ # Interactive CLI wizard

Building

./mvnw clean verify

Contributors should run the full build with JDK 21 for the closest local match to CI. The project supports Java 11+, and CI also checks Java 11, 17, and 25, but SpotBugs is skipped on JDK 22+ in this repository.

Running Tests

./mvnw test

Specification

This library implements the did:webvh DID Method v1.0 specification.

Changelog

See CHANGELOG.md.

Contributing

Contributions are welcome. Please read CONTRIBUTING.md for the development workflow, and docs/AGENTS.md / docs/ARCHITECTURE.md for contributor (and AI agent) guidelines and the technical design.

Before opening a PR, run:

./mvnw clean verify

This runs the full test suite, Checkstyle, SpotBugs, and JaCoCo coverage checks across all modules.

Security

If you believe you've found a security vulnerability, please follow the instructions in SECURITY.mddo not open a public GitHub issue.

License

Licensed under the Apache License, Version 2.0. By contributing to this project you agree to license your contributions under the same terms.

About

Java 11+ library for the did:webvh DID method with support for creating, resolving, updating, migrating, deactivating, and publishing parallel did:web documents.

Topics

Resources

Contributing

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages