Decentralized Identifiers
Decentralized Identifiers (DIDs) are the long-lived, self-controlled identifiers that anchor most of what runs through the EDK: issuer keys, verifier keys, holder bindings, OCA bundle authors, trust-list entries. Every DID resolves to a DID Document, which is the public artifact that other parties verify signatures against, look up service endpoints in, and use to bootstrap secure channels.
This section is about the REST surface the EDK provides on top of the IDK DID stack. It covers two things in particular:
- how a DID gets resolved and where the resolver fits in the call graph, and
- the two distinct REST APIs the EDK ships for creating and maintaining DIDs, what each one is good for, and how to pick between them.
The lower-level DID manager, providers, and DSL all live in the IDK and are documented under IDK DID Overview. This guide focuses on the REST-facing parts that you wire into a tenant deployment.
What ships with the EDK
The EDK builds on the IDK DID stack and adds the enterprise REST surface, persistence backends, and method providers needed for a production tenant.
| Concern | Module(s) | Notes |
|---|---|---|
| DID manager core | lib-did-manager-public, lib-did-manager-impl | DID lifecycle, providers, filter, projections; lives in the IDK |
| Provider SPI and DSL | lib-did-manager-public | DidProvider, didCreateOptions { ... } |
| Method providers | lib-did-methods-key, lib-did-methods-jwk, lib-did-methods-web, lib-did-methods-webvh-* | One module per supported DID method |
| Resolver | lib-did-resolver-public, lib-did-resolver-impl | Registry-based, accept-media-type aware, cache-enabled |
| Universal Resolver REST | lib-did-rest-resolver-server | DIF-compatible, base path /1.0 |
| Universal Registrar REST | lib-did-rest-registrar-server | DIF-compatible, base path /1.0 |
| Rich DID manager REST | services-did-manager-rest | Sphereon API, base path /api/dids/v1 |
| Persistence | lib-did-persistence-memory, lib-did-persistence-sqlite, lib-did-persistence-postgresql, lib-did-persistence-mysql | Memory and SQLite are IDK; PostgreSQL and MySQL ship with the EDK |
The persistence split follows the wider IDK/EDK boundary: in-memory and SQLite stores live in the IDK so they work in any embedded scenario, while Postgres and MySQL ship with the EDK because they assume a JVM enterprise deployment.
Choosing between the two REST APIs
The EDK exposes two DID management REST surfaces over the same underlying DID manager. They serve different jobs and you typically enable the one that matches the consumer.
The Universal Registrar implements the DIF Universal Registrar spec. Five endpoints, document-level operations, and a stateless request and response shape that mirrors the spec line by line. It is the right choice when an existing tool, integration, or cross-vendor pipeline already speaks the DIF protocol, when you need predictable interop with non-Sphereon registrars, or when the operation you want to perform really is just "create / update / deactivate a document".
The rich DID manager API is a Sphereon-defined REST surface under /api/dids/v1. It exposes the same lifecycle operations plus everything the Universal Registrar omits: listing and filtering DIDs, paginating results, projecting only the fields you need, performing key-by-key changes through dedicated sub-resources, attaching aliases or equivalent identifiers, inspecting the KMS key mappings, and forcing or invalidating the resolved-document cache. It is the right choice for tenant admin UIs, internal control planes, automation that needs to enumerate DIDs, and any workflow that wants to mutate a single verification method, service, or controller without replaying the entire document.
The two surfaces are not exclusive. You can mount both in the same tenant: keep the rich API behind your internal ingress for operators, and expose the Universal Registrar through whichever path your DIF-speaking integration hits. They share state because they share the DID manager underneath.
For an at-a-glance comparison:
| Aspect | Universal Registrar | Rich DID manager API |
|---|---|---|
| Specification | DIF Universal Registrar 1.0 | Sphereon, documented in OpenAPI |
| Base path | /1.0 | /api/dids/v1 |
| Operations | 5 endpoints | ~50 endpoints across 10 sub-adapters |
| Granularity | Document-level | Document + per-sub-resource |
| Listing / filtering | Not in the spec | Built in (pagination, sort, expand, filter) |
| KMS key mapping inspection | Hidden | First-class |
| Document cache control | Implicit | Explicit (/document, /document/refresh, /document/cache) |
| Best fit | Cross-vendor interop, DIF-aware integrations | Tenant admin UIs, automation, control planes |
The dedicated pages cover each in detail:
- Universal Registrar walks through the five DIF endpoints, request and response shapes, and how to wire it into a Spring Boot tenant.
- Rich REST API covers the full
/api/dids/v1surface, the list filter, the expand projection, and the sub-resource endpoints that exist precisely because document-level PUT cannot preserve KMS invariants.
DID resolution
Resolution is the lookup step every credential issuance, verification, and trust evaluation depends on: take a DID, fetch its current DID Document, and use the public material in it. Unlike registration, resolution is read-only and almost always anonymous from the perspective of the host that publishes the document.
The Universal Resolver REST surface is the same one that ships in the IDK. It exposes three endpoints under /1.0:
| Method | Path | Purpose |
|---|---|---|
GET | /1.0/identifiers/{identifier} | Resolve a DID to its DID Document |
GET | /1.0/methods | List DID methods this resolver understands |
GET | /1.0/properties | Get resolver capability metadata |
GET /1.0/identifiers/{did} honors the standard Accept header so callers can ask for application/did+ld+json, application/did+json, or the result-envelope application/ld+json;profile="https://w3id.org/did-resolution". The response carries the resolved DID Document plus didResolutionMetadata and didDocumentMetadata envelopes, in line with the DIF resolution result schema.
Behind the endpoint sits the DID resolver registry. The registry inspects the method prefix (did:web, did:key, ...) and dispatches to the registered method resolver. Method resolvers come from the lib-did-methods-* modules and register themselves through the same SPI the DID manager uses for creation, which keeps creation and resolution in sync: if a method can be created, it can be resolved, with identical semantics.
What this means in practice:
did:keyanddid:jwkresolve locally, deriving the document from the identifier itself with no network call.did:webresolves by fetchinghttps://{domain}/.well-known/did.json(or the path-encoded equivalent), following the W3Cdid:webrules.did:webvhresolves the verifiable history log from the publishing host and replays it to reconstruct the current document, validating each entry against the witness signatures.
The resolver caches results and honors invalidation: the cache is what /api/dids/v1/dids/{did}/document/refresh clears on the rich API, and what internal callers (issuer flows, OID4VP verifier bindings, trust evaluators) hit when they need the current public material for a DID without going back to the network on every call.
When to call resolution directly
Most EDK code does not call resolution explicitly. The issuer, the verifier, and the trust stack inject the DidResolver interface and rely on it transparently. Direct REST calls to /1.0/identifiers/{did} are mostly useful for:
- diagnostic tooling that needs to see exactly what a third party would see when they look up your DID;
- integrations from another runtime that cannot embed the IDK and needs an HTTP-shaped resolver;
- cross-tenant or cross-host monitoring that periodically refreshes a published
did:webdocument.
For the rich DID manager API, the GET /api/dids/v1/dids/{did}/resolve endpoint runs the same resolver but is scoped to a DID that is already tracked locally, which is convenient when you want resolution metadata alongside the locally-stored aliases, controllers, and key mappings.
Sub-pages
Related material
- IDK DID Overview for the DID manager DSL, providers, and method-specific options.
- Authentication and identity for how DIDs slot into the auth and matching pipelines.
- Container deployment: DID services for how the REST surface lands inside the
enterprise-didcontainer.