From c90505649b91adeda7e5d67954a1134ee0b3b26d Mon Sep 17 00:00:00 2001 From: Claude CoWork Date: Sat, 9 May 2026 08:21:31 -0700 Subject: [PATCH] codex-shell: pin agent uid/gid to 10001 (WOVED-147) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The slot OAuth init flow (WOVED-126) writes auth state to a long-lived PersistentVolumeClaim. When the chart's pinned image tag rolls forward (Renovate via WOVED-148), kubernetes recreates the slot's worker pod with the new image. If the new image's user has a different uid/gid than the one that originally wrote ~/./credentials.json, the new pod silently fails to read its own credentials — file ownership is by uid, not username — and the operator sees the OAuth prompt re-fire on every task. WOVED-147 calls this out as the unsexy critical failure mode. Pin uid/gid = 10001 for the AGENT user. Aligns with the woved worker images (which already used 10001) and avoids collision with the typical uid=1000 first-user on host machines if an operator ever bind-mounts a path. Username stays AGENT (claude / codex) for kubectl-exec UX — load-bearing invariant is the uid/gid pin, not the username string. Comment block at the useradd site documents the constraint so a future contributor doesn't bump it casually for cosmetic reasons. Companion change in nprodromou/woved adds the same immutability comment to the worker images (which already pin uid 10001) so both sides of the slot model carry the same invariant in source. Co-Authored-By: Claude Opus 4.7 (1M context) --- Dockerfile | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index 2cc754c..f4ea3a4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -263,11 +263,24 @@ RUN case "$AGENT" in \ claude) npm install -g "@anthropic-ai/claude-code@${CLAUDE_CODE_VERSION}" ;; \ esac && npm cache clean --force -# Non-root user. uid/gid 1000, name = AGENT. Matching the AGENT name to -# the user keeps PVC ownership obvious and avoids shell prompts that -# lie about which agent is running. -RUN groupadd -g 1000 ${AGENT} \ - && useradd -m -u 1000 -g 1000 -s /bin/bash ${AGENT} \ +# Non-root user. uid/gid 10001, name = AGENT. +# +# DO NOT bump uid/gid casually (WOVED-147). The slot OAuth init flow +# writes auth state to a PersistentVolumeClaim mounted at /home/${AGENT}. +# That PVC outlives any single pod — when the chart's pinned image tag +# rolls forward (Renovate, WOVED-148), kubernetes recreates the pod +# with the new image. If the new image's user has a different uid/gid, +# the new pod silently can't read its own credentials.json (file +# ownership is by uid, not username) and the operator sees the OAuth +# dance prompt re-fire on every task. +# +# 10001 picked to align with the woved worker images (which already +# baked it in) and to avoid colliding with the typical uid=1000 +# first-user on host machines if an operator ever bind-mounts a path. +# Username stays AGENT for kubectl-exec UX (`whoami` reports the +# agent identity), but the load-bearing invariant is the uid/gid pin. +RUN groupadd -g 10001 ${AGENT} \ + && useradd -m -u 10001 -g 10001 -s /bin/bash ${AGENT} \ && mkdir -p /home/${AGENT}/.config /home/${AGENT}/workspace \ && chown -R ${AGENT}:${AGENT} /home/${AGENT}