mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-09-20 01:04:15 +00:00
update indigo repo, fix pds carstore
This commit is contained in:
@@ -4,7 +4,7 @@ go 1.24.7
|
||||
|
||||
require (
|
||||
github.com/aws/aws-sdk-go v1.55.5
|
||||
github.com/bluesky-social/indigo v0.0.0-20251003000214-3259b215110e
|
||||
github.com/bluesky-social/indigo v0.0.0-20251014222321-1e8718ae9f33
|
||||
github.com/distribution/distribution/v3 v3.0.0
|
||||
github.com/distribution/reference v0.6.0
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2
|
||||
@@ -25,7 +25,6 @@ require (
|
||||
require (
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bshuster-repo/logrus-logstash-hook v1.0.0 // indirect
|
||||
github.com/carlmjohnson/versioninfo v0.22.5 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
||||
@@ -33,6 +32,7 @@ require (
|
||||
github.com/docker/docker-credential-helpers v0.8.2 // indirect
|
||||
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
|
||||
github.com/docker/go-metrics v0.0.1 // indirect
|
||||
github.com/earthboundkid/versioninfo/v2 v2.24.1 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.1.2 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
|
||||
@@ -16,8 +16,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY=
|
||||
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
|
||||
github.com/bluesky-social/indigo v0.0.0-20251003000214-3259b215110e h1:IutKPwmbU0LrYqw03EuwJtMdAe67rDTrL1U8S8dicRU=
|
||||
github.com/bluesky-social/indigo v0.0.0-20251003000214-3259b215110e/go.mod h1:n6QE1NDPFoi7PRbMUZmc2y7FibCqiVU4ePpsvhHUBR8=
|
||||
github.com/bluesky-social/indigo v0.0.0-20251014222321-1e8718ae9f33 h1:x06Y6VyYUCvqWl2AS4/3NBBbRf8wWNMd3YrI44NTHS8=
|
||||
github.com/bluesky-social/indigo v0.0.0-20251014222321-1e8718ae9f33/go.mod h1:GuGAU33qKulpZCZNPcUeIQ4RW6KzNvOy7s8MSUXbAng=
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
||||
github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70=
|
||||
@@ -28,8 +28,6 @@ github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdb
|
||||
github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/carlmjohnson/versioninfo v0.22.5 h1:O00sjOLUAFxYQjlN/bzYTuZiS0y6fWDQjMRvwtKgwwc=
|
||||
github.com/carlmjohnson/versioninfo v0.22.5/go.mod h1:QT9mph3wcVfISUKd0i9sZfVrPviHuSF+cUtLjm2WSf8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
@@ -60,6 +58,8 @@ github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ
|
||||
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
|
||||
github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=
|
||||
github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
|
||||
github.com/earthboundkid/versioninfo/v2 v2.24.1 h1:SJTMHaoUx3GzjjnUO1QzP3ZXK6Ee/nbWyCm58eY3oUg=
|
||||
github.com/earthboundkid/versioninfo/v2 v2.24.1/go.mod h1:VcWEooDEuyUJnMfbdTh0uFN4cfEIg+kHMuWB2CDCLjw=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/bluesky-social/indigo/atproto/client"
|
||||
"github.com/bluesky-social/indigo/atproto/atclient"
|
||||
)
|
||||
|
||||
// Sentinel errors
|
||||
@@ -26,7 +26,7 @@ type Client struct {
|
||||
accessToken string // For Basic Auth only
|
||||
httpClient *http.Client
|
||||
useIndigoClient bool // true if using indigo's OAuth client (handles auth automatically)
|
||||
indigoClient *client.APIClient // indigo's API client for OAuth requests
|
||||
indigoClient *atclient.APIClient // indigo's API client for OAuth requests
|
||||
}
|
||||
|
||||
// NewClient creates a new ATProto client for Basic Auth tokens (app passwords)
|
||||
@@ -41,7 +41,7 @@ func NewClient(pdsEndpoint, did, accessToken string) *Client {
|
||||
|
||||
// NewClientWithIndigoClient creates an ATProto client using indigo's API client
|
||||
// This uses indigo's native XRPC methods with automatic DPoP handling
|
||||
func NewClientWithIndigoClient(pdsEndpoint, did string, indigoClient *client.APIClient) *Client {
|
||||
func NewClientWithIndigoClient(pdsEndpoint, did string, indigoClient *atclient.APIClient) *Client {
|
||||
return &Client{
|
||||
pdsEndpoint: pdsEndpoint,
|
||||
did: did,
|
||||
@@ -125,7 +125,7 @@ func (c *Client) GetRecord(ctx context.Context, collection, rkey string) (*Recor
|
||||
err := c.indigoClient.Get(ctx, "com.atproto.repo.getRecord", params, &result)
|
||||
if err != nil {
|
||||
// Check for RecordNotFound error from indigo's APIError type
|
||||
var apiErr *client.APIError
|
||||
var apiErr *atclient.APIError
|
||||
if errors.As(err, &apiErr) {
|
||||
if apiErr.StatusCode == 404 || apiErr.Name == "RecordNotFound" {
|
||||
return nil, ErrRecordNotFound
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/bluesky-social/indigo/repo"
|
||||
"github.com/ipfs/go-cid"
|
||||
)
|
||||
|
||||
@@ -41,6 +42,22 @@ func (p *HoldPDS) AddCrewMember(ctx context.Context, memberDID, role string, per
|
||||
return cid.Undef, fmt.Errorf("failed to persist commit: %w", err)
|
||||
}
|
||||
|
||||
// Create a new session for the next operation (old session is now closed)
|
||||
newSession, err := p.carstore.NewDeltaSession(ctx, p.uid, nil)
|
||||
if err != nil {
|
||||
return cid.Undef, fmt.Errorf("failed to create new session: %w", err)
|
||||
}
|
||||
|
||||
// Load repo from the newly committed head (not NewRepo which creates empty MST)
|
||||
newRepo, err := repo.OpenRepo(ctx, newSession, root)
|
||||
if err != nil {
|
||||
return cid.Undef, fmt.Errorf("failed to reload repo after commit: %w", err)
|
||||
}
|
||||
|
||||
// Update the stored session and repo
|
||||
p.session = newSession
|
||||
p.repo = newRepo
|
||||
|
||||
return recordCID, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -5,11 +5,11 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/bluesky-social/indigo/atproto/crypto"
|
||||
"github.com/bluesky-social/indigo/atproto/atcrypto"
|
||||
)
|
||||
|
||||
// GenerateOrLoadKey generates a new K256 key pair or loads an existing one
|
||||
func GenerateOrLoadKey(keyPath string) (*crypto.PrivateKeyK256, error) {
|
||||
func GenerateOrLoadKey(keyPath string) (*atcrypto.PrivateKeyK256, error) {
|
||||
// Ensure directory exists
|
||||
dir := filepath.Dir(keyPath)
|
||||
if err := os.MkdirAll(dir, 0700); err != nil {
|
||||
@@ -27,9 +27,9 @@ func GenerateOrLoadKey(keyPath string) (*crypto.PrivateKeyK256, error) {
|
||||
}
|
||||
|
||||
// generateKey creates a new K256 (secp256k1) key pair using indigo's atcrypto
|
||||
func generateKey(keyPath string) (*crypto.PrivateKeyK256, error) {
|
||||
func generateKey(keyPath string) (*atcrypto.PrivateKeyK256, error) {
|
||||
// Generate K256 key (secp256k1) using indigo
|
||||
privateKey, err := crypto.GeneratePrivateKeyK256()
|
||||
privateKey, err := atcrypto.GeneratePrivateKeyK256()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate key: %w", err)
|
||||
}
|
||||
@@ -47,7 +47,7 @@ func generateKey(keyPath string) (*crypto.PrivateKeyK256, error) {
|
||||
}
|
||||
|
||||
// loadKey loads an existing private key from disk
|
||||
func loadKey(keyPath string) (*crypto.PrivateKeyK256, error) {
|
||||
func loadKey(keyPath string) (*atcrypto.PrivateKeyK256, error) {
|
||||
// Read key bytes
|
||||
keyBytes, err := os.ReadFile(keyPath)
|
||||
if err != nil {
|
||||
@@ -55,7 +55,7 @@ func loadKey(keyPath string) (*crypto.PrivateKeyK256, error) {
|
||||
}
|
||||
|
||||
// Try to parse as K256 private key
|
||||
privateKey, err := crypto.ParsePrivateBytesK256(keyBytes)
|
||||
privateKey, err := atcrypto.ParsePrivateBytesK256(keyBytes)
|
||||
if err != nil {
|
||||
// Check if this is an old P-256 PEM key (migration)
|
||||
if isPEMFormat(keyBytes) {
|
||||
|
||||
+25
-28
@@ -6,7 +6,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/bluesky-social/indigo/atproto/crypto"
|
||||
"github.com/bluesky-social/indigo/atproto/atcrypto"
|
||||
"github.com/bluesky-social/indigo/carstore"
|
||||
"github.com/bluesky-social/indigo/models"
|
||||
"github.com/bluesky-social/indigo/repo"
|
||||
@@ -21,7 +21,7 @@ type HoldPDS struct {
|
||||
repo *repo.Repo
|
||||
dbPath string
|
||||
uid models.Uid
|
||||
signingKey *crypto.PrivateKeyK256
|
||||
signingKey *atcrypto.PrivateKeyK256
|
||||
}
|
||||
|
||||
// NewHoldPDS creates or opens a hold PDS with SQLite carstore
|
||||
@@ -45,35 +45,35 @@ func NewHoldPDS(ctx context.Context, did, publicURL, dbPath, keyPath string) (*H
|
||||
return nil, fmt.Errorf("failed to create sqlite store: %w", err)
|
||||
}
|
||||
|
||||
cs := sqlStore.CarStore()
|
||||
// Use SQLiteStore directly, not the CarStore() wrapper
|
||||
// The wrapper has a bug where GetUserRepoHead checks CarShard.ID which SQLite doesn't populate
|
||||
cs := sqlStore
|
||||
|
||||
// For a single-user hold, we use a fixed UID (1)
|
||||
uid := models.Uid(1)
|
||||
|
||||
// Try to get existing repo head
|
||||
_, err = cs.GetUserRepoHead(ctx, uid)
|
||||
// Check if repo already exists with valid head
|
||||
head, err := cs.GetUserRepoHead(ctx, uid)
|
||||
hasValidRepo := (err == nil && head.Defined())
|
||||
|
||||
var session *carstore.DeltaSession
|
||||
var r *repo.Repo
|
||||
|
||||
// Create a session connected to this user's data in carstore
|
||||
session, err = cs.NewDeltaSession(ctx, uid, nil)
|
||||
if err != nil {
|
||||
// Repo doesn't exist yet, create new delta session
|
||||
session, err = cs.NewDeltaSession(ctx, uid, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create delta session: %w", err)
|
||||
}
|
||||
return nil, fmt.Errorf("failed to create delta session: %w", err)
|
||||
}
|
||||
|
||||
// Create new repo with session as blockstore (needs pointer)
|
||||
if !hasValidRepo {
|
||||
// No valid repo - create new empty repo
|
||||
r = repo.NewRepo(ctx, did, session)
|
||||
} else {
|
||||
// TODO: Load existing repo
|
||||
// For now, just create a new session
|
||||
session, err = cs.NewDeltaSession(ctx, uid, nil)
|
||||
// Repo exists with valid head - load from existing head
|
||||
r, err = repo.OpenRepo(ctx, session, head)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create delta session: %w", err)
|
||||
return nil, fmt.Errorf("failed to open existing repo: %w", err)
|
||||
}
|
||||
|
||||
r = repo.NewRepo(ctx, did, session)
|
||||
}
|
||||
|
||||
return &HoldPDS{
|
||||
@@ -94,7 +94,7 @@ func (p *HoldPDS) DID() string {
|
||||
}
|
||||
|
||||
// SigningKey returns the hold's signing key
|
||||
func (p *HoldPDS) SigningKey() *crypto.PrivateKeyK256 {
|
||||
func (p *HoldPDS) SigningKey() *atcrypto.PrivateKeyK256 {
|
||||
return p.signingKey
|
||||
}
|
||||
|
||||
@@ -106,19 +106,16 @@ func (p *HoldPDS) Bootstrap(ctx context.Context, ownerDID string) error {
|
||||
|
||||
// Check if repo already has commits
|
||||
head, err := p.carstore.GetUserRepoHead(ctx, p.uid)
|
||||
if err == nil {
|
||||
// Repo exists - check if we need to re-bootstrap due to key change
|
||||
// If the repo exists but is empty/invalid, we should re-bootstrap
|
||||
if head.String() == "" || head.String() == "b" {
|
||||
fmt.Printf("⚠️ Detected invalid repo state, re-bootstrapping...\n")
|
||||
} else {
|
||||
fmt.Printf("⏭️ Skipping PDS bootstrap: repo already initialized (head: %s)\n", head.String()[:16])
|
||||
return nil
|
||||
}
|
||||
if err != nil || !head.Defined() {
|
||||
// No repo exists yet, bootstrap
|
||||
fmt.Printf("🚀 Bootstrapping hold PDS with owner: %s\n", ownerDID)
|
||||
} else {
|
||||
// Repo exists and is valid
|
||||
fmt.Printf("⏭️ Skipping PDS bootstrap: repo already initialized (head: %s)\n", head.String()[:16])
|
||||
return nil
|
||||
}
|
||||
|
||||
// Add hold owner as first crew member with admin role
|
||||
fmt.Printf("🚀 Bootstrapping hold PDS with owner: %s\n", ownerDID)
|
||||
_, err = p.AddCrewMember(ctx, ownerDID, "admin", []string{"blob:read", "blob:write", "crew:admin"})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to add owner as crew member: %w", err)
|
||||
|
||||
Reference in New Issue
Block a user