An atproto PDS written in Go
1package identity 2 3import ( 4 "context" 5 "net/http" 6 "sync" 7) 8 9type BackingCache interface { 10 GetDoc(did string) (*DidDoc, bool) 11 PutDoc(did string, doc *DidDoc) error 12 BustDoc(did string) error 13 14 GetDid(handle string) (string, bool) 15 PutDid(handle string, did string) error 16 BustDid(handle string) error 17} 18 19type Passport struct { 20 h *http.Client 21 bc BackingCache 22 mu sync.RWMutex 23} 24 25func NewPassport(h *http.Client, bc BackingCache) *Passport { 26 if h == nil { 27 h = http.DefaultClient 28 } 29 30 return &Passport{ 31 h: h, 32 bc: bc, 33 } 34} 35 36func (p *Passport) FetchDoc(ctx context.Context, did string) (*DidDoc, error) { 37 skipCache, _ := ctx.Value("skip-cache").(bool) 38 39 if !skipCache { 40 p.mu.RLock() 41 cached, ok := p.bc.GetDoc(did) 42 p.mu.RUnlock() 43 44 if ok { 45 return cached, nil 46 } 47 } 48 49 // TODO: should coalesce requests here 50 doc, err := FetchDidDoc(ctx, p.h, did) 51 if err != nil { 52 return nil, err 53 } 54 55 p.mu.Lock() 56 p.bc.PutDoc(did, doc) 57 p.mu.Unlock() 58 59 return doc, nil 60} 61 62func (p *Passport) ResolveHandle(ctx context.Context, handle string) (string, error) { 63 skipCache, _ := ctx.Value("skip-cache").(bool) 64 65 if !skipCache { 66 p.mu.RLock() 67 cached, ok := p.bc.GetDid(handle) 68 p.mu.RUnlock() 69 70 if ok { 71 return cached, nil 72 } 73 } 74 75 did, err := ResolveHandle(ctx, p.h, handle) 76 if err != nil { 77 return "", err 78 } 79 80 p.mu.Lock() 81 p.bc.PutDid(handle, did) 82 p.mu.Unlock() 83 84 return did, nil 85} 86 87func (p *Passport) BustDoc(ctx context.Context, did string) error { 88 p.mu.Lock() 89 defer p.mu.Unlock() 90 return p.bc.BustDoc(did) 91} 92 93func (p *Passport) BustDid(ctx context.Context, handle string) error { 94 p.mu.Lock() 95 defer p.mu.Unlock() 96 return p.bc.BustDid(handle) 97}