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>
155 lines
No EOL
6.1 KiB
Markdown
155 lines
No EOL
6.1 KiB
Markdown
# 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
|
|
(`origin` → `forgejo-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.
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
dig +short reuse.coulomb.social A # must return 92.205.62.239
|
|
```
|
|
|
|
## Client configuration
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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
|
|
```
|
|
|
|
3. Restore by replacing `/data/reuse.db` from backup and restarting the pod.
|
|
4. 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. |