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

Configure Feed

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

begin embedded pds with xrpc endpoints and well-known

+1252 -29
+36 -2
.env.hold.example
··· 24 24 # Falls back to proxy mode automatically for non-S3 drivers. 25 25 STORAGE_DRIVER=filesystem 26 26 27 - # For S3/Storj/Minio: 27 + # S3 Access Credentials 28 28 AWS_ACCESS_KEY_ID=your_access_key 29 29 AWS_SECRET_ACCESS_KEY=your_secret_key 30 + 31 + # S3 Region 32 + # Examples: us-east-1, us-west-2, eu-west-1 33 + # For UpCloud: us-chi1, us-nyc1, de-fra1, uk-lon1, sg-sin1 34 + # Default: us-east-1 30 35 AWS_REGION=us-east-1 36 + 37 + # S3 Bucket Name 31 38 S3_BUCKET=atcr-blobs 32 39 33 - # For Storj/Minio (optional - custom S3 endpoint): 40 + # S3 Endpoint (for S3-compatible services like Storj, Minio, UpCloud) 41 + # Examples: 42 + # - Storj: https://gateway.storjshare.io 43 + # - UpCloud: https://[bucket-id].upcloudobjects.com 44 + # - Minio: http://minio:9000 45 + # Leave empty for AWS S3 34 46 # S3_ENDPOINT=https://gateway.storjshare.io 35 47 36 48 # For filesystem driver: ··· 48 60 # Writes (pushes) always require crew membership via PDS 49 61 # Default: false 50 62 HOLD_PUBLIC=false 63 + 64 + # ============================================================================== 65 + # Embedded PDS Configuration 66 + # ============================================================================== 67 + 68 + # Directory path for embedded PDS carstore (SQLite database) 69 + # Default: /var/lib/atcr-hold 70 + # If empty, embedded PDS is disabled 71 + # 72 + # Note: This should be a directory path, NOT a file path 73 + # Carstore creates db.sqlite3 inside this directory 74 + # 75 + # The embedded PDS makes the hold a proper ATProto user with: 76 + # - did:web identity (derived from HOLD_PUBLIC_URL hostname) 77 + # - DID document at /.well-known/did.json 78 + # - XRPC endpoints for crew management 79 + # - ATProto blob endpoints (wraps existing presigned URL logic) 80 + HOLD_DATABASE_DIR=/var/lib/atcr-hold 81 + 82 + # Path to signing key (auto-generated on first run if missing) 83 + # Default: {HOLD_DATABASE_DIR}/signing.key 84 + # HOLD_KEY_PATH=/var/lib/atcr-hold/signing.key 51 85 52 86 # ============================================================================== 53 87 # Registration (REQUIRED)
+8 -3
Dockerfile.hold
··· 1 1 FROM golang:1.25.2-trixie AS builder 2 2 3 + RUN apt-get update && \ 4 + apt-get install -y --no-install-recommends sqlite3 libsqlite3-dev && \ 5 + rm -rf /var/lib/apt/lists/* 6 + 3 7 WORKDIR /build 4 8 5 9 COPY go.mod go.sum ./ ··· 7 11 8 12 COPY . . 9 13 10 - RUN CGO_ENABLED=0 go build \ 11 - -ldflags="-s -w" \ 14 + RUN CGO_ENABLED=1 go build \ 15 + -ldflags="-s -w -linkmode external -extldflags '-static'" \ 16 + -tags sqlite_omit_load_extension \ 12 17 -trimpath \ 13 18 -o atcr-hold ./cmd/hold 14 19 ··· 21 26 COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ 22 27 # Copy timezone data for timestamp formatting 23 28 COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo 24 - # Copy optimized binary 29 + # Copy optimized binary (SQLite embedded) 25 30 COPY --from=builder /build/atcr-hold /atcr-hold 26 31 27 32 # Expose default port
+32
cmd/hold/main.go
··· 1 1 package main 2 2 3 3 import ( 4 + "context" 4 5 "encoding/json" 5 6 "fmt" 6 7 "log" ··· 11 12 12 13 "atcr.io/pkg/atproto" 13 14 "atcr.io/pkg/hold" 15 + "atcr.io/pkg/hold/pds" 14 16 indigooauth "github.com/bluesky-social/indigo/atproto/auth/oauth" 15 17 16 18 // Import storage drivers ··· 29 31 service, err := hold.NewHoldService(cfg) 30 32 if err != nil { 31 33 log.Fatalf("Failed to create hold service: %v", err) 34 + } 35 + 36 + // Initialize embedded PDS if database path is configured 37 + var holdPDS *pds.HoldPDS 38 + var xrpcHandler *pds.XRPCHandler 39 + if cfg.Database.Path != "" { 40 + // Generate did:web from public URL 41 + holdDID := pds.GenerateDIDFromURL(cfg.Server.PublicURL) 42 + log.Printf("Initializing embedded PDS with DID: %s", holdDID) 43 + 44 + // Initialize PDS with carstore and keys 45 + ctx := context.Background() 46 + holdPDS, err = pds.NewHoldPDS(ctx, holdDID, cfg.Server.PublicURL, cfg.Database.Path, cfg.Database.KeyPath) 47 + if err != nil { 48 + log.Fatalf("Failed to initialize embedded PDS: %v", err) 49 + } 50 + 51 + // Create blob store adapter 52 + blobStore := pds.NewHoldServiceBlobStore(service, holdDID) 53 + 54 + // Create XRPC handler 55 + xrpcHandler = pds.NewXRPCHandler(holdPDS, cfg.Server.PublicURL, blobStore) 56 + 57 + log.Printf("Embedded PDS initialized successfully") 32 58 } 33 59 34 60 // Setup HTTP routes ··· 117 143 http.Error(w, "method not allowed", http.StatusMethodNotAllowed) 118 144 } 119 145 }) 146 + 147 + // Register XRPC/ATProto PDS endpoints if PDS is initialized 148 + if xrpcHandler != nil { 149 + log.Printf("Registering ATProto PDS endpoints") 150 + xrpcHandler.RegisterHandlers(mux) 151 + } 120 152 121 153 // Create server 122 154 server := &http.Server{
+26 -2
deploy/.env.prod.template
··· 29 29 # Example: did:plc:abc123xyz789 30 30 HOLD_OWNER=did:plc:pddp4xt5lgnv2qsegbzzs4xg 31 31 32 + # Directory path for embedded PDS carstore (SQLite database) 33 + # Default: /var/lib/atcr-hold 34 + # If empty, embedded PDS is disabled 35 + # 36 + # Note: This should be a directory path, NOT a file path 37 + # Carstore creates db.sqlite3 inside this directory 38 + # 39 + # The embedded PDS makes the hold a proper ATProto user with: 40 + # - did:web identity (derived from HOLD_DOMAIN) 41 + # - DID document at /.well-known/did.json 42 + # - XRPC endpoints for crew management 43 + # - ATProto blob endpoints (wraps existing presigned URL logic) 44 + # 45 + # Example: For HOLD_DOMAIN=hold01.atcr.io, the hold becomes did:web:hold01.atcr.io 46 + HOLD_DATABASE_DIR=/var/lib/atcr-hold 47 + 48 + # Path to signing key (auto-generated on first run if missing) 49 + # Default: {HOLD_DATABASE_DIR}/signing.key 50 + # HOLD_KEY_PATH=/var/lib/atcr-hold/signing.key 51 + 32 52 # Allow public blob reads (pulls) without authentication 33 53 # - true: Anyone can pull images (read-only) 34 54 # - false: Only authenticated users can pull ··· 79 99 80 100 # S3 Region (for distribution S3 driver) 81 101 # UpCloud regions: us-chi1, us-nyc1, de-fra1, uk-lon1, sg-sin1, etc. 82 - # Default: us-chi1 83 - S3_REGION=us-chi1 102 + # Note: Use AWS_REGION (not S3_REGION) - this is what the hold service expects 103 + # Default: us-east-1 104 + AWS_REGION=us-chi1 84 105 85 106 # S3 Bucket Name 86 107 # Create this bucket in UpCloud Object Storage ··· 177 198 # ☐ Set APPVIEW_DOMAIN (e.g., atcr.io) 178 199 # ☐ Set HOLD_DOMAIN (e.g., hold01.atcr.io) 179 200 # ☐ Set HOLD_OWNER (your ATProto DID) 201 + # ☐ Set HOLD_DATABASE_DIR (default: /var/lib/atcr-hold) - enables embedded PDS 180 202 # ☐ Set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY 203 + # ☐ Set AWS_REGION (e.g., us-chi1) 181 204 # ☐ Set S3_BUCKET (created in UpCloud Object Storage) 182 205 # ☐ Set S3_ENDPOINT (UpCloud endpoint or custom domain) 183 206 # ☐ Configured DNS records: ··· 189 212 # 190 213 # After starting: 191 214 # ☐ Complete hold OAuth registration (run: /opt/atcr/get-hold-oauth.sh) 215 + # ☐ Verify hold PDS: curl https://hold01.atcr.io/.well-known/did.json 192 216 # ☐ Test registry: docker pull atcr.io/test/image 193 217 # ☐ Monitor logs: /opt/atcr/logs.sh
+6 -4
deploy/docker-compose.prod.yml
··· 98 98 HOLD_PUBLIC: ${HOLD_PUBLIC:-false} 99 99 HOLD_OWNER: ${HOLD_OWNER} 100 100 101 + # Embedded PDS configuration 102 + HOLD_DATABASE_DIR: ${HOLD_DATABASE_DIR:-/var/lib/atcr-hold} 103 + # HOLD_KEY_PATH: ${HOLD_KEY_PATH} # Optional, defaults to {HOLD_DATABASE_DIR}/signing.key 104 + 101 105 # Storage driver 102 106 STORAGE_DRIVER: ${STORAGE_DRIVER:-s3} 103 107 ··· 113 117 # STORAGE_DRIVER: filesystem 114 118 # STORAGE_ROOT_DIR: /var/lib/atcr/hold 115 119 volumes: 116 - # Only needed for filesystem driver 117 - # - atcr-hold-data:/var/lib/atcr/hold 118 - # OAuth token storage for hold registration 119 - - atcr-hold-tokens:/root/.atcr 120 + # PDS data (carstore SQLite + signing keys) 121 + - atcr-hold-data:/var/lib/atcr-hold 120 122 networks: 121 123 - atcr-network 122 124 healthcheck:
+3 -2
docker-compose.yml
··· 56 56 container_name: atcr-hold 57 57 ports: 58 58 - "8080:8080" 59 - # volumes: 60 - # - atcr-hold:/var/lib/atcr/hold 59 + volumes: 60 + # PDS data (carstore SQLite + signing keys) 61 + - atcr-hold:/var/lib/atcr-hold 61 62 restart: unless-stopped 62 63 dns: 63 64 - 8.8.8.8
+55 -16
docs/EMBEDDED_PDS.md
··· 510 510 511 511 ## Implementation Plan 512 512 513 - ### Phase 1: Basic PDS with Carstore (Current) 513 + ### Phase 1: Basic PDS with Carstore ✅ COMPLETED 514 514 515 - **Decision: Use indigo's carstore with SQLite backend** 515 + **Implementation: Using indigo's carstore with SQLite + DeltaSession** 516 516 517 517 ```go 518 518 import ( 519 519 "github.com/bluesky-social/indigo/carstore" 520 + "github.com/bluesky-social/indigo/models" 520 521 "github.com/bluesky-social/indigo/repo" 521 522 ) 522 523 523 524 type HoldPDS struct { 524 - did string 525 - carstore carstore.CarStore 526 - repo *repo.Repo 525 + did string 526 + carstore carstore.CarStore 527 + session *carstore.DeltaSession // Provides blockstore interface 528 + repo *repo.Repo 529 + dbPath string 530 + uid models.Uid // User ID for carstore (fixed: 1) 527 531 } 528 532 529 - func NewHoldPDS(did, dbPath string) (*HoldPDS, error) { 533 + func NewHoldPDS(ctx context.Context, did, dbPath string) (*HoldPDS, error) { 530 534 // Create SQLite-backed carstore 531 535 sqlStore, err := carstore.NewSqliteStore(dbPath) 536 + sqlStore.Open(dbPath) 532 537 cs := sqlStore.CarStore() 533 538 534 - // Get or create repo 535 - head, err := cs.GetUserRepoHead(ctx, did) 536 - var r *repo.Repo 537 - if err == carstore.ErrRepoNotFound { 538 - r, err = repo.NewRepo(ctx, did, cs.Blockstore()) 539 - } else { 540 - r, err = repo.OpenRepo(ctx, cs.Blockstore(), head) 541 - } 539 + // For single-hold use, fixed UID 540 + uid := models.Uid(1) 542 541 543 - return &HoldPDS{did: did, carstore: cs, repo: r}, nil 542 + // Create DeltaSession (provides blockstore interface) 543 + session, err := cs.NewDeltaSession(ctx, uid, nil) 544 + 545 + // Create repo with session as blockstore 546 + r := repo.NewRepo(ctx, did, session) 547 + 548 + return &HoldPDS{ 549 + did: did, 550 + carstore: cs, 551 + session: session, 552 + repo: r, 553 + dbPath: dbPath, 554 + uid: uid, 555 + }, nil 544 556 } 545 557 ``` 546 558 559 + **Key learnings:** 560 + - ✅ Carstore provides blockstore via `DeltaSession` (not direct access) 561 + - ✅ `models.Uid` is the user ID type (we use fixed UID(1)) 562 + - ✅ DeltaSession needs to be a pointer (`*carstore.DeltaSession`) 563 + - ✅ `repo.NewRepo()` accepts the session directly as blockstore 564 + 547 565 **Storage:** 548 566 - Single file: `/var/lib/atcr-hold/hold.db` (SQLite) 549 567 - Contains MST nodes, records, commits in carstore tables ··· 552 570 **Why SQLite carstore:** 553 571 - ✅ Single file persistence (like appview's SQLite) 554 572 - ✅ Official indigo storage backend 555 - - ✅ No custom blockstore implementation needed 556 573 - ✅ Handles compaction/cleanup automatically 557 574 - ✅ Migration path to Postgres/Scylla if needed 558 575 - ✅ Easy to replicate (Litestream, LiteFS, rsync) 576 + - ✅ CAR import/export support built-in 559 577 560 578 **Scale considerations:** 561 579 - SQLite carstore marked "experimental" but suitable for single-hold use ··· 563 581 - 1000 crew records = ~1-2MB database (trivial) 564 582 - Bluesky PDSs use carstore for millions of records 565 583 - If needed: migrate to Postgres-backed carstore (same API) 584 + 585 + ### Hold as Proper ATProto User 586 + 587 + **Decision:** Make holds full ATProto actors for discoverability and ecosystem integration. 588 + 589 + **What this enables:** 590 + - Hold becomes discoverable via ATProto directory 591 + - Can have profile (`app.bsky.actor.profile`) 592 + - Can post status updates (`app.bsky.feed.post`) 593 + - Users can follow holds 594 + - Social proof/reputation via ATProto social graph 595 + 596 + **MVP Scope:** 597 + We're building the minimal PDS needed for discoverability, not a full social client: 598 + - ✅ Signing keys (ES256K via `atproto/atcrypto`) 599 + - ✅ DID document (did:web at `/.well-known/did.json`) 600 + - ✅ Standard XRPC endpoints (`describeRepo`, `getRecord`, `listRecords`) 601 + - ✅ Profile record (`app.bsky.actor.profile`) 602 + - ⏸️ Posting functionality (later - other services can read our records) 603 + 604 + **Key insight:** Other ATProto services will "just work" as long as they can retrieve records from the hold's PDS. We don't need to implement full social features for the hold to participate in the ecosystem. 566 605 567 606 ### Crew Management: Individual Records 568 607
+55
go.mod
··· 17 17 github.com/spf13/cobra v1.8.0 18 18 go.yaml.in/yaml/v4 v4.0.0-rc.2 19 19 golang.org/x/crypto v0.39.0 20 + github.com/ipfs/go-cid v0.4.1 20 21 ) 21 22 22 23 require ( ··· 34 35 github.com/go-jose/go-jose/v4 v4.1.2 // indirect 35 36 github.com/go-logr/logr v1.4.2 // indirect 36 37 github.com/go-logr/stdr v1.2.2 // indirect 38 + github.com/gocql/gocql v1.7.0 // indirect 39 + github.com/gogo/protobuf v1.3.2 // indirect 40 + github.com/golang/snappy v0.0.4 // indirect 37 41 github.com/google/go-cmp v0.7.0 // indirect 38 42 github.com/google/go-querystring v1.1.0 // indirect 39 43 github.com/gorilla/handlers v1.5.2 // indirect 40 44 github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 // indirect 45 + github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect 46 + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect 47 + github.com/hashicorp/go-retryablehttp v0.7.5 // indirect 48 + github.com/hashicorp/golang-lru v1.0.2 // indirect 41 49 github.com/hashicorp/golang-lru/arc/v2 v2.0.6 // indirect 42 50 github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect 43 51 github.com/inconshreveable/mousetrap v1.1.0 // indirect 52 + github.com/ipfs/bbloom v0.0.4 // indirect 53 + github.com/ipfs/go-block-format v0.2.0 // indirect 54 + github.com/ipfs/go-blockservice v0.5.2 // indirect 55 + github.com/ipfs/go-datastore v0.6.0 // indirect 56 + github.com/ipfs/go-ipfs-blockstore v1.3.1 // indirect 57 + github.com/ipfs/go-ipfs-ds-help v1.1.1 // indirect 58 + github.com/ipfs/go-ipfs-exchange-interface v0.2.1 // indirect 59 + github.com/ipfs/go-ipfs-util v0.0.3 // indirect 60 + github.com/ipfs/go-ipld-cbor v0.1.0 // indirect 61 + github.com/ipfs/go-ipld-format v0.6.0 // indirect 62 + github.com/ipfs/go-ipld-legacy v0.2.1 // indirect 63 + github.com/ipfs/go-libipfs v0.7.0 // indirect 64 + github.com/ipfs/go-log v1.0.5 // indirect 65 + github.com/ipfs/go-log/v2 v2.5.1 // indirect 66 + github.com/ipfs/go-merkledag v0.11.0 // indirect 67 + github.com/ipfs/go-metrics-interface v0.0.1 // indirect 68 + github.com/ipfs/go-verifcid v0.0.3 // indirect 69 + github.com/ipld/go-car v0.6.1-0.20230509095817-92d28eb23ba4 // indirect 70 + github.com/ipld/go-codec-dagpb v1.6.0 // indirect 71 + github.com/ipld/go-ipld-prime v0.21.0 // indirect 72 + github.com/jackc/pgpassfile v1.0.0 // indirect 73 + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect 74 + github.com/jackc/pgx/v5 v5.5.0 // indirect 75 + github.com/jackc/puddle/v2 v2.2.1 // indirect 76 + github.com/jbenet/goprocess v0.1.4 // indirect 77 + github.com/jinzhu/inflection v1.0.0 // indirect 78 + github.com/jinzhu/now v1.1.5 // indirect 44 79 github.com/jmespath/go-jmespath v0.4.0 // indirect 80 + github.com/klauspost/cpuid/v2 v2.2.7 // indirect 81 + github.com/mattn/go-isatty v0.0.20 // indirect 82 + github.com/minio/sha256-simd v1.0.1 // indirect 45 83 github.com/mr-tron/base58 v1.2.0 // indirect 84 + github.com/multiformats/go-base32 v0.1.0 // indirect 85 + github.com/multiformats/go-base36 v0.2.0 // indirect 86 + github.com/multiformats/go-multibase v0.2.0 // indirect 87 + github.com/multiformats/go-multihash v0.2.3 // indirect 88 + github.com/multiformats/go-varint v0.0.7 // indirect 46 89 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect 47 90 github.com/opencontainers/image-spec v1.1.0 // indirect 91 + github.com/opentracing/opentracing-go v1.2.0 // indirect 92 + github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f // indirect 48 93 github.com/prometheus/client_golang v1.20.5 // indirect 49 94 github.com/prometheus/client_model v0.6.1 // indirect 50 95 github.com/prometheus/common v0.60.1 // indirect ··· 53 98 github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 // indirect 54 99 github.com/redis/go-redis/v9 v9.7.3 // indirect 55 100 github.com/sirupsen/logrus v1.9.3 // indirect 101 + github.com/spaolacci/murmur3 v1.1.0 // indirect 56 102 github.com/spf13/pflag v1.0.5 // indirect 103 + github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e // indirect 57 104 gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b // indirect 58 105 gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 // indirect 59 106 go.opentelemetry.io/contrib/bridges/prometheus v0.57.0 // indirect ··· 78 125 go.opentelemetry.io/otel/sdk/metric v1.32.0 // indirect 79 126 go.opentelemetry.io/otel/trace v1.32.0 // indirect 80 127 go.opentelemetry.io/proto/otlp v1.3.1 // indirect 128 + go.uber.org/atomic v1.11.0 // indirect 129 + go.uber.org/multierr v1.11.0 // indirect 130 + go.uber.org/zap v1.26.0 // indirect 81 131 golang.org/x/net v0.37.0 // indirect 82 132 golang.org/x/sync v0.15.0 // indirect 83 133 golang.org/x/sys v0.33.0 // indirect 84 134 golang.org/x/text v0.26.0 // indirect 85 135 golang.org/x/time v0.6.0 // indirect 136 + golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect 86 137 google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 // indirect 87 138 google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 // indirect 88 139 google.golang.org/grpc v1.68.0 // indirect 89 140 google.golang.org/protobuf v1.35.1 // indirect 141 + gopkg.in/inf.v0 v0.9.1 // indirect 90 142 gopkg.in/yaml.v2 v2.4.0 // indirect 143 + gorm.io/driver/postgres v1.5.7 // indirect 144 + gorm.io/gorm v1.25.9 // indirect 145 + lukechampine.com/blake3 v1.2.1 // indirect 91 146 )
+217
go.sum
··· 1 1 github.com/AdaLogics/go-fuzz-headers v0.0.0-20221103172237-443f56ff4ba8 h1:d+pBUmsteW5tM87xmVXHZ4+LibHRFn40SPAoZJOg2ak= 2 2 github.com/AdaLogics/go-fuzz-headers v0.0.0-20221103172237-443f56ff4ba8/go.mod h1:i9fr2JpcEcY/IHEvzCM3qXUZYOQHgR89dt4es1CgMhc= 3 + github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 3 4 github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 4 5 github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 6 + github.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5 h1:iW0a5ljuFxkLGPNem5Ui+KBjFJzKg4Fv2fnxe4dvzpM= 7 + github.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5/go.mod h1:Y2QMoi1vgtOIfc+6DhrMOGkLoGzqSV2rKp4Sm+opsyA= 5 8 github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= 6 9 github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= 10 + github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= 11 + github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= 12 + github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= 7 13 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 8 14 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 9 15 github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 10 16 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 17 + github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY= 18 + github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= 11 19 github.com/bluesky-social/indigo v0.0.0-20251003000214-3259b215110e h1:IutKPwmbU0LrYqw03EuwJtMdAe67rDTrL1U8S8dicRU= 12 20 github.com/bluesky-social/indigo v0.0.0-20251003000214-3259b215110e/go.mod h1:n6QE1NDPFoi7PRbMUZmc2y7FibCqiVU4ePpsvhHUBR8= 21 + github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= 22 + github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= 13 23 github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70= 14 24 github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= 15 25 github.com/bsm/ginkgo/v2 v2.7.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w= ··· 27 37 github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 28 38 github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= 29 39 github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= 40 + github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 30 41 github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 42 + github.com/cskr/pubsub v1.0.2 h1:vlOzMhl6PFn60gRlTQQsIfVwaPB/B/8MziK8FhEPt/0= 43 + github.com/cskr/pubsub v1.0.2/go.mod h1:/8MzYXk/NJAz782G8RPkFzXTZVu63VotefPnR9TIRis= 31 44 github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= 32 45 github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= 33 46 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 34 47 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 35 48 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 49 + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= 50 + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= 36 51 github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= 37 52 github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= 38 53 github.com/distribution/distribution/v3 v3.0.0 h1:q4R8wemdRQDClzoNNStftB2ZAfqOiN6UX90KJc4HjyM= ··· 47 62 github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= 48 63 github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= 49 64 github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= 65 + github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= 66 + github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= 50 67 github.com/go-jose/go-jose/v4 v4.1.2 h1:TK/7NqRQZfgAh+Td8AlsrvtPoUyiHh0LqVvokh+1vHI= 51 68 github.com/go-jose/go-jose/v4 v4.1.2/go.mod h1:22cg9HWM1pOlnRiY+9cQYJ9XHmya1bYW8OeDM6Ku6Oo= 52 69 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= ··· 58 75 github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= 59 76 github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= 60 77 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 78 + github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= 79 + github.com/gocql/gocql v1.7.0 h1:O+7U7/1gSN7QTEAaMEsJc1Oq2QHXvCWoF3DFK9HDHus= 80 + github.com/gocql/gocql v1.7.0/go.mod h1:vnlvXyFZeLBF0Wy+RS8hrOdbn0UWsWtdg07XJnFxZ+4= 61 81 github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 62 82 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 63 83 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= ··· 69 89 github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 70 90 github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= 71 91 github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= 92 + github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 93 + github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= 94 + github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 72 95 github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 73 96 github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 74 97 github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= ··· 76 99 github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= 77 100 github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= 78 101 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 102 + github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= 103 + github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= 104 + github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 79 105 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 80 106 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 107 + github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= 108 + github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 81 109 github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE= 82 110 github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w= 83 111 github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= ··· 86 114 github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 87 115 github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 h1:ad0vkEBuk23VJzZR9nkLVG0YAoN9coASF1GusYX6AlU= 88 116 github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0/go.mod h1:igFoXX2ELCW06bol23DWPB5BEWfZISOzSP5K2sbLea0= 117 + github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= 118 + github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= 89 119 github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= 90 120 github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= 121 + github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI= 122 + github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= 91 123 github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M= 92 124 github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= 93 125 github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= ··· 96 128 github.com/hashicorp/golang-lru/arc/v2 v2.0.6/go.mod h1:cfdDIX05DWvYV6/shsxDfa/OVcRieOt+q4FnM8x+Xno= 97 129 github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= 98 130 github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= 131 + github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ= 132 + github.com/huin/goupnp v1.0.3/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixHM7Y= 99 133 github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 100 134 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 101 135 github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs= 102 136 github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0= 137 + github.com/ipfs/go-bitswap v0.11.0 h1:j1WVvhDX1yhG32NTC9xfxnqycqYIlhzEzLXG/cU1HyQ= 138 + github.com/ipfs/go-bitswap v0.11.0/go.mod h1:05aE8H3XOU+LXpTedeAS0OZpcO1WFsj5niYQH9a1Tmk= 103 139 github.com/ipfs/go-block-format v0.2.0 h1:ZqrkxBA2ICbDRbK8KJs/u0O3dlp6gmAuuXUJNiW1Ycs= 104 140 github.com/ipfs/go-block-format v0.2.0/go.mod h1:+jpL11nFx5A/SPpsoBn6Bzkra/zaArfSmsknbPMYgzM= 141 + github.com/ipfs/go-blockservice v0.5.2 h1:in9Bc+QcXwd1apOVM7Un9t8tixPKdaHQFdLSUM1Xgk8= 142 + github.com/ipfs/go-blockservice v0.5.2/go.mod h1:VpMblFEqG67A/H2sHKAemeH9vlURVavlysbdUI632yk= 105 143 github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= 106 144 github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= 107 145 github.com/ipfs/go-datastore v0.6.0 h1:JKyz+Gvz1QEZw0LsX1IBn+JFCJQH4SJVFtM4uWU0Myk= 108 146 github.com/ipfs/go-datastore v0.6.0/go.mod h1:rt5M3nNbSO/8q1t4LNkLyUwRs8HupMeN/8O4Vn9YAT8= 147 + github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= 148 + github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= 149 + github.com/ipfs/go-ds-flatfs v0.5.1 h1:ZCIO/kQOS/PSh3vcF1H6a8fkRGS7pOfwfPdx4n/KJH4= 150 + github.com/ipfs/go-ds-flatfs v0.5.1/go.mod h1:RWTV7oZD/yZYBKdbVIFXTX2fdY2Tbvl94NsWqmoyAX4= 109 151 github.com/ipfs/go-ipfs-blockstore v1.3.1 h1:cEI9ci7V0sRNivqaOr0elDsamxXFxJMMMy7PTTDQNsQ= 110 152 github.com/ipfs/go-ipfs-blockstore v1.3.1/go.mod h1:KgtZyc9fq+P2xJUiCAzbRdhhqJHvsw8u2Dlqy2MyRTE= 153 + github.com/ipfs/go-ipfs-blocksutil v0.0.1 h1:Eh/H4pc1hsvhzsQoMEP3Bke/aW5P5rVM1IWFJMcGIPQ= 154 + github.com/ipfs/go-ipfs-blocksutil v0.0.1/go.mod h1:Yq4M86uIOmxmGPUHv/uI7uKqZNtLb449gwKqXjIsnRk= 155 + github.com/ipfs/go-ipfs-delay v0.0.1 h1:r/UXYyRcddO6thwOnhiznIAiSvxMECGgtv35Xs1IeRQ= 156 + github.com/ipfs/go-ipfs-delay v0.0.1/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= 111 157 github.com/ipfs/go-ipfs-ds-help v1.1.1 h1:B5UJOH52IbcfS56+Ul+sv8jnIV10lbjLF5eOO0C66Nw= 112 158 github.com/ipfs/go-ipfs-ds-help v1.1.1/go.mod h1:75vrVCkSdSFidJscs8n4W+77AtTpCIAdDGAwjitJMIo= 159 + github.com/ipfs/go-ipfs-exchange-interface v0.2.1 h1:jMzo2VhLKSHbVe+mHNzYgs95n0+t0Q69GQ5WhRDZV/s= 160 + github.com/ipfs/go-ipfs-exchange-interface v0.2.1/go.mod h1:MUsYn6rKbG6CTtsDp+lKJPmVt3ZrCViNyH3rfPGsZ2E= 161 + github.com/ipfs/go-ipfs-exchange-offline v0.3.0 h1:c/Dg8GDPzixGd0MC8Jh6mjOwU57uYokgWRFidfvEkuA= 162 + github.com/ipfs/go-ipfs-exchange-offline v0.3.0/go.mod h1:MOdJ9DChbb5u37M1IcbrRB02e++Z7521fMxqCNRrz9s= 163 + github.com/ipfs/go-ipfs-pq v0.0.3 h1:YpoHVJB+jzK15mr/xsWC574tyDLkezVrDNeaalQBsTE= 164 + github.com/ipfs/go-ipfs-pq v0.0.3/go.mod h1:btNw5hsHBpRcSSgZtiNm/SLj5gYIZ18AKtv3kERkRb4= 165 + github.com/ipfs/go-ipfs-routing v0.3.0 h1:9W/W3N+g+y4ZDeffSgqhgo7BsBSJwPMcyssET9OWevc= 166 + github.com/ipfs/go-ipfs-routing v0.3.0/go.mod h1:dKqtTFIql7e1zYsEuWLyuOU+E0WJWW8JjbTPLParDWo= 113 167 github.com/ipfs/go-ipfs-util v0.0.3 h1:2RFdGez6bu2ZlZdI+rWfIdbQb1KudQp3VGwPtdNCmE0= 114 168 github.com/ipfs/go-ipfs-util v0.0.3/go.mod h1:LHzG1a0Ig4G+iZ26UUOMjHd+lfM84LZCrn17xAKWBvs= 115 169 github.com/ipfs/go-ipld-cbor v0.1.0 h1:dx0nS0kILVivGhfWuB6dUpMa/LAwElHPw1yOGYopoYs= 116 170 github.com/ipfs/go-ipld-cbor v0.1.0/go.mod h1:U2aYlmVrJr2wsUBU67K4KgepApSZddGRDWBYR0H4sCk= 117 171 github.com/ipfs/go-ipld-format v0.6.0 h1:VEJlA2kQ3LqFSIm5Vu6eIlSxD/Ze90xtc4Meten1F5U= 118 172 github.com/ipfs/go-ipld-format v0.6.0/go.mod h1:g4QVMTn3marU3qXchwjpKPKgJv+zF+OlaKMyhJ4LHPg= 173 + github.com/ipfs/go-ipld-legacy v0.2.1 h1:mDFtrBpmU7b//LzLSypVrXsD8QxkEWxu5qVxN99/+tk= 174 + github.com/ipfs/go-ipld-legacy v0.2.1/go.mod h1:782MOUghNzMO2DER0FlBR94mllfdCJCkTtDtPM51otM= 175 + github.com/ipfs/go-libipfs v0.7.0 h1:Mi54WJTODaOL2/ZSm5loi3SwI3jI2OuFWUrQIkJ5cpM= 176 + github.com/ipfs/go-libipfs v0.7.0/go.mod h1:KsIf/03CqhICzyRGyGo68tooiBE2iFbI/rXW7FhAYr0= 119 177 github.com/ipfs/go-log v1.0.5 h1:2dOuUCB1Z7uoczMWgAyDck5JLb72zHzrMnGnCNNbvY8= 120 178 github.com/ipfs/go-log v1.0.5/go.mod h1:j0b8ZoR+7+R99LD9jZ6+AJsrzkPbSXbZfGakb5JPtIo= 179 + github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g= 121 180 github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY= 122 181 github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI= 182 + github.com/ipfs/go-merkledag v0.11.0 h1:DgzwK5hprESOzS4O1t/wi6JDpyVQdvm9Bs59N/jqfBY= 183 + github.com/ipfs/go-merkledag v0.11.0/go.mod h1:Q4f/1ezvBiJV0YCIXvt51W/9/kqJGH4I1LsA7+djsM4= 123 184 github.com/ipfs/go-metrics-interface v0.0.1 h1:j+cpbjYvu4R8zbleSs36gvB7jR+wsL2fGD6n0jO4kdg= 124 185 github.com/ipfs/go-metrics-interface v0.0.1/go.mod h1:6s6euYU4zowdslK0GKHmqaIZ3j/b/tL7HTWtJ4VPgWY= 186 + github.com/ipfs/go-peertaskqueue v0.8.1 h1:YhxAs1+wxb5jk7RvS0LHdyiILpNmRIRnZVztekOF0pg= 187 + github.com/ipfs/go-peertaskqueue v0.8.1/go.mod h1:Oxxd3eaK279FxeydSPPVGHzbwVeHjatZ2GA8XD+KbPU= 188 + github.com/ipfs/go-verifcid v0.0.3 h1:gmRKccqhWDocCRkC+a59g5QW7uJw5bpX9HWBevXa0zs= 189 + github.com/ipfs/go-verifcid v0.0.3/go.mod h1:gcCtGniVzelKrbk9ooUSX/pM3xlH73fZZJDzQJRvOUw= 190 + github.com/ipld/go-car v0.6.1-0.20230509095817-92d28eb23ba4 h1:oFo19cBmcP0Cmg3XXbrr0V/c+xU9U1huEZp8+OgBzdI= 191 + github.com/ipld/go-car v0.6.1-0.20230509095817-92d28eb23ba4/go.mod h1:6nkFF8OmR5wLKBzRKi7/YFJpyYR7+oEn1DX+mMWnlLA= 192 + github.com/ipld/go-car/v2 v2.13.1 h1:KnlrKvEPEzr5IZHKTXLAEub+tPrzeAFQVRlSQvuxBO4= 193 + github.com/ipld/go-car/v2 v2.13.1/go.mod h1:QkdjjFNGit2GIkpQ953KBwowuoukoM75nP/JI1iDJdo= 194 + github.com/ipld/go-codec-dagpb v1.6.0 h1:9nYazfyu9B1p3NAgfVdpRco3Fs2nFC72DqVsMj6rOcc= 195 + github.com/ipld/go-codec-dagpb v1.6.0/go.mod h1:ANzFhfP2uMJxRBr8CE+WQWs5UsNa0pYtmKZ+agnUw9s= 196 + github.com/ipld/go-ipld-prime v0.21.0 h1:n4JmcpOlPDIxBcY037SVfpd1G+Sj1nKZah0m6QH9C2E= 197 + github.com/ipld/go-ipld-prime v0.21.0/go.mod h1:3RLqy//ERg/y5oShXXdx5YIp50cFGOanyMctpPjsvxQ= 198 + github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= 199 + github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= 200 + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= 201 + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= 202 + github.com/jackc/pgx/v5 v5.5.0 h1:NxstgwndsTRy7eq9/kqYc/BZh5w2hHJV86wjvO+1xPw= 203 + github.com/jackc/pgx/v5 v5.5.0/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA= 204 + github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= 205 + github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= 206 + github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= 207 + github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= 208 + github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= 125 209 github.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0o= 126 210 github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= 211 + github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= 212 + github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= 213 + github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= 214 + github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 127 215 github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= 128 216 github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= 129 217 github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= 130 218 github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= 131 219 github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 132 220 github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 221 + github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= 222 + github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 133 223 github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 224 + github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 225 + github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 134 226 github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= 135 227 github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= 136 228 github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= 137 229 github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= 138 230 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 231 + github.com/koron/go-ssdp v0.0.3 h1:JivLMY45N76b4p/vsWGOKewBQu6uf39y8l+AQ7sDKx8= 232 + github.com/koron/go-ssdp v0.0.3/go.mod h1:b2MxI6yh02pKrsyNoQUsk4+YNikaGhe4894J+Q5lDvA= 139 233 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 234 + github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 140 235 github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 141 236 github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 237 + github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 238 + github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 142 239 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 143 240 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 144 241 github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= 145 242 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 243 + github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= 244 + github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= 245 + github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38yPW7c= 246 + github.com/libp2p/go-cidranger v1.1.0/go.mod h1:KWZTfSr+r9qEo9OkI9/SIEeAtw+NNoU0dXIXt15Okic= 247 + github.com/libp2p/go-libp2p v0.25.1 h1:YK+YDCHpYyTvitKWVxa5PfElgIpOONU01X5UcLEwJGA= 248 + github.com/libp2p/go-libp2p v0.25.1/go.mod h1:xnK9/1d9+jeQCVvi/f1g12KqtVi/jP/SijtKV1hML3g= 249 + github.com/libp2p/go-libp2p-asn-util v0.2.0 h1:rg3+Os8jbnO5DxkC7K/Utdi+DkY3q/d1/1q+8WeNAsw= 250 + github.com/libp2p/go-libp2p-asn-util v0.2.0/go.mod h1:WoaWxbHKBymSN41hWSq/lGKJEca7TNm58+gGJi2WsLI= 251 + github.com/libp2p/go-libp2p-record v0.2.0 h1:oiNUOCWno2BFuxt3my4i1frNrt7PerzB3queqa1NkQ0= 252 + github.com/libp2p/go-libp2p-record v0.2.0/go.mod h1:I+3zMkvvg5m2OcSdoL0KPljyJyvNDFGKX7QdlpYUcwk= 253 + github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= 254 + github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg= 255 + github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0= 256 + github.com/libp2p/go-msgio v0.3.0/go.mod h1:nyRM819GmVaF9LX3l03RMh10QdOroF++NBbxAb0mmDM= 257 + github.com/libp2p/go-nat v0.1.0 h1:MfVsH6DLcpa04Xr+p8hmVRG4juse0s3J8HyNWYHffXg= 258 + github.com/libp2p/go-nat v0.1.0/go.mod h1:X7teVkwRHNInVNWQiO/tAiAVRwSr5zoRz4YSTC3uRBM= 259 + github.com/libp2p/go-netroute v0.2.1 h1:V8kVrpD8GK0Riv15/7VN6RbUQ3URNZVosw7H2v9tksU= 260 + github.com/libp2p/go-netroute v0.2.1/go.mod h1:hraioZr0fhBjG0ZRXJJ6Zj2IVEVNx6tDTFQfSmcq7mQ= 261 + github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 146 262 github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 147 263 github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 148 264 github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs= 149 265 github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= 150 266 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 267 + github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= 268 + github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= 151 269 github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= 152 270 github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= 153 271 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= ··· 160 278 github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI= 161 279 github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= 162 280 github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= 281 + github.com/multiformats/go-multiaddr v0.8.0 h1:aqjksEcqK+iD/Foe1RRFsGZh8+XFiGo7FgUCZlpv3LU= 282 + github.com/multiformats/go-multiaddr v0.8.0/go.mod h1:Fs50eBDWvZu+l3/9S6xAE7ZYj6yhxlvaVZjakWN7xRs= 283 + github.com/multiformats/go-multiaddr-dns v0.3.1 h1:QgQgR+LQVt3NPTjbrLLpsaT2ufAA2y0Mkk+QRVJbW3A= 284 + github.com/multiformats/go-multiaddr-dns v0.3.1/go.mod h1:G/245BRQ6FJGmryJCrOuTdB37AMA5AMOVuO6NY3JwTk= 285 + github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= 286 + github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo= 163 287 github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g= 164 288 github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk= 289 + github.com/multiformats/go-multicodec v0.9.0 h1:pb/dlPnzee/Sxv/j4PmkDRxCOi3hXTz3IbPKOXWJkmg= 290 + github.com/multiformats/go-multicodec v0.9.0/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI16i14xuaojr/H7Ai54k= 165 291 github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U= 166 292 github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM= 293 + github.com/multiformats/go-multistream v0.4.1 h1:rFy0Iiyn3YT0asivDUIR05leAdwZq3de4741sbiSdfo= 294 + github.com/multiformats/go-multistream v0.4.1/go.mod h1:Mz5eykRVAjJWckE2U78c6xqdtyNUEhKSM0Lwar2p77Q= 167 295 github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= 168 296 github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= 169 297 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= ··· 175 303 github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= 176 304 github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= 177 305 github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= 306 + github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9 h1:1/WtZae0yGtPq+TI6+Tv1WTxkukpXeMlviSxvL7SRgk= 307 + github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9/go.mod h1:x3N5drFsm2uilKKuuYo6LdyD8vZAW55sH/9w+pbo1sw= 178 308 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 309 + github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 179 310 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 180 311 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 181 312 github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f h1:VXTQfuJj9vKR4TCkEuWIckKvdHFeJH/huIFJ9/cXOB0= ··· 205 336 github.com/redis/go-redis/v9 v9.0.5/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk= 206 337 github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM= 207 338 github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA= 339 + github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 208 340 github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= 209 341 github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= 342 + github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 210 343 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 344 + github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 211 345 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 212 346 github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= 213 347 github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 348 + github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= 349 + github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= 350 + github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= 351 + github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= 214 352 github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= 215 353 github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 216 354 github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= ··· 221 359 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 222 360 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 223 361 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 362 + github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 224 363 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 225 364 github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 226 365 github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 366 + github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= 367 + github.com/warpfork/go-testmark v0.12.1 h1:rMgCpJfwy1sJ50x0M0NgyphxYYPMOODIJHhsXyEHU0s= 368 + github.com/warpfork/go-testmark v0.12.1/go.mod h1:kHwy7wfvGSPh1rQJYKayD4AbtNaeyZdcGi9tNJTaa5Y= 369 + github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0 h1:GDDkbFiaK8jsSDJfjId/PEGEShv6ugrt4kYsC5UIDaQ= 370 + github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= 371 + github.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11 h1:5HZfQkwe0mIfyDmc1Em5GqlNRzcdtlv4HTNmdpt7XH0= 372 + github.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11/go.mod h1:Wlo/SzPmxVp6vXpGt/zaXhHH0fn4IxgqZc82aKg6bpQ= 227 373 github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e h1:28X54ciEwwUxyHn9yrZfl5ojgF4CBNLWX7LR0rvBkf4= 228 374 github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e/go.mod h1:pM99HXyEbSQHcosHc0iW7YFmwnscr+t9Te4ibko05so= 375 + github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 376 + github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 377 + github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 229 378 gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b h1:CzigHMRySiX3drau9C6Q5CAbNIApmLdat5jPMqChvDA= 230 379 gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b/go.mod h1:/y/V339mxv2sZmYYR64O07VuCpdNZqCTwO8ZcouTMI8= 231 380 gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 h1:qwDnMxjkyLmAFgcfgTnfJrmYKWhHnci3GjDqcZp1M3Q= ··· 274 423 go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= 275 424 go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= 276 425 go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= 426 + go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 427 + go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 277 428 go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= 278 429 go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= 430 + go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= 279 431 go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= 280 432 go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= 433 + go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= 434 + go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= 281 435 go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= 282 436 go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 437 + go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= 438 + go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= 439 + go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= 283 440 go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= 284 441 go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= 285 442 go.yaml.in/yaml/v4 v4.0.0-rc.2 h1:/FrI8D64VSr4HtGIlUtlFMGsm7H7pWTbj6vOLVZcA6s= 286 443 go.yaml.in/yaml/v4 v4.0.0-rc.2/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0= 287 444 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 288 445 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 446 + golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 447 + golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 448 + golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 289 449 golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= 290 450 golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= 451 + golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= 452 + golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= 453 + golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 454 + golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 455 + golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 456 + golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 457 + golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 458 + golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= 459 + golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= 291 460 golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 461 + golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 462 + golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 292 463 golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 464 + golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 465 + golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 466 + golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 467 + golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 293 468 golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= 294 469 golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= 295 470 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 296 471 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 472 + golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 473 + golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 474 + golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 475 + golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 297 476 golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= 298 477 golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= 299 478 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 300 479 golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 301 480 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 481 + golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 302 482 golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 483 + golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 484 + golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 485 + golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 486 + golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 487 + golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 303 488 golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 489 + golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 490 + golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 304 491 golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= 305 492 golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 493 + golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 306 494 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 495 + golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 307 496 golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= 308 497 golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= 309 498 golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= 310 499 golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 500 + golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 501 + golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 502 + golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 503 + golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 504 + golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 505 + golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 506 + golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 507 + golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 508 + golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 509 + golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 510 + golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= 511 + golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= 512 + golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 513 + golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 311 514 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 515 + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 312 516 golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= 313 517 golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= 314 518 google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 h1:M0KvPgPmDZHPlbRbaNU1APr28TvwvvdUPlSv7PUvy8g= ··· 321 525 google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= 322 526 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 323 527 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 528 + gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 324 529 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 325 530 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 531 + gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 532 + gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= 533 + gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 326 534 gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 535 + gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 327 536 gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 328 537 gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 329 538 gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 330 539 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 540 + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 331 541 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 332 542 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 543 + gorm.io/driver/postgres v1.5.7 h1:8ptbNJTDbEmhdr62uReG5BGkdQyeasu/FZHxI0IMGnM= 544 + gorm.io/driver/postgres v1.5.7/go.mod h1:3e019WlBaYI5o5LIdNV+LyxCMNtLOQETBXL2h4chKpA= 545 + gorm.io/driver/sqlite v1.5.5 h1:7MDMtUZhV065SilG62E0MquljeArQZNfJnjd9i9gx3E= 546 + gorm.io/driver/sqlite v1.5.5/go.mod h1:6NgQ7sQWAIFsPrJJl1lSNSu2TABh0ZZ/zm5fosATavE= 547 + gorm.io/gorm v1.25.9 h1:wct0gxZIELDk8+ZqF/MVnHLkA1rvYlBWUMv2EdsK1g8= 548 + gorm.io/gorm v1.25.9/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= 549 + honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 333 550 lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI= 334 551 lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
+22
pkg/hold/config.go
··· 3 3 import ( 4 4 "fmt" 5 5 "os" 6 + "path/filepath" 6 7 "time" 7 8 8 9 "github.com/distribution/distribution/v3/configuration" ··· 14 15 Storage StorageConfig `yaml:"storage"` 15 16 Server ServerConfig `yaml:"server"` 16 17 Registration RegistrationConfig `yaml:"registration"` 18 + Database DatabaseConfig `yaml:"database"` 17 19 } 18 20 19 21 // RegistrationConfig defines auto-registration settings ··· 57 59 WriteTimeout time.Duration `yaml:"write_timeout"` 58 60 } 59 61 62 + // DatabaseConfig defines embedded PDS database settings 63 + type DatabaseConfig struct { 64 + // Path is the directory path for carstore (from env: HOLD_DATABASE_DIR) 65 + // If empty, embedded PDS is disabled 66 + Path string `yaml:"path"` 67 + 68 + // KeyPath is the path to the signing key (from env: HOLD_KEY_PATH) 69 + // Defaults to {Path}/signing.key 70 + KeyPath string `yaml:"key_path"` 71 + } 72 + 60 73 // LoadConfigFromEnv loads all configuration from environment variables 61 74 func LoadConfigFromEnv() (*Config, error) { 62 75 cfg := &Config{ ··· 78 91 // Registration configuration (optional) 79 92 cfg.Registration.OwnerDID = os.Getenv("HOLD_OWNER") 80 93 cfg.Registration.AllowAllCrew = os.Getenv("HOLD_ALLOW_ALL_CREW") == "true" 94 + 95 + // Database configuration (optional - enables embedded PDS) 96 + // Note: HOLD_DATABASE_DIR is a directory path, carstore creates db.sqlite3 inside it 97 + cfg.Database.Path = getEnvOrDefault("HOLD_DATABASE_DIR", "/var/lib/atcr-hold") 98 + cfg.Database.KeyPath = os.Getenv("HOLD_KEY_PATH") 99 + if cfg.Database.KeyPath == "" && cfg.Database.Path != "" { 100 + // Default: signing key in same directory as carstore 101 + cfg.Database.KeyPath = filepath.Join(cfg.Database.Path, "signing.key") 102 + } 81 103 82 104 // Storage configuration - build from env vars based on storage type 83 105 storageType := getEnvOrDefault("STORAGE_DRIVER", "s3")
+44
pkg/hold/pds/blobstore_adapter.go
··· 1 + package pds 2 + 3 + import ( 4 + "context" 5 + 6 + "atcr.io/pkg/hold" 7 + ) 8 + 9 + // HoldServiceBlobStore adapts the hold service to implement the BlobStore interface 10 + type HoldServiceBlobStore struct { 11 + service *hold.HoldService 12 + holdDID string 13 + } 14 + 15 + // NewHoldServiceBlobStore creates a blob store adapter for the hold service 16 + func NewHoldServiceBlobStore(service *hold.HoldService, holdDID string) *HoldServiceBlobStore { 17 + return &HoldServiceBlobStore{ 18 + service: service, 19 + holdDID: holdDID, 20 + } 21 + } 22 + 23 + // GetPresignedDownloadURL returns a presigned URL for downloading a blob 24 + func (b *HoldServiceBlobStore) GetPresignedDownloadURL(digest string) (string, error) { 25 + // Use the hold service's existing presigned URL logic 26 + // We need to expose a wrapper method on HoldService 27 + ctx := context.Background() 28 + url, err := b.service.GetPresignedURL(ctx, hold.OperationGet, digest, b.holdDID) 29 + if err != nil { 30 + return "", err 31 + } 32 + return url, nil 33 + } 34 + 35 + // GetPresignedUploadURL returns a presigned URL for uploading a blob 36 + func (b *HoldServiceBlobStore) GetPresignedUploadURL(digest string) (string, error) { 37 + // Use the hold service's existing presigned URL logic 38 + ctx := context.Background() 39 + url, err := b.service.GetPresignedURL(ctx, hold.OperationPut, digest, b.holdDID) 40 + if err != nil { 41 + return "", err 42 + } 43 + return url, nil 44 + }
+113
pkg/hold/pds/crew.go
··· 1 + package pds 2 + 3 + import ( 4 + "context" 5 + "fmt" 6 + "io" 7 + "time" 8 + 9 + "github.com/ipfs/go-cid" 10 + ) 11 + 12 + // CrewRecord represents a crew member in the hold 13 + type CrewRecord struct { 14 + Member string `json:"member" cborgen:"member"` // DID of the crew member 15 + Role string `json:"role" cborgen:"role"` // "admin" or "member" 16 + Permissions []string `json:"permissions" cborgen:"permissions"` // e.g., ["blob:read", "blob:write"] 17 + AddedAt time.Time `json:"addedAt" cborgen:"addedAt"` 18 + } 19 + 20 + // MarshalCBOR implements cbg.CBORMarshaler 21 + func (c *CrewRecord) MarshalCBOR(w io.Writer) error { 22 + // TODO: Implement proper CBOR marshaling 23 + return fmt.Errorf("CBOR marshaling not yet implemented") 24 + } 25 + 26 + // UnmarshalCBOR implements cbg.CBORUnmarshaler 27 + func (c *CrewRecord) UnmarshalCBOR(r io.Reader) error { 28 + // TODO: Implement proper CBOR unmarshaling 29 + return fmt.Errorf("CBOR unmarshaling not yet implemented") 30 + } 31 + 32 + const ( 33 + CrewCollection = "io.atcr.hold.crew" 34 + ) 35 + 36 + // AddCrewMember adds a new crew member to the hold 37 + func (p *HoldPDS) AddCrewMember(ctx context.Context, memberDID, role string, permissions []string) (cid.Cid, error) { 38 + crewRecord := &CrewRecord{ 39 + Member: memberDID, 40 + Role: role, 41 + Permissions: permissions, 42 + AddedAt: time.Now(), 43 + } 44 + 45 + // Create record in repo 46 + recordCID, rkey, err := p.repo.CreateRecord(ctx, CrewCollection, crewRecord) 47 + if err != nil { 48 + return cid.Undef, fmt.Errorf("failed to create crew record: %w", err) 49 + } 50 + 51 + // TODO: Commit the changes 52 + // For now, just return the CID 53 + _ = rkey // We'll use rkey for GetCrewMember later 54 + 55 + return recordCID, nil 56 + } 57 + 58 + // GetCrewMember retrieves a crew member by their record key 59 + func (p *HoldPDS) GetCrewMember(ctx context.Context, rkey string) (*CrewRecord, error) { 60 + path := fmt.Sprintf("%s/%s", CrewCollection, rkey) 61 + 62 + _, rec, err := p.repo.GetRecord(ctx, path) 63 + if err != nil { 64 + return nil, fmt.Errorf("failed to get crew record: %w", err) 65 + } 66 + 67 + crewRecord, ok := rec.(*CrewRecord) 68 + if !ok { 69 + return nil, fmt.Errorf("record is not a CrewRecord") 70 + } 71 + 72 + return crewRecord, nil 73 + } 74 + 75 + // ListCrewMembers returns all crew members 76 + func (p *HoldPDS) ListCrewMembers(ctx context.Context) ([]*CrewRecord, error) { 77 + var crew []*CrewRecord 78 + 79 + err := p.repo.ForEach(ctx, CrewCollection, func(k string, v cid.Cid) error { 80 + // Get the full record 81 + path := fmt.Sprintf("%s/%s", CrewCollection, k) 82 + _, rec, err := p.repo.GetRecord(ctx, path) 83 + if err != nil { 84 + return err 85 + } 86 + 87 + if crewRecord, ok := rec.(*CrewRecord); ok { 88 + crew = append(crew, crewRecord) 89 + } 90 + 91 + return nil 92 + }) 93 + 94 + if err != nil { 95 + return nil, fmt.Errorf("failed to list crew members: %w", err) 96 + } 97 + 98 + return crew, nil 99 + } 100 + 101 + // RemoveCrewMember removes a crew member 102 + func (p *HoldPDS) RemoveCrewMember(ctx context.Context, rkey string) error { 103 + path := fmt.Sprintf("%s/%s", CrewCollection, rkey) 104 + 105 + err := p.repo.DeleteRecord(ctx, path) 106 + if err != nil { 107 + return fmt.Errorf("failed to delete crew record: %w", err) 108 + } 109 + 110 + // TODO: Commit the changes 111 + 112 + return nil 113 + }
+150
pkg/hold/pds/did.go
··· 1 + package pds 2 + 3 + import ( 4 + "crypto/ecdsa" 5 + "crypto/elliptic" 6 + "encoding/base64" 7 + "encoding/json" 8 + "fmt" 9 + "net/url" 10 + "strings" 11 + ) 12 + 13 + // DIDDocument represents a did:web document 14 + type DIDDocument struct { 15 + Context []string `json:"@context"` 16 + ID string `json:"id"` 17 + AlsoKnownAs []string `json:"alsoKnownAs,omitempty"` 18 + VerificationMethod []VerificationMethod `json:"verificationMethod"` 19 + Authentication []string `json:"authentication,omitempty"` 20 + AssertionMethod []string `json:"assertionMethod,omitempty"` 21 + Service []Service `json:"service,omitempty"` 22 + } 23 + 24 + // VerificationMethod represents a public key in a DID document 25 + type VerificationMethod struct { 26 + ID string `json:"id"` 27 + Type string `json:"type"` 28 + Controller string `json:"controller"` 29 + PublicKeyMultibase string `json:"publicKeyMultibase"` 30 + } 31 + 32 + // Service represents a service endpoint in a DID document 33 + type Service struct { 34 + ID string `json:"id"` 35 + Type string `json:"type"` 36 + ServiceEndpoint string `json:"serviceEndpoint"` 37 + } 38 + 39 + // GenerateDIDDocument creates a DID document for a did:web identity 40 + func (p *HoldPDS) GenerateDIDDocument(publicURL string) (*DIDDocument, error) { 41 + // Extract hostname from public URL 42 + hostname := strings.TrimPrefix(publicURL, "http://") 43 + hostname = strings.TrimPrefix(hostname, "https://") 44 + hostname = strings.Split(hostname, "/")[0] // Remove any path 45 + hostname = strings.Split(hostname, ":")[0] // Remove port for DID 46 + 47 + did := fmt.Sprintf("did:web:%s", hostname) 48 + 49 + // Convert public key to multibase format 50 + publicKeyMultibase, err := encodePublicKeyMultibase(p.signingKey.PublicKey) 51 + if err != nil { 52 + return nil, fmt.Errorf("failed to encode public key: %w", err) 53 + } 54 + 55 + doc := &DIDDocument{ 56 + Context: []string{ 57 + "https://www.w3.org/ns/did/v1", 58 + "https://w3id.org/security/multikey/v1", 59 + }, 60 + ID: did, 61 + VerificationMethod: []VerificationMethod{ 62 + { 63 + ID: fmt.Sprintf("%s#atproto", did), 64 + Type: "Multikey", 65 + Controller: did, 66 + PublicKeyMultibase: publicKeyMultibase, 67 + }, 68 + }, 69 + Authentication: []string{ 70 + fmt.Sprintf("%s#atproto", did), 71 + }, 72 + Service: []Service{ 73 + { 74 + ID: "#atproto_pds", 75 + Type: "AtprotoPersonalDataServer", 76 + ServiceEndpoint: publicURL, 77 + }, 78 + }, 79 + } 80 + 81 + return doc, nil 82 + } 83 + 84 + // encodePublicKeyMultibase encodes an ECDSA public key in multibase format 85 + // For P-256 keys, we use the compressed format with multicodec prefix 86 + func encodePublicKeyMultibase(pubKey ecdsa.PublicKey) (string, error) { 87 + // Check if this is a P-256 key 88 + if pubKey.Curve != elliptic.P256() { 89 + return "", fmt.Errorf("unsupported curve: only P-256 is supported") 90 + } 91 + 92 + // Use compressed point format 93 + // 0x02 if Y is even, 0x03 if Y is odd 94 + var prefix byte 95 + if pubKey.Y.Bit(0) == 0 { 96 + prefix = 0x02 97 + } else { 98 + prefix = 0x03 99 + } 100 + 101 + // Compressed format: prefix (1 byte) + X coordinate (32 bytes for P-256) 102 + xBytes := pubKey.X.Bytes() 103 + 104 + // Pad X to 32 bytes if needed 105 + paddedX := make([]byte, 32) 106 + copy(paddedX[32-len(xBytes):], xBytes) 107 + 108 + compressed := append([]byte{prefix}, paddedX...) 109 + 110 + // Multicodec prefix for P-256 public key: 0x1200 111 + // See https://github.com/multiformats/multicodec/blob/master/table.csv 112 + multicodec := []byte{0x80, 0x24} // P-256 public key multicodec 113 + 114 + // Combine multicodec + compressed key 115 + multicodecKey := append(multicodec, compressed...) 116 + 117 + // Encode in multibase (base58btc, prefix 'z') 118 + encoded := base64.RawURLEncoding.EncodeToString(multicodecKey) 119 + 120 + return "z" + encoded, nil 121 + } 122 + 123 + // MarshalDIDDocument converts a DID document to JSON using the stored public URL 124 + func (p *HoldPDS) MarshalDIDDocument() ([]byte, error) { 125 + doc, err := p.GenerateDIDDocument(p.publicURL) 126 + if err != nil { 127 + return nil, err 128 + } 129 + 130 + return json.MarshalIndent(doc, "", " ") 131 + } 132 + 133 + // GenerateDIDFromURL creates a did:web identifier from a public URL 134 + // Example: "http://hold1.example.com:8080" -> "did:web:hold1.example.com" 135 + func GenerateDIDFromURL(publicURL string) string { 136 + // Parse URL 137 + u, err := url.Parse(publicURL) 138 + if err != nil { 139 + // Fallback: assume it's just a hostname 140 + return fmt.Sprintf("did:web:%s", strings.Split(publicURL, ":")[0]) 141 + } 142 + 143 + // Use hostname without port for DID 144 + hostname := u.Hostname() 145 + if hostname == "" { 146 + hostname = "localhost" 147 + } 148 + 149 + return fmt.Sprintf("did:web:%s", hostname) 150 + }
+102
pkg/hold/pds/keys.go
··· 1 + package pds 2 + 3 + import ( 4 + "crypto/ecdsa" 5 + "crypto/elliptic" 6 + "crypto/rand" 7 + "crypto/x509" 8 + "encoding/pem" 9 + "fmt" 10 + "os" 11 + "path/filepath" 12 + ) 13 + 14 + // GenerateOrLoadKey generates a new K256 key pair or loads an existing one 15 + func GenerateOrLoadKey(keyPath string) (*ecdsa.PrivateKey, error) { 16 + // Ensure directory exists 17 + dir := filepath.Dir(keyPath) 18 + if err := os.MkdirAll(dir, 0700); err != nil { 19 + return nil, fmt.Errorf("failed to create key directory: %w", err) 20 + } 21 + 22 + // Check if key already exists 23 + if _, err := os.Stat(keyPath); err == nil { 24 + // Key exists, load it 25 + return loadKey(keyPath) 26 + } 27 + 28 + // Key doesn't exist, generate new one 29 + return generateKey(keyPath) 30 + } 31 + 32 + // generateKey creates a new K256 (secp256k1) key pair 33 + func generateKey(keyPath string) (*ecdsa.PrivateKey, error) { 34 + // Generate K256 key (secp256k1) 35 + privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) 36 + if err != nil { 37 + return nil, fmt.Errorf("failed to generate key: %w", err) 38 + } 39 + 40 + // Marshal private key to DER format 41 + derBytes, err := x509.MarshalECPrivateKey(privateKey) 42 + if err != nil { 43 + return nil, fmt.Errorf("failed to marshal private key: %w", err) 44 + } 45 + 46 + // Create PEM block 47 + pemBlock := &pem.Block{ 48 + Type: "EC PRIVATE KEY", 49 + Bytes: derBytes, 50 + } 51 + 52 + // Write to file with restrictive permissions 53 + file, err := os.OpenFile(keyPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) 54 + if err != nil { 55 + return nil, fmt.Errorf("failed to create key file: %w", err) 56 + } 57 + defer file.Close() 58 + 59 + if err := pem.Encode(file, pemBlock); err != nil { 60 + return nil, fmt.Errorf("failed to write PEM data: %w", err) 61 + } 62 + 63 + fmt.Printf("Generated new signing key at %s\n", keyPath) 64 + return privateKey, nil 65 + } 66 + 67 + // loadKey loads an existing private key from disk 68 + func loadKey(keyPath string) (*ecdsa.PrivateKey, error) { 69 + // Read PEM file 70 + pemData, err := os.ReadFile(keyPath) 71 + if err != nil { 72 + return nil, fmt.Errorf("failed to read key file: %w", err) 73 + } 74 + 75 + // Decode PEM block 76 + block, _ := pem.Decode(pemData) 77 + if block == nil { 78 + return nil, fmt.Errorf("failed to decode PEM block") 79 + } 80 + 81 + // Parse EC private key 82 + privateKey, err := x509.ParseECPrivateKey(block.Bytes) 83 + if err != nil { 84 + return nil, fmt.Errorf("failed to parse private key: %w", err) 85 + } 86 + 87 + fmt.Printf("Loaded existing signing key from %s\n", keyPath) 88 + return privateKey, nil 89 + } 90 + 91 + // PublicKeyToBase58 converts an ECDSA public key to base58 format for DID documents 92 + func PublicKeyToBase58(pubKey *ecdsa.PublicKey) (string, error) { 93 + // Marshal public key to X.509 SPKI format 94 + derBytes, err := x509.MarshalPKIXPublicKey(pubKey) 95 + if err != nil { 96 + return "", fmt.Errorf("failed to marshal public key: %w", err) 97 + } 98 + 99 + // TODO: Convert to base58 (need to import base58 library) 100 + // For now, just return hex encoding as placeholder 101 + return fmt.Sprintf("%x", derBytes), nil 102 + }
+105
pkg/hold/pds/server.go
··· 1 + package pds 2 + 3 + import ( 4 + "context" 5 + "crypto/ecdsa" 6 + "fmt" 7 + "os" 8 + "path/filepath" 9 + 10 + "github.com/bluesky-social/indigo/carstore" 11 + "github.com/bluesky-social/indigo/models" 12 + "github.com/bluesky-social/indigo/repo" 13 + ) 14 + 15 + // HoldPDS is a minimal ATProto PDS implementation for a hold service 16 + type HoldPDS struct { 17 + did string 18 + publicURL string 19 + carstore carstore.CarStore 20 + session *carstore.DeltaSession 21 + repo *repo.Repo 22 + dbPath string 23 + uid models.Uid 24 + signingKey *ecdsa.PrivateKey 25 + } 26 + 27 + // NewHoldPDS creates or opens a hold PDS with SQLite carstore 28 + func NewHoldPDS(ctx context.Context, did, publicURL, dbPath, keyPath string) (*HoldPDS, error) { 29 + // Ensure directory exists 30 + dir := filepath.Dir(dbPath) 31 + if err := os.MkdirAll(dir, 0755); err != nil { 32 + return nil, fmt.Errorf("failed to create database directory: %w", err) 33 + } 34 + 35 + // Generate or load signing key 36 + signingKey, err := GenerateOrLoadKey(keyPath) 37 + if err != nil { 38 + return nil, fmt.Errorf("failed to initialize signing key: %w", err) 39 + } 40 + 41 + // Create and open SQLite-backed carstore 42 + // dbPath is the directory, carstore creates and opens db.sqlite3 inside it 43 + sqlStore, err := carstore.NewSqliteStore(dbPath) 44 + if err != nil { 45 + return nil, fmt.Errorf("failed to create sqlite store: %w", err) 46 + } 47 + 48 + cs := sqlStore.CarStore() 49 + 50 + // For a single-user hold, we use a fixed UID (1) 51 + uid := models.Uid(1) 52 + 53 + // Try to get existing repo head 54 + _, err = cs.GetUserRepoHead(ctx, uid) 55 + 56 + var session *carstore.DeltaSession 57 + var r *repo.Repo 58 + 59 + if err != nil { 60 + // Repo doesn't exist yet, create new delta session 61 + session, err = cs.NewDeltaSession(ctx, uid, nil) 62 + if err != nil { 63 + return nil, fmt.Errorf("failed to create delta session: %w", err) 64 + } 65 + 66 + // Create new repo with session as blockstore (needs pointer) 67 + r = repo.NewRepo(ctx, did, session) 68 + } else { 69 + // TODO: Load existing repo 70 + // For now, just create a new session 71 + session, err = cs.NewDeltaSession(ctx, uid, nil) 72 + if err != nil { 73 + return nil, fmt.Errorf("failed to create delta session: %w", err) 74 + } 75 + 76 + r = repo.NewRepo(ctx, did, session) 77 + } 78 + 79 + return &HoldPDS{ 80 + did: did, 81 + publicURL: publicURL, 82 + carstore: cs, 83 + session: session, 84 + repo: r, 85 + dbPath: dbPath, 86 + uid: uid, 87 + signingKey: signingKey, 88 + }, nil 89 + } 90 + 91 + // DID returns the hold's DID 92 + func (p *HoldPDS) DID() string { 93 + return p.did 94 + } 95 + 96 + // SigningKey returns the hold's signing key 97 + func (p *HoldPDS) SigningKey() *ecdsa.PrivateKey { 98 + return p.signingKey 99 + } 100 + 101 + // Close closes the session and carstore 102 + func (p *HoldPDS) Close() error { 103 + // TODO: Close session properly 104 + return nil 105 + }
+273
pkg/hold/pds/xrpc.go
··· 1 + package pds 2 + 3 + import ( 4 + "encoding/json" 5 + "fmt" 6 + "net/http" 7 + ) 8 + 9 + // XRPC handler for ATProto endpoints 10 + 11 + // XRPCHandler handles XRPC requests for the embedded PDS 12 + type XRPCHandler struct { 13 + pds *HoldPDS 14 + publicURL string 15 + blobStore BlobStore 16 + } 17 + 18 + // BlobStore interface wraps the existing hold service storage operations 19 + type BlobStore interface { 20 + // GetPresignedDownloadURL returns a presigned URL for downloading a blob 21 + GetPresignedDownloadURL(digest string) (string, error) 22 + // GetPresignedUploadURL returns a presigned URL for uploading a blob 23 + GetPresignedUploadURL(digest string) (string, error) 24 + } 25 + 26 + // NewXRPCHandler creates a new XRPC handler 27 + func NewXRPCHandler(pds *HoldPDS, publicURL string, blobStore BlobStore) *XRPCHandler { 28 + return &XRPCHandler{ 29 + pds: pds, 30 + publicURL: publicURL, 31 + blobStore: blobStore, 32 + } 33 + } 34 + 35 + // RegisterHandlers registers all XRPC endpoints 36 + func (h *XRPCHandler) RegisterHandlers(mux *http.ServeMux) { 37 + // Standard PDS endpoints 38 + mux.HandleFunc("/xrpc/com.atproto.server.describeServer", h.HandleDescribeServer) 39 + mux.HandleFunc("/xrpc/com.atproto.repo.describeRepo", h.HandleDescribeRepo) 40 + mux.HandleFunc("/xrpc/com.atproto.repo.getRecord", h.HandleGetRecord) 41 + mux.HandleFunc("/xrpc/com.atproto.repo.listRecords", h.HandleListRecords) 42 + 43 + // Blob endpoints (wrap existing presigned URL logic) 44 + mux.HandleFunc("/xrpc/com.atproto.repo.uploadBlob", h.HandleUploadBlob) 45 + mux.HandleFunc("/xrpc/com.atproto.sync.getBlob", h.HandleGetBlob) 46 + 47 + // DID document 48 + mux.HandleFunc("/.well-known/did.json", h.HandleDIDDocument) 49 + } 50 + 51 + // HandleDescribeServer returns server metadata 52 + func (h *XRPCHandler) HandleDescribeServer(w http.ResponseWriter, r *http.Request) { 53 + if r.Method != http.MethodGet { 54 + http.Error(w, "method not allowed", http.StatusMethodNotAllowed) 55 + return 56 + } 57 + 58 + response := map[string]interface{}{ 59 + "did": h.pds.DID(), 60 + "availableUserDomains": []string{}, 61 + "inviteCodeRequired": false, 62 + "links": map[string]string{}, 63 + } 64 + 65 + w.Header().Set("Content-Type", "application/json") 66 + json.NewEncoder(w).Encode(response) 67 + } 68 + 69 + // HandleDescribeRepo returns repository information 70 + func (h *XRPCHandler) HandleDescribeRepo(w http.ResponseWriter, r *http.Request) { 71 + if r.Method != http.MethodGet { 72 + http.Error(w, "method not allowed", http.StatusMethodNotAllowed) 73 + return 74 + } 75 + 76 + // Get repo parameter 77 + repo := r.URL.Query().Get("repo") 78 + if repo == "" || repo != h.pds.DID() { 79 + http.Error(w, "invalid repo", http.StatusBadRequest) 80 + return 81 + } 82 + 83 + // Generate DID document 84 + didDoc, err := h.pds.GenerateDIDDocument(h.publicURL) 85 + if err != nil { 86 + http.Error(w, fmt.Sprintf("failed to generate DID document: %v", err), http.StatusInternalServerError) 87 + return 88 + } 89 + 90 + // Extract handle from did:web (remove "did:web:" prefix) 91 + handle := h.pds.DID() 92 + if len(handle) > 8 && handle[:8] == "did:web:" { 93 + handle = handle[8:] // "did:web:example.com" -> "example.com" 94 + } 95 + 96 + // TODO: Get actual repo head from carstore 97 + response := map[string]interface{}{ 98 + "did": h.pds.DID(), 99 + "handle": handle, 100 + "didDoc": didDoc, 101 + "collections": []string{CrewCollection}, 102 + "handleIsCorrect": true, 103 + } 104 + 105 + w.Header().Set("Content-Type", "application/json") 106 + json.NewEncoder(w).Encode(response) 107 + } 108 + 109 + // HandleGetRecord retrieves a record from the repository 110 + func (h *XRPCHandler) HandleGetRecord(w http.ResponseWriter, r *http.Request) { 111 + if r.Method != http.MethodGet { 112 + http.Error(w, "method not allowed", http.StatusMethodNotAllowed) 113 + return 114 + } 115 + 116 + repo := r.URL.Query().Get("repo") 117 + collection := r.URL.Query().Get("collection") 118 + rkey := r.URL.Query().Get("rkey") 119 + 120 + if repo == "" || collection == "" || rkey == "" { 121 + http.Error(w, "missing required parameters", http.StatusBadRequest) 122 + return 123 + } 124 + 125 + if repo != h.pds.DID() { 126 + http.Error(w, "invalid repo", http.StatusBadRequest) 127 + return 128 + } 129 + 130 + // Only support crew collection for now 131 + if collection != CrewCollection { 132 + http.Error(w, "collection not found", http.StatusNotFound) 133 + return 134 + } 135 + 136 + crewRecord, err := h.pds.GetCrewMember(r.Context(), rkey) 137 + if err != nil { 138 + http.Error(w, fmt.Sprintf("failed to get record: %v", err), http.StatusNotFound) 139 + return 140 + } 141 + 142 + response := map[string]interface{}{ 143 + "uri": fmt.Sprintf("at://%s/%s/%s", h.pds.DID(), collection, rkey), 144 + "value": crewRecord, 145 + } 146 + 147 + w.Header().Set("Content-Type", "application/json") 148 + json.NewEncoder(w).Encode(response) 149 + } 150 + 151 + // HandleListRecords lists records in a collection 152 + func (h *XRPCHandler) HandleListRecords(w http.ResponseWriter, r *http.Request) { 153 + if r.Method != http.MethodGet { 154 + http.Error(w, "method not allowed", http.StatusMethodNotAllowed) 155 + return 156 + } 157 + 158 + repo := r.URL.Query().Get("repo") 159 + collection := r.URL.Query().Get("collection") 160 + 161 + if repo == "" || collection == "" { 162 + http.Error(w, "missing required parameters", http.StatusBadRequest) 163 + return 164 + } 165 + 166 + if repo != h.pds.DID() { 167 + http.Error(w, "invalid repo", http.StatusBadRequest) 168 + return 169 + } 170 + 171 + // Only support crew collection for now 172 + if collection != CrewCollection { 173 + http.Error(w, "collection not found", http.StatusNotFound) 174 + return 175 + } 176 + 177 + crew, err := h.pds.ListCrewMembers(r.Context()) 178 + if err != nil { 179 + http.Error(w, fmt.Sprintf("failed to list records: %v", err), http.StatusInternalServerError) 180 + return 181 + } 182 + 183 + records := make([]map[string]interface{}, len(crew)) 184 + for i, member := range crew { 185 + // TODO: Get actual rkey from somewhere 186 + records[i] = map[string]interface{}{ 187 + "uri": fmt.Sprintf("at://%s/%s/%s", h.pds.DID(), collection, member.Member), 188 + "value": member, 189 + } 190 + } 191 + 192 + response := map[string]interface{}{ 193 + "records": records, 194 + } 195 + 196 + w.Header().Set("Content-Type", "application/json") 197 + json.NewEncoder(w).Encode(response) 198 + } 199 + 200 + // HandleUploadBlob wraps existing presigned upload URL logic 201 + func (h *XRPCHandler) HandleUploadBlob(w http.ResponseWriter, r *http.Request) { 202 + if r.Method != http.MethodPost { 203 + http.Error(w, "method not allowed", http.StatusMethodNotAllowed) 204 + return 205 + } 206 + 207 + // TODO: Authentication check 208 + 209 + // Read digest from query or calculate from body 210 + digest := r.URL.Query().Get("digest") 211 + if digest == "" { 212 + http.Error(w, "digest required", http.StatusBadRequest) 213 + return 214 + } 215 + 216 + // Get presigned upload URL from existing blob store 217 + uploadURL, err := h.blobStore.GetPresignedUploadURL(digest) 218 + if err != nil { 219 + http.Error(w, fmt.Sprintf("failed to get upload URL: %v", err), http.StatusInternalServerError) 220 + return 221 + } 222 + 223 + // Return 302 redirect to presigned URL 224 + http.Redirect(w, r, uploadURL, http.StatusFound) 225 + } 226 + 227 + // HandleGetBlob wraps existing presigned download URL logic 228 + func (h *XRPCHandler) HandleGetBlob(w http.ResponseWriter, r *http.Request) { 229 + if r.Method != http.MethodGet { 230 + http.Error(w, "method not allowed", http.StatusMethodNotAllowed) 231 + return 232 + } 233 + 234 + did := r.URL.Query().Get("did") 235 + digest := r.URL.Query().Get("cid") 236 + 237 + if did == "" || digest == "" { 238 + http.Error(w, "missing required parameters", http.StatusBadRequest) 239 + return 240 + } 241 + 242 + if did != h.pds.DID() { 243 + http.Error(w, "invalid did", http.StatusBadRequest) 244 + return 245 + } 246 + 247 + // Get presigned download URL from existing blob store 248 + downloadURL, err := h.blobStore.GetPresignedDownloadURL(digest) 249 + if err != nil { 250 + http.Error(w, fmt.Sprintf("failed to get download URL: %v", err), http.StatusInternalServerError) 251 + return 252 + } 253 + 254 + // Return 302 redirect to presigned URL 255 + http.Redirect(w, r, downloadURL, http.StatusFound) 256 + } 257 + 258 + // HandleDIDDocument returns the DID document 259 + func (h *XRPCHandler) HandleDIDDocument(w http.ResponseWriter, r *http.Request) { 260 + if r.Method != http.MethodGet { 261 + http.Error(w, "method not allowed", http.StatusMethodNotAllowed) 262 + return 263 + } 264 + 265 + doc, err := h.pds.GenerateDIDDocument(h.publicURL) 266 + if err != nil { 267 + http.Error(w, fmt.Sprintf("failed to generate DID document: %v", err), http.StatusInternalServerError) 268 + return 269 + } 270 + 271 + w.Header().Set("Content-Type", "application/did+json") 272 + json.NewEncoder(w).Encode(doc) 273 + }
+5
pkg/hold/service.go
··· 42 42 43 43 return service, nil 44 44 } 45 + 46 + // GetPresignedURL is a public wrapper around getPresignedURL for use by PDS blob store 47 + func (s *HoldService) GetPresignedURL(ctx context.Context, operation PresignedURLOperation, digest string, did string) (string, error) { 48 + return s.getPresignedURL(ctx, operation, digest, did) 49 + }