Content Policy: PII screening & the business-vs-personal gate
Configure the dashboard's Content Policy tab — screen every write for PII, PCI & secrets, and keep personal content out of the shared corporate graph.
Governed memory means deciding what should never land in the shared store in the first place. Two questions are worth asking on every write:
- Does this leak PII? An email, a card number, an API key copied into a memory crosses a team boundary and becomes a recall away from anyone.
- Is this even business content? A personal aside ("remind me to book a dentist") doesn't belong in the corporate knowledge graph at all.
The Content Policy tab answers both, per organization, at write time. This guide walks through every control, what each one does, and a sensible starting configuration.
Where it lives: the dashboard's Manage → Content Policy tab. It has two sub-tabs: Policy (the controls below) and Recent Actions (the audit trail of what got flagged, masked, or dropped).
Everything is opt-in. A fresh organization screens nothing — both gates default to off. You turn on what you need; nothing changes silently.
Give it a few minutes. Settings are cached per worker, so a change can take a short while to take effect across every instance.
Step 1 — Enable PII screening
The PII, PCI & Secrets card has one master switch — Enable PII screening (off by default: "Off = no screening. On = screen every write per the action below."). Turn it on, then pick what happens when something is detected.
Action on detection
| Action | What it does |
|---|---|
| Flag only | Stores the memory with a contains_pii warning + an audit entry. Nothing is blocked or altered — a safe way to measure exposure before you enforce. (The default when you first enable screening.) |
| Mask | Redacts the sensitive spans in place and stores the rest. jane@acme.com becomes a redacted span; the surrounding memory survives. |
| Drop | Rejects the whole write — nothing persists. The caller gets a 422. |
A good rollout is Flag only → review Recent Actions for a week → switch to Mask or Drop once you see what your agents actually write.
Categories
Seven detectors, each a checkbox: Email addresses · Phone numbers · Payment cards (PCI) · IBAN / bank accounts · National IDs (SSN, etc.) · API keys · Secrets / credentials.
Leave them all unchecked to screen everything — that's the secure default ("None selected — all categories are screened"). Only narrow the set if you have a deliberate reason to ignore a category.
Two detection paths, one setting. PII is caught two ways: a deterministic, span-aware scanner (regex + Luhn/IBAN/entropy validation) that runs before enrichment and is the only path precise enough to mask; and a free-form LLM signal during enrichment that catches PII phrased in prose. The free-form path's recall is only as good as your enrichment model — if you rely on Mask/Drop for free-form text, configure a capable
enrichment.model. When the LLM path can't pinpoint spans, Mask falls back to Flag.
Step 2 — Enable the business-vs-personal gate
The Business vs Personal card classifies each write as business or personal and applies a disposition to the personal ones. Flip Enable business-vs-personal gate on ("Off = store everything. On = apply the disposition below to personal content."), then choose:
| Non-business disposition | What happens to personal content |
|---|---|
| Store normally | No filtering — the classification is recorded for visibility, nothing else. |
| Keep private | Retained only in the creating agent's scope (scope_agent) — invisible to the team/fleet. |
| Drop | Not stored at all — keeps the corporate graph clean. |
"Business" is work, projects, customers, code, decisions, operations — anything an organization keeps in shared memory. "Personal" is private individual matters unrelated to work. The classifier returns one or the other on every write.
Step 3 — (Optional) Turn on the fast pre-gate
The accurate backstop runs after enrichment. The fast pre-gate is an optimization that runs before it — a cheap classifier that rejects confidently-personal content before you pay to enrich and embed it. It only matters for the Drop disposition, so its controls light up only when Drop is selected (otherwise you'll see "Applies to the Drop disposition only — ignored for the current one").
-
Enable fast pre-gate — "Reject confident personal content before enrichment/embedding — cheaper and faster than the post-enrichment gate, which stays the accurate backstop." Pure cost/latency win; the post-enrichment gate still catches anything the fast pass lets through.
-
Fail closed when the classifier is unavailable — this is the compliance lever, and it's worth understanding:
- Off (the default) — fail open. If the pre-gate classifier can't run, the write is stored and the post-enrichment gate is relied on as the backstop. Availability over strictness.
- On — fail closed. If the classifier can't run, the write is rejected with a
503. Strictness over availability.
Treat the pre-gate as a hard control? Turn fail-closed on — otherwise a classifier outage silently lets personal content through the fast path. Want writes to never blocked by an LLM hiccup? Leave it off.
Step 4 — Watch it work: Recent Actions
The Recent Actions sub-tab is the audit trail of every enforcement decision — so "what is the policy actually catching?" is a tab, not a guess. You'll see entries like:
pii_flag/pii_mask/pii_drop— PII detected and handled per your action.nonbusiness_keep_private/nonbusiness_drop— personal content scoped down or rejected by the post-enrichment gate.nonbusiness_pregate_drop— rejected early by the fast pre-gate.nonbusiness_pregate_unavailable— a fail-closed503because the classifier was down.
This is where you confirm a rollout before tightening it — start in Flag / Store, read the log, then enforce.
A sensible starting point
| Setting | Start with | Tighten to |
|---|---|---|
| PII screening | On, Flag only, all categories | Mask (or Drop for regulated data) |
| Categories | all (none checked) | narrow only with a reason |
| Business-vs-personal | On, Store normally | Keep private or Drop |
| Fast pre-gate | off | On once you move to Drop |
| Fail closed | off | On if the gate is a compliance control |
Enforce only after Recent Actions shows you what real traffic looks like.
Configuring it without the dashboard
The tab edits per-organization settings in core-api. Self-hosted? Set the same keys directly via PUT /api/settings?tenant_id=<org> with a partial governance block (unsent keys are left unchanged):
{
"governance": {
"pii": {
"enabled": true,
"action": "mask",
"categories": { "email": true, "credit_card": true }
},
"non_business": {
"enabled": true,
"disposition": "drop",
"pregate": { "enabled": true, "fail_closed": true }
}
}
}Defaults are opt-in (enabled is false everywhere). Stick to the allowed values or the save is rejected with a 422:
| Key | Type / allowed values | Default |
|---|---|---|
governance.pii.enabled | bool | false |
governance.pii.action | "flag" | "mask" | "drop" | "flag" |
governance.pii.categories.<name> — one per category (email, phone, credit_card, iban, national_id, api_key, secret) | bool | false (none ⇒ all screened) |
governance.non_business.enabled | bool | false |
governance.non_business.disposition | "store" | "keep_private" | "drop" | "store" |
governance.non_business.pregate.enabled | bool | false |
governance.non_business.pregate.fail_closed | bool | false (fail-open) |
What you've got
Two gates on the front door of shared memory, both off until you ask for them:
- PII screening — flag, mask, or drop emails, cards, IBANs, national IDs, keys, and secrets, caught before they cross a boundary.
- The business-vs-personal gate — keep personal content out of the corporate graph, with an optional fast pre-gate and a fail-open/closed choice for when the classifier is down.
- Recent Actions — the audit trail that turns "is the policy working?" into something you can read.
For the concepts behind this — scopes, trust tiers, keystones, and where PII detection fits — see Governed memory and the Governance concept guide.
caura-memclaw · Apache 2.0 — governed memory in the open. ⭐ Star on GitHub · Join Discord · memclaw.net
Governed memory: scopes, trust tiers & keystone policies
Who sees what, who can change the fleet's knowledge, and what every agent must obey — visibility scopes, trust tiers, and keystone policies.
The Karpathy Loop: memory that learns from outcomes
Make shared memory improve with use — report outcomes, reinforce what works, auto-generate rules from failures, and tune retrieval per agent.