reuse-surface/docs/deploy/reuse-kubernetes.md
tegwick f9d957a221
Some checks failed
CI Smoke / host-smoke (push) Successful in 1s
CI Smoke / container-smoke (push) Successful in 2s
ci / validate-registry (push) Has been cancelled
Build and Publish Container Image / build-and-push (push) Successful in 1m4s
REUSE-WP-0019-T06: hub freshness monitoring, docs, close workplan
reuse_surface/stats.py: _hub_summary() now reports composed_at, stale,
age_days, freshness_threshold_days (REUSE_SURFACE_FRESHNESS_DAYS env,
default 7), and a computed stale_warning. New hub_client.hub_federated()
backs it. format_stats_markdown surfaces a STALE marker when triggered.

.forgejo/workflows/ci.yml: new informational (non-failing) hub freshness
check against the live production hub on every push -- prints a
:⚠️: annotation when stale, never fails the build.

docs/RegistryFederation.md: new section tying together the webhook (T02),
scheduled fallback (T03), and freshness visibility (T06) into one
explanation. docs/deploy/reuse-kubernetes.md: updated for the T03 Forgejo
migration and the now-automated image.yaml build; image promotion
checklist updated for the known /health ingress bug (verify via
/v1/repos or /v1/federated instead).

14 new pytest cases, 173 total pass. Live-verified against production:
reuse-surface stats correctly showed composed_at/age_days for the real
federated index. Separately discovered and confirmed (via a live signed
webhook test) that reuse-surface-env moving to ExternalSecret/OpenBao
custody (railiance-apps commit 706f6c7, found while updating these docs)
did not break the T02/T03 webhook -- the synced value still matches what
the hub actually uses.

REUSE-WP-0019 is now fully complete (T01-T06). SCOPE.md and
docs/IntentScopeGapAnalysis.md updated to reflect closure.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-08 00:09:58 +02:00

6.1 KiB

reuse-surface Service — Kubernetes Deployment

Companion to RAILIANCE-WP-0007 (railiance-apps Helm release).

Image

This repo's own canonical remote migrated to Forgejo in REUSE-WP-0019-T03 (originforgejo-remote:coulomb/reuse-surface.git; the old Gitea copy is kept read-only, not deleted). Currently deployed production image is still gitea.coulomb.social/coulomb/reuse-surface:e3ae22e (built manually before the migration, per RAILIANCE-WP-0007) — this doc's manual build commands still target that registry since that's what's actually live. The repo also builds forgejo.coulomb.social/coulomb/reuse-surface:latest automatically on every push via .forgejo/workflows/image.yaml; switching the deployed registry over is a deliberate follow-up, not done as part of T06.

docker build -t gitea.coulomb.social/coulomb/reuse-surface:<tag> .
docker push gitea.coulomb.social/coulomb/reuse-surface:<tag>

Required environment

Variable Purpose
REUSE_SURFACE_TOKEN Bearer token for write API
REUSE_SURFACE_FORGEJO_WEBHOOK_SECRET HMAC for POST /v1/webhooks/forgejo
REUSE_SURFACE_DB SQLite path (default /data/reuse.db)
REUSE_SURFACE_CACHE_DIR Remote index cache (default /data/cache)

Mount a PVC at /data for persistence. Production injects secrets via ExternalSecret reuse-surface-runtime → Kubernetes Secret reuse-surface-env (OpenBao path platform/workloads/reuse/reuse-surface/runtime-secrets, CCR-2026-0005). Operator runbook: railiance-apps/docs/reuse-surface-on-railiance01.md; rotation: railiance-platform/docs/reuse-surface-runtime-secrets-rotation-runbook.md.

Probes

  • Liveness/readiness: GET /health on port 8000

Browser landing page

