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.
54 lines
1.8 KiB
Go
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
|
|
}
|