From 0672d52a5a6d1259ab92d5990010a440f62408ec Mon Sep 17 00:00:00 2001 From: Mark Turansky Date: Tue, 2 Jun 2026 15:57:14 -0400 Subject: [PATCH] feat(spec): session SA requires system:image-builder for registry push Document that the CP must bind system:image-builder ClusterRole to session-{id}-sa at provision time. Without this binding the session SA only has image-puller rights (granted automatically by OpenShift) and agents cannot push built images to the internal registry. Verified against running cluster: session SA receives system:image-pullers via the namespace default RoleBinding but UNAUTHORIZED on blob upload without explicit system:image-builder binding. Co-Authored-By: Claude Sonnet 4.6 --- specs/control-plane/control-plane.spec.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/specs/control-plane/control-plane.spec.md b/specs/control-plane/control-plane.spec.md index 753fc8cb1..7fe6e64b4 100755 --- a/specs/control-plane/control-plane.spec.md +++ b/specs/control-plane/control-plane.spec.md @@ -62,6 +62,7 @@ Handles `session ADDED` and `session MODIFIED (phase=Pending)` events by provisi 3. ServiceAccount (no automount) 4. Pod (runner image + env vars) 5. Service (ClusterIP on port 8001 pointing at the pod) +6. RoleBinding granting `system:image-builder` ClusterRole to `session-{id}-sa` (enables push to the OpenShift internal image registry) On `phase=Stopping` → calls `deprovisionSession` (deletes pods). On `DELETED` → calls `cleanupSession` (deletes pod, secret, service account, service, namespace). @@ -87,6 +88,8 @@ The CP creates a Pod (not a Job) for each session. Key pod attributes: | CPU request/limit | 500m / 2000m | Generous for Claude Code | | Memory request/limit | 512Mi / 4Gi | Claude Code is memory-intensive | +The CP binds the `system:image-builder` ClusterRole to `session-{id}-sa` via a namespace-scoped RoleBinding at provision time. This grants the runner pod push access to the OpenShift internal image registry (`image-registry.openshift-image-registry.svc:5000`), enabling agents to build and push images using daemonless tools such as `crane`. Pull access is provided automatically to all SAs in the namespace by OpenShift via the `system:image-pullers` RoleBinding created at namespace initialization. + ### Start Context Assembly `assembleInitialPrompt` builds `INITIAL_PROMPT` from four sources in order: @@ -532,3 +535,4 @@ The `ambient-control-plane` ServiceAccount does not have `delete` on `namespaces | CP token endpoint over Secret-write renewal | Secret writes are async push with no synchronization guarantee vs. token TTL; synchronous pull from CP eliminates the race entirely | | Runner SA token for CP auth | K8s SA tokens are already mounted in every pod, long-lived, and K8s-managed — no new secrets or out-of-band key distribution required | | CP is sole token source — no BOT_TOKEN Secret | CP creates the runner pod, so it is always reachable before the runner's first token request; retaining a Secret adds complexity and a second failure mode with the same blast radius | +| `system:image-builder` bound to session SA at provision time | Agents need push access to the internal image registry to build and distribute images; OpenShift grants pull automatically via `system:image-pullers` at namespace init but push requires an explicit RoleBinding; co-locating it with the other session SA grants keeps all RBAC provisioning in one place |