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

Configure Feed

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

clean up some lexicon usage

+18 -24
+3
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 + 45 + // BskyPostCollection is the collection name for Bluesky posts 46 + BskyPostCollection = "app.bsky.feed.post" 44 47 45 48 // SailorProfileCollection is the collection name for user profiles 46 49 SailorProfileCollection = "io.atcr.sailor.profile"
+4 -3
pkg/hold/pds/manifest_post.go
··· 9 9 "strings" 10 10 "time" 11 11 12 + "atcr.io/pkg/atproto" 12 13 bsky "github.com/bluesky-social/indigo/api/bsky" 13 14 "github.com/distribution/distribution/v3/registry/storage/driver" 14 15 ) ··· 70 71 71 72 // Create post struct with facets and embed 72 73 post := &bsky.FeedPost{ 73 - LexiconTypeID: "app.bsky.feed.post", 74 + LexiconTypeID: atproto.BskyPostCollection, 74 75 Text: text, 75 76 Facets: facets, 76 77 Embed: embed, ··· 82 83 rkey, recordCID, err := p.repomgr.CreateRecord( 83 84 ctx, 84 85 p.uid, 85 - "app.bsky.feed.post", 86 + atproto.BskyPostCollection, 86 87 post, 87 88 ) 88 89 ··· 91 92 } 92 93 93 94 // Build ATProto URI for the post 94 - postURI := fmt.Sprintf("at://%s/app.bsky.feed.post/%s", p.did, rkey) 95 + postURI := fmt.Sprintf("at://%s/%s/%s", p.did, atproto.BskyPostCollection, rkey) 95 96 96 97 slog.Info("Created manifest post", 97 98 "uri", postURI,
+3 -2
pkg/hold/pds/manifest_post_test.go
··· 4 4 "strings" 5 5 "testing" 6 6 7 + "atcr.io/pkg/atproto" 7 8 bsky "github.com/bluesky-social/indigo/api/bsky" 8 9 ) 9 10 ··· 196 197 197 198 // Verify the complete post structure 198 199 post := &bsky.FeedPost{ 199 - LexiconTypeID: "app.bsky.feed.post", 200 + LexiconTypeID: atproto.BskyPostCollection, 200 201 Text: text, 201 202 Facets: facets, 202 203 Langs: []string{"en"}, ··· 250 251 251 252 // Verify the complete post structure 252 253 post := &bsky.FeedPost{ 253 - LexiconTypeID: "app.bsky.feed.post", 254 + LexiconTypeID: atproto.BskyPostCollection, 254 255 Text: text, 255 256 Facets: facets, 256 257 Langs: []string{"en"},
+4 -8
pkg/hold/pds/status.go
··· 6 6 "log/slog" 7 7 "time" 8 8 9 + "atcr.io/pkg/atproto" 9 10 bsky "github.com/bluesky-social/indigo/api/bsky" 10 - ) 11 - 12 - const ( 13 - // StatusPostCollection is the collection name for Bluesky posts 14 - StatusPostCollection = "app.bsky.feed.post" 15 11 ) 16 12 17 13 // SetStatus creates a new status post on Bluesky ··· 40 36 // Create post struct 41 37 now := time.Now() 42 38 post := &bsky.FeedPost{ 43 - LexiconTypeID: "app.bsky.feed.post", 39 + LexiconTypeID: atproto.BskyPostCollection, 44 40 Text: text, 45 41 CreatedAt: now.Format(time.RFC3339), 46 42 } 47 43 48 44 // Use repomgr.CreateRecord to create the post with auto-generated TID 49 45 // CreateRecord automatically generates a unique TID using the repo's clock 50 - rkey, recordCID, err := p.repomgr.CreateRecord(ctx, p.uid, StatusPostCollection, post) 46 + rkey, recordCID, err := p.repomgr.CreateRecord(ctx, p.uid, atproto.BskyPostCollection, post) 51 47 if err != nil { 52 48 return fmt.Errorf("failed to create status post: %w", err) 53 49 } 54 50 55 51 slog.Info("Created status post", 56 - "collection", StatusPostCollection, 52 + "collection", atproto.BskyPostCollection, 57 53 "rkey", rkey, 58 54 "cid", recordCID.String(), 59 55 "text", text)
+3 -10
pkg/hold/pds/status_test.go
··· 61 61 listPosts := func() ([]map[string]any, error) { 62 62 req := makeXRPCGetRequest(atproto.RepoListRecords, map[string]string{ 63 63 "repo": did, 64 - "collection": StatusPostCollection, 64 + "collection": atproto.BskyPostCollection, 65 65 "limit": "100", 66 66 "reverse": "true", // Most recent first 67 67 }) ··· 134 134 } 135 135 // URI format: at://did:web:test.example.com/app.bsky.feed.post/3m3c4... 136 136 // We just check that it contains the collection 137 - if !contains(uri, StatusPostCollection) { 138 - t.Errorf("Expected URI to contain collection %s, got %s", StatusPostCollection, uri) 137 + if !contains(uri, atproto.BskyPostCollection) { 138 + t.Errorf("Expected URI to contain collection %s, got %s", atproto.BskyPostCollection, uri) 139 139 } 140 140 }) 141 141 ··· 224 224 t.Errorf("Expected text '🔴 Current status: offline', got '%s'", text) 225 225 } 226 226 }) 227 - } 228 - 229 - func TestStatusPostCollection(t *testing.T) { 230 - // Verify constant 231 - if StatusPostCollection != "app.bsky.feed.post" { 232 - t.Errorf("Expected StatusPostCollection 'app.bsky.feed.post', got '%s'", StatusPostCollection) 233 - } 234 227 } 235 228 236 229 // Helper function to check if a string contains a substring
+1 -1
pkg/hold/pds/xrpc.go
··· 366 366 repoHandle, err := repo.OpenRepo(ctx, session, head) 367 367 if err == nil { 368 368 postCount := 0 369 - _ = repoHandle.ForEach(ctx, "app.bsky.feed.post", func(k string, v cid.Cid) error { 369 + _ = repoHandle.ForEach(ctx, atproto.BskyPostCollection, func(k string, v cid.Cid) error { 370 370 postCount++ 371 371 return nil 372 372 })