Health
Both are unauthenticated and say nothing else. A linked gateway with no bundle
yet answers
/mcp with a plain 503, so clients don’t loop through sign-in.
Logs
JSON lines on stderr, one object each, withtime, level and msg (stdout
carries MCP in stdio mode). The ones worth alerting on:
mcp client notes each new agent (client, person and MCP protocol version)
once.
Stopping
SIGTERM or SIGINT closes the listener, its connections and the database
pools, then exits. Nothing is lost: every event was already on disk.
Resources
Each database gets a pool of up to 10 connections. A query returns at mostlimits.max_rows rows and limits.max_bytes bytes, and stops at either. A
write drains its RETURNING rows, so its row count stays exact. Statement and
lock timeouts come from the policy.
The catalog (names and types) is re-read at start, after every write, when a
statement names something unknown (at most every five seconds), and in linked
mode every five minutes. A read may take 30 seconds. In linked mode, a
database whose catalog couldn’t be read at start is tried again after 1, 2, 4
and so on seconds, at most every 30 seconds, until it can be.
Upgrades
- Deploy Midplane Cloud before gateways. A gateway’s sync reports what it supports, and a newer gateway’s calls need a cloud that knows them; an older cloud refuses its status, and the sync fails until the cloud is upgraded.
- Gateways halt rather than half-enforce. A bundle that needs a feature a gateway lacks halts it, and the dashboard warns before publishing one to a gateway that would.
- Restarts keep enforcing. A linked gateway enforces its cached bundle while it reconnects, and resumes pushing its audit log where it stopped.
- The audit file, the bundle cache and the identity carry over between versions; keep them on a volume.
Backups
Back up the audit file if you rely on it as your record (or export it on a schedule withmidplane audit export --since), and keep the identity in your
secret manager. The bundle cache is rebuilt from the cloud.
A restored audit file is behind what Midplane Cloud holds: its next events
take sequences the cloud already has, with other events. The cloud stores a
batch only up to the first of them, and its signed ack says where; that
makes the file a new instance (logged once, with from naming the old one),
which sends the rest. The cloud keeps the old instance’s events, flagged as a
conflict. Events recorded after the backup survive only as far as the cloud
got them, in its export under the old instance: the file keeps every
instance it was, oldest first, in its previous_instances row (sqlite3 audit.db "SELECT value FROM meta WHERE key = 'previous_instances'").
verify --checkpoints then matches the new instance’s hashes
(audit).
Tested by apps/gateway/test/link.e2e.test.ts (readiness, 503 without a
bundle, halting, SIGTERM, restarts with the cloud down, a database down and
back), apps/gateway/test/gateway.e2e.test.ts (caps, timeouts, the catalog,
local mode refusing to start without a database) and
apps/gateway/test/health.test.ts (the retry schedule, what counts against a
database’s health).