From 4a69aff6be6022312e90c0f1df7df4f2ed1cdd86 Mon Sep 17 00:00:00 2001 From: Dave Hudson Date: Fri, 8 May 2026 15:05:58 +0100 Subject: [PATCH] fix(sandcastle): forward CLAUDE_CODE_OAUTH_TOKEN into sandbox container In-container `claude` CLI was failing with "Not logged in" because host ~/.claude credentials don't reach the sandbox. Resolve the token via varlock from Infisical's dev env and forward it through SANDBOX_ENV. Wraps the `sandcastle` script with `varlock run --path .sandcastle/` so process.env is populated before main.ts runs. --- .sandcastle/.env.schema | 13 +++++++++++-- .sandcastle/main.ts | 27 +++++++++++++++++++-------- package.json | 2 +- 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/.sandcastle/.env.schema b/.sandcastle/.env.schema index cbe210f..516940f 100644 --- a/.sandcastle/.env.schema +++ b/.sandcastle/.env.schema @@ -10,5 +10,14 @@ INFISICAL_CLIENT_ID= # @type=infisicalClientSecret @sensitive INFISICAL_CLIENT_SECRET= -# Add real secrets here as they're introduced, e.g.: -# ANTHROPIC_API_KEY=infisical() +# Claude Code CLI auth for the sandbox container. Sandcastle's docker +# sandbox runs `claude` inside the container, which needs auth — host +# `~/.claude` credentials don't reach the sandbox. We forward this into +# SANDBOX_ENV in main.ts so the in-container CLI can authenticate without +# an interactive `/login`. +# +# Dev-only: Sandcastle is a developer tool — staging/prod environments +# don't run it, so this token is only populated in the `dev` Infisical +# environment. @required=false avoids failing varlock loads in other envs. +# @required=false +CLAUDE_CODE_OAUTH_TOKEN=infisical() diff --git a/.sandcastle/main.ts b/.sandcastle/main.ts index 040f2b4..645d209 100644 --- a/.sandcastle/main.ts +++ b/.sandcastle/main.ts @@ -46,18 +46,28 @@ const COPY_TO_WORKTREE: readonly string[] = []; // fails loudly if a package's main entry is missing. const INSTALL_AND_VERIFY = "bun install && bun run typecheck"; -// Forward the Infisical machine-identity credentials into the sandbox so -// varlock-resolved env (`infisical()` items) works inside the container. -// The credentials live in the host shell env, sourced from the macOS keychain -// via ~/.zshenv. We fail fast if they are missing — the AFK agent cannot -// resolve real env values otherwise, and silent fallback to placeholder -// builds masks misconfiguration. +// Forward credentials into the sandbox container: +// - INFISICAL_CLIENT_ID / _SECRET: machine-identity creds so varlock-resolved +// env (`infisical()` items) works inside the container. Sourced from the +// macOS keychain via ~/.zshenv on the host. +// - CLAUDE_CODE_OAUTH_TOKEN: auth for the in-container `claude` CLI. Without +// this the implementer/reviewer agents fail with `Not logged in · Please +// run /login` because host `~/.claude` credentials don't reach the sandbox. +// Resolved by varlock from Infisical's `dev` environment (this token is +// intentionally absent from staging/prod — sandcastle is dev-only). +// +// The orchestrator is launched via `varlock run --path .sandcastle/` (see the +// `sandcastle` script in the root package.json), so process.env already has +// these values resolved before main.ts runs. We fail fast if they're missing — +// the AFK agent cannot do useful work otherwise, and silent fallback masks +// misconfiguration. const requireEnv = (key: string): string => { const value = process.env[key]; if (!value) { throw new Error( - `${key} is not set on the host. Sandcastle requires Infisical credentials to inject ` + - `into the sandbox so varlock can resolve env values. See CLAUDE.md for keychain setup.`, + `${key} is not set. Sandcastle is launched via \`varlock run\` which should ` + + `resolve this from Infisical (dev env). See .sandcastle/CODING_STANDARDS.md ` + + `for keychain setup.`, ); } return value; @@ -65,6 +75,7 @@ const requireEnv = (key: string): string => { const SANDBOX_ENV: Record = { INFISICAL_CLIENT_ID: requireEnv("INFISICAL_CLIENT_ID"), INFISICAL_CLIENT_SECRET: requireEnv("INFISICAL_CLIENT_SECRET"), + CLAUDE_CODE_OAUTH_TOKEN: requireEnv("CLAUDE_CODE_OAUTH_TOKEN"), }; // ---------- Agent specs ---------- diff --git a/package.json b/package.json index acbcb16..965d409 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "env:scan": "varlock scan", "ci": "TURBO_NO_UPDATE_NOTIFIER=1 turbo typecheck test --output-logs=errors-only && biome check . && bun run check:no-suppressions && bun run env:scan", "prepare": "husky", - "sandcastle": "bun .sandcastle/main.ts", + "sandcastle": "varlock run --path .sandcastle/ -- bun .sandcastle/main.ts", "sandcastle:analyze": "bun .sandcastle/analyze.ts", "sandcastle:build": "bunx sandcastle docker build-image", "cleanup": "bun scripts/cleanup-branches.ts"