Documentation · Operating
Setting up auditing
This page is an outline. What is here is accurate, but it is
not yet the whole story — each section ends with a note on what is still to be
written. For anything it does not answer, DEPLOYMENT.md and
USING-MCP.md in the repository are the complete references.
Auditing is off unless a sink is named. MCP_AUDIT_SINK is the class name of a
com.mcpdbwizard.pub.McpAuditSink; a mistyped one stops start-up rather than leaving the server
silently unaudited.
| Variable | Meaning |
|---|---|
MCP_AUDIT_SINK | Sink class. Unset means no auditing. |
MCP_AUDIT_LEVEL | names (default) or values. |
MCP_AUDIT_MAX_BYTES | Cap on the recorded response, default 8192. 0 for no cap. |
names is the default on purpose
At values the record carries argument values and the response body — production data, chosen by a
model. That turns your sink into a store with retention, encryption and erasure obligations.
Switch it on deliberately, not by accident.
A truncated response still carries its full byte size and a SHA-256 of the whole payload, with
truncated: true. Truncation costs readability, not integrity.
Surviving an outage: the spool
MCP_AUDIT_SPOOL_DIR turns on a write-ahead spool. Every record goes to disk before any
delivery attempt and is removed only once the sink confirms it, so records survive a sink outage and
survive the server dying — the spool is read back and replayed on the next start.
| Variable | Meaning |
|---|---|
MCP_AUDIT_SPOOL_DIR | Spool directory. Unset means no spool. Put it on a persistent volume. |
MCP_AUDIT_SPOOL_MAX_BYTES | Cap, default 100 MB. |
MCP_AUDIT_SPOOL_ON_FULL | drop (default) or block. |
MCP_AUDIT_SPOOL_FSYNC | never (default) or always. |
MCP_AUDIT_SPOOL_KEY / _FILE | Encrypts each spooled record (AES-256-GCM). Unset means plaintext. |
Three sentences to read before relying on it.
Delivery is at-least-once. A crash between delivering a batch and deleting it replays that batch, so
a consumer can see a record twice. Every record carries an id — dedupe on it.
fsync=never survives the process, not the machine. Records live through a crash, an OOM kill or a
container restart. They do not survive power loss. always covers that too, at a disk round trip per
tool call.
A full spool refuses new records rather than discarding old ones. Watch for Audit record not spooled in the log — it means the trail has holes.
Expect a web/ subdirectory under the spool: the console records proxied requests while each
generated server records its own tool calls, and a spool tolerates exactly one writer. That is the
isolation working, not a stray directory.
Encrypting the spool
It protects the FILE, not the PROCESS. Anyone who can read the container’s environment or its memory has the key. It defends what outlives the process and travels: disk images, volume snapshots, backups.
Encrypting the volume is usually the better answer — no code, and it also covers the configs, the accounts and the runtime workspaces sitting in the same directory, which this does not.
Switching it on over an existing spool is safe. Switching it off, or changing the key, is not: those records can no longer be read, and an undecryptable segment is quarantined rather than retried or deleted. Drain the spool before rotating the key.
To write. The record’s field-by-field shape; writing your own sink against the SPI; the Kafka reference sink and its buffer-loss caveat; retention and erasure guidance for
valuesmode.