ai · tutorial · agents

Your Agent Passed Every Safety Check. Then It Deleted the Database File

SQL firewalls can't stop rm: SQLite is a file, and files die at the OS layer. The 5-command Linux cage (separate user + staging copy) that makes prod deletion kernel-impossible. Inside.

A mechanical bank vault time lock beside an electronic one
A mechanical bank vault time lock beside an electronic one · Photo: Banjodog, public domain, via Wikimedia Commons

Your cage has no floor

Picture the standard agent-safety stack in 2026: read-only database handles, a regex firewall blocking DROP and unbounded DELETE, a migration gate demanding human approval. Genuinely good work. Then the agent runs rm db/app.db — or overwrites it with garbage, or "tidies" the directory — and every layer watches helplessly. Nothing was disobeyed. The guards were never consulted. Deleting a file is an operating-system operation, and SQLite is only a file.

This is the exact shape of the PocketOS disaster: obedience treated as a control. An agent that ignores instructions ignores all of them equally — SQL rules included. Anything enforced by telling is bypassable by doing something you weren't told about.

BoringPush is the $199-once Next.js kit where this lesson is load-bearing. The SQL cage (read-only handles, firewall, human-gated migrations, off-site backups) already ships in the template. The filesystem cage below closes the hole SQL guards structurally cannot: the file itself.

Two cages, two enforcers

Threat SQL-layer guards Filesystem cage
DROP TABLE users Blocked (firewall + scanner) N/A
DELETE FROM orders (no WHERE) Blocked N/A
rm db/app.db Never consulted Permission denied (kernel)
Overwrite db with garbage Never consulted Permission denied (kernel)
Delete the staging copy N/A Allowed — rebuilt hourly, prod untouched
Argue with the enforcer Possible (prompt tricks) Impossible (kernel checks UID on every syscall)

The last row is the whole essay. Application guards constrain what the app allows. OS permissions constrain what the machine allows. Only one of them survives a disobedient agent.

Five commands, one standing user

The design fits in a paragraph: the agent runs as its own Linux user (agent, no sudo, key only). Prod's db directory is owned by the app user at mode 750 — the agent cannot list it, let alone delete from it. The agent works on a staging copy in its own directory, refreshed hourly by an atomic sqlite3 .backup cron job the agent never touches. Agent sessions are pointed at the copy, and the kit's read-only database handle for agents respects the same setting, so both layers agree on which file the agent sees.

Worst case, spelled out: the agent deletes the staging copy. The next hourly refresh rebuilds it. Production never flinches. That is the entire SQLite-destruction threat model, closed, on a $5 VPS with stock Linux — no containers, no special tooling, the same UID trick Android uses to sandbox Termux.

If you can think it, BoringPush can make it — and what buyers think after reading disaster postmortems is "make it so the agent physically cannot." The kit ships both cages: a read-only database handle for agents, and a filesystem setup guide in the docs that works on any VPS.

Verify it (don't trust it)

Cages get tested, not believed:

sudo -u agent ls /var/www/myapp/db          # → Permission denied ✅
sudo -u agent rm /var/www/myapp/db/app.db   # → Permission denied ✅
sudo -u agent touch /srv/agent-staging/db/probe  # → works ✅

FAQ

Doesn't the read-only SQLite flag already do this?

No — readonly: true governs SQL writes through that handle. It says nothing about rm, overwrite, or directory operations. Different layer, different enforcer.

Why not Docker instead of OS users?

Containers are a fine cage too, but they add image builds, volume mapping, and a daemon to babysit — overhead a solo founder feels weekly. UID + mode bits are free, auditable in five commands, and survive reboots without orchestration.

What about Supabase and MongoDB?

They're servers, not files — no rm equivalent exists. Their twin of this cage is credential separation: service keys absent from the agent's environment, least-privilege roles (app_agent/appAgent) only. Same principle, different layer.

One user per agent or per session?

Per agent (a standing agent user) is enough — sessions are time, users are identity. Go ephemeral per-session only for untrusted third-party agents.

I deleted my staging copy. Now what?

Wait up to an hour, or re-run the .backup command yourself. That's the point: the copy is disposable by design. If losing it scared you, imagine it had been prod.

+ all the boring stuff (analytics, auth, SQLite ↔ Supabase, full marketplace, webhooks)

The Agent-First Shipping Kit

Command from your phone.

Built so autonomous AI agents build what you want without breaking what's already paying you.

$199

Get BoringPush

One-time payment · Lifetime updates · Unlimited projects.

Keep reading