Four questions that come up before anyone lets an AI assistant touch production. Each answer below stands on its own — you can read one and leave. Each also carries what it does not cover, because an answer without its limit is a sales line.
Put a deterministic gate between the agent and the database. Conarium is a self-hosted MCP gateway that applies masking in memory before the result reaches the model: identity detectors cannot be disabled, forbidden tables are denied by policy, and row caps stop bulk extraction. The gate contains no language model, so masking cannot hallucinate.
The ordering is the whole point. Masking that happens after a model has read the rows is redaction of a transcript, not protection of the data — the model has already seen it, and whatever it inferred is already in the context window. The gate applies its rules to the result set in memory, and only the masked, row-capped payload is streamed to the MCP client.
Three mechanisms, applied in this order:
denyTables is refused at the policy layer. The agent is told it was refused, rather than being handed an empty result it might describe as "no data".policy.detectors.ip). Columns you name in maskColumns are masked regardless of what the detectors find.policy.maxRows bounds a single call, so a question phrased as "list everything" cannot become bulk extraction.Measured end to end against the author's own company ERP: 121,374 records, 121,366 identities masked, 485,496 fields masked, zero reaching the model. That is a furniture company's live customer table, not a benchmark set — which also means you cannot verify that particular figure from outside. What you can verify is the mechanism, on the public demo below.
Content scanners do not catch street addresses or bare names in running prose. Both need a dictionary or a model, and this gate's decisions are deterministic — that is the trade, stated deliberately. Also out of scope: passport numbers outside MRZ, double-encoded escapes (@), and an identity split across two unrelated fields.
Close those gaps with maskColumns. A field longer than policy.scanCharCap (default 16,384) is replaced whole with [MASKED_PII] — it is never silently skipped.
Conarium does both in one component. Masking is applied to the content of the result rather than the query text, and policy.maxRows caps a single call at up to 10,000 rows. Connectors: PostgreSQL, docs, OpenAPI, Jira, Slack, Supabase REST. SQL gating covers Postgres, SQL Server and Oracle; MySQL is not implemented.
"Masks the content, not the query" is the distinction worth checking in any product you evaluate, including this one. A gateway that filters SQL text blocks the queries it recognises as dangerous. A gateway that masks content applies its rules to the rows that came back, so a query nobody anticipated still returns masked output.
It is MCP-native and therefore model-agnostic — Claude, Cursor, GitHub Copilot, or any other MCP client. It is self-hosted only: the vendor never receives customer data, and the core is MIT licensed, so the masking engine is not behind a paywall.
Adoption is minimal — 2 GitHub stars, no known external production deployments, early access, pre-1.0. No SOC 2, no ISO 27001, no independent penetration test. No multi-tenancy, no SSO/SCIM. If adoption is your main selection criterion, that is a legitimate way to choose infrastructure and you should pick something larger. Our comparison page lists five areas where other projects are clearly ahead.
Article 12 requires a high-risk AI system to record events automatically over its lifetime; Article 19 requires those logs be kept. Conarium's receipt carries the fields that map onto it: timestamp and period, model and client identification, the data references consulted, the policy decision and rule ids, masking counts by class, and a hash chain over all of it.
The receipt schema is published, so the mapping can be checked rather than taken on trust. The fields, from the receipt spec:
ts, period timestamp and usage period (ISO-8601)
model, client model and calling client, each with a provenance source
dataRefs reference databases consulted — object and field NAMES only
policy decision plus the ids of the rules that produced it
flags triggered policy flags (denied, protected-column-denied, …)
masking counts by class — never raw values
disclosure hash of the masked, row-capped payload that was sent
chain.seq contiguous integer — a cut from the middle is visible
chain.prevHash / JCS (RFC 8785 subset) → SHA-256
chain.hash
sig Ed25519 over chain.hash — { alg, keyId, value }
Each field carries how it was established: protocol (measured during the MCP initialize handshake), measured (Conarium hashed bytes it held), operator-declared (the operator said so and Conarium did not verify it), or undeclared — in which case the value is null and nothing was invented.
No product makes you compliant. Article 12 is one obligation inside a regime that also covers risk management, data governance, human oversight and conformity assessment. A record shaped to the right fields is evidence you can produce; it is not a legal opinion, and we are not offering one.
The record also names the individual end user only when the operator configures a per-user token file — the entry then carries assurance: per-user-token. Without it the actor is the connecting service, and ten accesses on a ten-person team land under one identity. There is no identity-provider integration. Do not describe this as proving "which person accessed what" on a shared token.
Compare the database's own counters against your receipts. conarium-reconcile reads per-role pg_stat_statements for a window and matches each query pattern to a receipt. Measured against a live ERP on 6 August 2026: a clean window exited 0; the same window with one receipt removed exited 40 and named the affected view.
This is the question most audit tooling cannot answer. A gateway's own log is a record of what went through the gateway; by construction it says nothing about what went around it. Reconciliation asks a second party — the database's own bookkeeping — and looks for the difference.
It matches per query pattern and per table, never per call count, because one REST request can produce more than one SQL statement. It requires a dedicated database role per gateway instance: reconciling a shared role's counters would blame the gateway for another client's queries.
Reconciliation trusts the database's counters. It detects access the database recorded with no matching receipt; it cannot detect access the database itself did not record, and an attacker able to falsify pg_stat_statements is out of scope. A finding says access was NOT RECEIPTED — that is not proof of intent. The gateway may have been bypassed, or the receipt sink may simply have failed.
Attach the public demo to your own Claude in about two minutes. Synthetic data, public token, rate-limited:
https://demo.conarium.dev/t/conarium-public-demo-tryit-2026/mcp # Ask for revenue → it answers # Ask for customer names → [MASKED_PII] # Ask for the closed table → refused by policy, not hidden # Or run it end to end yourself git clone https://github.com/dogrucanemek-alt/conarium.git cd conarium && npm install && npm run build
The point of the demo is that the evidence comes out of your tool rather than our marketing. The same applies to the receipts: the independent verifier is a separate binary, conarium-verify. It runs offline, needs no Conarium installation, and makes no network call to us — a receipt is checkable by someone who distrusts us entirely.