# Data flow, storage, and retention ## Goal Trace every byte a question touches: where questions, facts, documents, drafts, and answers travel, where each copy rests, who can read it, and when it disappears. ## Scope Component `law-mcp` (MCP host; default profile `all`, default `--port 8722`, default bind `127.0.0.1`). Scenario: one `tools/call` round trip over stdio or HTTP, plus the optional Lens publication and neural-search legs. All case names, hashes, and hostnames below are synthetic examples. ## Applies to | Branch | Coverage in this article | |---|---| | MCP (`law-mcp-server`) | full data-flow trace incl. answers/neural/OTLP legs | | `law serve` | not covered — serve state is the world + the shared decision journal, see [Deploy a private HTTP service](/operate/private-http-service/), [Backup and restore](/operate/backup-restore/) | ## Prerequisites - The server root (`--root` / `LAW_MCP_ROOT`, real), or a checkout the server can find by its two markers — the profiles directory and the compiled-corpus directory (real root discovery). - Know which legs are enabled: HTTP (`--http`), call journal (on by default, `LAW_MCP_LOG=0` disables), OTLP (`LAW_MCP_OTLP_URL`), Lens answers (`LAW_ANSWERS_URL` + `LAW_ANSWERS_TOKEN`), neural appendix (`LAW_NEURAL_URL` + `LAW_NEURAL_TOKEN`). All real (`law-mcp/src/main.rs`, `publication.rs`, `reference/search.rs`). ## Steps 1. Send the question. Over stdio the request is one JSON line on stdin; over HTTP it is `POST /mcp` with a single JSON-RPC object (real: batches are refused, `http.rs`). The request carries the tool name, the predicate with arguments, and the case facts with provenance. 2. The server reads the corpus and the case. Reads are confined to the root: a case package `//` must contain `law.toml` (real, `execution.rs`), and a path that canonicalizes outside the root is refused (`case_ask.outside_root`, real). Symlink escapes are refused, not followed. 3. The engine evaluates in memory. Pure evaluation writes no files: the answer document (statuses, applied rules, provenance hashes `programHash`/`caseHash`/`resultHash`/`codeHash`) is built in the call thread and returned in the response (real). The writing tool classes — `editing` (case packages), `drafting` (author-tree files) — are the exception, listed in the [effects matrix](/operate/tool-profiles-input-policy/#effects-the-authoritative-matrix). 4. Optional leg A — call journal. On the HTTP transport only (stdio never journals, real, `reqlog.rs`), each call's primary record goes to the journald native socket `/run/systemd/journal/socket`, or — if the socket is absent — as one JSON line to stderr (real, `journal.rs`). A late-finishing call adds a second record for the same call id (`OVERRUN_FINISHED`), so journal reads correlate by id rather than counting lines (see [Logs, audit, decision journals](/operate/logs-audit-decision-journals/)). Bodies are capped at 64 KiB per side by default (`LAW_MCP_LOG_MAX_BODY`, `0` uncaps, real). 5. Optional leg B — Lens answers. The four publication tools (`law_capture_answer`, `law_prepare_answer`, `law_publish_answer`, `law_revoke_answer`, real, `publication.rs`) forward to `POST {LAW_ANSWERS_URL}/internal/mcp/call` with schema `law.answers.mcp-call/0.1` (real). Without `LAW_ANSWERS_URL` and a non-empty token the tools answer `ANSWERS_UNAVAILABLE` and nothing leaves the host. 6. Optional leg C — neural appendix. `law_search` posts the query text plus the lexical `taken` candidates (kind/id/point, no case facts) to `{LAW_NEURAL_URL}/v1/appendix` (real, `reference/search.rs`). Without `LAW_NEURAL_URL` the leg reports `active:false` and the answer stays purely lexical. 7. Optional export — `--export-neural-input PATH` writes the search index input document to exactly that path (real, `main.rs`). Together with the `editing`/`drafting` tool writes and per-call temp workbench files, this is the complete file-write surface — see the [effects matrix](/operate/tool-profiles-input-policy/#effects-the-authoritative-matrix) for the authoritative list. ## Expected result - The client holds the full answer document in the response. The MCP host has no separate persistent answer store and keeps no long-lived response object after the call returns (real) — but with journaling on (the HTTP default), request and response bodies, including case facts, are persisted in the log infrastructure (journald, stderr capture, or the external collector) and must be handled as case data: same access rights, retention, backup, and cleanup rules as the case directory itself. Audit the whole deployment, not just the absence of a database inside the process. - Case inputs live where the operator put them and where the deployment copies them: the case directory (`law.toml`, `law.lock`, `queries/*.json`, `.lawcase` files), the client that sent the facts, and — when journaling is on — the journal records described below. - Journal records live in the host journal (journald) or the process stderr stream — see article 09 for fields and redaction. - A prepared Lens document is a non-public frozen document held by the answers service for 24 hours (reference behavior of `law_prepare_answer`); publishing returns a URL plus a separate revoke secret, and revoking closes HTML, API, and download. The MCP host stores none of these. ## Result check - With the journal on stderr, one successful call produces one primary JSON line containing `MCP_TOOL` and `MCP_CALL_ID` (real field names). Line count is NOT a call count: a late-finishing computation may add an `OVERRUN_FINISHED` record for the same call, a journal-write failure can drop a record while the call itself succeeds, an oversized datagram is resent truncated (content lost, record survives), and transport failures produce no call record at all. Correlate by `MCP_CALL_ID` and event type; see article 09. The guarantees of the MCP call log and the `law serve` decision journal differ — do not treat either as a backup or as an audit-completeness proof on its own. - With `LAW_ANSWERS_URL` unset, `law_prepare_answer` returns `isError:true` with code `ANSWERS_UNAVAILABLE` (real). - With `LAW_NEURAL_URL` unset, `law_search` carries `neural.active:false` (real). - `GET /healthz` is never journaled (real, `reqlog.rs`): the balancer's polls leave no case-data trace. ## Failures and diagnostics - `413` on `POST /mcp`: request body over the 1 MiB implementation limit (`MAX_BODY`, real). Reduce the request only in ways that preserve its meaning: use the supported references to a prepared case, remove genuinely redundant content, or split independent objects. Do NOT split one logically connected case into separate computations without established equivalence: `A ∧ B → C` evaluated on `{A, B}` yields `C`, while two separate runs on `{A}` and `{B}` may not — and in models with exceptions and conflicts the effect of splitting is even less obvious. - `ANSWERS_TOO_LARGE`: Lens response over 32 MiB (real, `publication.rs`). `BODY_SIZE` from other services: response over 4 MiB (real, `service_client.rs`). - `SERVICE_CONFIG` with `https_required`: a service URL that is not HTTPS to a non-loopback host (real). Fix the URL; there is no bypass flag. - Oversized journal datagram: resent without bodies with `MCP_TRUNCATED=oversize` (real). The record survives; the content does not — do not treat the journal as a backup. ## Support boundaries - Retention of journal records is the host's policy (journald `MaxRetentionSec`, log rotation): the server sets no TTL and runs no cleanup job. Retention of prepared/published Lens documents is the answers service's behavior (24 hours frozen, explicit revoke). - Backups cover the inputs the operator owns: case directories, `law.toml`/`law.lock`, and the journal archive. There is no server database to dump. - The MCP host never deletes caller data: cleanup means the operator's retention policy plus `law_revoke_answer` for published documents. - "Secure" in this article means only: confined reads (root containment), no silent copies (stdio journals nothing; each network leg needs an explicit URL + token), and named storage places. It does not mean encryption at rest — that is the host's disk policy. ## Next step - Article 09 ([Logs, audit trail, and decision journals](/operate/logs-audit-decision-journals/)) for what exactly lands in each journal record and what is redacted; article 10 ([Runtime filesystem isolation](/operate/runtime-filesystem-isolation/)) for directory rights and the Arxo-vs-external-network boundary in full.