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:
2026-10-27 17:29:17 -04:00
parent 0242ab5b5e
commit 8ee535f6ae
14 changed files with 740 additions and 49 deletions
+10
View File
@@ -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