Skip to content
← Docs

Guardrails

Guardrails are policies matched against a query/command before it runs. Each rule has a regular-expression pattern and an action: allow, deny, or require_approval. Rules are evaluated by priority (lowest number first) and the first match wins; if nothing matches, the action is allow (RBAC still applies).

Rules are created and managed in the console at app.subnomic.com.

Create a rule

  1. Go to Guardrails → New rule.
  2. Give it a name and a regex pattern (matched case-insensitively against the statement).
  3. Choose an action and severity, and a priority (lower = checked first).
  4. Save. Toggle Enabled on/off any time.

Common rules (recipes)

Copy the pattern into a new rule and pick the action. Patterns are case-insensitive, so drop also matches DROP.

Guardrails run on every query before it executes — reads included. So yes, you can gate or block plain SELECTs too (e.g. require approval to read a sensitive table). RBAC's read/write split is separate and applies first.

SQL (Postgres / MySQL)

\bdrop\s+(table|database|schema)\bdeny

Block dropping tables, databases or schemas outright.

\btruncate\bdeny

Block TRUNCATE (instant, irreversible data wipe).

\b(delete|update)\b(?![\s\S]*\bwhere\b)deny

Block DELETE / UPDATE that has no WHERE clause (whole-table change).

\b(delete|update|insert)\brequire_approval

Require approval before any write (DELETE / UPDATE / INSERT).

\b(grant|revoke|alter\s+user|create\s+role)\bdeny

Block privilege / user-management statements.

\bfrom\s+payments\brequire_approval

Require approval to read a sensitive table (e.g. payments / pii) — yes, this gates SELECTs.

\bselect\s+\*require_approval

Discourage unbounded SELECT * (gate it behind approval).

\bselect\brequire_approval

Aggressive: require approval for any read on this target.

Redis

^\s*(flushall|flushdb)\bdeny

Block wiping the whole keyspace.

^\s*(config|shutdown|keys)\brequire_approval

Gate admin / expensive commands (CONFIG, SHUTDOWN, KEYS *).

Mongo

"(drop|dropDatabase|deleteMany)"deny

Block destructive Mongo commands (the query is a JSON command document).

SSH (server terminals)

Set the rule's target type to server (or * for everything). The pattern is matched against each command line you type — evaluated when you press Enter, before it reaches the host. A denied command never runs; the line is cleared and a notice is shown.

\brm\s+-rf?\b.*\s/(\s|$)deny

Block recursive deletes that touch the filesystem root.

\b(shutdown|reboot|halt|poweroff)\bdeny

Block commands that take the host down.

\bsystemctl\s+(stop|restart|disable)\brequire_approval

Gate service-disrupting actions behind a just-in-time grant.

SSH command rules are best-effort, not a hard boundary. They reconstruct the typed line, so a determined user can work around them (bash -c "…", base64 / eval, here-docs, :!cmd from an editor, history recall, or pasting multi-line scripts). Parsing is also suspended inside full-screen apps (vim, htop, less). Real enforcement stays in RBAC, the per-server require approval to connect flag, and full session recording — guardrails are a guard against mistakes and casual misuse on top.

Allow exceptions

Because the first matching rule wins, put a narrow allow rule at a lower priority (checked first) to carve an exception out of a broader deny:

# priority 10 — allow deletes on the scratch table
pattern:  \bdelete\s+from\s+scratch\b
action:   allow

# priority 100 — deny every other delete
pattern:  \bdelete\b
action:   deny
Regex tips: \b marks a word boundary so \bdrop\b doesn't match "dropdown"; \s+ matches any whitespace; ^ anchors to the start (handy for Redis commands). Patterns match the raw statement text, so they are a safety net, not a SQL parser — keep them simple and layer deny + require_approval.

What happens

  • deny — the query or command is blocked, recorded, and a system log is written.
  • require_approval — blocked unless the user holds an active just-in-time grant for that target (see Access requests). The database console shows a "Request access" button; the SSH terminal shows a dismissible banner without dropping the session.

Guardrails apply per-query to the database console and per-command to SSH terminals (set a rule's target type to server). If the rule set can't be loaded, evaluation fails closed — the statement is blocked rather than allowed.

Permissions

guardrail.read guardrail.manage