share the host ~/.agents read-only, so skills come along

A sandboxed harness found none of your skills, prompt templates or
standing AGENTS.md, because those live in ~/.agents and only the project
was mounted. It is now bind-mounted at /home/sandbox/.agents by default
-- bound, never copied -- with --no-agents to opt out and --agents-dir
for a curated set, reported by --print-policy and by doctor.

Read-only is the load-bearing part and has no escape hatch: a skill is an
instruction the model follows later, on the host, so a sandbox that could
write ~/.agents had a persistence path out of itself, planted as a
helpful-looking SKILL.md. It holds even under --insecure-keep-root, the
mount lands after /home/sandbox itself so it cannot be shadowed by the
home volume or tmpfs, and an explicit --mount on the same destination
wins because podman refuses duplicates. A missing or unreadable directory
is a silent skip like the pi bridge's; an explicit --agents-dir that
resolves to nothing is a hard error.

The exposure is stated in docs/threat-model.md: the tree, its .git history
and anything pasted into a skill are readable by whatever runs in there.

tests/unit/09_agents.sh covers resolution, precedence and the
bind-never-copy canary; tests/e2e/12_sandbox.sh mounts it in a real
container, checks the host tree survives a run byte for byte and that
even root inside the sandbox cannot write it.

LLM-Generated: true
This commit is contained in:
2026-10-27 20:30:53 -04:00
parent 8ee535f6ae
commit ed2eb462d6
10 changed files with 437 additions and 18 deletions
+29 -2
View File
@@ -57,6 +57,33 @@ 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.
## Your skills and standing instructions
`~/.agents` is shared read-only at `/home/sandbox/.agents` by default, so the sandboxed
harness finds the same skills, prompt templates and `AGENTS.md` you use on the host.
A project's own `.agents/` needs nothing: it arrives with the project mount, and harnesses
that read both see the project's alongside yours.
```bash
# the everyday case: your skills are simply there
pi --allow aipi.reeseapps.com
# a run where the harness should not have them: reviewing untrusted code, or a
# skills set that mentions internal hosts you would rather it not repeat back
pi --net none --no-agents -- bash -lc 'make lint'
# a curated set for sandboxes -- e.g. host skills stay yours, this dir is what you share
pi --agents-dir ~/.config/ai-sandbox/agents --allow aipi.reeseapps.com
# two sets, per trust level, chosen by config file in a repo
# .ai-sandbox/config: agents_dir = ./.agents-shared
```
It is read-only and there is no flag to change that: a skill is an instruction you
follow on the host later, so letting a sandbox write one would let it persist past the
run. If a workflow really needs to *author* skills, do it in the project directory and
move the result into `~/.agents` yourself, where you can read it first.
## Per harness
```bash
@@ -126,8 +153,8 @@ pi --allow aipi.reeseapps.com --mount ../docs:/docs:ro
# nothing persistent, nothing readable, nothing writable outside the repo
pi --net none --ephemeral-home --read-only -- make build
# an entirely blank sandbox for untrusted code
pi --net none --no-cwd --ephemeral-home --read-only -- bash
# an entirely blank sandbox for untrusted code: no project, no home, no skills
pi --net none --no-cwd --ephemeral-home --read-only --no-agents -- bash
# CI-ish: refuse to start if the policy or environment is not exactly right
pi --strict-args --net allow --allow-file .ai-sandbox/allow --env-file .ci/env -- npm ci