Skip to content

affinidi/affinidi-webvh-service

Affinidi WebVH Service

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.

Architecture

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)
└──────────────┘

Components

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

Quick Start

Requirements

  • Rust 1.94.0+ (2024 Edition)
  • Node.js 20+ (only if building the management UI)

Option 1: Unified daemon (recommended for getting started)

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-daemon

The 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:webvh identifier. No VTA required. Daemon-only. See docs/bootstrap_startup.md.

See webvh-daemon/README.md for configuration details.

Option 2: Standalone services

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.toml

See each crate's README for detailed setup instructions.

Option 3: Non-interactive / scripted setup

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.key

Air-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.toml

Other 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.

Example Client

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.

Building

cargo build -p affinidi-webvh-server --example client

Usage

  1. Start the webvh-server with DIDComm authentication configured.

  2. Run the example, pointing it at the server. --webvh-did is the service's own DID — it's printed by webvh-server setup and webvh-daemon setup on completion. Re-print it any time with webvh-server show-did (or webvh-daemon show-did):

    cargo run -p affinidi-webvh-server --example client -- \
      --server-url http://localhost:8530 \
      --webvh-did did:webvh:...:localhost%3A8530:server
  3. The example will generate a did:key identity 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-acl command in another terminal), then press Enter.

  4. 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...
    

Support & feedback

If you face any issues or have suggestions, please don't hesitate to contact us using this link.

Reporting technical issues

If you have a technical issue with the Affinidi WebVH Service codebase, you can also create an issue directly in GitHub.

  1. Ensure the bug was not already reported by searching on GitHub under Issues.

  2. 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.

Contributing

Want to contribute?

Head over to our CONTRIBUTING guidelines.

About

WebVH DID Infrastructure Services

Topics

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors