Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
Affinidi WebVH Watcher
The WebVH Watcher is a read-only DID mirror. The control planes it is
configured to mirror push it signed webvh/sync/* Trust Tasks, and it serves
what they push publicly. It provides redundancy and geographic distribution
for DID resolution without managing DIDs.
The watcher has its own DID, with keys in a VTA context, like every other service here. It verifies every log it mirrors exactly as a hosting server does, and it applies a sync only when its source signed it.
IMPORTANT: did-hosting-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.
Getting Started
Prerequisites
- Rust 1.94.0+ (2024 Edition)
1. Build
The binary is produced at target/release/webvh-watcher.
2. Set up
webvh-watcher setup provisions the watcher's DID from a VTA context
(interactive, online), or webvh-watcher setup --from <recipe.toml> for any
VTA mode — see examples/webvh-watcher-build.toml. It writes config.toml,
stores the DID's keys in the secrets backend, and writes watcher-did.jsonl
for you to publish on a hosting server. The part of config.toml that says
whom it mirrors:
[]
# Only a webvh/sync/* Trust Task signed by one of these DIDs is applied.
= ["did:webvh:control.example.com"]
3. Configure the control plane
On each control plane the watcher mirrors, map the watcher's URL to its DID:
[[]]
= "https://watcher1.example.com"
= "did:webvh:…:watcher1.example.com"
A DID whose log names https://watcher1.example.com in its watchers
parameter is then pushed there through the control plane's outbox — the same
delivery, retry and acknowledgement as its edges — over the transport the
watcher's DID advertises (TSP, DIDComm, or HTTPS).
4. Start the watcher
How It Works
control plane ──(signed webvh/sync/update|delete|batch, TSP · DIDComm · HTTPS)──► webvh-watcher
Clients ──(GET /{mnemonic}/did.jsonl)──► webvh-watcher
- A DID is published at the control plane.
- The control plane queues a signed
webvh/sync/update/0.2for every configured watcher the log names. - The watcher verifies the document's proof (
proofPurpose: authentication, bound to its issuer, addressed to the watcher, fresh, not a replay) and that the issuer is one ofsync.source_dids; then it verifies the log — its chain and witness proofs, the slot it names, and that it strictly extends anything already mirrored for that DID — and answers with a signed#response, which settles the outbox entry. - Clients resolve DIDs from any watcher.
A document that does not verify gets no reply. A verified one from a DID that
is not a source is refused notAuthorized (signed), and is not recorded.
Configuration
The watcher is configured via a TOML file. By default it looks
for config.toml in the current directory. You can specify a
different path with the --config flag or the
WATCHER_CONFIG_PATH environment variable.
Environment Variable Overrides
| Variable | Description |
|---|---|
WATCHER_CONFIG_PATH |
Path to config file |
WATCHER_SERVER_HOST |
Bind host |
WATCHER_SERVER_PORT |
Bind port |
WATCHER_LOG_LEVEL |
Log level |
WATCHER_SERVER_DID |
The watcher's DID |
WATCHER_MEDIATOR_DID |
Its mediator |
WATCHER_SOURCE_DIDS |
Comma-separated source DIDs |
WATCHER_SERVER_RESOLVE_CACHE_MAX_AGE_SECS |
DID-resolution max-age (0 = no-cache; default 300) |
WATCHER_SERVER_RESOLVE_CACHE_SHARED_MAX_AGE_SECS |
DID-resolution s-maxage (empty clears) |
WATCHER_SERVER_RESOLVE_CACHE_STALE_WHILE_REVALIDATE_SECS |
DID-resolution stale-while-revalidate (empty clears) |
The same settings live in [server.resolve_cache] (max_age_secs,
shared_max_age_secs, stale_while_revalidate_secs).
CLI Commands
webvh-watcher # Run watcher (default)
webvh-watcher setup # Interactive online VTA setup
webvh-watcher setup --setup-key-out <path> # Mint a setup key for a headless online setup
webvh-watcher setup --from <recipe.toml> [--setup-key-file <path>]
webvh-watcher health # Diagnostics
API Endpoints
Public (unauthenticated)
| Method | Path | Description |
|---|---|---|
GET |
/api/health |
Health check |
GET |
/{mnemonic}/did.jsonl |
Resolve DID log |
GET |
/{mnemonic}/did-witness.json |
Resolve witness |
GET |
/.well-known/did.jsonl |
Root DID log |
GET |
/.well-known/did-witness.json |
Root witness |
Trust Tasks
| Method | Path | Description |
|---|---|---|
POST |
/api/trust-tasks |
The HTTPS binding of webvh/sync/update/0.2, webvh/sync/delete/0.2, webvh/sync/batch/0.1 |
The same documents are accepted over TSP and DIDComm on the watcher's mediator connection.
Library Usage
The webvh-watcher crate can be used as a library (e.g., by the did-hosting-daemon). It exposes:
webvh_watcher::config::AppConfig— configurationwebvh_watcher::server::AppState— application statewebvh_watcher::routes::router()— Axum routerwebvh_watcher::server::run()— standalone entry point
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 DID Hosting 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.
Contributing
Want to contribute? Head over to our CONTRIBUTING guidelines.