railiance-apps/docs/reuse-surface-on-railiance01.md
tegwick 40f0cb741d
All checks were successful
CI Smoke / host-smoke (push) Successful in 3s
CI Smoke / container-smoke (push) Successful in 48s
Deploy reuse-surface runtime secrets via OpenBao External Secrets
Add openbao-reuse ClusterSecretStore handoff, reuse-surface-runtime
ExternalSecret, ESO token bootstrap script, and Makefile targets for
CCR-2026-0005 / RAILIANCE-WP-0011.
2026-07-07 22:34:34 +02:00

7.6 KiB

reuse-surface on railiance01

Federation service deployment for https://reuse.coulomb.social.

Companion workplans: RAILIANCE-WP-0007 (Helm release), REUSE-WP-0011 (service + CLI).

Hosts and DNS

Server IP Role
Railiance01 92.205.62.239 Production k3s — deploy here
CoulombCore 92.205.130.254 Bootstrap / prerelease only
Record Production target Current public DNS (2026-06-15)
reuse.coulomb.social A 92.205.62.239 Propagated on 8.8.8.8 / 1.1.1.1; drop any AAAA/forwarding still pointing elsewhere
hub.coulomb.social A 92.205.62.239 (future) 92.205.130.254 (CoulombCore bootstrap OK for now)

Let's Encrypt HTTP-01 on Railiance01 requires the public A record to reach 92.205.62.239. Service and TLS are live on the production cluster.

dig +short reuse.coulomb.social A
KUBECONFIG=~/.kube/config-hosteurope kubectl get certificate -n reuse

If DNS is being changed in the future, use an explicit resolve only during propagation:

curl -k --resolve reuse.coulomb.social:443:92.205.62.239 https://reuse.coulomb.social/health
KUBECONFIG=~/.kube/config-hosteurope kubectl port-forward -n reuse svc/reuse-surface 18001:8000
export REUSE_SURFACE_URL=http://127.0.0.1:18001

Release surface

Item Value
Namespace reuse
Helm release reuse
Chart charts/reuse-surface
Values helm/reuse-surface-values.yaml
Image gitea.coulomb.social/coulomb/reuse-surface:<tag>
Landing image nginxinc/nginx-unprivileged:1.27-alpine
Secret reuse-surface-env (see Runtime secrets)

Runtime secrets

