pub fn canonical_did(value: &str) -> StringExpand description
A DID from a URL, spelled the way this project stores it.
Both a path segment and a query value are percent-decoded once before a
handler sees them, and a local agent’s DID already contains a
percent-encoding of its own: a DID on a bare loopback host is written
did:web:localhost%3A3400, so naming one in a URL means encoding the
% as well. A client that builds URLs properly does that and
this changes nothing; a person with curl and a DID copied out of a log
writes the bare colon, and without this gets an empty answer for an agent
that is plainly there.
The repair is unambiguous rather than a guess: in did:web a colon
separates path segments, a port must be written %3A, and a bare
host:3400 in the identifier position is not a DID this server could have
minted. Anything that is not a did:web with a numeric last segment comes
back untouched, so a did:plc, a handle and a properly encoded DID all
pass straight through.
Applied to every DID-valued input rather than only where a bug was noticed: a filter that silently matched nothing is worse than a 404, because a caller reads it as “no such children” rather than as “ask differently”.
It lives here rather than beside either caller because both the query
service and the personal data server’s com.atproto.repo.* routes need
exactly this repair, and two copies of it would agree until one was
edited.
use didbot_identity::canonical_did;
assert_eq!(
canonical_did("did:web:localhost:3400"),
"did:web:localhost%3A3400"
);
// Already encoded, a plain hostname, and a `did:plc` all pass through.
assert_eq!(
canonical_did("did:web:localhost%3A3400"),
"did:web:localhost%3A3400"
);
assert_eq!(canonical_did("did:web:agents.example"), "did:web:agents.example");
assert_eq!(canonical_did("did:plc:abc123"), "did:plc:abc123");