Files
anchorage/internal/pkg/ipfs/ipfs.go
William Gill 12bf35caf8 anchorage v1.0 initial tree
Greenfield Go multi-tenant IPFS Pinning Service wire-compatible with the
IPFS Pinning Services API spec. Paired 1:1 with Kubo over localhost RPC,
clustered via embedded NATS JetStream, Postgres source-of-truth with
RLS-enforced tenancy, Fiber + huma v2 for the HTTP surface, Authentik
OIDC for session login with kid-rotated HS256 JWT API tokens.

Feature-complete against the 22-milestone build plan, including the
ship-it v1.0 gap items:

  * admin CLIs: drain/uncordon, maintenance, mint-token, rotate-key,
    prune-denylist, rebalance --dry-run, cache-stats, cluster-presences
  * TTL leader election via NATS KV, fence tokens, JetStream dedup
  * rebalancer (plan/apply split), reconciler, requeue sweeper
  * ristretto caches with NATS-backed cross-node invalidation
    (placements live-nodes + token denylist)
  * maintenance watchdog for stuck cluster-pause flag
  * Prometheus /metrics with CIDR ACL, HTTP/pin/scheduler/cache gauges
  * rate limiting: session (10/min) + anonymous global (120/min)
  * integration tests: rebalance, refcount multi-org, RLS belt
  * goreleaser (tar + deb/rpm/apk + Alpine Docker) targeting Gitea

Stack: Cobra/Viper, Fiber v2 + huma v2, embedded NATS JetStream,
pgx/sqlc/golang-migrate, ristretto, TypeID, prometheus/client_golang,
testcontainers-go.
2026-04-16 18:13:36 -05:00

54 lines
1.8 KiB
Go

// Package ipfs is the abstraction anchorage uses to reach its paired IPFS
// backend (currently Kubo over HTTP RPC; potentially embedded Kubo or
// ipfs-cluster later). Everything above this layer works in terms of the
// Backend interface so swapping implementations needs no touch anywhere
// else.
package ipfs
import (
"context"
"errors"
)
// ErrNotFound is the canonical "CID isn't pinned / doesn't exist" sentinel.
var ErrNotFound = errors.New("ipfs: not found")
// PinStatus reports the backend's view of a single CID.
type PinStatus struct {
CID string
Pinned bool
// Type is the backend-reported pin kind ("recursive" for full DAG
// pins, "direct" for shallow, "indirect" for transitive children).
Type string
}
// Backend is the operation surface anchorage uses against its paired
// IPFS daemon. Implementations must be safe for concurrent use.
type Backend interface {
// Pin instructs the backend to hold the given CID. Idempotent.
// Origins may be passed as peer multiaddrs the backend should try
// first for bitswap (spec: PinAddRequest.origins).
Pin(ctx context.Context, cid string, origins []string) error
// Unpin instructs the backend to drop the pin. Idempotent.
Unpin(ctx context.Context, cid string) error
// Status reports whether the CID is locally pinned and how.
Status(ctx context.Context, cid string) (*PinStatus, error)
// List returns every CID currently pinned locally. Used by the
// reconciler to diff against Postgres.
List(ctx context.Context) ([]string, error)
// ID returns the backend's libp2p peer info, notably its multiaddrs.
// anchorage uses this to populate the nodes.multiaddrs column if
// the operator didn't pre-fill it in config.
ID(ctx context.Context) (*IDInfo, error)
}
// IDInfo is the minimal view of /api/v0/id anchorage needs.
type IDInfo struct {
PeerID string
Addresses []string
}