docs: explain Codex and Claude credential ownership

Squash-merges the adapter credential ownership doc PR.

- Documents codex_local host-owns-auth topology and CODEX_HOME auth precedence
- Documents claude_local snapshot-owns-auth topology (sandbox targets only)
- Adds API-key vs ChatGPT-subscription guidance for high-concurrency fleets
- Adds deferred config-validation warning spec for sandbox credential mismatches
- Fixes: SSH scope mismatch (Greptile P1) and codex auth.json materialization note (Greptile P2)

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Nicky Leach 2026-07-08 18:09:40 -07:00 committed by GitHub
parent 3288ec4f47
commit 187a929d97
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 133 additions and 2 deletions

View File

@ -8,7 +8,8 @@ The `claude_local` adapter runs Anthropic's Claude Code CLI locally. It supports
## Prerequisites
- Claude Code CLI installed (`claude` command available)
- `ANTHROPIC_API_KEY` set in the environment or agent config
- Either `ANTHROPIC_API_KEY` in adapter env/host env, or a Claude Code
subscription login available to the execution target
## Configuration Fields
@ -69,6 +70,33 @@ On-call checklist if you see this in production:
The adapter creates a temporary directory with symlinks to Paperclip skills and passes it via `--add-dir`. This makes skills discoverable without polluting the agent's working directory.
## Remote credential ownership
`claude_local` uses a snapshot-owns-auth topology for managed sandbox execution
targets. When the run uses a sandbox execution target and no explicit
`CLAUDE_CONFIG_DIR` is configured, Paperclip creates a remote
`CLAUDE_CONFIG_DIR` under the run's Claude runtime directory. It uploads
sanitized host-side settings such as `settings.json` and `CLAUDE.md`, but the
managed seed does not upload host Claude credential files.
After the seed is copied, the remote materialization command checks the
execution target's own `$HOME/.claude` directory. For each missing credential
file, it copies `.credentials.json` or `credentials.json` from that remote home
into the managed `CLAUDE_CONFIG_DIR`. That means credentials baked into the
sandbox image win for managed remote Claude runs.
Worked example: a sandbox image contains `$HOME/.claude/.credentials.json` from
its own Claude Code login. Paperclip starts a managed remote `claude_local` run,
uploads only the sanitized config seed, and sets `CLAUDE_CONFIG_DIR` to the
remote runtime config path. Because the managed config has no credential file,
the adapter copies the sandbox image's `$HOME/.claude/.credentials.json` into
that path before invoking Claude. The sandbox snapshot owns the credential for
the run.
This differs from [`codex_local`](/adapters/codex-local), where a
Paperclip-managed sandbox run uploads a host-owned `CODEX_HOME/auth.json` and
therefore shadows any Codex login already present inside the sandbox image.
For manual local CLI usage outside heartbeat runs (for example running as `claudecoder` directly), use:
```sh

View File

@ -8,7 +8,11 @@ The `codex_local` adapter runs OpenAI's Codex CLI locally. It supports session p
## Prerequisites
- Codex CLI installed (`codex` command available)
- `OPENAI_API_KEY` set in the environment or agent config
- Either a host Codex login with `~/.codex/auth.json`, or a per-agent
`OPENAI_API_KEY` configured in adapter env (Paperclip materializes this into
`$CODEX_HOME/auth.json` for managed homes; Codex CLI reads credentials from
`auth.json`, not directly from the process environment — for a self-managed
external `CODEX_HOME`, write `auth.json` there directly instead)
## Configuration Fields
@ -55,6 +59,83 @@ A managed home is created empty, so the adapter must provision auth into it befo
- **Genuine external overrides** (a `CODEX_HOME` outside the Paperclip-managed company tree) are treated as self-managed and are never seeded or overwritten.
- **Fail-fast guard:** if a managed home ends up with no usable `auth.json` and no configured API key, the run fails with an explicit `adapter_failed` ("no Codex credentials provisioned for managed home …") rather than emitting an unauthenticated request.
### Auth ownership and precedence
`codex_local` is host-owns-auth when Paperclip owns the effective
`CODEX_HOME`. The winning credential file is:
1. **Per-agent API key:** when adapter env contains a non-empty
`OPENAI_API_KEY`, Paperclip writes `$CODEX_HOME/auth.json` with only
`{ "OPENAI_API_KEY": "..." }`. This overwrites any existing file or symlink
at that path. Codex CLI versions that Paperclip supports read the key from
`auth.json`, not directly from the process environment.
2. **Host ChatGPT-subscription login:** when no per-agent key is configured,
Paperclip symlinks `auth.json` from the shared host Codex home into the
managed home. The symlink keeps rotating/single-use refresh tokens live
instead of copying a stale token into the managed home.
3. **External `CODEX_HOME`:** if adapter env points `CODEX_HOME` outside the
Paperclip-managed company tree, that home is self-managed. Paperclip does
not seed or overwrite it, so its own `auth.json` wins.
For sandbox or SSH execution, Paperclip uploads the effective managed
`CODEX_HOME` and launches Codex with `CODEX_HOME` pointing at that uploaded
directory. Any `auth.json` already baked into the sandbox image is shadowed in
managed-home mode. If the host has no usable `auth.json` and no per-agent
`OPENAI_API_KEY`, the managed run fails fast instead of falling back to an
in-sandbox login.
Worked example: a worker runs in a sandbox image that already has
`$HOME/.codex/auth.json`, and the Paperclip host is logged in with a ChatGPT
subscription. For a managed `codex_local` agent, Paperclip symlinks the host
`auth.json` into the agent's managed home, uploads that home to the sandbox,
and sets `CODEX_HOME` to the uploaded path. Codex reads the host-owned
uploaded file, so the sandbox image login does not win.
For high-concurrency sandbox fleets, prefer a per-agent `OPENAI_API_KEY` over a
shared ChatGPT-subscription login. API-key mode produces a standalone
`auth.json` for each managed home and avoids many concurrent sandboxes sharing
one rotating subscription credential. The tradeoff is billing: API-key mode is
metered per token through the OpenAI API, while ChatGPT-subscription auth uses
the subscription's flat-plan economics and quota behavior. Pick the mode
deliberately for the fleet's cost and concurrency profile.
### Deferred config-validation warning spec
This section specifies a warning that is not implemented yet. The warning should
help operators notice the host-owns-auth topology before they run a sandbox
fleet with ChatGPT-subscription credentials.
- **Source fields:** resolved Codex auth mode (`api` vs `subscription`) and
execution target kind (`local`, `remote:ssh`, or `remote:sandbox`). Infer the
auth mode from the final managed `$CODEX_HOME/auth.json` shape after seeding:
`{ "OPENAI_API_KEY": ... }` means API-key mode; subscription-token-shaped
host auth, including the symlinked host file, means ChatGPT-subscription
mode. Use top-level shape only; do not read credential values into the
warning. The target kind comes from `AdapterExecutionTarget`.
- **Transformations:** during config preparation or home seeding for a run,
classify `(subscription auth mode AND remote/sandbox execution target)` as a
warning condition. This is classification only. Do not read credential values
into the warning.
- **Sink fields:** emit a warning log line through `onLog("stderr", ...)` and,
when the config-validation surface supports it, a surfaced validation warning.
The sink may include only the auth-mode label and target type.
- **Retention:** warning text may appear in ephemeral run logs or validation
results. Do not persist auth material.
- **Attacker-observable IDs:** none new. The warning must not print token
values, email addresses, or `auth.json` contents.
- **Hook point:** wire the check at the `codex_local` execute/config-prep path
around managed-home seeding: `execute.ts` already has `executionTarget` /
target transport in scope, calls `seedManagedCodexHome`, and calls
`evaluateCodexCredentialReadiness` before uploading `CODEX_HOME`. If the
warning is factored into `codex-home.ts`, pass the resolved target
classification in rather than making `codex-home.ts` inspect execution
targets on its own.
Because the warning touches authentication behavior, implementation must go
through the security-review gate. Treat this docs section as the follow-up
implementation spec, not as authorization to add the warning in a docs-only
change.
## Manual Local CLI
For manual local CLI usage outside heartbeat runs (for example running as `codexcoder` directly), use:

View File

@ -31,6 +31,28 @@ When a heartbeat fires, Paperclip:
| [Process](/adapters/process) | `process` | Executes arbitrary shell commands |
| [HTTP](/adapters/http) | `http` | Sends webhooks to external agents |
## Credential ownership for sandbox targets
Local CLI adapters can run on the Paperclip host, SSH targets, or managed
sandbox targets. The adapter decides which credential home is authoritative
before the CLI starts:
| Adapter | Credential topology | Which credential file wins on managed sandbox targets |
|---------|---------------------|-------------------------------------------------------|
| [`codex_local`](/adapters/codex-local) | Host-owns-auth for Paperclip-managed `CODEX_HOME` | A host-owned `auth.json` is symlinked into the managed `CODEX_HOME` and uploaded to the sandbox. If a per-agent `OPENAI_API_KEY` is configured, Paperclip writes an API-key `auth.json` instead and that file wins. A login baked into the sandbox image is shadowed because Codex runs with Paperclip's uploaded `CODEX_HOME`. |
| [`claude_local`](/adapters/claude-local) | Snapshot-owns-auth for managed remote Claude config | Paperclip uploads only sanitized settings and skill/runtime assets. When the remote managed config has no Claude credential files, it copies `.credentials.json` or `credentials.json` from the sandbox image's own `$HOME/.claude`, so the image's login wins. |
Worked examples:
- **Codex sandbox with host ChatGPT login:** the host `~/.codex/auth.json`
is symlinked into the managed home, then uploaded as the sandbox
`CODEX_HOME`. Codex reads that uploaded file and does not use any
`auth.json` already present inside the sandbox image.
- **Claude sandbox with image login:** Paperclip materializes a remote
`CLAUDE_CONFIG_DIR`, then fills missing `.credentials.json` /
`credentials.json` from the sandbox image's own `$HOME/.claude`. The
snapshot's Claude login is the credential source for the run.
### Hermes local vs gateway
Use `hermes_local` when Paperclip should start the local `hermes` CLI on the