railiance-apps/docs/reuse-surface-on-railiance01.md
tegwick 0968f1d5d5
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Add reuse-webhook-smoke and rotation runbook links
RAILIANCE-WP-0011-T04: smoke checks unsigned 401, signed no-op 200, ESO Ready,
and /v1/federated; point operator docs at platform rotation runbook.
2026-07-08 00:01:21 +02:00

215 lines
7.8 KiB
Markdown

# 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:<tag>` |
| Landing image | `nginxinc/nginx-unprivileged:1.27-alpine` |
| Secret | `reuse-surface-env` (see [Runtime secrets](#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:
```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
```
**Rotation** (never commit or paste values into chat):
Full procedure: `railiance-platform/docs/reuse-surface-runtime-secrets-rotation-runbook.md`
```bash
# After updating field(s) in OpenBao — force ESO sync, roll hub, reconcile webhook if needed
kubectl --kubeconfig ~/.kube/config-hosteurope annotate externalsecret \
reuse-surface-runtime -n reuse force-sync="$(date +%s)" --overwrite
make reuse-runtime-es-status
kubectl --kubeconfig ~/.kube/config-hosteurope rollout restart deployment/reuse-surface -n reuse
make reuse-forgejo-webhook # required when REUSE_SURFACE_FORGEJO_WEBHOOK_SECRET changed
make reuse-webhook-smoke
```
One-time lane bootstrap (CCR-2026-0005):
```bash
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):
```bash
export REUSE_SURFACE_TOKEN=$(
bao kv get -field=REUSE_SURFACE_TOKEN \
platform/workloads/reuse/reuse-surface/runtime-secrets
)
```
Or proxy via ops-warden: `warden access reuse-surface-hub-write-token --fetch`.
**Break-glass** (OpenBao unreachable): read materialized Secret `reuse-surface-env`
via kubectl — see `ops-warden/wiki/playbooks/reuse-surface-hub-write-token.md`.
## 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
```
Smoke (unsigned `401`, signed no-op `200`, ESO Ready, `/v1/federated`):
```bash
make reuse-webhook-smoke
```
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.