Skip to content

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.