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:
@@ -110,6 +110,23 @@ reach its model, because "sandbox is broken" is a worse bug report than an expli
|
||||
refusal. And the bridge never widens the allowlist; it warns with the exact `--allow`
|
||||
to add, since an agent silently unable to answer looks like a network failure.
|
||||
|
||||
### 10 — The `.agents` share
|
||||
A sandboxed harness found none of your skills, prompt templates or standing `AGENTS.md`,
|
||||
because those live in `~/.agents` and only the project is mounted. It is now bind-mounted
|
||||
read-only at `/home/sandbox/.agents` by default (`agents`, `agents_dir`, `agents_dest`;
|
||||
`--agents`, `--no-agents`, `--agents-dir`), which is where pi looks for user-level skills.
|
||||
Bound, never copied, so the sandbox always sees the live tree and nothing of it lands in
|
||||
ai-sandbox state.
|
||||
|
||||
Read-only is the load-bearing part and there is deliberately no way to lift it: 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` (e2e-checked), the mount is nested
|
||||
inside the home volume *after* it so it cannot be shadowed, 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.
|
||||
|
||||
## Follow-ups (not yet done)
|
||||
|
||||
* **`--net unix` mode**: `--network=none` plus a unix-socket proxy and an in-container
|
||||
@@ -123,7 +140,6 @@ to add, since an agent silently unable to answer looks like a network failure.
|
||||
* **Quarantine mode for untrusted repos**: `--net none` + read-only mount +
|
||||
`--ephemeral-home` as a single flag, plus a warning if `.git` hooks exist.
|
||||
* **`--record`**: optional pcap/CONNECT-log capture of a session for review.
|
||||
* **`--mount` for agent skills/plugins**: first-class flag instead of a generic mount.
|
||||
* Packaging: a `quadlet`/systemd unit for a long-lived named gate.
|
||||
* Signature verification for the base where the registry signs it (cosign/sigstore),
|
||||
so a pin is authenticated rather than merely immutable; and an optional local
|
||||
|
||||
Reference in New Issue
Block a user