.env.example to .env, fill in your values, and pass it with --env-file (or compose).
The engine refuses to boot on an invalid configuration: the connection, port, and transport values are schema-validated, and DATABASE_URL is required unless your policy file supplies a databases: block.
Connection and core
¹
DATABASE_URL is required for the single-database path. It is not required when MIDPLANE_POLICY_FILE supplies a databases: block whose entries carry their own url. At least one DSN source must be configured or the engine refuses to boot.
Examples
Column masking
MIDPLANE_MASK_SALT is the variable the standalone engine reads when you run it directly. When a control plane manages the engine — Midplane Cloud, or self-host — you instead set MIDPLANE_MASK_SALT_MASTER once on the control plane, which derives a per-project salt from it and injects that as MIDPLANE_MASK_SALT into each engine it spawns.Audit shipping
Deny webhook
Payload
POST {url} with content-type: application/json and this body:
string
Always
"denial".integer
Payload version. Currently
3.integer
Milliseconds since the Unix epoch.
string (ULID)
Groups every audit event for this query.
string (ULID)
The
id of the DECIDED audit row this notification mirrors.string
__self_host__ for OSS; the customer ULID for Cloud.string | null
The MCP
clientInfo.name. null for non-MCP callers.string | null
The MCP
clientInfo.version. null for non-MCP callers.string | null
The
intent argument from the query tool (≤ 500 chars). Always populated for query denials; null for denials surfaced through other paths.string | null
The cloud-issued ULID for the MCP token that opened the session (added
0.6.0). null whenever the X-Midplane-Token-Id header is absent or malformed — on self-host, on a malformed header, and on non-MCP callers.string
The rule that denied —
table_access, multi_statement, dangerous_statement, or parse_error. See denial reasons.string
The human-readable message — the same text the agent received.
string | null
SELECT, DELETE, etc. when parsing succeeded; null when it didn’t (e.g. a parse_error denial).string[]
Schema-qualified table names the query referenced.
string
The raw SQL, truncated at 1024 characters. Empty when the engine couldn’t match an
ATTEMPTED row to the denial (rare — only under buffer pressure).boolean
true when sql_preview was truncated.POST is fire-and-forget: a 5-second timeout, no retries, and any 2xx counts as delivered. A webhook failure never blocks or fails the underlying audit write — the DECIDED row is the source of truth and always lands, so if the receiver is down the denial is still recorded, the webhook just doesn’t fire.
Receivers pinned to
schema_version: 2 should widen their handler — the intent_source key is gone. Receivers on schema_version: 1 should also drop agent_identity and adopt the split agent_name / agent_version keys. The 0.6.0 mcp_token_id addition is additive-nullable and did not bump schema_version; v3 receivers that don’t know the field just ignore it.Telemetry
Anonymous usage telemetry is enabled by default on self-host. Disable it with either variable below. See telemetry for exactly what’s collected.Self-host control plane
The variables above configure the standalone engine. The variables below apply only when you run the control plane in self-host mode — it spawns the engine for you as a local subprocess. For the full setup, see self-host deploy & configuration.
The control plane also reads
BETTER_AUTH_SECRET, BETTER_AUTH_URL, and the local-KMS key and token pepper — see self-host configuration for that full set.
Related
Policy file schema
What
MIDPLANE_POLICY_FILE points at.HTTP endpoints
The routes
INDEXER_TOKEN unlocks.