bridge the host pi config, with the credential by env only
The image ships pi with no provider and no preferences, so a sandboxed run
meant configuring the model twice. ai-sandbox now reads the host
~/.pi/agent/{models,settings}.json and rewrites them instead of copying them:
every provider apiKey becomes "$AI_API_TOKEN" (pi resolves an env reference
itself, so no value is written to disk), '!command' header values are dropped
rather than executed, settings that point into $HOME are dropped because that
path is not mounted, and auth.json / models-store.json / sessions / extensions
are never read.
The rewritten pair is seeded into /home/sandbox/.pi/agent at container start,
owned by uid 1000 and mode 600, and is treated as managed: a host change is
re-seeded, an edit made inside the sandbox is kept and reported, a deletion
takes the host version again.
The token follows the existing environment rules -- one variable, passed by
name through the 0600 env file, announced when auto-passed. A bridged run
whose token variable is unset or empty is refused instead of starting a pi
that cannot reach its model, and the bridge never widens the allowlist: it
warns with the exact --allow to add.
Docs and examples now use a self-hosted gateway (https://aipi.reeseapps.com)
instead of vendor API endpoints, since that is the only thing this tool is
used against. 391 assertions pass (make lint, ./tests/run.sh --e2e).
LLM-Generated: true
This commit is contained in:
+59
-19
@@ -7,7 +7,14 @@ Concrete invocations. `pi` below is whatever you aliased to `ai-sandbox`.
|
||||
```bash
|
||||
cd ~/Projects/myapp
|
||||
ai-sandbox doctor # is this machine ready?
|
||||
pi --allow api.anthropic.com --env ANTHROPIC_API_KEY
|
||||
pi --allow aipi.reeseapps.com --env AI_API_TOKEN
|
||||
```
|
||||
|
||||
With your pi config bridged, the token is found on its own, so the allowlist is the
|
||||
only thing you have to say:
|
||||
|
||||
```bash
|
||||
pi --allow aipi.reeseapps.com
|
||||
```
|
||||
|
||||
Same thing, but remembered by the repo (so teammates review the policy, not just
|
||||
@@ -15,25 +22,58 @@ your shell history):
|
||||
|
||||
```bash
|
||||
mkdir -p .ai-sandbox
|
||||
printf 'api.anthropic.com\n' > .ai-sandbox/allow
|
||||
pi --env ANTHROPIC_API_KEY
|
||||
printf 'aipi.reeseapps.com\n' > .ai-sandbox/allow
|
||||
pi
|
||||
```
|
||||
|
||||
## Pi's own configuration
|
||||
|
||||
The bridge rewrites your host `~/.pi/agent/{models,settings}.json` instead of copying
|
||||
it: providers, models, thinking budgets and preferences come along, the credential
|
||||
becomes `"$AI_API_TOKEN"`, and anything that only works on the host (a `!command`
|
||||
header, a path into `~`) is dropped with a warning. `auth.json` is never bridged.
|
||||
|
||||
```bash
|
||||
# the everyday case: same provider as the host, token from the environment
|
||||
pi --allow aipi.reeseapps.com
|
||||
|
||||
# a model your gateway serves, chosen per run
|
||||
pi --allow aipi.reeseapps.com --model juggernaut
|
||||
|
||||
# keep the model's endpoint pinned by IP as well, on a LAN inference host
|
||||
pi --allow aipi.reeseapps.com --allow 10.20.0.7:11434
|
||||
|
||||
# a different variable name for the credential
|
||||
pi --allow aipi.reeseapps.com --pi-token-env GW_TOKEN
|
||||
|
||||
# do not hand the token over automatically: the run then fails closed unless you
|
||||
# pass it yourself, which is what you want in shared scripts
|
||||
pi --allow aipi.reeseapps.com --env AI_API_TOKEN --no-pi-token-env
|
||||
|
||||
# no bridge: pi starts with its own defaults inside the volume
|
||||
pi --no-pi-config --allow aipi.reeseapps.com
|
||||
```
|
||||
|
||||
Edits made inside the sandbox are kept; changes to the host config are picked up on
|
||||
the next run; deleting `~/.pi/agent/models.json` inside the sandbox re-seeds it.
|
||||
|
||||
## Per harness
|
||||
|
||||
```bash
|
||||
# pi (baked into the image)
|
||||
# pi (baked into the image, config bridged)
|
||||
pi --allow aipi.reeseapps.com
|
||||
|
||||
# pi with a model chosen on the command line
|
||||
pi --harness 'pi --model turbo' --allow aipi.reeseapps.com
|
||||
|
||||
# a gateway that speaks a different protocol still needs only its host
|
||||
pi --allow gateway.internal:8443 --env AI_API_TOKEN
|
||||
|
||||
# OpenAI Codex CLI / Claude Code: they read their own config files, so mount or
|
||||
# generate what they need and allow their endpoints; the pi bridge does not apply
|
||||
pi --allow chatgpt.com --env CODEX_API_KEY
|
||||
pi --allow api.anthropic.com --env ANTHROPIC_API_KEY
|
||||
|
||||
# pi with a proxy/harness you pass explicitly
|
||||
pi --harness 'pi --model sonnet' --allow api.anthropic.com --env ANTHROPIC_API_KEY
|
||||
|
||||
# OpenAI Codex CLI (install it into the image with --install or use a custom base)
|
||||
pi --allow chatgpt.com --allow api.openai.com --env OPENAI_API_KEY
|
||||
|
||||
# Claude Code
|
||||
pi --allow api.anthropic.com --env ANTHROPIC_API_KEY --env CLAUDE_CODE_OAUTH_TOKEN
|
||||
|
||||
# just a shell in the sandbox
|
||||
pi --shell
|
||||
|
||||
@@ -64,7 +104,7 @@ pi --allow proxy.golang.org --allow sum.golang.org
|
||||
pi --allow registry-1.docker.io --allow auth.docker.io --allow production.cloudflare.docker.com
|
||||
|
||||
# a project that also needs its dev database (non-HTTP: pinned IP + port)
|
||||
pi --allow api.anthropic.com --allow 10.20.0.5:5432 --env ANTHROPIC_API_KEY
|
||||
pi --allow aipi.reeseapps.com --allow 10.20.0.5:5432
|
||||
```
|
||||
|
||||
## Git
|
||||
@@ -77,7 +117,7 @@ pi --allow github.com --env GH_TOKEN -- git push origin main
|
||||
pi --allow github.com --mount ~/.ssh:ro -- git push origin main
|
||||
|
||||
# read-only view of a sibling repo for context
|
||||
pi --allow api.anthropic.com --env ANTHROPIC_API_KEY --mount ../docs:/docs:ro
|
||||
pi --allow aipi.reeseapps.com --mount ../docs:/docs:ro
|
||||
```
|
||||
|
||||
## Tightening a run
|
||||
@@ -96,12 +136,12 @@ pi --strict-args --net allow --allow-file .ai-sandbox/allow --env-file .ci/env -
|
||||
## Long-lived gate shared by several sandboxes
|
||||
|
||||
```bash
|
||||
ai-sandbox gate up --allow api.anthropic.com --allow registry.npmjs.org
|
||||
ai-sandbox gate up --allow aipi.reeseapps.com --allow registry.npmjs.org
|
||||
# name=aisb-gate
|
||||
# port=18081
|
||||
# proxy_env=export HTTPS_PROXY=http://100.64.0.1:18081
|
||||
|
||||
ai-sandbox --proxy 100.64.0.1:18081 --allow api.anthropic.com --shell
|
||||
ai-sandbox --proxy 100.64.0.1:18081 --allow aipi.reeseapps.com --shell
|
||||
ai-sandbox gate logs # what did it allow/deny?
|
||||
ai-sandbox gate down
|
||||
```
|
||||
@@ -132,13 +172,13 @@ harness = pi
|
||||
cpus = 8
|
||||
memory = 8g
|
||||
mount = ../specs:/specs:ro
|
||||
env = ANTHROPIC_API_KEY
|
||||
env = AI_API_TOKEN
|
||||
```
|
||||
|
||||
`.ai-sandbox/allow`:
|
||||
|
||||
```
|
||||
api.anthropic.com
|
||||
aipi.reeseapps.com
|
||||
registry.npmjs.org
|
||||
```
|
||||
|
||||
|
||||
@@ -70,6 +70,16 @@ Be honest about these, and design your work accordingly.
|
||||
10. **Rootless podman still means shared kernel and shared UID namespace.** Files
|
||||
the container writes are owned by *you*. There is no uid boundary between
|
||||
"agent's file" and "your file" beyond ownership metadata and your own discipline.
|
||||
11. **The pi bridge reads a file that may hold a live key.** It rewrites `apiKey` to
|
||||
an environment reference and never copies `auth.json`, but the *endpoint* in your
|
||||
`models.json` becomes an endpoint the sandbox can be pointed at, and the token it
|
||||
uses is your real one. So the bridged config is a convenience, not a narrower
|
||||
credential: prefer a per-sandbox token from your gateway if it can mint one, and
|
||||
note that a bridged run whose token variable is unset or empty is refused rather
|
||||
than started. Sandboxed edits to `~/.pi/agent` persist in the home volume and are
|
||||
kept over later host changes (see the README), which is convenience with a
|
||||
consequence: a hostile run could rewrite the sandboxed pi config for later runs of
|
||||
that volume. `--ephemeral-home` removes that.
|
||||
|
||||
## Residual risk rating
|
||||
|
||||
|
||||
Reference in New Issue
Block a user