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
112 lines
4.4 KiB
Plaintext
112 lines
4.4 KiB
Plaintext
# config for ai-sandbox -- copy to ~/.config/ai-sandbox/config and edit
|
|
#
|
|
# Format: key = value. '#' starts a comment.
|
|
# Precedence: built-in default < this file (user) < ./.ai-sandbox/config (repo)
|
|
# < AISB_* environment variable < command line flag.
|
|
#
|
|
# Keys listed under "repeatable" accumulate; every other key replaces.
|
|
|
|
# --- network ---------------------------------------------------------------
|
|
# none | allow | full. allow is the safe default and requires an allowlist.
|
|
net = allow
|
|
|
|
# Address that stands for the host's loopback inside the container
|
|
# (--network=pasta:--map-host-loopback,<addr>). Change only if it collides
|
|
# with a real network you need to reach.
|
|
# gate_addr = 100.64.0.1
|
|
|
|
# Preferred first port for the per-session egress gate; the next free port is
|
|
# used if it is busy.
|
|
# gate_port = 18081
|
|
|
|
# --- harness ---------------------------------------------------------------
|
|
# Default in-container command: pi | codex | claude | none | "<your command>"
|
|
# harness = pi
|
|
|
|
# --- mounts and layout -----------------------------------------------------
|
|
# workdir = /project
|
|
|
|
# --- limits ----------------------------------------------------------------
|
|
# cpus = 4
|
|
# memory = 4g
|
|
# pids = 512
|
|
# tmpfs_size = 1g
|
|
|
|
# --- image -----------------------------------------------------------------
|
|
# One base for both images (the gate is squid from this distro's apt, not a
|
|
# third-party image). base_image may itself be pinned: name@sha256:...
|
|
# base_image = docker.io/library/ubuntu:24.04
|
|
# node_version = 22.23.4
|
|
# pi_version = 1.4.0
|
|
#
|
|
# The tag is resolved to a registry digest and that digest becomes part of the
|
|
# image tag, so builds are reproducible and a base patch is visible instead of
|
|
# quietly folding into an existing image. Every base_ttl_days the digest is
|
|
# re-resolved -- a manifest read, not a download. 0 re-resolves on every build,
|
|
# -1 never does.
|
|
# base_ttl_days = 7
|
|
#
|
|
# 0 keeps the recorded digest even past its TTL: nothing reaches the registry
|
|
# unless you say so. An explicit refresh is `ai-sandbox update [--check]`.
|
|
# base_auto_update = 1
|
|
|
|
# --- pi --------------------------------------------------------------------
|
|
# Bridge ~/.pi/agent/{models,settings}.json into the sandbox, with every provider
|
|
# apiKey replaced by an environment reference (nothing secret is written into the
|
|
# container). 0 = pi starts with its own defaults.
|
|
# pi_config = 1
|
|
#
|
|
# The variable the bridged config reads the credential from, and whether ai-sandbox
|
|
# passes that one variable automatically when it is set. If it reaches neither, the
|
|
# run is refused instead of launching a pi that cannot talk to its model.
|
|
# pi_token_env = AI_API_TOKEN
|
|
# pi_token_auto = 1
|
|
#
|
|
# Where to read the host config from (default ~/.pi/agent).
|
|
# pi_agent_dir = ~/.pi/agent
|
|
|
|
# --- skills and standing instructions ---------------------------------------
|
|
# Your host ~/.agents is bind-mounted read-only at /home/sandbox/.agents, which is
|
|
# where pi (and the other harnesses) look for user-level skills, prompt templates and
|
|
# standing instructions. Read-only, and there is no way to make it writable: a skill is
|
|
# an instruction the model follows later, on the host, so a sandbox that could write one
|
|
# would have a persistence path out of itself. 0 = your skills stay at home.
|
|
# agents = 1
|
|
#
|
|
# Which directory to share ('~' is expanded). A project's own .agents/ needs no setting
|
|
# here: it comes in with the project mount, alongside this one.
|
|
# agents_dir = ~/.agents
|
|
#
|
|
# Where it lands in the container. Keep it under HOME -- that is the only place a
|
|
# harness looks for user-level skills.
|
|
# agents_dest = /home/sandbox/.agents
|
|
|
|
# --- behaviour -------------------------------------------------------------
|
|
# SELinux bind-mount option: ':z' (shared), ':Z' (private) or empty to disable.
|
|
# Defaults to ':z' when getenforce exists.
|
|
# selinux = :z
|
|
|
|
# Persist /home/sandbox in a named volume so harness logins and tool caches
|
|
# survive between runs. 0 = use a throwaway tmpfs instead.
|
|
# home_cache = 1
|
|
|
|
# Appended verbatim to the podman invocation (power users; word-split).
|
|
# extra_args = --annotation io.k8s.description="agent sandbox"
|
|
|
|
# --- repeatable keys -------------------------------------------------------
|
|
# These may appear more than once, and are merged with --allow / --env / etc.
|
|
#
|
|
# allow = aipi.reeseapps.com
|
|
# allow = registry.npmjs.org:443
|
|
# allow = 10.20.0.5:5432
|
|
#
|
|
# env = AI_API_TOKEN
|
|
# env = CI_JOB_TOKEN
|
|
#
|
|
# mount = ~/.ssh:ro
|
|
# mount = ../docs:/docs:ro
|
|
#
|
|
# install = postgresql-client
|
|
#
|
|
# label = team=platform
|