Production ingress routes HTTPS / to a static landing Deployment (reuse-surface-landing, RAILIANCE-WP-0008). API paths are unchanged:

  • /health and /v1/* → hub service container
  • / → informational HTML for browser visitors (no login, no secrets)

Agents and CLI clients should target /health and /v1/* only, not /.

Public URL and DNS

Item Value
URL https://reuse.coulomb.social
DNS A record 92.205.62.239 (Railiance01 production)

CoulombCore (92.205.130.254) held a bootstrap deploy; production release uses KUBECONFIG=~/.kube/config-hosteurope. Verify propagation:

dig +short reuse.coulomb.social A   # must return 92.205.62.239

Client configuration

export REUSE_SURFACE_URL=https://reuse.coulomb.social
export REUSE_SURFACE_TOKEN=<write-token>
reuse-surface hub status

Operational hardening

The hub runs as a single-replica Deployment with SQLite on a PVC (A5 containerized service). A6 (managed platform) is deferred until multi-replica or Postgres backing is required.

Backup and restore (SQLite PVC)

  1. Identify the PVC mounted at /data (stores reuse.db and remote index cache).
  2. Snapshot or copy while the pod is running (SQLite WAL-safe copy) or scale to zero briefly for a cold copy:
kubectl -n <namespace> exec deploy/reuse-surface -- \
  sqlite3 /data/reuse.db '.backup /tmp/reuse-backup.db'
kubectl -n <namespace> cp deploy/reuse-surface:/tmp/reuse-backup.db ./reuse-backup.db
  1. Restore by replacing /data/reuse.db from backup and restarting the pod.
  2. Re-register repos if the database is empty (reuse-surface hub list).

Verify backup once per environment after deploy changes.

TLS certificate renewal

Ingress TLS is managed by the cluster cert issuer (Railiance01 companion chart). Monitor certificate expiry on reuse.coulomb.social. Renewal is automatic when the issuer is healthy; on failure, check ingress secret reuse-surface-tls and cert-manager / companion operator logs.

Token rotation

  1. Generate a new REUSE_SURFACE_TOKEN value.
  2. Update Kubernetes Secret reuse-surface-env.
  3. Rolling restart the hub Deployment.
  4. Update operator workstations and CI secrets that call write endpoints.
  5. Confirm reuse-surface hub register fails with the old token and succeeds with the new token.

Image promotion checklist

  1. Tag image from CI commit. .forgejo/workflows/image.yaml already builds and pushes forgejo.coulomb.social/coulomb/reuse-surface:main-<short-sha> automatically on every push that touches Dockerfile/reuse_surface/**/ schemas/**/pyproject.toml — verify it's green rather than building by hand, unless promoting from the still-live gitea.coulomb.social registry (current production posture).
  2. Run pytest -q and reuse-surface validate on that commit (CI already does this; re-verify locally if promoting outside CI).
  3. Update Helm values image tag in railiance-apps (helm/reuse-surface-values.yaml).
  4. Deploy to Railiance01 (make reuse-deploy); verify GET /v1/federated and GET /v1/repos (not GET /health — see the known ingress routing issue below).
  5. Smoke reuse-surface hub list and GET /v1/federated capability count; check reuse-surface stats shows a fresh composed_at post-deploy.
  6. Record image digest in workplan or progress log.

Known issue (found 2026-07-07, not fixed): the public ingress's exact-path /health rule 404s (shadowed by the catch-all / rule to the landing page) — confirmed ingress-layer only via direct port-forward and the Deployment's own passing readiness/liveness probes. Use GET /v1/repos or GET /v1/federated for external verification instead. Flagged to railiance-apps; not this repo's fix to make (shared ingress template).

SQLite vs Postgres (cnpg) — decision criteria

Stay on SQLite while:

  • Single replica is acceptable.
  • RPO of occasional PVC snapshot is sufficient.
  • Write volume is low (repo registration changes only).

Consider Postgres (e.g. CloudNative-PG) when:

  • Multiple hub replicas or zero-downtime failover is required.
  • RPO/RTO targets need point-in-time recovery beyond PVC snapshots.
  • Federation cache metadata or audit tables grow beyond comfortable SQLite size.

Implementation deferred unless an operator approves migration. Document only until then.