Skip to main content
A policy is the set of access rules Midplane enforces on every query: which tables an agent can read, which it can write, and everything it can’t touch. The model is identical whether you self-host or use Cloud — only where you author it differs.
Your policy is the Database pane on a project: a Table access list (which tables) and a What a write may do list (which statements). Edit both, hit Save once, and the running engine hot-reloads. A new database starts read-only.

Read-only by default

The starting point is always the same: all reads allow, all writes deny. A fresh Midplane is immediately safe — an agent can explore and query, but can’t change anything until you grant a write, one table at a time. See the policy engine for how each decision is made. A policy is built from four optional keys: A fifth key, databases, serves more than one database from one endpoint (0.2.0+); each entry carries its own copy of the four. Use databases or the top-level keys, not both — when a databases block is present, the top-level keys and DATABASE_URL are ignored. Every key, type, and default is in the policy schema reference.

The three access levels

Every table resolves to one of three levels. A query is denied if any table it touches fails its required permission.
read_write grants row writes, not whole-table destruction. A no-WHERE DELETE/UPDATE and any DROP/TRUNCATE/ALTER are refused even on a read_write table — so a write grant can’t be escalated into a wipe. For exactly what each level permits and how names resolve, see table access and guardrails; for the write classes, what a write may do.

Set a default, then override per table

Pick the level for any table you don’t list, then add the exceptions:
  • default: read — the recommended start. Reads everywhere, writes nowhere until granted.
  • default: deny — locked down. Only the tables you list are visible. Use it when the agent should see a named allowlist and nothing else.
Avoid default: read_write — it makes every unlisted table writable, including tables created later. midplane policy lint flags it as an error.
In the database’s Table access list, set Default access to read. Each row is a table; each column is a level. Add a row per exception and pick its level — overrides win over the default. Set feature_flags to read_write and audit_log to deny.
With this policy:
  • SELECT * FROM usersallowed (default: read).
  • DELETE FROM users WHERE id = 1denied (users is read-only).
  • INSERT INTO feature_flags (name) VALUES ('beta')allowed (feature_flags is read_write).
  • SELECT * FROM audit_logdenied (audit_log is deny, no access at all).

Schema-qualified names

Table keys may be bare (feature_flags) or schema-qualified (public.users, stripe.charges). Schema-qualified keys match first, so a canonical policy still matches agent SQL that uses bare names in public.
Bare references always resolve to public — Midplane pins each connection’s search path to public, pg_catalog. Tables outside public must be schema-qualified in both the policy and the agent’s SQL (for example app_data.orders: read plus FROM app_data.orders).

Serve multiple databases

One Midplane endpoint can front several databases (0.2.0+). Replace the top-level table_access with a databases array; each entry has its own connection URL and policy.
string
required
Short identifier the agent uses to pick the database — lowercase, starts with a letter, up to 32 characters.
string
required
The connection DSN. Supports ${ENV_VAR} interpolation, so DSNs and passwords stay in environment variables, not the file. An unset referenced variable fails loudly at boot.
string
default:"postgres"
Only postgres — omit it.
object
The same per-table policy as above, scoped to this database.
With two or more databases configured, the MCP tool surface adapts — query takes a database argument and a list_databases tool appears. A single database changes none of this.
Add each database’s URL under the project; each gets its own policy grid. The project exposes one endpoint, and the agent picks a database by name.

What a write may do

Table access answers which tables. A second set of rules answers what a write may do, and fires regardless of table access — a table you granted read_write still can’t be wiped or altered. Writes fall into three classes, each with its own independent rule (0.17.0+): Each class takes one of three values — refuse, ask, or allow — set in the dashboard’s What a write may do list, or in YAML across the guardrails (refuse) and approvals (ask) blocks. Ask holds the statement until a human approves it; see write approvals. Tightening a class is always safe: refuse can never let through something ask was holding. Two deeper invariants, multi-statement queries and unparseable SQL, are always denied and have no toggle anywhere. See what Midplane blocks for the full rule set.

Test before you ship

Try a statement runs one statement through the engine that enforces your policy and shows you its verdict, without touching your database. Saving then hot-reloads the running engine — no restart, no redeploy — and the edit is audited with the teammate who made it.
Column masking is authored separately — in Cloud, the project’s Exposure scan. See column masking.

Examples

Complete, copy-pasteable policies for the shapes that come up most. None set a guardrails block, so whole-table writes and schema changes stay refused — add one only to relax them.

SaaS product database

Reads open by default, a couple of operational tables writable, the audit log locked out entirely.

Feature flags only

Read everything, write exactly one table.

Writes, but a human sees each one

Grant the write, then hold it. Row changes to orders run only once an approver says yes; everything else is unchanged.
The engine needs a reviewer it can reach before this boots — see write approvals.

Multiple databases

A production database plus a read-only analytics warehouse through one endpoint (0.2.0+). DSNs come from environment variables via ${ENV_VAR}, so no secrets live in the file.

Reference

Policy schema

Every key, type, and default.

Table access & guardrails

How each level behaves, plus the guardrails and rules that deny dangerous shapes.

Write approvals

Hold a write until a human approves it, and what the agent does while it waits.

Author with the CLI

Scaffold, validate, lint, and test policies.