A fork of the Cocoon PDS but being made more distributed.
11

Configure Feed

Select the types of activity you want to include in your feed.

refactor identity package

Hailey (Jul 19, 2025, 9:30 AM -0700) 6f90d8a4 5f0d713f

+89 -59
+73 -54
identity/identity.go
··· 13 13 "github.com/bluesky-social/indigo/util" 14 14 ) 15 15 16 - func ResolveHandle(ctx context.Context, cli *http.Client, handle string) (string, error) { 17 - if cli == nil { 18 - cli = util.RobustHTTPClient() 19 - } 20 - 21 - var did string 22 - 23 - _, err := syntax.ParseHandle(handle) 16 + func ResolveHandleFromTXT(ctx context.Context, handle string) (string, error) { 17 + name := fmt.Sprintf("_atproto.%s", handle) 18 + recs, err := net.LookupTXT(name) 24 19 if err != nil { 25 - return "", err 20 + return "", fmt.Errorf("handle could not be resolved via txt: %w", err) 26 21 } 27 22 28 - recs, err := net.LookupTXT(fmt.Sprintf("_atproto.%s", handle)) 29 - if err == nil { 30 - for _, rec := range recs { 31 - if strings.HasPrefix(rec, "did=") { 32 - did = strings.Split(rec, "did=")[1] 33 - break 23 + for _, rec := range recs { 24 + if strings.HasPrefix(rec, "did=") { 25 + maybeDid := strings.Split(rec, "did=")[1] 26 + if _, err := syntax.ParseDID(maybeDid); err == nil { 27 + return maybeDid, nil 34 28 } 35 29 } 36 - } else { 37 - fmt.Printf("erorr getting txt records: %v\n", err) 38 30 } 39 31 40 - if did == "" { 41 - req, err := http.NewRequestWithContext( 42 - ctx, 43 - "GET", 44 - fmt.Sprintf("https://%s/.well-known/atproto-did", handle), 45 - nil, 46 - ) 47 - if err != nil { 48 - return "", nil 49 - } 32 + return "", fmt.Errorf("handle could not be resolved via txt: no record found") 33 + } 50 34 51 - resp, err := http.DefaultClient.Do(req) 52 - if err != nil { 53 - return "", nil 54 - } 55 - defer resp.Body.Close() 35 + func ResolveHandleFromWellKnown(ctx context.Context, cli *http.Client, handle string) (string, error) { 36 + ustr := fmt.Sprintf("https://%s/.well=known/atproto-did", handle) 37 + req, err := http.NewRequestWithContext( 38 + ctx, 39 + "GET", 40 + ustr, 41 + nil, 42 + ) 43 + if err != nil { 44 + return "", fmt.Errorf("handle could not be resolved via web: %w", err) 45 + } 56 46 57 - if resp.StatusCode != http.StatusOK { 58 - io.Copy(io.Discard, resp.Body) 59 - return "", fmt.Errorf("unable to resolve handle") 60 - } 47 + resp, err := cli.Do(req) 48 + if err != nil { 49 + return "", fmt.Errorf("handle could not be resolved via web: %w", err) 50 + } 51 + defer resp.Body.Close() 61 52 62 - b, err := io.ReadAll(resp.Body) 63 - if err != nil { 64 - return "", err 65 - } 53 + b, err := io.ReadAll(resp.Body) 54 + if err != nil { 55 + return "", fmt.Errorf("handle could not be resolved via web: %w", err) 56 + } 66 57 67 - maybeDid := string(b) 58 + if resp.StatusCode != http.StatusOK { 59 + return "", fmt.Errorf("handle could not be resolved via web: invalid status code %d", resp.StatusCode) 60 + } 68 61 69 - if _, err := syntax.ParseDID(maybeDid); err != nil { 70 - return "", fmt.Errorf("unable to resolve handle") 71 - } 62 + maybeDid := string(b) 72 63 73 - did = maybeDid 64 + if _, err := syntax.ParseDID(maybeDid); err != nil { 65 + return "", fmt.Errorf("handle could not be resolved via web: invalid did in document") 74 66 } 75 67 76 - return did, nil 68 + return maybeDid, nil 77 69 } 78 70 79 - func FetchDidDoc(ctx context.Context, cli *http.Client, did string) (*DidDoc, error) { 71 + func ResolveHandle(ctx context.Context, cli *http.Client, handle string) (string, error) { 80 72 if cli == nil { 81 73 cli = util.RobustHTTPClient() 82 74 } 83 75 84 - var ustr string 76 + _, err := syntax.ParseHandle(handle) 77 + if err != nil { 78 + return "", err 79 + } 80 + 81 + if maybeDidFromTxt, err := ResolveHandleFromTXT(ctx, handle); err == nil { 82 + return maybeDidFromTxt, nil 83 + } 84 + 85 + if maybeDidFromWeb, err := ResolveHandleFromWellKnown(ctx, cli, handle); err == nil { 86 + return maybeDidFromWeb, nil 87 + } 88 + 89 + return "", fmt.Errorf("handle could not be resolved") 90 + } 91 + 92 + func DidToDocUrl(did string) (string, error) { 85 93 if strings.HasPrefix(did, "did:plc:") { 86 - ustr = fmt.Sprintf("https://plc.directory/%s", did) 94 + return fmt.Sprintf("https://plc.directory/%s", did), nil 87 95 } else if strings.HasPrefix(did, "did:web:") { 88 - ustr = fmt.Sprintf("https://%s/.well-known/did.json", strings.TrimPrefix(did, "did:web:")) 96 + return fmt.Sprintf("https://%s/.well-known/did.json", strings.TrimPrefix(did, "did:web:")), nil 89 97 } else { 90 - return nil, fmt.Errorf("did was not a supported did type") 98 + return "", fmt.Errorf("did was not a supported did type") 99 + } 100 + } 101 + 102 + func FetchDidDoc(ctx context.Context, cli *http.Client, did string) (*DidDoc, error) { 103 + if cli == nil { 104 + cli = util.RobustHTTPClient() 105 + } 106 + 107 + ustr, err := DidToDocUrl(did) 108 + if err != nil { 109 + return nil, err 91 110 } 92 111 93 112 req, err := http.NewRequestWithContext(ctx, "GET", ustr, nil) ··· 95 114 return nil, err 96 115 } 97 116 98 - resp, err := http.DefaultClient.Do(req) 117 + resp, err := cli.Do(req) 99 118 if err != nil { 100 119 return nil, err 101 120 } ··· 103 122 104 123 if resp.StatusCode != 200 { 105 124 io.Copy(io.Discard, resp.Body) 106 - return nil, fmt.Errorf("could not find identity in plc registry") 125 + return nil, fmt.Errorf("unable to find did doc at url. did: %s. url: %s", did, ustr) 107 126 } 108 127 109 128 var diddoc DidDoc ··· 127 146 return nil, err 128 147 } 129 148 130 - resp, err := http.DefaultClient.Do(req) 149 + resp, err := cli.Do(req) 131 150 if err != nil { 132 151 return nil, err 133 152 }
+16 -5
identity/passport.go
··· 19 19 type Passport struct { 20 20 h *http.Client 21 21 bc BackingCache 22 - lk sync.Mutex 22 + mu sync.RWMutex 23 23 } 24 24 25 25 func NewPassport(h *http.Client, bc BackingCache) *Passport { ··· 30 30 return &Passport{ 31 31 h: h, 32 32 bc: bc, 33 - lk: sync.Mutex{}, 34 33 } 35 34 } 36 35 ··· 38 37 skipCache, _ := ctx.Value("skip-cache").(bool) 39 38 40 39 if !skipCache { 40 + p.mu.RLock() 41 41 cached, ok := p.bc.GetDoc(did) 42 + p.mu.RUnlock() 43 + 42 44 if ok { 43 45 return cached, nil 44 46 } 45 47 } 46 48 47 - p.lk.Lock() // this is pretty pathetic, and i should rethink this. but for now, fuck it 48 - defer p.lk.Unlock() 49 - 49 + // TODO: should coalesce requests here 50 50 doc, err := FetchDidDoc(ctx, p.h, did) 51 51 if err != nil { 52 52 return nil, err 53 53 } 54 54 55 + p.mu.Lock() 55 56 p.bc.PutDoc(did, doc) 57 + p.mu.Unlock() 56 58 57 59 return doc, nil 58 60 } ··· 61 63 skipCache, _ := ctx.Value("skip-cache").(bool) 62 64 63 65 if !skipCache { 66 + p.mu.RLock() 64 67 cached, ok := p.bc.GetDid(handle) 68 + p.mu.RUnlock() 69 + 65 70 if ok { 66 71 return cached, nil 67 72 } ··· 72 77 return "", err 73 78 } 74 79 80 + p.mu.Lock() 75 81 p.bc.PutDid(handle, did) 82 + p.mu.Unlock() 76 83 77 84 return did, nil 78 85 } 79 86 80 87 func (p *Passport) BustDoc(ctx context.Context, did string) error { 88 + p.mu.Lock() 89 + defer p.mu.Unlock() 81 90 return p.bc.BustDoc(did) 82 91 } 83 92 84 93 func (p *Passport) BustDid(ctx context.Context, handle string) error { 94 + p.mu.Lock() 95 + defer p.mu.Unlock() 85 96 return p.bc.BustDid(handle) 86 97 }