Low-latency AT Protocol interaction indexer.
20

Configure Feed

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

api: add resolveMiniDoc

Aly Raffauf (Jul 4, 2026, 11:54 AM EDT) 74c98821 c859f5b8

+78 -5
+11 -1
README.md
··· 90 90 91 91 ## API 92 92 93 - Asterism implements all five current endpoints from the [microcosm links XRPC namespace](https://constellation.microcosm.blue/) (the older `/links/*` REST endpoints are deprecated upstream in favor of these and aren't implemented here): 93 + Asterism implements all five current endpoints from the [microcosm links XRPC namespace](https://constellation.microcosm.blue/) (the older `/links/*` REST endpoints are deprecated upstream in favor of these and aren't implemented here), plus one identity endpoint borrowed from [Slingshot](https://slingshot.microcosm.blue/): 94 94 95 95 ### `GET /xrpc/blue.microcosm.links.getBacklinksCount` 96 96 ··· 167 167 Response: `{"counts_by_other_subject": [{"subject": "...", "total": 42, "distinct": 12}], "cursor": "..."}` 168 168 169 169 Note the DID filter parameter is `did` here, not `linkDid` like `getManyToMany` — a real inconsistency in the upstream Constellation API (their own source flags it as a known TODO), preserved here for compatibility rather than "fixed." 170 + 171 + ### `GET /xrpc/blue.microcosm.identity.resolveMiniDoc` 172 + 173 + Resolve a handle or DID to its identity. Asterism already resolves DIDs to verify commit signatures against the repo's signing key, so this endpoint is essentially free. 174 + 175 + | Parameter | Description | 176 + | ------------ | ------------------------------------- | 177 + | `identifier` | Handle or DID to resolve (required) | 178 + 179 + Response: `{"did": "...", "handle": "...", "pds": "...", "signing_key": "..."}` 170 180 171 181 ## Roadmap 172 182
+5 -3
cmd/asterism/main.go
··· 68 68 panic(err) 69 69 } 70 70 71 - server := &api.Server{Store: linkStore} 71 + directory := identity.DefaultDirectory() 72 + 73 + server := &api.Server{Store: linkStore, Directory: directory} 72 74 go func() { 73 75 if err := server.Run(cli.Listen); err != nil { 74 76 panic(err) ··· 86 88 87 89 bf := &backfill.Backfill{ 88 90 Client: &xrpc.Client{Host: relayHTTPHost(cli.Relay)}, 89 - Directory: identity.DefaultDirectory(), 91 + Directory: directory, 90 92 Store: linkStore, 91 93 } 92 94 ··· 103 105 consumer := &firehose.Consumer{ 104 106 WantedCollections: wantedCollections, 105 107 Store: linkStore, 106 - Directory: bf.Directory, 108 + Directory: directory, 107 109 Backfill: bf, 108 110 } 109 111
+57
internal/api/resolve_mini_doc.go
··· 1 + package api 2 + 3 + import ( 4 + "context" 5 + "encoding/json" 6 + "log" 7 + "net/http" 8 + "time" 9 + 10 + "github.com/bluesky-social/indigo/atproto/syntax" 11 + ) 12 + 13 + type getMiniDidDocResponse struct { 14 + Did string `json:"did"` 15 + Handle string `json:"handle"` 16 + Pds string `json:"pds"` 17 + SigningKey string `json:"signing_key"` 18 + } 19 + 20 + func (s *Server) GetMiniDidDoc(w http.ResponseWriter, r *http.Request) { 21 + query := r.URL.Query() 22 + 23 + identifier := query.Get("identifier") 24 + if identifier == "" { 25 + http.Error(w, "identifier is required", http.StatusBadRequest) 26 + return 27 + } 28 + 29 + atID, err := syntax.ParseAtIdentifier(identifier) 30 + if err != nil { 31 + http.Error(w, "invalid identifier", http.StatusBadRequest) 32 + return 33 + } 34 + 35 + resolveCtx, cancel := context.WithTimeout(r.Context(), 10*time.Second) 36 + defer cancel() 37 + 38 + identity, err := s.Directory.Lookup(resolveCtx, atID) 39 + if err != nil { 40 + log.Println("resolve mini doc:", err) 41 + http.Error(w, "internal error", http.StatusInternalServerError) 42 + return 43 + } 44 + 45 + var signingKey string 46 + if key, ok := identity.Keys["atproto"]; ok { 47 + signingKey = key.PublicKeyMultibase 48 + } 49 + 50 + w.Header().Set("Content-Type", "application/json") 51 + json.NewEncoder(w).Encode(getMiniDidDocResponse{ 52 + Did: identity.DID.String(), 53 + Handle: identity.Handle.String(), 54 + Pds: identity.PDSEndpoint(), 55 + SigningKey: signingKey, 56 + }) 57 + }
+5 -1
internal/api/server.go
··· 4 4 "net/http" 5 5 6 6 "github.com/alyraffauf/asterism/internal/store" 7 + "github.com/bluesky-social/indigo/atproto/identity" 7 8 ) 8 9 9 10 type Server struct { 10 - Store *store.Store 11 + Store *store.Store 12 + Directory identity.Directory 11 13 } 12 14 13 15 func (s *Server) Run(addr string) error { ··· 19 21 mux.HandleFunc("GET /xrpc/blue.microcosm.links.getBacklinks", s.GetBacklinks) 20 22 mux.HandleFunc("GET /xrpc/blue.microcosm.links.getManyToMany", s.GetManyToMany) 21 23 mux.HandleFunc("GET /xrpc/blue.microcosm.links.getManyToManyCounts", s.GetManyToManyCounts) 24 + 25 + mux.HandleFunc("GET /xrpc/blue.microcosm.identity.resolveMiniDoc", s.GetMiniDidDoc) 22 26 23 27 return http.ListenAndServe(addr, withCORS(mux)) 24 28 }