# 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. ```bash 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: ```bash 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:` | | Landing image | `nginxinc/nginx-unprivileged:1.27-alpine` | | Secret | `reuse-surface-env` (see [Runtime secrets](#runtime-secrets)) | ## Runtime secrets Custody is the Kubernetes Secret `reuse-surface-env` in namespace `reuse` on Railiance01. This is **not** an OpenBao KV path today — see **RAILIANCE-WP-0011** for a planned OpenBao + External Secrets migration. | 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: ```bash kubectl --kubeconfig ~/.kube/config-hosteurope rollout restart deployment/reuse-surface -n reuse kubectl --kubeconfig ~/.kube/config-hosteurope rollout status deployment/reuse-surface -n reuse ``` **Create or rotate** (generate values locally — never commit or paste into chat): ```bash kubectl create namespace reuse --dry-run=client -o yaml | kubectl apply -f - kubectl create secret generic reuse-surface-env \ --namespace reuse \ --from-literal=REUSE_SURFACE_TOKEN="$(openssl rand -hex 32)" \ --from-literal=REUSE_SURFACE_FORGEJO_WEBHOOK_SECRET="$(openssl rand -hex 32)" \ --dry-run=client -o yaml | kubectl apply -f - kubectl --kubeconfig ~/.kube/config-hosteurope rollout restart deployment/reuse-surface -n reuse ``` **Export for a shell session** (streams to your terminal only): ```bash 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: ```bash make reuse-forgejo-webhook ``` Manual verification (unsigned body should return `401`, not `503`): ```bash 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 `coulomb` → `https://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 ```bash # 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 ```bash 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 ```bash 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: ```bash make reuse-deploy ``` Bootstrap copy on CoulombCore (`92.205.130.254`) was removed 2026-06-15 — use `config-hosteurope` only.