# Panic Room, a local zero-dependency sandbox for agents

> Source: <https://github.com/storesm/panicroom>
> Published: 2026-09-02 15:15:40+00:00

Open source, MIT. Your agent gets a copy of the repo, fake credentials, a throwaway database and a container. You get an audit trail and a diff to sign off.

```
ln -s $PWD/panic.py ~/.local/bin/panic

cd ~/code/myapp
panic run --db -c "claude -p 'refactor the billing module'"
panic shot <id> http://localhost:3000 --serve "npm run dev"
panic report <id>      # files, commands, sql, egress, screenshots
panic diff <id>
panic approve <id>     # applies to the real repo, unstaged
panic rm <id>
```

First run builds a `panicroom:base`

image (node 22, git, chromium, psql, claude-code). After that a
sandbox costs a `git clone`

.

**Files.** The repo is cloned to `~/.panicroom/<id>/repo`

, your uncommitted work is carried over and
committed as a baseline, so every later diff is the agent's doing and not yours. `panic diff`

is a
plain git diff; `approve`

is a plain `git apply`

, left unstaged for you to read before committing.
No filesystem watcher, git already knows.

**Commands.** `$HOME/.claude/settings.json`

in the sandbox carries a PreToolUse hook that appends
every tool call to `/panic/audit.jsonl`

. That is the agent's real actions, not scraped terminal
output, and it lives outside the repo so it never shows up in the diff.

**Database.** `--db`

starts a throwaway postgres beside the sandbox with `log_statement=all`

and
points every SQL-shaped env var at it. On exit the log is kept and `panic report`

shows the writes,
reads filtered out. `--db-schema dump.sql`

seeds it so the agent works against your real shape.

**Network.** A proxy bound to the docker gateway, never the LAN. Every host the sandbox connects to
is logged, and every host that appears in your real `.env`

is refused with a 403. If the agent tries
to reach prod, it fails and the attempt is on the record. Hosts and ports, no TLS interception.

**Screenshots.** `panic shot`

starts your dev server inside the sandbox, waits, and drives headless
chromium at it. PNGs land in the sandbox audit dir.

Every `.env*`

in the source is read for its keys, never its values. URLs keep their scheme and point
at localhost, connection strings become `panic:panic@localhost`

, everything else becomes
`panic-dummy-<key>`

. The generated `.env`

is git-excluded, so it can never ride along into `approve`

,
and it is passed to the container as `--env-file`

so shell tools see it too. Only your agent API key
crosses the boundary from your own environment.

Check: `python3 test_panic.py`

(round trip, proxy allow and block, sql filtering, no creds in the patch).

Teams, billing, a web dashboard, a hosted control plane. One founder, one repo, one terminal.

Issues and pull requests are welcome. Keep it to one Python file with no dependencies outside the
stdlib, and make `python3 test_panic.py`

pass.

MIT, see [LICENSE](/storesm/panicroom/blob/main/LICENSE).
