A container registry that uses the AT Protocol for manifest storage and S3 for blob storage.
0
fork

Configure Feed

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

some lexicon json cleanup. code formatting

+144 -88
+5 -5
go.mod
··· 1 1 module atcr.io 2 2 3 - go 1.24.7 3 + go 1.25.5 4 4 5 5 require ( 6 6 github.com/aws/aws-sdk-go v1.55.5 7 - github.com/bluesky-social/indigo v0.0.0-20251031012455-0b4bd2478a61 7 + github.com/bluesky-social/indigo v0.0.0-20251218205144-034a2c019e64 8 8 github.com/distribution/distribution/v3 v3.0.0 9 9 github.com/distribution/reference v0.6.0 10 10 github.com/earthboundkid/versioninfo/v2 v2.24.1 ··· 32 32 github.com/yuin/goldmark v1.7.13 33 33 go.opentelemetry.io/otel v1.32.0 34 34 go.yaml.in/yaml/v4 v4.0.0-rc.2 35 - golang.org/x/crypto v0.39.0 35 + golang.org/x/crypto v0.44.0 36 36 golang.org/x/image v0.34.0 37 37 golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 38 38 gorm.io/gorm v1.25.9 ··· 143 143 go.uber.org/atomic v1.11.0 // indirect 144 144 go.uber.org/multierr v1.11.0 // indirect 145 145 go.uber.org/zap v1.26.0 // indirect 146 - golang.org/x/net v0.37.0 // indirect 146 + golang.org/x/net v0.47.0 // indirect 147 147 golang.org/x/sync v0.19.0 // indirect 148 - golang.org/x/sys v0.33.0 // indirect 148 + golang.org/x/sys v0.38.0 // indirect 149 149 golang.org/x/text v0.32.0 // indirect 150 150 golang.org/x/time v0.6.0 // indirect 151 151 google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 // indirect
+8 -8
go.sum
··· 20 20 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 21 21 github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY= 22 22 github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= 23 - github.com/bluesky-social/indigo v0.0.0-20251031012455-0b4bd2478a61 h1:lU2NnyuvevVWtE35sb4xWBp1AQxa1Sv4XhexiWlrWng= 24 - github.com/bluesky-social/indigo v0.0.0-20251031012455-0b4bd2478a61/go.mod h1:GuGAU33qKulpZCZNPcUeIQ4RW6KzNvOy7s8MSUXbAng= 23 + github.com/bluesky-social/indigo v0.0.0-20251218205144-034a2c019e64 h1:84EWie083DZT0eMo76kcZ0mBDcLUmWQu5UFE8/3ZW4k= 24 + github.com/bluesky-social/indigo v0.0.0-20251218205144-034a2c019e64/go.mod h1:KIy0FgNQacp4uv2Z7xhNkV3qZiUSGuRky97s7Pa4v+o= 25 25 github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= 26 26 github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= 27 27 github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70= ··· 466 466 golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 467 467 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 468 468 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 469 - golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= 470 - golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= 469 + golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU= 470 + golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc= 471 471 golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= 472 472 golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= 473 473 golang.org/x/image v0.34.0 h1:33gCkyw9hmwbZJeZkct8XyR11yH889EQt/QH4VmXMn8= ··· 487 487 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 488 488 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 489 489 golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 490 - golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= 491 - golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= 490 + golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= 491 + golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= 492 492 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 493 493 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 494 494 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= ··· 510 510 golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 511 511 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 512 512 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 513 - golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= 514 - golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 513 + golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= 514 + golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= 515 515 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 516 516 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 517 517 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-37
lexicons/io/atcr/hold.json
··· 1 - { 2 - "lexicon": 1, 3 - "id": "io.atcr.hold", 4 - "defs": { 5 - "main": { 6 - "type": "record", 7 - "description": "Storage hold definition for Bring Your Own Storage (BYOS). Defines where blobs are stored.", 8 - "key": "any", 9 - "record": { 10 - "type": "object", 11 - "required": ["endpoint", "owner", "createdAt"], 12 - "properties": { 13 - "endpoint": { 14 - "type": "string", 15 - "format": "uri", 16 - "description": "URL of the hold service (e.g., 'https://hold1.example.com')" 17 - }, 18 - "owner": { 19 - "type": "string", 20 - "format": "did", 21 - "description": "DID of the hold owner" 22 - }, 23 - "public": { 24 - "type": "boolean", 25 - "description": "Whether this hold allows public blob reads (pulls) without authentication. Writes always require crew membership.", 26 - "default": false 27 - }, 28 - "createdAt": { 29 - "type": "string", 30 - "format": "datetime", 31 - "description": "Hold creation timestamp" 32 - } 33 - } 34 - } 35 - } 36 - } 37 - }
+47
lexicons/io/atcr/hold/captain.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "io.atcr.hold.captain", 4 + "defs": { 5 + "main": { 6 + "type": "record", 7 + "description": "Represents the hold's ownership and metadata. Stored as a singleton record at rkey 'self' in the hold's embedded PDS.", 8 + "key": "literal:self", 9 + "record": { 10 + "type": "object", 11 + "required": ["owner", "public", "allowAllCrew", "enableBlueskyPosts", "deployedAt"], 12 + "properties": { 13 + "owner": { 14 + "type": "string", 15 + "format": "did", 16 + "description": "DID of the hold owner" 17 + }, 18 + "public": { 19 + "type": "boolean", 20 + "description": "Whether this hold allows public blob reads (pulls) without authentication" 21 + }, 22 + "allowAllCrew": { 23 + "type": "boolean", 24 + "description": "Allow any authenticated user to register as crew" 25 + }, 26 + "enableBlueskyPosts": { 27 + "type": "boolean", 28 + "description": "Enable Bluesky posts when manifests are pushed" 29 + }, 30 + "deployedAt": { 31 + "type": "string", 32 + "format": "datetime", 33 + "description": "RFC3339 timestamp of when the hold was deployed" 34 + }, 35 + "region": { 36 + "type": "string", 37 + "description": "S3 region where blobs are stored" 38 + }, 39 + "provider": { 40 + "type": "string", 41 + "description": "Deployment provider (e.g., fly.io, aws, etc.)" 42 + } 43 + } 44 + } 45 + } 46 + } 47 + }
+13 -20
lexicons/io/atcr/hold/crew.json
··· 4 4 "defs": { 5 5 "main": { 6 6 "type": "record", 7 - "description": "Crew membership for a storage hold. Stored in the hold owner's PDS to maintain control over write access. Supports explicit DIDs (with backlinks), wildcard access, and handle patterns. Crew members can push blobs to the hold. Read access is controlled by the hold's public flag, not crew membership.", 7 + "description": "Crew member in a hold's embedded PDS. Grants access permissions to push blobs to the hold. Stored in the hold's embedded PDS (one record per member).", 8 8 "key": "any", 9 9 "record": { 10 10 "type": "object", 11 - "required": ["hold", "role", "createdAt"], 11 + "required": ["member", "role", "permissions", "addedAt"], 12 12 "properties": { 13 - "hold": { 14 - "type": "string", 15 - "format": "at-uri", 16 - "description": "AT-URI of the hold record (e.g., 'at://did:plc:owner/io.atcr.hold/hold1')" 17 - }, 18 13 "member": { 19 14 "type": "string", 20 15 "format": "did", 21 - "description": "DID of crew member (for individual access with backlinks). Exactly one of 'member' or 'memberPattern' must be set." 22 - }, 23 - "memberPattern": { 24 - "type": "string", 25 - "description": "Pattern for matching multiple users. Supports wildcards: '*' (all users), '*.domain.com' (handle glob). Exactly one of 'member' or 'memberPattern' must be set." 16 + "description": "DID of the crew member" 26 17 }, 27 18 "role": { 28 19 "type": "string", 29 - "description": "Member's role/permissions for write access. 'owner' = hold owner, 'write' = can push blobs. Read access is controlled by hold's public flag.", 30 - "knownValues": ["owner", "write"] 20 + "description": "Member's role in the hold", 21 + "knownValues": ["owner", "admin", "write", "read"] 31 22 }, 32 - "expiresAt": { 33 - "type": "string", 34 - "format": "datetime", 35 - "description": "Optional expiration for this membership" 23 + "permissions": { 24 + "type": "array", 25 + "description": "Specific permissions granted to this member", 26 + "items": { 27 + "type": "string" 28 + } 36 29 }, 37 - "createdAt": { 30 + "addedAt": { 38 31 "type": "string", 39 32 "format": "datetime", 40 - "description": "Membership creation timestamp" 33 + "description": "RFC3339 timestamp of when the member was added" 41 34 } 42 35 } 43 36 }
+48
lexicons/io/atcr/hold/layer.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "io.atcr.hold.layer", 4 + "defs": { 5 + "main": { 6 + "type": "record", 7 + "key": "tid", 8 + "description": "Represents metadata about a container layer stored in the hold. Stored in the hold's embedded PDS for tracking and analytics.", 9 + "record": { 10 + "type": "object", 11 + "required": ["digest", "size", "mediaType", "repository", "userDid", "userHandle", "createdAt"], 12 + "properties": { 13 + "digest": { 14 + "type": "string", 15 + "description": "Layer digest (e.g., sha256:abc123...)" 16 + }, 17 + "size": { 18 + "type": "integer", 19 + "description": "Size in bytes" 20 + }, 21 + "mediaType": { 22 + "type": "string", 23 + "description": "Media type (e.g., application/vnd.oci.image.layer.v1.tar+gzip)" 24 + }, 25 + "repository": { 26 + "type": "string", 27 + "description": "Repository this layer belongs to" 28 + }, 29 + "userDid": { 30 + "type": "string", 31 + "format": "did", 32 + "description": "DID of user who uploaded this layer" 33 + }, 34 + "userHandle": { 35 + "type": "string", 36 + "format": "handle", 37 + "description": "Handle of user (for display purposes)" 38 + }, 39 + "createdAt": { 40 + "type": "string", 41 + "format": "datetime", 42 + "description": "RFC3339 timestamp of when the layer was uploaded" 43 + } 44 + } 45 + } 46 + } 47 + } 48 + }
+7 -2
lexicons/io/atcr/manifest.json
··· 8 8 "key": "tid", 9 9 "record": { 10 10 "type": "object", 11 - "required": ["repository", "digest", "mediaType", "schemaVersion", "holdEndpoint", "createdAt"], 11 + "required": ["repository", "digest", "mediaType", "schemaVersion", "createdAt"], 12 12 "properties": { 13 13 "repository": { 14 14 "type": "string", ··· 19 19 "type": "string", 20 20 "description": "Content digest (e.g., 'sha256:abc123...')" 21 21 }, 22 + "holdDid": { 23 + "type": "string", 24 + "format": "did", 25 + "description": "DID of the hold service where blobs are stored (e.g., 'did:web:hold01.atcr.io'). Primary reference for hold resolution." 26 + }, 22 27 "holdEndpoint": { 23 28 "type": "string", 24 29 "format": "uri", 25 - "description": "Hold service endpoint where blobs are stored (e.g., 'https://hold1.bob.com'). Historical reference." 30 + "description": "Hold service endpoint URL where blobs are stored. DEPRECATED: Use holdDid instead. Kept for backward compatibility." 26 31 }, 27 32 "mediaType": { 28 33 "type": "string",
+3 -3
pkg/appview/middleware/registry.go
··· 33 33 type validationCacheEntry struct { 34 34 serviceToken string 35 35 validUntil time.Time 36 - err error // Cached error for fast-fail 37 - mu sync.Mutex // Per-entry lock to serialize cache population 38 - inFlight bool // True if another goroutine is fetching the token 36 + err error // Cached error for fast-fail 37 + mu sync.Mutex // Per-entry lock to serialize cache population 38 + inFlight bool // True if another goroutine is fetching the token 39 39 done chan struct{} // Closed when fetch completes 40 40 } 41 41
+1 -1
pkg/appview/ogcard/card.go
··· 403 403 404 404 // Common colors 405 405 var ( 406 - ColorBackground = color.RGBA{R: 22, G: 27, B: 34, A: 255} // #161b22 - GitHub dark elevated 406 + ColorBackground = color.RGBA{R: 22, G: 27, B: 34, A: 255} // #161b22 - GitHub dark elevated 407 407 ColorText = color.RGBA{R: 230, G: 237, B: 243, A: 255} // #e6edf3 - Light text 408 408 ColorMuted = color.RGBA{R: 125, G: 133, B: 144, A: 255} // #7d8590 - Muted text 409 409 ColorAccent = color.RGBA{R: 47, G: 129, B: 247, A: 255} // #2f81f7 - Blue accent
+1 -1
pkg/appview/routes/routes.go
··· 12 12 "atcr.io/pkg/appview/middleware" 13 13 "atcr.io/pkg/appview/readme" 14 14 "atcr.io/pkg/auth/oauth" 15 - "github.com/go-chi/chi/v5" 16 15 indigooauth "github.com/bluesky-social/indigo/atproto/auth/oauth" 16 + "github.com/go-chi/chi/v5" 17 17 ) 18 18 19 19 // UIDependencies contains all dependencies needed for UI route registration
+6 -6
pkg/appview/storage/manifest_store_test.go
··· 926 926 childDigest := digest.FromBytes(childManifest) 927 927 928 928 tests := []struct { 929 - name string 930 - manifestList []byte 931 - childExists bool // Whether the child manifest exists 932 - wantErr bool 933 - wantErrType string // "ErrManifestBlobUnknown" or empty 934 - checkErrDigest string // Expected digest in error 929 + name string 930 + manifestList []byte 931 + childExists bool // Whether the child manifest exists 932 + wantErr bool 933 + wantErrType string // "ErrManifestBlobUnknown" or empty 934 + checkErrDigest string // Expected digest in error 935 935 }{ 936 936 { 937 937 name: "valid manifest list - child exists",
+1 -1
pkg/atproto/lexicon.go
··· 41 41 // TangledProfileCollection is the collection name for tangled profiles 42 42 // Stored in hold's embedded PDS (singleton record at rkey "self") 43 43 TangledProfileCollection = "sh.tangled.actor.profile" 44 - 44 + 45 45 // BskyPostCollection is the collection name for Bluesky posts 46 46 BskyPostCollection = "app.bsky.feed.post" 47 47
+4 -4
pkg/auth/token/handler.go
··· 31 31 32 32 // Handler handles /auth/token requests 33 33 type Handler struct { 34 - issuer *Issuer 35 - validator *auth.SessionValidator 36 - deviceStore *db.DeviceStore // For validating device secrets 37 - postAuthCallback PostAuthCallback 34 + issuer *Issuer 35 + validator *auth.SessionValidator 36 + deviceStore *db.DeviceStore // For validating device secrets 37 + postAuthCallback PostAuthCallback 38 38 oauthSessionValidator OAuthSessionValidator 39 39 } 40 40