Custody is OpenBao KV at platform/workloads/reuse/reuse-surface/runtime-secrets on coulombcore (https://bao.coulomb.social), delivered to the workload through External Secrets (ExternalSecret reuse/reuse-surface-runtime → Secret reuse-surface-env, ClusterSecretStore openbao-reuse). Lane: CCR-2026-0005 / RAILIANCE-WP-0011.

Key Required Purpose
REUSE_SURFACE_TOKEN yes Bearer token for hub write API (POST /v1/repos, compose, etc.)
REUSE_SURFACE_FORGEJO_WEBHOOK_SECRET for webhooks HMAC secret for POST /v1/webhooks/forgejo (REUSE-WP-0019-T02)

The chart injects both keys via envFrom.secretRef on the reuse-surface Deployment. After patching the Secret, restart the Deployment so new values are picked up:

kubectl --kubeconfig ~/.kube/config-hosteurope rollout restart deployment/reuse-surface -n reuse
kubectl --kubeconfig ~/.kube/config-hosteurope rollout status deployment/reuse-surface -n reuse

Bootstrap / rotate (update OpenBao fields — never commit or paste into chat):

# Operator: update both fields in OpenBao, then refresh ESO and roll the Deployment
make reuse-runtime-es-status
kubectl --kubeconfig ~/.kube/config-hosteurope rollout restart deployment/reuse-surface -n reuse
make reuse-forgejo-webhook   # required when rotating REUSE_SURFACE_FORGEJO_WEBHOOK_SECRET

One-time lane bootstrap (CCR-2026-0005):

make reuse-openbao-eso-token-apply
make reuse-runtime-es-deploy
make reuse-runtime-es-status

Export for a shell session (streams to your terminal only):

export REUSE_SURFACE_TOKEN=$(
  kubectl --kubeconfig ~/.kube/config-hosteurope get secret reuse-surface-env -n reuse \
    -o jsonpath='{.data.REUSE_SURFACE_TOKEN}' | base64 -d
)

Or proxy via ops-warden: warden access reuse-surface-hub-write-token --fetch.

Forgejo org webhook

Org-level push webhook on Forgejo (coulomb) triggers hub recompose when any repo changes paths under registry/indexes/. The HMAC secret must match REUSE_SURFACE_FORGEJO_WEBHOOK_SECRET in reuse-surface-env.

Requires a Forgejo admin API token at /tmp/forgejo-tegwick-api-token (or FORGEJO_ADMIN_TOKEN). Idempotent setup:

make reuse-forgejo-webhook

Manual verification (unsigned body should return 401, not 503):

curl -fsS -o /dev/null -w "%{http_code}\n" -X POST \
  https://reuse.coulomb.social/v1/webhooks/forgejo \
  -H "Content-Type: application/json" -d '{}'

Live status (2026-07-07): org webhook id 1 on coulombhttps://reuse.coulomb.social/v1/webhooks/forgejo, push events only.

Browser landing page

https://reuse.coulomb.social/ serves a static no-login landing page from the Helm-managed reuse-surface-landing Deployment and Service. It exists for humans who open the hostname in a browser; it does not change the API service.

Ingress routing is intentionally split:

  • HTTP / redirects permanently to https://reuse.coulomb.social/;
  • HTTPS /health and /v1/* route to svc/reuse-surface;
  • HTTPS / and other non-API browser paths route to svc/reuse-surface-landing.

The rendered page includes noindex,nofollow, a short service description, and links to /health, /v1/federated, and this operator runbook. It must not include REUSE_SURFACE_TOKEN or any other runtime secret.

Rollback: set landing.enabled: false in helm/reuse-surface-values.yaml and run make reuse-deploy; the ingress will return to routing all / traffic to the API service.

Deploy

# 1. Pin image tag in helm/reuse-surface-values.yaml
# 2. Ensure reuse-surface-env has both keys (see Runtime secrets above)
# 3. Production (Railiance01, defaults to ~/.kube/config-hosteurope)
make reuse-deploy
make reuse-forgejo-webhook   # after webhook secret is in the cluster Secret
make reuse-status

# Restore kubeconfig from the node if missing:
# ssh tegwick@92.205.62.239 'sudo cat /etc/rancher/k3s/k3s.yaml' \
#   | sed 's|127.0.0.1|92.205.62.239|' > ~/.kube/config-hosteurope

Smoke checks

make reuse-smoke
curl -I http://reuse.coulomb.social/
curl -fsS https://reuse.coulomb.social/
curl -fsS https://reuse.coulomb.social/health
curl -fsS https://reuse.coulomb.social/v1/federated

export REUSE_SURFACE_TOKEN=$(kubectl --kubeconfig ~/.kube/config-hosteurope get secret reuse-surface-env -n reuse \
  -o jsonpath='{.data.REUSE_SURFACE_TOKEN}' | base64 -d)
export REUSE_SURFACE_URL=https://reuse.coulomb.social
reuse-surface hub status
reuse-surface hub list
curl -fsS "$REUSE_SURFACE_URL/v1/federated" | jq '.capabilities | length'

Deployed image tag: see helm/reuse-surface-values.yaml (currently e3ae22e, deployed 2026-07-07 for REUSE-WP-0019-T01/T02). Dogfood: reuse-surface repo registered; federated index returns 61 capabilities.

Known issue (found 2026-07-07, not fixed): the public ingress serves GET /v1/* correctly but 404s on the exact-path /health rule — Traefik appears to shadow it with the catch-all / rule routed to reuse-surface-landing. Confirmed NOT a pod/service problem: /health works correctly via direct port-forward to svc/reuse-surface, and the Deployment's own internal readiness/liveness probes (which hit /health directly, bypassing ingress) pass fine — the pod is 1/1 Ready. Only public external monitoring against https://reuse.coulomb.social/health is affected; real API clients using /v1/* are unaffected. Needs an explicit Traefik router priority annotation (or a path-rule reorder) on charts/reuse-surface/templates/ingress.yaml — deliberately not changed here since it's a shared production ingress edit outside this task's scope.

Operations

make reuse-logs
make reuse-status

Image promotion: build from coulomb/reuse-surface, push to Gitea OCI, update helm/reuse-surface-values.yaml image.tag, then:

make reuse-deploy

Bootstrap copy on CoulombCore (92.205.130.254) was removed 2026-06-15 — use config-hosteurope only.