ADR 0002: The Trust Boundary
Detected raw PII stays in the trusted zone (runtime and Python NER sidecar); the untrusted zone sees only tokens and safe metadata
Status: Accepted
Context
The product claim is that AI can be used with sensitive data without the sensitive data leaving your control. That claim only holds if there is a precise, enforced boundary that raw values never cross. Without a stated boundary, "safe" is a hope. Everything outside the trusted zone, the LLM, external tools, logs, and vector stores, has to be treated as untrusted, because any of them can be logged, cached, or exfiltrated.
Decision
We define one trust boundary and keep raw PII inside the trusted zone.
- Trusted zone (the OGuardAI runtime, plus the NER sidecar when deployed): raw PII, token-to-value mappings, encryption keys, and policy rules. A correctly configured TLS-terminating ingress in front of the runtime is part of the trusted path (see below).
- Untrusted zone (LLMs, tools, logs, vector stores): only
{{type:id:cap}}tokens, safe metadata, and encrypted session blobs. - The one deliberate exception: an explicit policy whitelist can pass a specific value through. It is opt-in per policy, never a default.
A TLS-terminating reverse proxy or ingress in front of the runtime is part of this trusted path. It sees request URLs and bodies before the runtime does, so it must be configured to honor the boundary: log a fixed route label rather than the raw request line, and stream request bodies upstream rather than buffer them to disk. The reference nginx configuration under deploy/docker/ does both. Treat that hop the way you treat the NER sidecar, on an internal network and configured to hold no raw value.
The boundary is enforced at the type level, not just by convention. In crates/core/src/token.rs, a token's Display renders only {{type:id:cap}} (via render_display_token), while Debug redacts, so a raw value cannot leak into a log line or an error message by accident. crates/core/src/log_hygiene.rs backs the "no PII in logs" rule. On the return path, crates/output-guard re-scans model output for new PII the model may have generated, catching values that were never in the input.
Outbound across the boundary: tokenized text plus safe entity context (type, and metadata such as gender or formality), never the raw value. Inbound: model output that contains tokens. The token-to-value mapping and the keys never cross.
Consequences
- The boundary is the legal and technical foundation of the product: reviewers and auditors can point to exactly what leaves the runtime.
- Logs, traces, vector stores, and third-party tools can hold OGuardAI output without holding PII, because the tokens are safe to persist.
- Passing a real value to the model is possible but always explicit, through a policy whitelist, so it shows up in review rather than happening by omission.
- The boundary constrains the design: any feature that would send raw values outward (richer prompts, tool arguments, embeddings) has to tokenize first or declare a whitelist, which is intentional friction.
Reference
See the Architecture Overview trust boundary section for the full zone model.
ADR 0001: The Line
A built-in safe floor that policy may only add to or tighten, never silently weaken; invalid config or a missing required detector fails closed
ADR 0003: Semantic Tokens Over Redaction
Replace sensitive values with typed semantic tokens that carry safe metadata, instead of irreversible masks