From cdb1870b1cbaef2547a1b7b7409aefdfe0382d4e Mon Sep 17 00:00:00 2001 From: codex Date: Thu, 28 May 2026 18:20:09 -0400 Subject: [PATCH] docs(agent): clarify claude oauth env --- README.md | 43 ++++++++++--------- docs/prds/0008-git-gate.md | 2 +- docs/prds/0010-cred-proxy.md | 4 +- docs/prds/0011-per-file-md-manifest.md | 2 +- .../agent-credential-proxy-landscape.md | 2 +- docs/research/claude-code-token-revocation.md | 2 +- .../research/git-secret-scanning-hardening.md | 2 +- docs/research/manifest-format-and-grouping.md | 2 +- docs/research/polish-priorities.md | 2 +- docs/research/remote-docker-vm-isolation.md | 6 +-- docs/research/secret-minimization-over-dlp.md | 4 +- examples/bottles/claude.md | 2 +- scripts/demo-record.sh | 4 +- scripts/demo.sh | 4 +- 14 files changed, 41 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 2a27aa1..030ac6b 100644 --- a/README.md +++ b/README.md @@ -313,7 +313,7 @@ egress: role: claude_code_oauth auth: scheme: Bearer - token_ref: BOT_BOTTLE_OAUTH_TOKEN + token_ref: BOT_BOTTLE_CLAUDE_OAUTH_TOKEN --- Common Claude provider boundary. @@ -389,11 +389,12 @@ Working examples live under `examples/`. Pipelock's design lives in rationale in `docs/research/pipelock-assessment.md`. The trust boundary rationale lives in `docs/prds/0011-per-file-md-manifest.md`. -## Auth: OAuth token, not API key +## Auth: Claude OAuth token, not API key -bot-bottle authenticates `claude` inside the container with the same -Pro/Max subscription you already use on the host, via a long-lived OAuth -token. No `ANTHROPIC_API_KEY` is needed. +Bottles that use `agent_provider.template: claude` authenticate +`claude` inside the container with the same Pro/Max subscription you +already use on the host, via a long-lived OAuth token. No +`ANTHROPIC_API_KEY` is needed. **Why a token instead of mounting `~/.claude.json`:** on macOS, Claude Code stores OAuth credentials in the encrypted Keychain, not in @@ -409,28 +410,28 @@ claude setup-token # browser login, prints a ~1-year OAuth token ``` Stash the token in your shell env (e.g. `~/.zshrc` or a secret manager) -as `BOT_BOTTLE_OAUTH_TOKEN`: +as `BOT_BOTTLE_CLAUDE_OAUTH_TOKEN`: ```sh -export BOT_BOTTLE_OAUTH_TOKEN="" +export BOT_BOTTLE_CLAUDE_OAUTH_TOKEN="" ``` -The bottle reaches the Anthropic API only through the cred-proxy -sidecar. To let `claude` authenticate, declare a route in -`bottle.cred_proxy.routes` with `role: "anthropic-base-url"` and -`token_ref: "BOT_BOTTLE_OAUTH_TOKEN"`: +The Claude bottle reaches the Anthropic API only through the cred-proxy +sidecar. To let `claude` authenticate, declare an egress route with +`role: claude_code_oauth` and +`token_ref: BOT_BOTTLE_CLAUDE_OAUTH_TOKEN`: -```jsonc -{ - "path": "/anthropic/", - "upstream": "https://api.anthropic.com", - "auth_scheme": "Bearer", - "token_ref": "BOT_BOTTLE_OAUTH_TOKEN", - "role": "anthropic-base-url" -} +```yaml +egress: + routes: + - host: api.anthropic.com + role: claude_code_oauth + auth: + scheme: Bearer + token_ref: BOT_BOTTLE_CLAUDE_OAUTH_TOKEN ``` -At launch, `cli.py` reads `BOT_BOTTLE_OAUTH_TOKEN` from the host +At launch, `cli.py` reads `BOT_BOTTLE_CLAUDE_OAUTH_TOKEN` from the host env and forwards it into the cred-proxy container's environ — never into the agent's. The agent receives `ANTHROPIC_BASE_URL` pointing at `http://cred-proxy:9099/anthropic` and a non-secret placeholder for @@ -439,7 +440,7 @@ the proxy strips and replaces the header on every request). `printenv` inside the agent does not surface the real token, and the value is never written to disk or placed on argv on the host. -A bottle without an `anthropic-base-url` route has no path to the +A Claude bottle without a `claude_code_oauth` route has no path to the Anthropic API — there is no fallback that forwards the token directly to the agent. Caveats: the token is bound to your subscription tier (Pro/Max/Team/Enterprise), it does not work with `claude --bare` diff --git a/docs/prds/0008-git-gate.md b/docs/prds/0008-git-gate.md index 9e20adf..77d7230 100644 --- a/docs/prds/0008-git-gate.md +++ b/docs/prds/0008-git-gate.md @@ -26,7 +26,7 @@ entry and pushes straight at gitea/github with ssh-gate doing dumb L4 forwarding. There is no boundary between "the agent thinks this commit is fine" and "the secret hits an external remote." If a compromised or careless agent stages a `.env`, slips a token into -a fixture, or commits the `BOT_BOTTLE_OAUTH_TOKEN` itself, `git +a fixture, or commits the `BOT_BOTTLE_CLAUDE_OAUTH_TOKEN` itself, `git push` ships it. Host-side pre-commit / pre-push hooks are the usual defense, but diff --git a/docs/prds/0010-cred-proxy.md b/docs/prds/0010-cred-proxy.md index ecc587e..145a066 100644 --- a/docs/prds/0010-cred-proxy.md +++ b/docs/prds/0010-cred-proxy.md @@ -230,7 +230,7 @@ common upstreams (Anthropic, GitHub, Gitea, npm) as ``` ┌── Host (macOS) ──────────────────────────────────────────────────┐ │ Secrets at rest (keychain / .env): │ -│ BOT_BOTTLE_OAUTH_TOKEN, GITHUB_TOKEN, │ +│ BOT_BOTTLE_CLAUDE_OAUTH_TOKEN, GITHUB_TOKEN, │ │ GITEA_SERVER_TOKEN, NPM_TOKEN │ │ │ docker run -e KEY (no =VALUE on argv) │ │ ▼ │ @@ -315,7 +315,7 @@ Why the agent can't reach the sidecar's environ: + validate route shape, role enum, path uniqueness, singleton- role constraints. - **`bot_bottle/backend/docker/prepare.py`** — drop the - legacy `BOT_BOTTLE_OAUTH_TOKEN` → `CLAUDE_CODE_OAUTH_TOKEN` + legacy `BOT_BOTTLE_CLAUDE_OAUTH_TOKEN` → `CLAUDE_CODE_OAUTH_TOKEN` forward entirely. cred-proxy is the only path the Anthropic OAuth token reaches the bottle. When a route claims the `anthropic-base-url` role, write `ANTHROPIC_BASE_URL` diff --git a/docs/prds/0011-per-file-md-manifest.md b/docs/prds/0011-per-file-md-manifest.md index b6494e7..afc3f90 100644 --- a/docs/prds/0011-per-file-md-manifest.md +++ b/docs/prds/0011-per-file-md-manifest.md @@ -261,7 +261,7 @@ cred_proxy: - path: /anthropic/ upstream: https://api.anthropic.com auth_scheme: Bearer - token_ref: BOT_BOTTLE_OAUTH_TOKEN + token_ref: BOT_BOTTLE_CLAUDE_OAUTH_TOKEN role: anthropic-base-url - path: /gitea/dideric/ upstream: https://gitea.dideric.is diff --git a/docs/research/agent-credential-proxy-landscape.md b/docs/research/agent-credential-proxy-landscape.md index 63ddaa5..dd4b8eb 100644 --- a/docs/research/agent-credential-proxy-landscape.md +++ b/docs/research/agent-credential-proxy-landscape.md @@ -78,7 +78,7 @@ The remaining credible designs reduce to three: ### Anthropic / Claude Code **Today's wiring** (`bot_bottle/cli/start.py`): the host's -`BOT_BOTTLE_OAUTH_TOKEN` is forwarded into the bottle as +`BOT_BOTTLE_CLAUDE_OAUTH_TOKEN` is forwarded into the bottle as `CLAUDE_CODE_OAUTH_TOKEN` via `docker run -e CLAUDE_CODE_OAUTH_TOKEN` (no `=value`, so the value never lands on argv — good). Inside the bottle, claude runs as `node` (UID 1000) with diff --git a/docs/research/claude-code-token-revocation.md b/docs/research/claude-code-token-revocation.md index 99efc16..37476b2 100644 --- a/docs/research/claude-code-token-revocation.md +++ b/docs/research/claude-code-token-revocation.md @@ -63,7 +63,7 @@ For a known-leaked or suspected-leaked token: 1. Revoke the entry at `claude.ai/settings/claude-code`. 2. Run "Log out all sessions" under Settings → Account → Active Sessions. 3. Run `claude setup-token` to mint a replacement, and rotate it into - `BOT_BOTTLE_OAUTH_TOKEN` immediately. + `BOT_BOTTLE_CLAUDE_OAUTH_TOKEN` immediately. 4. Email Anthropic support at `support.anthropic.com`. Security issues sometimes get attention that GitHub issues do not. diff --git a/docs/research/git-secret-scanning-hardening.md b/docs/research/git-secret-scanning-hardening.md index ef17df2..6ab5137 100644 --- a/docs/research/git-secret-scanning-hardening.md +++ b/docs/research/git-secret-scanning-hardening.md @@ -88,7 +88,7 @@ already on the attacker's box. Detection has to be at *commit* time Two surfaces are exposed: 1. **The bot-bottle repo itself.** Development happens on a host - with `BOT_BOTTLE_OAUTH_TOKEN`, Gitea tokens, and other + with `BOT_BOTTLE_CLAUDE_OAUTH_TOKEN`, Gitea tokens, and other credentials in the environment. A fixture, test snapshot, log capture, or pasted-in debug output could carry one of them into a tracked file. The repo's Gitea remote is private, but mirrors or diff --git a/docs/research/manifest-format-and-grouping.md b/docs/research/manifest-format-and-grouping.md index 8539686..4fc4995 100644 --- a/docs/research/manifest-format-and-grouping.md +++ b/docs/research/manifest-format-and-grouping.md @@ -254,7 +254,7 @@ cred_proxy: - path: /anthropic/ upstream: https://api.anthropic.com auth_scheme: Bearer - token_ref: BOT_BOTTLE_OAUTH_TOKEN + token_ref: BOT_BOTTLE_CLAUDE_OAUTH_TOKEN role: anthropic-base-url egress: allowlist: [example.com] diff --git a/docs/research/polish-priorities.md b/docs/research/polish-priorities.md index f0c15f4..5199fc9 100644 --- a/docs/research/polish-priorities.md +++ b/docs/research/polish-priorities.md @@ -33,7 +33,7 @@ on top of working onboarding. ### Onboarding friction A first-time user today goes through five steps: install Docker, -install `uv`, set `BOT_BOTTLE_OAUTH_TOKEN`, write +install `uv`, set `BOT_BOTTLE_CLAUDE_OAUTH_TOKEN`, write `bot-bottle.json`, run `./cli.py start`. One of those is "author a JSON manifest." Polished tools in this category let users skip that step on day one. The fix is an `init` subcommand diff --git a/docs/research/remote-docker-vm-isolation.md b/docs/research/remote-docker-vm-isolation.md index c161409..fc1b094 100644 --- a/docs/research/remote-docker-vm-isolation.md +++ b/docs/research/remote-docker-vm-isolation.md @@ -102,7 +102,7 @@ work: - **Typing latency.** Interactive Claude sessions over SSH have visible per-keystroke latency; usually fine on wired/fiber, less fine on Wi-Fi-to-cloud. Mosh helps if it's bothersome. -- **Token shipping.** `BOT_BOTTLE_OAUTH_TOKEN` has to live on the +- **Token shipping.** `BOT_BOTTLE_CLAUDE_OAUTH_TOKEN` has to live on the remote box for the launcher to forward it into containers. Use the provider's secret-injection path (cloud-init user-data, `flyctl secrets`, Tailscale-served local file, etc.). Never echo the @@ -130,7 +130,7 @@ The minimum-viable workflow, no bot-bottle code changes: (`curl -fsSL https://get.docker.com | sh`). 3. SSH in. 4. `git clone` bot-bottle on the VM, drop a manifest in place, - inject `BOT_BOTTLE_OAUTH_TOKEN` via the provider's secrets path. + inject `BOT_BOTTLE_CLAUDE_OAUTH_TOKEN` via the provider's secrets path. 5. `./cli.py start ` — the existing launcher handles the rest. 6. On exit: destroy the VM. No host artifacts persist. @@ -157,7 +157,7 @@ Build a custom OCI image `FROM docker:dind` that bakes in: the dind storage. Without this step, the first in-VM `docker build` runs `apt-get` and a global `npm install -g @anthropic-ai/claude-code`, which adds 30–90 s to every cold start. -- A `flyctl secrets`-injected `BOT_BOTTLE_OAUTH_TOKEN`, exposed to +- A `flyctl secrets`-injected `BOT_BOTTLE_CLAUDE_OAUTH_TOKEN`, exposed to the VM's PID 1 as an env var. - An entrypoint that starts dockerd, waits for it to be healthy, then either drops into a shell or directly runs `cli.py start `. diff --git a/docs/research/secret-minimization-over-dlp.md b/docs/research/secret-minimization-over-dlp.md index 2b055b0..f9c538c 100644 --- a/docs/research/secret-minimization-over-dlp.md +++ b/docs/research/secret-minimization-over-dlp.md @@ -79,7 +79,7 @@ The agent's conversation channel is therefore wide open as an exfil path. A prompt-injected agent that has been told a secret can ship it to Anthropic as conversation text, formatted however it likes, and pipelock sees only `CONNECT api.anthropic.com:443`. The -`BOT_BOTTLE_OAUTH_TOKEN` itself rides this exact path. +`BOT_BOTTLE_CLAUDE_OAUTH_TOKEN` itself rides this exact path. ### 3. Out-of-band channels exist regardless @@ -134,7 +134,7 @@ per-bottle gate that: Two concrete instances worth implementing: -**Anthropic-API gate.** Holds `BOT_BOTTLE_OAUTH_TOKEN`. Agent's +**Anthropic-API gate.** Holds `BOT_BOTTLE_CLAUDE_OAUTH_TOKEN`. Agent's `ANTHROPIC_BASE_URL` points at the gate; gate injects `Authorization: Bearer …` and forwards to api.anthropic.com. The token is no longer in the bottle's env. Once the token is out, diff --git a/examples/bottles/claude.md b/examples/bottles/claude.md index bc7994c..a47037a 100644 --- a/examples/bottles/claude.md +++ b/examples/bottles/claude.md @@ -8,7 +8,7 @@ egress: role: claude_code_oauth auth: scheme: Bearer - token_ref: BOT_BOTTLE_OAUTH_TOKEN + token_ref: BOT_BOTTLE_CLAUDE_OAUTH_TOKEN --- Common Claude provider boundary. Drop this file into diff --git a/scripts/demo-record.sh b/scripts/demo-record.sh index 907ba80..914d389 100755 --- a/scripts/demo-record.sh +++ b/scripts/demo-record.sh @@ -11,8 +11,8 @@ if ! command -v vhs >/dev/null 2>&1; then exit 1 fi -if [ -z "${BOT_BOTTLE_OAUTH_TOKEN:-}" ]; then - echo "demo-record: BOT_BOTTLE_OAUTH_TOKEN is unset; claude inside the bottle will not auth" >&2 +if [ -z "${BOT_BOTTLE_CLAUDE_OAUTH_TOKEN:-}" ]; then + echo "demo-record: BOT_BOTTLE_CLAUDE_OAUTH_TOKEN is unset; claude inside the bottle will not auth" >&2 exit 1 fi diff --git a/scripts/demo.sh b/scripts/demo.sh index 85571ad..1211e58 100755 --- a/scripts/demo.sh +++ b/scripts/demo.sh @@ -14,9 +14,9 @@ set -euo pipefail cd "$(dirname "$0")/.." -if [ -z "${BOT_BOTTLE_OAUTH_TOKEN:-}" ]; then +if [ -z "${BOT_BOTTLE_CLAUDE_OAUTH_TOKEN:-}" ]; then cat <<'EOF' >&2 -demo: BOT_BOTTLE_OAUTH_TOKEN is unset. The bottle launches claude, +demo: BOT_BOTTLE_CLAUDE_OAUTH_TOKEN is unset. The bottle launches claude, which needs the token to authenticate. Set it in your shell env (e.g. ~/.zshrc) — see README §Auth — then re-run. EOF