Status: v1 shipped (Anthropic auto-pipe only); Codex/Gemini + markdown export = v1.1
Slug: sandbox-byok-free-runtime
Last updated: 2026-06-05
Archived planning sections (ToT alternatives, Reflexion gates, assumption checks, implementation plan checklist, open questions) lived here pre-v1; they're now in git history. This file keeps only the decisions and the boundary model.
bin/sandbox.sh up with zero LLM-auth configuration. The sandbox
inherits the host's LLM provider auth (Anthropic OAuth, OpenAI Codex
token, etc.), pipes it via the existing tmpfs / docker exec mechanism,
and lets the user run any LLM CLI inside the container as if on the
host. Sandbox-side state (conversations, prompt cache, MCPs) persists
across docker rm in a per-login named volume; transcripts also export
as markdown to the host inbox.
Verifiable success: a fresh-machine user with Claude Code installed on
host can run bin/sandbox.sh up && claude inside the container and get
a working prompt without supplying any keys, env vars, or config.
- Auto-pipe with probe-and-skip per provider. Cheap and graceful; reconsider explicit opt-in if maintenance crosses ~3 providers.
- Persistent sandbox-side LLM state in a per-login named volume. Local first; remote sync (git/Obsidian Sync) deferred. Markdown export to host inbox makes the archive Obsidian-readable by construction.
- Users install MCPs as needed. No baked-in MCP servers.
- Full inherited scope. No attempt to mint a read-only sub-token.
Rejected: wholesale bind-mount of host ~/.claude/ (tunnels work
context — Linear/Slack MCP configs, host conversations); container-side
OAuth flow (headless device-code is fragile, host already has the
session).
| Provider | Host source | Container destination | Probe |
|---|---|---|---|
| Anthropic (Claude Code) | ~/.claude/.credentials.json (0600) |
~/.claude/.credentials.json (0400, in named volume) |
[[ -s ~/.claude/.credentials.json ]] |
| OpenAI (Codex) | ~/.codex/auth.json if exists |
~/.codex/auth.json (named volume) |
[[ -s ~/.codex/auth.json ]] |
| Gemini | ~/.config/gemini/credentials.json if exists |
same path (named volume) | [[ -s ~/.config/gemini/credentials.json ]] |
All piped via docker exec -i sh -c 'cat > /run/secrets/<provider>_token'
(the established GH_TOKEN pattern). Entrypoint reads → writes to the
in-container destination → shred -us the tmpfs file. Token never
appears in -e env, --build-arg, image layers, or docker inspect.
Mounted at /workspace/home/.config/llm-state/:
claude/projects/— conversations started inside the sandbox. Isolated from host~/.claude/projects/(work conversations never cross the boundary).claude/cache/,claude/agents/— prompt cache, downloaded models.- Per-provider mirrors for Codex / Gemini.
On periodic autosave + EXIT trap, export new Claude Code sessions from
<login>-llm/claude/projects/*.jsonl to:
$SANDBOX_INBOX_DIR/conversations/<iso-ts>/<convo-slug>.md
Standard session-to-markdown rendering (frontmatter + one ## user /
## assistant section per turn). Readable in Obsidian; a user can mv
an export into people/<ldap>/active/<slug>.md to bring it under
worklog FSM. Git-syncable independently of the named volume.
- Container FS (apt installs, /tmp).
- Tmpfs
/run/secrets/*(auto, by design). - Shell history (unless user persists
~/.bash_historyinto<login>-toolchains). - Reasoning traces / tool-call logs not yet exported by autosave.
- Host
~/.claude/projects/(work conversations + work MCP configs). - Host
~/.claude/settings.jsonhooks (sandbox has its own minimal hooks; never inherits the host autosave/compact-kernels chain). - Host SSH keys.
- Host
~/.config/<work-mcp>/(Linear, Slack, work Gmail).
GITHUB_TOKEN/GH_ENTERPRISE_TOKENrefused at entrypoint.SANDBOX_REFUSE_PATTERNSactive (user-configurable employer-shape filter).- Anthropic OAuth tokens are user-personal (no employer dimension at
the OAuth provider level), so forwarding them doesn't widen the
boundary the way
GITHUB_TOKENwould.