Date: 2026-08-30 00:52 UTC
Repository: $HOME/Documents/dotfiles
Scope: .local/bin/pi.sh, the Pi runner image, the parallel-agent extension, current Podman mounts, and the relevant upstream documentation.
The current design is close, but the full-home read-only bind currently in the working tree is the wrong protection for this machine. In this environment it also exposes nested Podman storage mounts, some of which are still writable.
The simplest fix is:
$HOME:$HOME:ro bind. Keep the earlier selective-mount design already present in HEAD.This preserves the normal workflow: Pi can edit the current workspace, Git metadata, Pi state, and published reports. It avoids adding Gondolin, Landlock, another user, or a second workflow.
There is one unavoidable boundary: a process cannot both write a host directory and be protected from a mistaken command that targets that same directory. For a completely protected audit, use a read-only workspace plus disposable state or a copy-on-write workspace. That should be a separate audit mode, not a replacement for the current coding workflow.
.local/bin/pi.sh starts Pi in a rootless Podman container.--read-only protects the container image filesystem, not writable host bind mounts.--volume "$HOME:$HOME:ro", then overlays writable mounts for the workspace, .git, ~/.pi, Pi state, and published sites..pi/agent/extensions/agents.ts starts parallel workers as child pi processes inside the same container. They are separate processes, not separate OS/container sandboxes; they inherit the same mounts and permissions.Path: .local/bin/pi.sh, the uncommitted --volume "$HOME:$HOME:ro" line.
Evidence: A disposable Podman run using the same full-home bind was inspected without changing files. The container could see $HOME/.local/share/containers/storage/overlay and overlay-containers; both reported writable with test -w. The current host has that storage on a separate read-write mount. A read-only parent bind did not make those nested mounts read-only here.
Why it matters: A broad rm -rf "$HOME" may encounter many nested mountpoints, and a targeted command can definitely operate on the exposed writable storage. This can delete or damage images, container metadata, merged filesystems, or running-service data. The full-home bind also makes every user-readable home file visible to Pi, including files that only needed to be inaccessible rather than read-only.
Smallest next step: Remove the full-home bind and restore the selective mounts from HEAD/commit fd1f26f. Do not rely on :ro on a parent directory to protect separate submounts.
Path: .local/bin/pi.sh, the /run/podman socket bind and CONTAINER_HOST/DOCKER_HOST variables.
Evidence: The wrapper exposes the host user's rootless Podman API. Podman's official documentation says the API grants full access to all Podman functionality, permits arbitrary code execution as the API user, and has no ability to limit or audit that access.
Why it matters: Even if the host storage path is not directly visible, an agent with the socket can ask the host Podman service to create, remove, inspect, or run containers and mount host paths. :ro on the socket file is not an API permission filter.
Smallest next step: Make the socket optional. Normal pi.sh runs should omit the socket and both API environment variables. Add a small explicit --podman wrapper option for the uncommon tasks that need nested Podman/Testcontainers. If those tasks are not needed, remove the socket support entirely.
Paths: workspace and Git common directory; ~/.pi; $XDG_STATE_HOME/pi; $XDG_DATA_HOME/published-sites; project .pi/agent when running from the dotfiles repository.
Evidence: These are explicitly mounted rw in pi.sh, because the normal workflow needs them. A controlled disposable test showed that rm -rf "$HOME" receives read-only errors at nested writable mount roots and did not delete the test files. A direct command aimed inside a writable mount did delete its test file.
Why it matters: The broad typo is helped by mount boundaries, but no mount can protect a directory that Pi must be allowed to modify. A command such as rm -rf "$HOME/.pi/*" remains destructive by design.
Smallest next step: Keep these mounts for normal coding. For unattended/read-only audits, use a separate mode with no host rw binds except a disposable copy-on-write workspace, and export/review the result afterward if needed.
Path: .pi/agent/extensions/agents.ts.
Evidence: RpcWorker.start() spawns pi directly with cwd and inherited environment. It does not call pi.sh or create another Podman container. The extension also passes the parent session's active tools to every worker.
Why it matters: One worker can affect the same writable mounts used by the parent and sibling workers. A prompt saying “read-only” is not an enforcement mechanism.
Smallest next step: Rename/document this as process-level parallelism. For audit jobs, start Pi with a read-only tool allowlist where possible (read, grep, find, ls); this is only a tool restriction, not a security boundary. The container mount changes above remain the real protection.
Keep the existing rootless Podman runner and its useful hardening:
--userns=keep-id;no-new-privileges;Use only selective host mounts:
rw: current workspace and required Git common directory;rw: ~/.pi, Pi state, and published-sites;ro: config, AWS profiles, scripts, libraries, and web helper;This is a small rollback plus one opt-in switch, rather than a new sandbox architecture.
rm -rf, but it can only see Pi tool calls. Shell indirection, Python, or another process can bypass pattern matching. Use as a warning layer, not the boundary.write/edit operations still need separate routing. More complexity than this setup needs today.:O overlays: protect host directories by making changes container-local, but normal edits/state no longer appear in the current host paths without an export/merge step.sh -n .local/bin/pi.sh successfully.no-new-privileges, and private bind propagation.Accessed 2026-08-30 UTC:
run, read-only roots, bind volumes, and user namespaces: https://docs.podman.io/en/latest/markdown/podman-run.1.html