Protect a database
The wire proxy
This is gmonk's main path. The proxy sits in the database connection, so it does both jobs at once: it prevents statements it couldn't undo, and it captures the before-image of the ones it allows.
postgres:// URL, you're using the proxy.
Run it
# your database URL lives in an env var — never passed on the CLI or stored
export APP_DB_URL=postgres://user:pw@db-host:5432/appdb
gmonk protect --backend-ref APP_DB_URL --listen 6432
# then point the agent at gmonk instead of the database:
# postgres://agent@localhost:6432/appdb MySQL works the same way. gmonk protect picks
the dialect from the URL scheme, so a mysql:// backend launches
the MySQL wire proxy and the agent points at
mysql://agent@localhost:6432/appdb.
What happens to a statement
- Authenticate — the proxy records who the agent is.
- Connect as gmonk — it holds its own backend connection using gmonk's role, rather than relaying the agent's identity. This is what makes it compose with enforcement.
- Gate — every statement is scored by the reversibility
oracle.
DROP,TRUNCATEand aDELETEwith noWHEREare refused at the wire and never reach the database. - Capture, then forward — the before-image is snapshotted on the agent's own connection, in the same transaction as the write.
Reads are relayed verbatim, both directions. gmonk does not parse or re-encode result sets, so there is no risk of mangling them.
What it captures
Single-table UPDATE, DELETE and
INSERT — plain or parameterized, in autocommit or inside a
client-managed transaction. INSERT is captured with a
RETURNING to_jsonb(t) rewrite. A Parse batch on the
extended protocol is buffered to its Sync and gated as one unit.
Undo is operation-based where it can be: an additive change
like balance - 30 reverses as + 30, which
preserves a concurrent write instead of clobbering it with a stale
absolute value.
Hardening
| Flag / variable | What it does |
|---|---|
--guarantee | Prove-then-commit: refuse any write gmonk can't prove it can undo. Works inside a client transaction via SAVEPOINT-bracketed rollback. |
GMONK_PROXY_PASSWORD--tls-key / --tls-cert | Require a password and encrypt the client → gmonk hop. Needed to run beyond loopback. |
GMONK_PG_BACKEND_TLSGMONK_MYSQL_BACKEND_TLS | The gmonk → database hop. Host-aware by default: a remote target is require (gmonk refuses to send its credentials in cleartext), loopback is prefer. |
gmonk enforce | Revoke the agent's direct write grants so the proxy is the only way in. See Enforcement. |
Honest limits
- Binary-format bind parameters fall back to WAL or trigger capture — text params are inlined.
- Extended-protocol
Flushpipelining is not yet handled. - SCRAM (client- and backend-facing) is not implemented; MD5 client auth is.
- MySQL: guarantee mode is an advise-fallback — the
prove-then-commit rollback plumbing is Postgres-only for now. Multi-row and
non-auto-increment
INSERTare flag-only, and the blast-radiusminRowsguard is Postgres-only. - Only single-table statements. The proxy finds the affected
rows by parsing, so a multi-statement batch, a CTE, a cascade, a join or a
range update is not captured here — parsing can't prove which rows
those touched. They're flag-only unless the table also carries a
capture trigger, which observes the real
row changes. Same for binary bind params and statements with their own
RETURNING.