Files
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

123 lines
4.0 KiB
SQL

-- name: CreatePin :one
INSERT INTO pins (request_id, org_id, cid, name, meta, origins, status)
VALUES ($1, $2, $3, $4, $5, $6, 'queued')
RETURNING *;
-- name: GetPin :one
SELECT * FROM pins WHERE request_id = $1 AND org_id = $2;
-- name: GetExistingLivePinByCID :one
SELECT * FROM pins
WHERE org_id = $1 AND cid = $2 AND status <> 'failed'
LIMIT 1;
-- name: UpdatePinStatus :exec
UPDATE pins SET status = $2, failure_reason = $3 WHERE request_id = $1;
-- name: DeletePin :exec
DELETE FROM pins WHERE request_id = $1 AND org_id = $2;
-- name: ListPins :many
SELECT * FROM pins
WHERE org_id = $1
ORDER BY created DESC
LIMIT $2 OFFSET $3;
-- FilterPins honors the full IPFS Pinning API spec filter surface.
--
-- All filters are optional; empty arrays / NULL values short-circuit to
-- "no restriction" via the IS NULL / array-length guards. Name matching
-- supports four modes through a single `match` discriminator:
--
-- 'exact' — LIKE (case-sensitive, no wildcards)
-- 'iexact' — ILIKE anchored both ends
-- 'partial' — LIKE %name%
-- 'ipartial' — ILIKE %name% (uses pg_trgm GIN)
--
-- `meta` is treated as a jsonb "contains" check — the pin's meta must
-- be a superset of the requested map.
--
-- name: FilterPins :many
SELECT *
FROM pins
WHERE org_id = $1
AND (COALESCE(array_length(sqlc.arg(cids)::text[], 1), 0) = 0
OR cid = ANY(sqlc.arg(cids)::text[]))
AND (COALESCE(array_length(sqlc.arg(statuses)::text[], 1), 0) = 0
OR status = ANY(sqlc.arg(statuses)::text[]))
AND (sqlc.narg(name)::text IS NULL
OR (sqlc.arg(match_mode)::text = 'exact' AND name = sqlc.narg(name)::text)
OR (sqlc.arg(match_mode)::text = 'iexact' AND lower(name) = lower(sqlc.narg(name)::text))
OR (sqlc.arg(match_mode)::text = 'partial' AND name LIKE '%' || sqlc.narg(name)::text || '%')
OR (sqlc.arg(match_mode)::text = 'ipartial' AND name ILIKE '%' || sqlc.narg(name)::text || '%'))
AND (sqlc.narg(before)::timestamptz IS NULL OR created < sqlc.narg(before)::timestamptz)
AND (sqlc.narg(after)::timestamptz IS NULL OR created >= sqlc.narg(after)::timestamptz)
AND (sqlc.arg(meta_filter)::jsonb = '{}'::jsonb
OR meta @> sqlc.arg(meta_filter)::jsonb)
ORDER BY created DESC
LIMIT sqlc.arg(pin_limit)
OFFSET sqlc.arg(pin_offset);
-- ReplacePin swaps a pin's CID atomically. Used by POST /v1/pins/{rid}
-- (pin replace per spec). Placements + refcount are left to the caller
-- to rearrange in the same transaction.
--
-- name: ReplacePin :one
UPDATE pins
SET cid = $3,
name = $4,
meta = $5,
origins = $6,
status = 'queued',
failure_reason = NULL
WHERE request_id = $1 AND org_id = $2
RETURNING *;
-- name: InsertPlacement :one
INSERT INTO pin_placements (request_id, node_id, status, fence)
VALUES ($1, $2, 'queued', $3)
RETURNING *;
-- name: GetPlacement :one
SELECT * FROM pin_placements WHERE request_id = $1 AND node_id = $2;
-- name: ListPlacementsForPin :many
SELECT pp.*, n.multiaddrs
FROM pin_placements pp JOIN nodes n ON n.id = pp.node_id
WHERE pp.request_id = $1;
-- name: ListPlacementsForNode :many
SELECT * FROM pin_placements
WHERE node_id = $1 AND status = $2
ORDER BY created_at;
-- name: UpdatePlacementStatusFenced :execrows
UPDATE pin_placements
SET status = $3, failure_reason = $4, attempts = attempts + 1
WHERE request_id = $1 AND node_id = $2 AND fence = $5;
-- name: ReplacePlacementFence :one
UPDATE pin_placements
SET node_id = $3, fence = fence + 1, status = 'queued', failure_reason = NULL
WHERE request_id = $1 AND node_id = $2
RETURNING *;
-- name: StuckPlacements :many
SELECT request_id, node_id, fence
FROM pin_placements
WHERE status = 'queued' AND created_at < (now() - make_interval(secs => $1));
-- name: IncRefcount :exec
INSERT INTO pin_refcount (node_id, cid, count)
VALUES ($1, $2, 1)
ON CONFLICT (node_id, cid) DO UPDATE SET count = pin_refcount.count + 1;
-- name: DecRefcount :one
UPDATE pin_refcount
SET count = count - 1
WHERE node_id = $1 AND cid = $2
RETURNING count;
-- name: DeleteRefcount :exec
DELETE FROM pin_refcount WHERE node_id = $1 AND cid = $2 AND count <= 0;