# decisions.coulomb.social — origin for the informed-decision approver surface **Status:** applied and live. `https://decisions.coulomb.social/` answers 200 over a valid Let's Encrypt certificate as of 2026-09-10 14:32 UTC. 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`. ## Deployed 2026-09-10 Applied with operator approval. Evidence: | Check | Result | | --- | --- | | Placeholder pod | `1/1 Running`, `/healthz` → `ok` in-pod | | Certificate `informed-decision-tls` | `Ready=True`, ACME order `valid` | | Issuer / subject | `CN=decisions.coulomb.social`, Let's Encrypt `YR2` | | Validity | `2026-09-10` → `2026-12-09` (cert-manager renews) | | `GET https://decisions.coulomb.social/` | `HTTP/2 200`, chain verify `0` | | `GET https://decisions.coulomb.social/auth/callback` | `200` — the exact redirect URI resolves | `/auth/callback` currently returns the placeholder page via the SPA `try_files` fallback. That is the correct behaviour for now: the origin answers, which is what the registration needs. The real surface will handle the path when `INFD-WP-0001-T08` ships. **`informed-decision` is unblocked to submit `docs/keycape-client-registration.md` to `key-cape` and close `KEY-WP-0013-T02`.** ## Redeploy / recovery ```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 ``` That `curl` succeeded on 2026-09-10, which is the gate `informed-decision` was waiting on. **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`.