95 lines
4.4 KiB
Markdown
95 lines
4.4 KiB
Markdown
|
|
# decisions.coulomb.social — origin for the informed-decision approver surface
|
||
|
|
|
||
|
|
**Status:** manifests written and dry-run clean; **not applied**.
|
||
|
|
Last reviewed: 2026-09-10
|
||
|
|
|
||
|
|
## Why this host exists before the app does
|
||
|
|
|
||
|
|
`informed-decision` owns the browser-facing approver UI that `approval-engine`
|
||
|
|
deliberately does not contain. Its workplan task `INFD-WP-0001-T07` must publish
|
||
|
|
two strings to `key-cape` — an OIDC `client_id` and a full callback URI — and
|
||
|
|
close `KEY-WP-0013-T02`, which has been blocked on them since 2026-09-08.
|
||
|
|
|
||
|
|
Both strings are now fixed except for the host:
|
||
|
|
|
||
|
|
| Field | Value |
|
||
|
|
| --- | --- |
|
||
|
|
| `client_id` | `informed-decision-approver` |
|
||
|
|
| Redirect URI | `https://decisions.coulomb.social/auth/callback` |
|
||
|
|
| Flow | authorization code + S256 PKCE, public client |
|
||
|
|
| Scopes | `openid`, `approval:read`, `approval:approve` |
|
||
|
|
|
||
|
|
Redirect URIs match byte-exactly at `/authorize`. A registration pointing at a
|
||
|
|
host that does not answer fails closed at first login and presents as a rejected
|
||
|
|
approval rather than as a registration defect — which is exactly the failure
|
||
|
|
`approval-engine` avoided by refusing to invent these strings. So the origin has
|
||
|
|
to be real before the registration is submitted, and that is S5 work here rather
|
||
|
|
than in `informed-decision`.
|
||
|
|
|
||
|
|
The approver UI itself is `INFD-WP-0001-T08`, still gated on `approval-engine`
|
||
|
|
`APPROVAL-WP-0002-T01` and on intake `INFD-IN-0003` (the evidence copy must reach
|
||
|
|
`audit-core` independently of this component). This host therefore ships a
|
||
|
|
placeholder first and the real surface later.
|
||
|
|
|
||
|
|
## What is in the repo
|
||
|
|
|
||
|
|
| File | Contents |
|
||
|
|
| --- | --- |
|
||
|
|
| `manifests/informed-decision-origin.yaml` | Namespace, placeholder nginx ConfigMap/Deployment/Service on `informed-decision:80` |
|
||
|
|
| `manifests/informed-decision-ingress.yaml` | Traefik Ingress + `letsencrypt-prod` certificate for `decisions.coulomb.social` |
|
||
|
|
|
||
|
|
The placeholder is `nginxinc/nginx-unprivileged`, read-only root filesystem,
|
||
|
|
non-root, `noindex`. When the real surface lands it takes over the same Service
|
||
|
|
name and the origin file shrinks to the Namespace.
|
||
|
|
|
||
|
|
## Deliberate single path rule
|
||
|
|
|
||
|
|
The Ingress carries exactly one rule: `/` Prefix to one backend.
|
||
|
|
|
||
|
|
`reuse-surface` reported on 2026-07-07 that an Ingress declaring an `Exact`
|
||
|
|
`/health` rule alongside a catch-all `/` Prefix rule on the same host had the
|
||
|
|
exact match swallowed by the catch-all — the public health URL returned 404 from
|
||
|
|
the landing container while the pod was `1/1 Ready` and its own probes passed.
|
||
|
|
Splitting `/auth`, `/api` and `/` across backends here would reproduce that on a
|
||
|
|
host whose whole purpose is an exact-match redirect URI. If a second backend ever
|
||
|
|
becomes necessary, set `traefik.ingress.kubernetes.io/router.priority`
|
||
|
|
explicitly rather than relying on rule order.
|
||
|
|
|
||
|
|
## Preconditions verified 2026-09-10
|
||
|
|
|
||
|
|
- DNS `decisions.coulomb.social` → `92.205.62.239` (same A record as `reuse`).
|
||
|
|
- `letsencrypt-prod` ClusterIssuer `Ready=True`.
|
||
|
|
- No existing Ingress claims `decisions.coulomb.social`.
|
||
|
|
- Server-side dry-run of all five objects against the live API is clean.
|
||
|
|
The namespaced four were validated against an existing namespace, since a
|
||
|
|
server dry-run cannot create the new one first; see
|
||
|
|
`DRY_RUN_CREATE_NAMESPACES` in `tools/k8s-server-dry-run.sh`.
|
||
|
|
|
||
|
|
## To deploy (operator approval required — not yet given)
|
||
|
|
|
||
|
|
```bash
|
||
|
|
export KUBECONFIG=$HOME/.kube/config-hosteurope
|
||
|
|
kubectl apply -f manifests/informed-decision-origin.yaml
|
||
|
|
kubectl apply -f manifests/informed-decision-ingress.yaml
|
||
|
|
kubectl -n informed-decision get pods,svc,ingress
|
||
|
|
kubectl -n informed-decision get certificate informed-decision-tls -w
|
||
|
|
curl -sSI https://decisions.coulomb.social/ | head -1
|
||
|
|
```
|
||
|
|
|
||
|
|
Only when that `curl` succeeds does `informed-decision` submit
|
||
|
|
`docs/keycape-client-registration.md` to `key-cape`.
|
||
|
|
|
||
|
|
**Note on the kubeconfig:** `~/.kube/config-hosteurope` names port `16443`, but
|
||
|
|
the `k3s-api-railiance01` ops-bridge tunnel currently listens on `16444`.
|
||
|
|
Override with `--server https://127.0.0.1:16444` or fix the kubeconfig; `bridge
|
||
|
|
check` reports the tunnel healthy either way, so it does not surface the
|
||
|
|
mismatch.
|
||
|
|
|
||
|
|
## HTTP → HTTPS
|
||
|
|
|
||
|
|
The Ingress is `websecure`-only, following the `forgejo` and `coulomb-social`
|
||
|
|
pattern, so port 80 stays free for cert-manager HTTP-01 solvers. Plain
|
||
|
|
`http://decisions.coulomb.social/` will not redirect. `reuse-surface` adds a
|
||
|
|
separate `-http-redirect` Ingress for this; add one here if a bare-host redirect
|
||
|
|
is wanted. It is not required for the OIDC flow, which is always `https`.
|