THREAT_MODEL.md in the repo.
If an attacker can reach your database without going through Midplane, Midplane never sees it.
Trust boundaries
Every query crosses the same boundaries in the same order:Vectors covered
These are the attack shapes Midplane is built to stop. Each is pinned by the adversarial corpus.SQL injection via raw text in MCP arguments
SQL injection via raw text in MCP arguments
The agent hands Midplane SQL as text. Midplane parses it into an AST with
libpg_query, the real Postgres parser — no regex on SQL, ever. Anything that doesn’t parse is denied by parse_error. Midplane never enforces policy on text it can’t read.Multi-statement (stacked-statement) injection
Multi-statement (stacked-statement) injection
The classic
SELECT 1; DROP TABLE users vector — the same class disclosed by Datadog Security Labs against the deprecated Anthropic Postgres MCP. The parser counts statements, not semicolons, so comments and string literals don’t inflate the count. multi_statement denies anything that parses to more than one statement.Destructive writes against production
Destructive writes against production
table_access denies any write whose target table isn’t read_write in your policy — INSERT, UPDATE, DELETE, MERGE, DROP, TRUNCATE, ALTER, GRANT, REVOKE, CREATE, EXECUTE, CALL, COPY, and more, at any AST depth. With no policy at all, every write denies. Grants are explicit and per-table.CTE-embedded writes
CTE-embedded writes
WITH x AS (DELETE FROM y RETURNING *) SELECT * FROM x looks like a SELECT at the top level. The recursive AST walk catches the inner INSERT/UPDATE/DELETE at any depth and checks it against that target’s permission.Vectors out of scope
These are deliberately not covered. Stating them plainly is the point — Midplane secures the path through it, and these sit outside that path.“Out of scope” means Midplane makes no claim here — not that the risk doesn’t exist. The same spirit runs through what we claim, and what we don’t: we name what we don’t cover rather than imply we cover everything.
Self-host vs hosted
Both deployment models run the same open-source engine, so the enforcement is identical. The trust difference is entirely about where the container runs and who holds your credentials — where it runs, not what code runs. See Cloud vs self-host for the operational side.Residual exposure
Even when Midplane does exactly its job, some exposure remains. Self-host has none of Midplane’s infrastructure in the picture — your database URL stays in your network and the audit log is a Postgres you run; the trust question reduces to whether you trust the MIT code you’re running, which you can read before you run it. Hosted adds the residual that comes with managing credentials for you:- Decrypted credentials in process memory for the cache TTL plus a short grace window — see credential encryption for the exact TTL. Never written to disk.
- Connection pools held warm during the cache window; a process compromise inside that window exposes warm connections.
- The Postgres index of audit data is queryable by Midplane operators with database access until retention expires.
- In-flight session cookies and MCP tokens, as for any HTTP service.
What we claim, and what we don’t
Security marketing is full of claims that don’t survive contact with how a system actually works. Here’s what Midplane does not claim, stated plainly:- “We don’t see your queries.” We do. Every query is logged as the audit record — that’s the product.
- “Your data never leaves your database.” Query results pass through Midplane in memory on the way back to your agent. We don’t persist results, but they exist in our process for the duration of the response.
- “We’re SOC 2 certified.” Not yet. SOC 2 work is on the long-term roadmap, gated by enterprise demand.
- AST-based parsing. No regex on SQL. Not now, not ever. Every query is parsed into a real syntax tree before any rule runs.
- The same Docker image runs in our infrastructure and on your machine. The trust difference is where the container runs, not what code runs.
- Audit-fail-fails-the-query. If the pre-execute audit write fails, the query does not execute. No query reaches your database unaudited.
- The OSS engine is MIT. No copyleft, no BSL, no “source-available.” The license stays MIT.
Report a vulnerability
Found something the threat model missed? Report it privately — we treat security reports as the highest priority. Email security@midplane.ai with a description and affected version(s), steps to reproduce or a proof-of-concept, and your proposed remediation if you have one. We aim to acknowledge within 2 business days, coordinate disclosure, and credit reporters in the release notes once a fix ships. Scope. In scope: the engine (parse, policy, audit, execute), the MCP server (stdio and Streamable HTTP), and the published Docker image (midplane/midplane). Out of scope: third-party dependencies (libpg_query, the MCP SDK, the SQLite bindings — report those upstream), the hosted product (midplane.ai, covered by a separate disclosure policy), and your own database, network, or agent configuration (outside the engine’s boundary).
Next steps
Adversarial corpus
The bypass attempts that must stay denied, and the queries that must stay allowed.
How Cloud secures your data
Credential encryption, key isolation, and the exact cache TTL.