A container registry that uses the AT Protocol for manifest storage and S3 for blob storage. atcr.io
docker container atproto go
80
fork

Configure Feed

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

lower cached plc hits to 8 hours

+32 -33
+1 -1
go.mod
··· 7 7 github.com/bluesky-social/indigo v0.0.0-20251021193747-543ab1124beb 8 8 github.com/distribution/distribution/v3 v3.0.0 9 9 github.com/distribution/reference v0.6.0 10 + github.com/earthboundkid/versioninfo/v2 v2.24.1 10 11 github.com/go-chi/chi/v5 v5.2.3 11 12 github.com/golang-jwt/jwt/v5 v5.2.2 12 13 github.com/google/uuid v1.6.0 ··· 44 45 github.com/docker/docker-credential-helpers v0.8.2 // indirect 45 46 github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect 46 47 github.com/docker/go-metrics v0.0.1 // indirect 47 - github.com/earthboundkid/versioninfo/v2 v2.24.1 // indirect 48 48 github.com/felixge/httpsnoop v1.0.4 // indirect 49 49 github.com/go-jose/go-jose/v4 v4.1.2 // indirect 50 50 github.com/go-logr/logr v1.4.2 // indirect
+1 -1
pkg/appview/jetstream/processor.go
··· 30 30 func NewProcessor(database *sql.DB, useCache bool) *Processor { 31 31 p := &Processor{ 32 32 db: database, 33 - directory: identity.DefaultDirectory(), 33 + directory: atproto.GetDirectory(), 34 34 useCache: useCache, 35 35 } 36 36
+2 -2
pkg/appview/middleware/registry.go
··· 81 81 82 82 // initATProtoResolver initializes the name resolution middleware 83 83 func initATProtoResolver(ctx context.Context, ns distribution.Namespace, _ driver.StorageDriver, options map[string]any) (distribution.Namespace, error) { 84 - // Use indigo's default directory (includes caching) 85 - directory := identity.DefaultDirectory() 84 + // Use shared directory with 8h cache TTL 85 + directory := atproto.GetDirectory() 86 86 87 87 // Get default hold DID from config (required) 88 88 // Expected format: "did:web:hold01.atcr.io"
+1 -1
pkg/appview/ui.go
··· 128 128 } 129 129 130 130 // StaticSubdir returns an fs.FS for a subdirectory within static/ 131 - func StaticSubdir(name string) http.Handler { 131 + func StaticSubdir(name string) http.Handler { 132 132 sub, err := fs.Sub(staticFS, "static/"+name) 133 133 if err != nil { 134 134 panic(err)
+23 -1
pkg/auth/oauth/client.go
··· 36 36 return &App{ 37 37 clientApp: clientApp, 38 38 baseURL: baseURL, 39 - directory: identity.DefaultDirectory(), 39 + directory: atproto.GetDirectory(), 40 40 }, nil 41 41 } 42 42 ··· 177 177 178 178 return true 179 179 } 180 + 181 + // ResolveDIDToPDS resolves a DID to its PDS endpoint (for reference) 182 + // This is an alternative approach if we don't trust the token's issuer claim 183 + func ResolveDIDToPDS(ctx context.Context, did string) (string, error) { 184 + directory := atproto.GetDirectory() 185 + didParsed, err := syntax.ParseDID(did) 186 + if err != nil { 187 + return "", fmt.Errorf("invalid DID: %w", err) 188 + } 189 + 190 + ident, err := directory.LookupDID(ctx, didParsed) 191 + if err != nil { 192 + return "", fmt.Errorf("failed to resolve DID: %w", err) 193 + } 194 + 195 + pdsEndpoint := ident.PDSEndpoint() 196 + if pdsEndpoint == "" { 197 + return "", fmt.Errorf("no PDS endpoint found for DID") 198 + } 199 + 200 + return pdsEndpoint, nil 201 + }
+1 -1
pkg/auth/session.go
··· 42 42 // NewSessionValidator creates a new ATProto session validator 43 43 func NewSessionValidator() *SessionValidator { 44 44 return &SessionValidator{ 45 - directory: identity.DefaultDirectory(), 45 + directory: atproto.GetDirectory(), 46 46 httpClient: &http.Client{}, 47 47 cache: make(map[string]*CachedSession), 48 48 }
+2 -2
pkg/auth/token/handler.go
··· 9 9 "strings" 10 10 "time" 11 11 12 - "github.com/bluesky-social/indigo/atproto/identity" 13 12 "github.com/bluesky-social/indigo/atproto/syntax" 14 13 15 14 "atcr.io/pkg/appview/db" 15 + "atcr.io/pkg/atproto" 16 16 "atcr.io/pkg/auth" 17 17 ) 18 18 ··· 158 158 // Call post-auth callback for AppView business logic (profile management, etc.) 159 159 if h.postAuthCallback != nil { 160 160 // Resolve PDS endpoint for callback 161 - directory := identity.DefaultDirectory() 161 + directory := atproto.GetDirectory() 162 162 atID, err := syntax.ParseAtIdentifier(username) 163 163 if err == nil { 164 164 ident, err := directory.Lookup(r.Context(), *atID)
+1 -24
pkg/hold/pds/auth.go
··· 14 14 15 15 "atcr.io/pkg/atproto" 16 16 "github.com/bluesky-social/indigo/atproto/atcrypto" 17 - "github.com/bluesky-social/indigo/atproto/identity" 18 17 "github.com/bluesky-social/indigo/atproto/syntax" 19 18 "github.com/golang-jwt/jwt/v5" 20 19 ) ··· 190 189 } 191 190 192 191 return &session, nil 193 - } 194 - 195 - // ResolveDIDToPDS resolves a DID to its PDS endpoint (for reference) 196 - // This is an alternative approach if we don't trust the token's issuer claim 197 - func ResolveDIDToPDS(ctx context.Context, did string) (string, error) { 198 - directory := identity.DefaultDirectory() 199 - didParsed, err := syntax.ParseDID(did) 200 - if err != nil { 201 - return "", fmt.Errorf("invalid DID: %w", err) 202 - } 203 - 204 - ident, err := directory.LookupDID(ctx, didParsed) 205 - if err != nil { 206 - return "", fmt.Errorf("failed to resolve DID: %w", err) 207 - } 208 - 209 - pdsEndpoint := ident.PDSEndpoint() 210 - if pdsEndpoint == "" { 211 - return "", fmt.Errorf("no PDS endpoint found for DID") 212 - } 213 - 214 - return pdsEndpoint, nil 215 192 } 216 193 217 194 // ValidateOwnerOrCrewAdmin validates that the request has valid authentication ··· 509 486 // Returns the atcrypto.PublicKey for signature verification 510 487 func fetchPublicKeyFromDID(ctx context.Context, did string) (atcrypto.PublicKey, error) { 511 488 // Use indigo's identity resolution 512 - directory := identity.DefaultDirectory() 489 + directory := atproto.GetDirectory() 513 490 atID, err := syntax.ParseAtIdentifier(did) 514 491 if err != nil { 515 492 return nil, fmt.Errorf("invalid DID format: %w", err)