> ## Documentation Index
> Fetch the complete documentation index at: https://midplane.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Standalone engine

> Run the MIT engine on its own — no account, no dashboard, no control plane. One MCP config block guards one database from your machine.

The standalone engine is Midplane's query path on its own: parse, policy, audit, execute. It guards **one database** for **one agent on one machine**, keeps its audit log in a local SQLite file, and needs no account and no server of yours. It's the same enforcement code that runs behind [Cloud](/docs/cloud/overview) and [self-host](/docs/self-host/deploy) — a query denied here is denied there, for the same reason.

It's the right shape when you want the guardrails and nothing else: a database on your laptop, a CI pipeline, a scratch environment. When you want a dashboard, a searchable audit log across a team, per-agent OAuth scope, or [write approvals](/docs/concepts/write-approvals), you want a control plane — [Cloud](/docs/quickstart) is the fastest way to get one.

## Point an agent at it

`npx` fetches the [`midplane`](https://www.npmjs.com/package/midplane) package on first run, so there's nothing to install ahead of time. Add this to your MCP client's config — Claude Code, Claude Desktop, and Cursor all take this shape:

```json theme={null}
{
  "mcpServers": {
    "midplane": {
      "command": "npx",
      "args": ["-y", "midplane", "server", "--stdio"],
      "env": { "DATABASE_URL": "postgres://user:pass@host:5432/db" }
    }
  }
}
```

Restart the client and the Midplane tools appear. That config is already the safe default: **reads allowed, writes and DDL denied, every query audited.** There's nothing to configure to be safe — you configure only to open things up.

<Warning>
  Keep the connection string in the `env` block, never in `args`. A DSN on a command line leaks to `ps aux` and your shell history. The block still lands in a plaintext config file, so give Midplane its own least-privilege Postgres role: it governs which SQL runs, not what the role underneath it can reach. See [connect your database](/docs/connect/overview#a-least-privilege-role) for the role recipe.
</Warning>

### Requirements

**Node 22.16+ or 24+** (the audit log uses the `node:sqlite` builtin), or **Bun 1.3+**. `npx` ships with Node, so there's nothing else to install — no native modules, no compiler. On anything older the binary refuses to start and says so, rather than failing partway through with a stack trace from whichever dependency reached a newer builtin first.

## Try it

Ask your agent to **list the tables** — an allowed read that goes straight through. Then ask it to **delete a row**. Midplane denies it before it runs: writes deny by default under [`table_access`](/docs/concepts/table-access), even a bounded one-row `DELETE … WHERE`, until you grant `read_write` on that table.

Read the denial back from the local audit log:

```bash theme={null}
npx -y midplane audit denies
```

```
2026-08-19T12:24:16Z DENIED table_access agent=midplane-cli qid=01M0CZNDFTRSNC2BW5FQ3YZWTF
  sql:    DELETE FROM users WHERE id = 1
  reason: Midplane denied this query because writes to table `users` are not allowed by the
          table-access policy (`users` resolves to `read`, which permits reads only). Another
          write to `users` will be denied the same way.
  intent: remove a test account

1 denial in the last 24h
```

The `intent` is the agent's own plain-language "why," recorded next to the SQL and the decision. Nothing was modified, and the attempt is on the record either way — see [the audit trail](/docs/concepts/audit-trail).

The log is a SQLite database at `~/.midplane/audit.db` (override with `DB_PATH`). `midplane audit` also has `tail`, `since`, `show`, and `stats`; see the [CLI reference](/docs/reference/cli#midplane-audit).

## Open specific tables up

The default denies every write. To grant some, generate a policy file:

```bash theme={null}
npx -y midplane init
```

It connects read-only, introspects your schema, suggests a tenant column, and writes a schema-validated `midplane.policy.yaml`. Point the server at it by adding `MIDPLANE_POLICY_FILE` to the same `env` block:

```json theme={null}
"env": {
  "DATABASE_URL": "postgres://user:pass@host:5432/db",
  "MIDPLANE_POLICY_FILE": "/absolute/path/to/midplane.policy.yaml"
}
```

`init` needs a terminal; `npx -y midplane policy init` is the flag-driven equivalent for CI. See [writing policies](/docs/policies/overview) and the [policy schema](/docs/reference/policy-schema).

## In CI, or as a long-lived sidecar

For anything that isn't a local MCP client, run the same engine as a container serving Streamable HTTP instead of stdio — self-contained, with no Node or `node_modules` inside it:

```bash theme={null}
curl -O https://raw.githubusercontent.com/midplaneai/midplane/main/engine/.env.example
mv .env.example .env   # set DATABASE_URL in the file — never inline with -e
docker run --env-file .env -p 8080:8080 -v midplane-audit:/data midplane/midplane:latest
```

The MCP endpoint comes up at `http://localhost:8080/mcp`. Audit lands in the mounted volume, and `DB_PATH` defaults to `/data/audit.db` inside a container. Both artifacts are built from the same source at the same version.

<Note>
  `--stdio` and `--http` on `midplane server` override [`MIDPLANE_TRANSPORT`](/docs/reference/environment-variables). The npm package defaults to HTTP like the image does, which is why the client config above passes `--stdio` explicitly.
</Note>

## What ships, and how to check it

Midplane publishes two artifacts per release, plus a registry entry, all at the same version:

| Artifact     | Identifier                                           | Transport                                            |
| ------------ | ---------------------------------------------------- | ---------------------------------------------------- |
| npm package  | [`midplane`](https://www.npmjs.com/package/midplane) | stdio                                                |
| Docker image | `midplane/midplane`                                  | Streamable HTTP                                      |
| MCP registry | `ai.midplane/midplane`                               | listed under DNS-verified ownership of `midplane.ai` |

Because this is a security tool you're being asked to run with `npx`, the package is built to be checked rather than trusted:

* **Not minified**, deliberately — anyone evaluating what `npx midplane` does should be able to read the artifact.
* **No install scripts and no native modules** — nothing executes at install time.
* **Runtime dependencies stay external** rather than vendored, so `npm audit` and Dependabot can still see them.

Releases are published through [trusted publishing](https://docs.npmjs.com/trusted-publishers) over OIDC, which attests [provenance](https://docs.npmjs.com/generating-provenance-statements) automatically. Verify any release with `npm view midplane dist.attestations`.

<Note>
  **0.19.0 carries no provenance attestation; later releases do.** Creating a package on npm requires setting its access, which a bypass-2FA token may no longer perform — so the first publish of any package has to come from an interactive session, which has no CI identity to sign with. That applies once, to the release that created the package.
</Note>

See the [threat model](/docs/security/threat-model) for what the engine defends against and what it doesn't.

## What you don't get

Compared to [Cloud](/docs/cloud/overview) or a [self-hosted control plane](/docs/self-host/deploy):

* **No dashboard.** The audit log is read from the CLI, not a UI.
* **No per-agent OAuth scope.** Scope comes from the policy file and the database role, not a consent screen — see [what you grant at consent](/docs/agents/overview#what-you-grant-at-consent).
* **No [write approvals](/docs/concepts/write-approvals).** Holding a write for a human needs a control plane for the human to review it in.
* **Audit is local and per-machine.** A SQLite file on the box, not a searchable store across a team.

[Column masking](/docs/concepts/masking) and [multi-database policies](/docs/policies/overview#serve-multiple-databases) both work standalone.

Moving up doesn't change your policy: the same [table access](/docs/concepts/table-access) model and the same YAML carry over, and the control plane spawns this same engine.

## Next steps

<Columns cols={3}>
  <Card title="Try Cloud instead" icon="cloud" href="/docs/quickstart">
    Dashboard, hosted audit log, and per-agent scope — the recommended path.
  </Card>

  <Card title="Write a policy" icon="shield-check" href="/docs/policies/overview">
    Grant the reads and writes your agent actually needs.
  </Card>

  <Card title="CLI reference" icon="terminal" href="/docs/reference/cli">
    Every subcommand, flag, and exit code.
  </Card>
</Columns>
