This is a PROOF OF CONCEPT ONLY Python implementation of the Full DID Resolver and Verifiable Data Registry (VDR) components the did:webplus method, each backed by SQLite for DID document storage.
This codebase is meant to demonstrate that it is possible to have a second, interoperable implementation of (some components of) the did:webplus DID method. It is meant ONLY AS A PROOF OF CONCEPT and should NOT BE USED in any real scenario, as the code has not been completely reviewed or fully security audited. This codebase will not be maintained or kept up to date. Pull requests will not be accepted.
This codebase was produced by a coding AI by pointing it at the did:webplus spec and having it implement the DID resolver and VDR. The AI was guided through this process by the author of the Rust reference implementation of did:webplus in order to produce a minimal viable demonstration of interoperability between this implementation and the Rust reference implementation. See interop for more on interoperability testing, including how to run it.
- Python 3.10+
- uv for package management
uv syncWith dev dependencies for testing:
uv sync --extra dev# Resolve a DID (fetches from VDR if not cached)
uv run did-webplus resolve "did:webplus:ledgerdomain.github.io:did-webplus-spec:uFiANVlMledNFUBJNiZPuvfgzxvJlGGDBIpDFpM4DXW6Bow"
# Resolve a DID at a specific versionId (fetches from VDR if not cached)
uv run did-webplus resolve "did:webplus:ledgerdomain.github.io:did-webplus-spec:uFiANVlMledNFUBJNiZPuvfgzxvJlGGDBIpDFpM4DXW6Bow?versionId=1"
# Resolve a DID at a specific selfHash (fetches from VDR if not cached)
uv run did-webplus resolve "did:webplus:ledgerdomain.github.io:did-webplus-spec:uFiANVlMledNFUBJNiZPuvfgzxvJlGGDBIpDFpM4DXW6Bow?selfHash=uFiAsMCOasGw6SDizP1hIvfCtwGKKNBpjU-SmTfIMi5Lc6A"
# JSON output
uv run did-webplus resolve "did:webplus:ledgerdomain.github.io:did-webplus-spec:uFiANVlMledNFUBJNiZPuvfgzxvJlGGDBIpDFpM4DXW6Bow" -o json
# Offline mode (fail if not in local store)
uv run did-webplus resolve "did:webplus:ledgerdomain.github.io:did-webplus-spec:uFiANVlMledNFUBJNiZPuvfgzxvJlGGDBIpDFpM4DXW6Bow" --no-fetch
# Options (can also be set via environment variables)
uv run did-webplus resolve "did:webplus:ledgerdomain.github.io:did-webplus-spec:uFiANVlMledNFUBJNiZPuvfgzxvJlGGDBIpDFpM4DXW6Bow" --base-dir ./mydata --vdg-url https://vdg.example.comThe did subcommands implement a minimal DID controller: a single Ed25519 key stored under --base-dir (default ~/.poc-did-webplus-py) in a subdirectory named for each controlled DID. Use did create to register a new DID with a VDR; it prints the DID so you can pass it to did update or did deactivate. The same --base-dir is used for the resolver's did_documents.db and the controller's keys.
# DID controller: create a new DID (prints the DID on success). Note that the host (hostname and port, which in
# this case is `localhost:8085`) must match that of the VDR exactly.
uv run did-webplus did create http://localhost:8085
# DID controller: create with custom base directory
uv run did-webplus did create http://localhost:8085 --base-dir ./mydata
# DID controller: update a DID (key rotation)
uv run did-webplus did update "did:webplus:localhost%3A8085:uFiYourRootHashHere"
# DID controller: deactivate a DID (tombstone)
uv run did-webplus did deactivate "did:webplus:localhost%3A8085:uFiYourRootHashHere" --confirm THIS-IS-IRREVERSIBLE
# DID controller: use http scheme override for non-localhost VDRs. This (opting out of TLS-encrypted HTTP
# for specific hosts) is intended only for testing, and should not be used in production.
uv run did-webplus did create https://example.com:3000 --http-scheme-override example.com=http# Run VDR service in listen mode. Note that there are two concepts of host here. One is the "listen host",
# which is 0.0.0.0:<listen-port> where <listen-port> defaults to 80. The other is "DID host", which is the
# hostname and port that the VDR checks to be present in the DIDs that the VDR hosts -- any DID create
# operations run against this VDR must specify that host exactly (see `did create` above). The --listen-port
# argument would be used e.g. in dockerized or reverse proxy configurations where the listen port and the
# externally exposed port are different.
uv run did-webplus listen --did-hostname localhost --did-port 8085 --listen-port 8085uv run pytest