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.
69 lines
2.8 KiB
Docker
69 lines
2.8 KiB
Docker
# anchorage runtime image (Alpine).
|
|
#
|
|
# Designed to be invoked by GoReleaser's `dockers:` block — GoReleaser
|
|
# builds the static binary under dist/ and sets that as the build
|
|
# context, so this file only has to install runtime dependencies and
|
|
# COPY the binary into a minimal Alpine base.
|
|
#
|
|
# For a standalone `docker build` (not via GoReleaser) the binary must
|
|
# already exist at ./anchorage in the build context; see Makefile's
|
|
# `docker-local` target for the local-build wrapper.
|
|
#
|
|
# Why Alpine and not distroless:
|
|
# - busybox shell is available for `docker exec -it … sh` triage.
|
|
# - apk add for ad-hoc debug tools (curl, busybox-extras, strace) at
|
|
# runtime without rebuilding.
|
|
# - matches the uid/gid conventions the .deb/.rpm packages use, so an
|
|
# operator who flips between compose and systemd deploys sees the
|
|
# same `anchorage` user owning the files.
|
|
#
|
|
# Size is ~15 MB final image (5 MB Alpine + 3 MB CA/tz + ~7 MB static
|
|
# anchorage binary). Small enough; debuggable when we need it.
|
|
|
|
FROM alpine:3.20
|
|
|
|
# Pin to a specific uid/gid so volume permissions stay stable across
|
|
# image rebuilds and match the nfpm-generated /etc/passwd on the host.
|
|
ARG ANCHORAGE_UID=8080
|
|
ARG ANCHORAGE_GID=8080
|
|
|
|
RUN apk add --no-cache \
|
|
ca-certificates \
|
|
tzdata \
|
|
tini \
|
|
&& addgroup -S -g ${ANCHORAGE_GID} anchorage \
|
|
&& adduser -S -D -H -u ${ANCHORAGE_UID} -G anchorage -s /sbin/nologin anchorage \
|
|
&& mkdir -p /etc/anchorage /var/lib/anchorage /var/log/anchorage \
|
|
&& chown -R anchorage:anchorage /etc/anchorage /var/lib/anchorage /var/log/anchorage \
|
|
&& chmod 0750 /var/lib/anchorage /var/log/anchorage
|
|
|
|
COPY anchorage /usr/local/bin/anchorage
|
|
RUN chmod 0755 /usr/local/bin/anchorage
|
|
|
|
# OCI default ports:
|
|
# 8080 — HTTP + WebSocket
|
|
# 4222 — NATS client
|
|
# 6222 — NATS cluster gossip (peer ↔ peer)
|
|
EXPOSE 8080 4222 6222
|
|
|
|
# State lives on a volume so the stable node.id and NATS JetStream
|
|
# files survive container restarts. Compose / k8s mounts a named volume
|
|
# here; the nfpm-packaged install uses the same /var/lib/anchorage path
|
|
# so muscle memory transfers.
|
|
VOLUME ["/var/lib/anchorage"]
|
|
|
|
# Liveness only — /v1/ready returning 503 during drain would flap the
|
|
# container otherwise. The orchestrator (compose / Swarm / k8s) layers
|
|
# its own readiness probe on /v1/ready separately.
|
|
HEALTHCHECK --interval=10s --timeout=3s --start-period=15s --retries=3 \
|
|
CMD wget --quiet --spider http://127.0.0.1:8080/v1/health || exit 1
|
|
|
|
USER anchorage:anchorage
|
|
WORKDIR /var/lib/anchorage
|
|
|
|
# tini reaps zombies and forwards SIGTERM so the Go runtime's graceful
|
|
# shutdown actually runs (the Fiber server drains, consumers drain,
|
|
# NATS drains — see internal/app/anchorage/app.go:Close).
|
|
ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/anchorage"]
|
|
CMD ["serve", "--config", "/etc/anchorage/anchorage.yaml"]
|