- OCaml 94%
- Go 5%
- Shell <1%
did #
Decentralized Identifiers for OCaml.
A Decentralized Identifier (DID) is a URI of the form
did:<method>:<method-specific-id> that names a subject, such as a person or
a service. The method decides who answers for the name. A did:web identifier
rests on its domain's own HTTPS server. A did:plc identifier resolves through
the PLC directory run by Bluesky, a central service. A client can still check
that directory's answer, because each identifier is the hash of its signed
genesis operation and every later update is signed by a rotation key, so a
client that checks the operation log can tell a forged document from a real
one. Resolving an identifier yields a DID document that lists the subject's
verification keys and service endpoints; ATProto (Bluesky), for example, finds
an account's data server this way. This library implements the syntax and
document model of W3C DID Core 1.0, with resolvers for
did:web and did:plc in separate packages.
Packages #
did: DID syntax and the document data model, with no I/O.did-web: thedid:webresolver, over HTTPS.did-plc: thedid:plcresolver, which queries a PLC directory (https://plc.directoryby default). It checks the identifier's syntax but neither the genesis hash nor the operation log, so it trusts the directory's answer.did-resolve: oneresolvefunction that dispatches todid-webordid-plcby method.
Installation #
Install with opam:
$ opam install did did-web did-plc did-resolve
If opam cannot find the packages, they may not yet be released in
the public opam-repository. Add the overlay repository, then
install them:
$ opam repo add samoht https://tangled.org/gazagnaire.org/opam-overlay.git
$ opam update
$ opam install did did-web did-plc did-resolve
Usage #
Syntax #
Parse a DID and extract its method and method-specific id:
# let did = Did.of_string_exn "did:plc:z72i7hdynmk6r22z27h6tvur" in
(Did.method_name did, Did.method_specific_id did)
- : string * string = ("plc", "z72i7hdynmk6r22z27h6tvur")
Method names must be lowercase, while the method-specific id keeps its case:
# Did.of_string "did:Web:example.com"
- : (Did.t, Did.error) result =
Error (`Msg "invalid character 'W' in DID method name at offset 4")
URL construction #
The URL of a did:web document is a pure function of the DID. The
specification maps the method-specific id to an HTTPS path, reading : as a
path separator:
# Did_web.url_of_did (Did.of_string_exn "did:web:example.com")
- : (string, Did.error) result =
Ok "https://example.com/.well-known/did.json"
# Did_web.url_of_did (Did.of_string_exn "did:web:example.com:users:alice")
- : (string, Did.error) result =
Ok "https://example.com/users/alice/did.json"
Resolution #
The did:web and did:plc resolvers run under Eio:
let resolve_web env did =
Eio.Switch.run @@ fun sw ->
match
Did_web.resolve
~sw ~clock:(Eio.Stdenv.clock env) ~mono:(Eio.Stdenv.mono_clock env) ~net:(Eio.Stdenv.net env) did
with
| Ok doc -> Fmt.pr "resolved: %a@." Did.Document.pp doc
| Error e -> Fmt.pr "failed: %a@." Did_web.pp_error e
Service lookup #
A DID document indexes its service endpoints by id (a fragment) and by
type. ATProto names an account's data server #atproto_pds:
# let did = Did.of_string_exn "did:web:example.com" in
let doc =
Did.Document.v
~service:[{
id = "#atproto_pds";
type_ = ["AtprotoPersonalDataServer"];
service_endpoint = "https://pds.example.com";
}]
did in
Option.map
(fun (s : Did.Document.service) -> s.service_endpoint)
(Did.Document.service_by_id doc "#atproto_pds")
- : string option = Some "https://pds.example.com"
Licence #
ISC. See LICENSE.md.