Skip to content

feat: add did:plc implementation#33

Merged
alanshaw merged 10 commits into
mainfrom
ash/feat/did-plc-impl
Jun 30, 2026
Merged

feat: add did:plc implementation#33
alanshaw merged 10 commits into
mainfrom
ash/feat/did-plc-impl

Conversation

@alanshaw

@alanshaw alanshaw commented Jun 25, 2026

Copy link
Copy Markdown
Member

Adds a did:plc implementation that is compatible with ucantone and not atproto specific. PR also includes a resolver and client library for creating/updating/deactivating the DID.

Based on the spec: https://github.com/did-method-plc/did-method-plc/blob/main/website/spec/v0.1/did-plc.md

...and 2 existing reference implementations to verify compatibility:

@alanshaw
alanshaw requested review from Peeja, bajtos, Copilot and frrist June 25, 2026 16:57
@alanshaw alanshaw changed the title feat: add did:plc implementation feat: add did:plc implementation Jun 25, 2026
Comment thread did/document.go Outdated
ID DID `json:"id"`
Controller OneOrMany[DID] `json:"controller,omitempty"`
AlsoKnownAs []DID `json:"alsoKnownAs,omitempty"`
AlsoKnownAs []string `json:"alsoKnownAs,omitempty"`

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be a string array per spec https://www.w3.org/TR/did-1.1/#core-properties

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, definitely shouldn't be DID. Possibly should be url.URL, though? It can't be just any string, it has to be…well, a URI, really, but that's what url.URL actually implements.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have made it into a did.URL, which is a url.URL with JSON marshal/unmarshal.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a did:plc implementation (types + signing + resolver + directory client) intended to be compatible with ucantone without being atproto-specific. It also updates the core DID Document representation and context parsing for interoperability with PLC directory outputs.

Changes:

  • Add did:plc data model + operation/tombstone builders, plus signing and signature verification helpers.
  • Add a PLC resolver and directory client (fetch last operation, publish updates, deactivate).
  • Update DID Document parsing/typing (alsoKnownAs as []string) and accept DID Core v1.0 context on input.

Reviewed changes

Copilot reviewed 15 out of 17 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
did/web/web_test.go Updates did:web resolver tests for alsoKnownAs now being []string.
did/plc/types.go Adds PLC operation/tombstone types and builder helpers.
did/plc/types_test.go Unit tests for PLC type builders and CID-linking behavior.
did/plc/resolver.go Adds a did:plc resolver that fetches documents from a directory endpoint.
did/plc/resolver_test.go Tests for PLC resolver behavior and request construction.
did/plc/client.go Adds a directory client for Last, Update, and Deactivate.
did/plc/client_test.go Tests for directory client request/response handling, including tombstones.
did/plc/plc.go Adds DID derivation (New) plus signing and verification helpers.
did/plc/plc_test.go Tests for DID derivation determinism and signature verification.
did/plc/e2e_test.go Optional e2e tests against the live PLC directory behind -tags e2e.
did/plc/README.md Usage documentation for resolver/client and operation flows.
did/plc/gen/main.go Codegen driver for CBOR and DagJSON encoders.
did/plc/json_gen.go Generated DagJSON codecs for PLC types.
did/plc/cbor_gen.go Generated CBOR codecs for PLC types.
did/document.go Changes Document.AlsoKnownAs to []string and updates JSON decoding accordingly.
did/context.go Accepts DID Core v1.0 context on input for interoperability.
did/context_test.go Updates/extends tests for accepting v1.0 context and updated error messages.
Files not reviewed (2)
  • did/plc/cbor_gen.go: Generated file
  • did/plc/json_gen.go: Generated file

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread did/plc/types.go Outdated
Comment thread did/plc/plc.go
Comment thread did/plc/client.go
Comment thread did/plc/resolver_test.go Outdated
@bajtos

bajtos commented Jun 26, 2026

Copy link
Copy Markdown

Where can I find more details about the did:plc scheme? Is https://web.plc.directory/spec/v0.1/did-plc the spec we want to implement?

@alanshaw

Copy link
Copy Markdown
Member Author

Where can I find more details about the did:plc scheme? Is https://web.plc.directory/spec/v0.1/did-plc the spec we want to implement?

Spec: https://github.com/did-method-plc/did-method-plc/blob/main/website/spec/v0.1/did-plc.md

...and I've been referencing the 2 existing implementations to verify compatibility:

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 17 changed files in this pull request and generated 2 comments.

Files not reviewed (2)
  • did/plc/cbor_gen.go: Generated file
  • did/plc/json_gen.go: Generated file

Comment thread did/plc/plc.go
Comment thread did/plc/plc.go

@Peeja Peeja left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, with a couple of comments. The v1 one might be a blocker, though, if it means it won't work properly.

Comment thread did/document.go Outdated
ID DID `json:"id"`
Controller OneOrMany[DID] `json:"controller,omitempty"`
AlsoKnownAs []DID `json:"alsoKnownAs,omitempty"`
AlsoKnownAs []string `json:"alsoKnownAs,omitempty"`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, definitely shouldn't be DID. Possibly should be url.URL, though? It can't be just any string, it has to be…well, a URI, really, but that's what url.URL actually implements.

Comment thread did/context_test.go

@bajtos bajtos left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TBH, I don't see why we cannot use the go-didplc implementation, but I'll trust your judgement.

I skimmed through the code and don't see any obvious issues.

I asked Claude to review the diff; see the comments below. None of them is blocking this PR from landing. The first one about map-key ordering looks important if it's valid, but it's something we can fix in a follow-up pull request.

Comment thread did/plc/cbor_gen.go
Comment thread did/plc/client.go Outdated
Comment thread did/plc/plc.go
@alanshaw

Copy link
Copy Markdown
Member Author

TBH, I don't see why we cannot use the go-didplc implementation, but I'll trust your judgement.

Some justification for not using https://github.com/did-method-plc/go-didplc

  1. The first thing the README states is "This codebase is pre-v1.0, there may be breaking API changes" so immediately I felt as though this library was not maintained and might not work.
  2. Uses go-ipld-cbor which pulls in boxo, which pulls in go-ipld-prime - something we're explicitly trying to avoid.
  3. Pulls in atproto crypto library https://github.com/bluesky-social/indigo/atproto/atcrypto and uses those interfaces for signing/verifying. Those interfaces that are not compatible with our multikey ed25519 or secp256k1 implementations.
  4. Contains hard coded atproto specific keys and values for verification methods and services.
  5. The client takes DIDs as strings, not a DID type.
  6. The client has no facility to fetch the last operation - you have to fetch the whole chain.
  7. Deals with legacy operations that we will never see.

@alanshaw
alanshaw merged commit a8f24fe into main Jun 30, 2026
1 check passed
@alanshaw
alanshaw deleted the ash/feat/did-plc-impl branch June 30, 2026 10:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants