ADR-0011: Builder authentication via Keycloak OIDC¶
Status: Accepted — 003-governance-depth (2026-07) Principles served: VII, III Source: specs/003-governance-depth/research.md G-6; specs/003-governance-depth/plan.md pinned decision 4
Context¶
The walking skeleton's inference gateway authenticated the builder with a
constant-time static-token comparison — a placeholder the code itself
labeled "OIDC in a later slice". A shared static token is unattributable
and unrotatable, and provenance recorded only a builder-<prefix>
placeholder as the builder subject.
Decision¶
The builder adapter authenticates with the Keycloak client-credentials
flow as a confidential client (tend-builder in the existing realm) and
sends the resulting JWT. The inference gateway validates it: issuer must be
the in-cluster Keycloak realm URL, audience must be
tend-inference-gateway, and the signature is checked against a cached
JWKS (keys refreshed on unknown kid). The token's sub/azp becomes the
builder subject recorded in telemetry and provenance. The cutover was a
single coordinated change — adapter and gateway ship from one repo — with
the static-token path deleted, not left as a fallback, because a
permanent fallback would hollow out the control. The gateway remains fail
closed: no token, bad issuer, bad audience, bad signature, or JWKS
unavailable all return 401.
Alternatives considered¶
- Keep the shared static token — rejected: unattributable and unrotatable; the placeholder existed precisely to be closed.
- mTLS client certificates — rejected: Keycloak is already the platform IdP and the constitution's named component; certificate lifecycle adds operational burden OIDC does not.
- Per-app ServiceAccount tokens for the builder — rejected: the builder is a platform component, not an app; its contract is the narrow Principle VII seam.
Consequences¶
Builder identity in provenance is now a real, attributable subject, and credentials rotate through standard IdP machinery. The builder contract narrows to standard OIDC — less bespoke than the token prefix — which lowers, not raises, builder switching cost (Principle VII). Fail-closed means a Keycloak outage stops the builder rather than admitting anonymous calls, the intended trade.