update indigo repo, fix pds carstore

This commit is contained in:
Evan Jarrett
2025-10-14 23:18:01 -05:00
parent 21e6d08f75
commit 4c930e8ae5
6 changed files with 58 additions and 44 deletions
+2 -2
View File
@@ -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
+4 -4
View File
@@ -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=
+4 -4
View File
@@ -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
+17
View File
@@ -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
}
+6 -6
View File
@@ -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
View File
@@ -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)