An earlier version said the origin unblocks KEY-WP-0013-T02, 'blocked since 2026-09-08'. Both halves are wrong. T02 is done, and so is T05 -- the task that actually held the human registration. key-cape published the contract shape from its side and closed a follow-up informed-decision prompted (KEY-WP-0030). Nothing there waits on this host. The error came from repeating informed-decision's workplan prose of 2026-09-09 without checking the source repo. The origin work stands on its own: T07's acceptance requires a real origin regardless of who else is waiting. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EJbh7o7UWF4tQ5jxygnNGu Assistant: claude-code Assistant-Model: opus Assistant-Process: 2072522@bnt-lap001 Assistant-Session: 46173adf-7302-4ede-99d6-963b61359928
8.5 KiB
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 cannot do so against a host that
does not answer.
Correction, 2026-09-11. An earlier version of this document said the origin
unblocks key-cape's KEY-WP-0013-T02, "blocked since 2026-09-08". That is
wrong twice over, and the error came from repeating informed-decision's
workplan prose of 2026-09-09 without checking the source repo. KEY-WP-0013-T02
is done; so is T05, the task that actually carried the human registration.
key-cape published the contract shape from its side — including the
approval:read scope informed-decision asked for and a fix to the assurance
object's at field — and closed a follow-up (KEY-WP-0030) that
informed-decision prompted. Nothing in key-cape is waiting on this host.
What remains is informed-decision's own T07, whose acceptance requires the
real origin regardless.
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 |
manifests/informed-decision-http-redirect.yaml |
redirectScheme Middleware + web-entrypoint Ingress, HTTP → HTTPS |
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 asreuse). letsencrypt-prodClusterIssuerReady=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_NAMESPACESintools/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 |
GET http://decisions.coulomb.social/ |
301/308 → HTTPS (added 2026-09-10) |
/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 can now submit docs/keycape-client-registration.md to
key-cape. That closes its own INFD-WP-0001-T07, not a key-cape task —
see the correction above.
Redeploy / recovery
export KUBECONFIG=$HOME/.kube/config-hosteurope
kubectl apply -f manifests/informed-decision-origin.yaml
kubectl apply -f manifests/informed-decision-ingress.yaml
kubectl apply -f manifests/informed-decision-http-redirect.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
manifests/informed-decision-http-redirect.yaml adds a redirectScheme
Middleware plus a web-entrypoint Ingress, following the reuse-surface
pattern. Applied 2026-09-10.
http://decisions.coulomb.social/ now returns 308 to HEAD and 301 to GET.
That split is not a defect in this configuration — reuse.coulomb.social, which
uses the identical Middleware, behaves the same way. It is Traefik's own
behaviour on this cluster.
The main Ingress stays websecure-only; this is a second router on port 80
rather than a change to the first.
Router priority is load-bearing here
cert-manager solves HTTP-01 by creating a temporary solver Ingress on this
same host and this same web entrypoint, serving
/.well-known/acme-challenge/<token>. A catch-all / redirect on that
entrypoint competes with it directly.
Traefik would normally settle this by rule length — the solver's Path rule is
much longer than PathPrefix(/) — but reuse-surface's 2026-07-07 report is
precisely a case of a specific rule losing to a catch-all when precedence was
left implicit. There the symptom was a 404 someone noticed within the day. Here
it would be a silently failed renewal about 60 days out, surfacing as an
expired certificate on the origin backing an OIDC redirect URI — the kind of
failure that presents as a broken login rather than as an expired cert.
So the redirect router pins traefik.ingress.kubernetes.io/router.priority: "1".
Any solver router, using the default computed priority, outbids it.
Verified by probe, twice, on 2026-09-10. A throwaway Ingress mimicking the
solver shape (web entrypoint, Exact acme-challenge path, default priority)
was applied and the challenge path was polled:
| Condition | /.well-known/acme-challenge/<token> |
/ |
|---|---|---|
| No solver present | 301 (redirect catches it — correct) |
301 |
| Solver present | 200 (solver wins) |
301 |
| Solver removed | 301 |
301 |
Traefik takes 30–60s to converge on Ingress changes, so the flip is not instant; poll rather than reading a single response. The probe was deleted after each run and is not in the repo — recreate it from this table if the redirect or the priority annotation is ever changed.
The next real renewal is due around 2026-11-09. Confirm the certificate's
notAfter advances then; that is the only test that exercises the real solver
rather than a probe of its shape.