A collection of services to operate WebVH DIDs in production.
IMPORTANT: affinidi-webvh-service crates are provided "as is" without any warranties or guarantees, and by using this framework, users agree to assume all risks associated with its deployment and use including implementing security, and privacy measures in their applications. Affinidi assumes no liability for any issues arising from the use or modification of the project.
The workspace contains six crates that can be deployed independently or combined into a single binary:
Standalone mode: Daemon mode:
┌──────────────┐ ┌───────────────────────┐
│ webvh-control│ (UI + proxy + registry) │ webvh-daemon │
│ :8532 │──────┐ │ :8534 │
└──────────────┘ │ │ ┌───────────────────┐ │
├─► webvh-server │ │ / server │ │
┌──────────────┐ │ :8530 │ │ /witness witness│ │
│ webvh-server │◄─────┘ │ │ /watcher watcher│ │
│ :8530 │ ├─► webvh-witness │ │ /control control│ │
└──────────────┘ │ :8531 │ └───────────────────┘ │
┌──────────────┐ │ │ (shared listener, │
│webvh-witness │◄─────┘ │ separate stores) │
│ :8531 │ ├─► webvh-watcher └───────────────────────┘
└──────────────┘ │ :8533
┌──────────────┐ │
│webvh-watcher │◄─────┘
│ :8533 │ (read-only DID mirror)
└──────────────┘
| Crate | Binary | Description |
|---|---|---|
| webvh-server | webvh-server |
Read-only DID hosting edge node — serves DID documents publicly and receives sync updates from the control plane via DIDComm |
| webvh-witness | webvh-witness |
Witness node — generates and manages cryptographic witness proofs for DID integrity verification |
| webvh-watcher | webvh-watcher |
Read-only DID mirror — receives pushed DID updates from servers and serves them publicly for redundancy |
| webvh-control | webvh-control |
Control plane — DID lifecycle management via DIDComm and REST API, management UI, service registry, passkey authentication |
| webvh-daemon | webvh-daemon |
Unified daemon — embeds server + witness + watcher + control plane in a single binary (recommended for most deployments) |
| webvh-common | (library) | Shared types, traits, auth, ACL, storage, config, and passkey modules used by all services |
- Rust 1.94.0+ (2024 Edition)
- Node.js 20+ (only if building the management UI)
The daemon runs all services on a single port:
git clone https://github.com/affinidi/affinidi-webvh-service.git
cd affinidi-webvh-service
cargo build -p affinidi-webvh-daemon --release
./target/release/webvh-daemonThe daemon supports two identity modes at setup time:
- VTA-managed (default) — a parent VTA provisions the daemon's keys and DID. Requires VTA credentials.
- Self-managed — the daemon generates its own keys and self-hosts a
did:webvhidentifier. No VTA required. Daemon-only. See docs/bootstrap_startup.md.
See webvh-daemon/README.md for configuration details.
Run each service independently for distributed deployments:
# Build all services
cargo build --workspace --release
# Run each service with its own config
webvh-server setup && webvh-server
webvh-witness setup && webvh-witness
webvh-control setup && webvh-control
webvh-watcher --config watcher-config.tomlSee each crate's README for detailed setup instructions.
Every setup subcommand accepts --from <recipe.toml> for CI / scripted
deployments. The recipe is declarative TOML with no secrets — keys are
generated by setup; cloud credentials come from the environment.
Online (VTA reachable from CI runner):
# Phase 1: mint an ephemeral did:key, get the operator's PNM command, exit.
webvh-server setup --setup-key-out setup.key --context webvh
# (operator enrols the printed did:key at the VTA on a workstation)
# Phase 2: recipe drives everything else.
webvh-server setup --from examples/webvh-server-build.toml \
--setup-key-file setup.keyAir-gapped (no VTA network access — both phases non-interactive):
# Phase 1: vta_mode = "offline-prepare" in the recipe.
webvh-server setup --from recipe.toml
# → writes bootstrap-request.json + persists ephemeral seed in secret backend.
# (operator ferries the request to the VTA admin and receives sealed bundle)
# Phase 2: flip vta_mode = "offline-complete" + add bundle_path/expect_digest.
webvh-server setup --from recipe.toml
# → opens the sealed bundle, finishes setup. No TTY required.VTA-less daemon (self-managed):
# daemon recipe with vta_mode = "self-managed" — generates its own keys.
webvh-daemon setup --from examples/webvh-daemon-build.tomlOther flags: --force-reprovision to rotate an existing install (backs up
config.toml first), --non-interactive to fail fast on missing fields
instead of prompting, <binary> uninstall to tear down. Full reference in
docs/bootstrap_startup.md.
The webvh-server crate includes an example CLI (webvh-server/examples/client.rs)
that demonstrates the full flow of programmatically creating a did:webvh DID
and uploading it to a running webvh-control or webvh-daemon. It handles DIDComm v2
authentication, DID document construction, WebVH log entry creation, and
upload.
cargo build -p affinidi-webvh-server --example client-
Start the webvh-server with DIDComm authentication configured.
-
Run the example, pointing it at the server.
--webvh-didis the service's own DID — it's printed bywebvh-server setupandwebvh-daemon setupon completion. Re-print it any time withwebvh-server show-did(orwebvh-daemon show-did):cargo run -p affinidi-webvh-server --example client -- \ --server-url http://localhost:8530 \ --webvh-did did:webvh:...:localhost%3A8530:server
-
The example will generate a
did:keyidentity and pause, printing the DID:Generated DID: did:key:z6Mk... Ensure this DID is in the server ACL (e.g. via webvh-server invite). Press Enter to continue...Add the printed DID to the server's ACL (for example, by running the webvh-server
add-aclcommand in another terminal), then press Enter. -
The example will authenticate, create the DID, upload it, and verify resolution. On success it prints a summary:
DID Created and Hosted Successfully! Mnemonic: apple-banana SCID: FHcGtSJ... DID URL: http://localhost:8530/apple-banana/did.jsonl DID: did:webvh:FHcGtSJ...:localhost%3A8085:apple-banana Public Key: z6Mk...
If you face any issues or have suggestions, please don't hesitate to contact us using this link.
If you have a technical issue with the Affinidi WebVH Service codebase, you can also create an issue directly in GitHub.
-
Ensure the bug was not already reported by searching on GitHub under Issues.
-
If you're unable to find an open issue addressing the problem, open a new one. Be sure to include a title and clear description, as much relevant information as possible, and a code sample or an executable test case demonstrating the expected behaviour that is not occurring.
Want to contribute?
Head over to our CONTRIBUTING guidelines.