···13131414// Represents an atproto identity. Could be a regular user account, or a service account (eg, feed generator)
1515type Identity struct {
1616- DID syntax.DID
1616+ DID syntax.DID `json:"did"`
17171818 // Handle/DID mapping must be bi-directionally verified. If that fails, the Handle should be the special 'handle.invalid' value
1919- Handle syntax.Handle
1919+ Handle syntax.Handle `json:"handle"`
20202121 // These fields represent a parsed subset of a DID document. They are all nullable. Note that the services and keys maps do not preserve order, so they don't exactly round-trip DID documents.
2222- AlsoKnownAs []string
2323- Services map[string]ServiceEndpoint
2424- Keys map[string]VerificationMethod
2222+ AlsoKnownAs []string `json:"alsoKnownAs"`
2323+ Services map[string]ServiceEndpoint `json:"services"`
2424+ Keys map[string]VerificationMethod `json:"keys"`
2525}
26262727// Sub-field type for [Identity], representing a crytographic public key declared as a "verificationMethod" in the DID document.
2828type VerificationMethod struct {
2929- Type string
3030- PublicKeyMultibase string
2929+ Type string `json:"type"`
3030+ PublicKeyMultibase string `json:"publicKeyMultibase"`
3131}
32323333// Sub-field type for [Identity], representing a service endpoint URL declared in the DID document.
3434type ServiceEndpoint struct {
3535- Type string
3636- URL string
3535+ Type string `json:"type"`
3636+ URL string `json:"url"`
3737}
38383939// Extracts the information relevant to atproto from an arbitrary DID document.
+4-1
cmd/butterfly/cmd_discover.go
···106106 var s store.Store
107107 switch storeMode {
108108 case "stdout":
109109- s = &store.StdoutStore{Mode: store.StdoutStoreModeStats}
109109+ s = &store.StdoutStore{Mode: store.StdoutStoreModePassthrough}
110110 case "tarfiles":
111111 s = store.NewTarfilesStore(storageDir)
112112 case "duckdb":
···151151 fmt.Printf("Failed to resolve %s: %v", did, err)
152152 }
153153 }
154154+155155+ v, err := resolver.ResolveDID(ctx, "did:plc:upo6iq6ekh66d4mbhmiy6se4")
156156+ fmt.Println(v)
154157155158 return nil
156159}
+32-10
cmd/butterfly/identity/store_directory.go
···2727}
28282929type handleEntry struct {
3030- Updated time.Time
3131- DID syntax.DID
3232- Err error
3030+ Updated time.Time `json:"updated"`
3131+ DID syntax.DID `json:"did"`
3232+ Err error `json:"err"`
3333}
34343535type identityEntry struct {
3636- Updated time.Time
3737- Identity *identity.Identity
3838- Err error
3636+ Updated time.Time `json:"updated"`
3737+ Identity *identity.Identity `json:"identity"`
3838+ Err error `json:"err"`
3939}
40404141// var _ identity.Directory = (*StoreDirectory)(nil) TODO is this needed?
···300300func getHandle(store store.Store, handle syntax.Handle) (*handleEntry, error) {
301301 entryJSON, err := store.KvGet(handleCache, string(handle))
302302 if entryJSON != "" {
303303- // TODO - is this parse safe? do we need to be checking the output or anything?
304303 var entry handleEntry
305304 if err := json.Unmarshal([]byte(entryJSON), &entry); err != nil {
305305+ return nil, err
306306+ }
307307+ if err := validateHandleEntry(&entry); err != nil {
306308 return nil, err
307309 }
308310 return &entry, nil
···311313}
312314313315func putHandle(store store.Store, handle syntax.Handle, entry *handleEntry) error {
314314- // TODO - is this right?
315316 entryJSON, err := json.Marshal(entry)
316317 if err != nil {
317318 return err
···326327func getIdent(store store.Store, did syntax.DID) (*identityEntry, error) {
327328 entryJSON, err := store.KvGet(identityCache, string(did))
328329 if entryJSON != "" {
329329- // TODO - is this parse safe? do we need to be checking the output or anything?
330330 var entry identityEntry
331331 if err := json.Unmarshal([]byte(entryJSON), &entry); err != nil {
332332 return nil, err
333333 }
334334+ if err := validateIdentityEntry(&entry); err != nil {
335335+ return nil, err
336336+ }
334337 return &entry, nil
335338 }
336339 return nil, err
337340}
338341339342func putIdent(store store.Store, did syntax.DID, entry *identityEntry) error {
340340- // TODO - is this right?
341343 entryJSON, err := json.Marshal(entry)
342344 if err != nil {
343345 return err
···348350func delIdent(store store.Store, did syntax.DID) error {
349351 return store.KvDel(identityCache, string(did))
350352}
353353+354354+func validateHandleEntry(entry *handleEntry) error {
355355+ _, err := syntax.ParseDID(string(entry.DID))
356356+ if err != nil {
357357+ return fmt.Errorf("invalid handle entry: invalid did, %w", err)
358358+ }
359359+ return nil
360360+}
361361+362362+func validateIdentityEntry(entry *identityEntry) error {
363363+ _, err := syntax.ParseDID(string(entry.Identity.DID))
364364+ if err != nil {
365365+ return fmt.Errorf("invalid identity entry: invalid did, %w", err)
366366+ }
367367+ _, err = syntax.ParseHandle(string(entry.Identity.Handle))
368368+ if err != nil {
369369+ return fmt.Errorf("invalid identity entry: invalid handle, %w", err)
370370+ }
371371+ return nil
372372+}