mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-09-26 12:14:17 +00:00
136 lines
3.6 KiB
Go
136 lines
3.6 KiB
Go
package pds
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/bluesky-social/indigo/atproto/crypto"
|
|
"github.com/bluesky-social/indigo/carstore"
|
|
"github.com/bluesky-social/indigo/models"
|
|
"github.com/bluesky-social/indigo/repo"
|
|
)
|
|
|
|
// HoldPDS is a minimal ATProto PDS implementation for a hold service
|
|
type HoldPDS struct {
|
|
did string
|
|
publicURL string
|
|
carstore carstore.CarStore
|
|
session *carstore.DeltaSession
|
|
repo *repo.Repo
|
|
dbPath string
|
|
uid models.Uid
|
|
signingKey *crypto.PrivateKeyK256
|
|
}
|
|
|
|
// NewHoldPDS creates or opens a hold PDS with SQLite carstore
|
|
func NewHoldPDS(ctx context.Context, did, publicURL, dbPath, keyPath string) (*HoldPDS, error) {
|
|
// Ensure directory exists
|
|
dir := filepath.Dir(dbPath)
|
|
if err := os.MkdirAll(dir, 0755); err != nil {
|
|
return nil, fmt.Errorf("failed to create database directory: %w", err)
|
|
}
|
|
|
|
// Generate or load signing key
|
|
signingKey, err := GenerateOrLoadKey(keyPath)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to initialize signing key: %w", err)
|
|
}
|
|
|
|
// Create and open SQLite-backed carstore
|
|
// dbPath is the directory, carstore creates and opens db.sqlite3 inside it
|
|
sqlStore, err := carstore.NewSqliteStore(dbPath)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create sqlite store: %w", err)
|
|
}
|
|
|
|
cs := sqlStore.CarStore()
|
|
|
|
// 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)
|
|
|
|
var session *carstore.DeltaSession
|
|
var r *repo.Repo
|
|
|
|
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)
|
|
}
|
|
|
|
// Create new repo with session as blockstore (needs pointer)
|
|
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)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create delta session: %w", err)
|
|
}
|
|
|
|
r = repo.NewRepo(ctx, did, session)
|
|
}
|
|
|
|
return &HoldPDS{
|
|
did: did,
|
|
publicURL: publicURL,
|
|
carstore: cs,
|
|
session: session,
|
|
repo: r,
|
|
dbPath: dbPath,
|
|
uid: uid,
|
|
signingKey: signingKey,
|
|
}, nil
|
|
}
|
|
|
|
// DID returns the hold's DID
|
|
func (p *HoldPDS) DID() string {
|
|
return p.did
|
|
}
|
|
|
|
// SigningKey returns the hold's signing key
|
|
func (p *HoldPDS) SigningKey() *crypto.PrivateKeyK256 {
|
|
return p.signingKey
|
|
}
|
|
|
|
// Bootstrap initializes the hold with the owner as the first crew member
|
|
func (p *HoldPDS) Bootstrap(ctx context.Context, ownerDID string) error {
|
|
if ownerDID == "" {
|
|
return nil
|
|
}
|
|
|
|
// 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
|
|
}
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
|
|
fmt.Printf("✅ Added %s as hold admin\n", ownerDID)
|
|
return nil
|
|
}
|
|
|
|
// Close closes the session and carstore
|
|
func (p *HoldPDS) Close() error {
|
|
// TODO: Close session properly
|
|
return nil
|
|
}
|