Re-identification risk analysis¶
Deliverable expected by the DPO (plan §9). Status: MVP — to be completed with the DPO before any operation on real data.
1. What the proxy guarantees¶
No detected identifier (host, IP, e-mail, repository, image, service account,
secret) leaves the machine in clear over channel 1. Verified by mitmproxy
capture in a real session: tests/phase3_e2e.sh → 0 occurrences over
427 KB outbound, and by tests/corpus_eval.py on the annotated corpus.
2. ASSUMED leaks — the four preserved attributes¶
jo's answer §3.4: the four attributes are kept because the model needs them to reason correctly. Each one is an information leak, documented here for what it is.
| Preserved attribute | What Anthropic can deduce from it | Why we accept it |
|---|---|---|
Environment (-prod, -staging) |
The prod/staging/dev split of the infrastructure, and therefore its relative size | Without it, the model cannot calibrate the risk of an action: it would treat prod as a sandbox |
| /24 co-membership | The network topology: how many subnets, how many hosts per subnet, which hosts cohabit | Network reasoning (routing, firewall, blast radius) is inoperative without it |
| Human vs service | The ratio of human accounts / technical accounts, so an order of magnitude for team size | A service account incident is not handled like a human account incident |
| Internal vs external | The perimeter boundary: what is ours, what is at a third party | Distinguishing a public dependency from an internal service is the basis of attack surface analysis |
3. The structural limit (plan §9)¶
A perfect substitution does not make anonymous. What identifies is no longer the name but the structure:
- who commits on what, at what frequency, with whom;
- which roles, what team size, what schedules;
- the shape of the dependency graph between services.
A pseudonymized graph is re-identified as soon as auxiliary information exists: public GitHub activity, org chart, job postings, conferences. No improvement in tokenization corrects that.
Real mitigation: minimization at the tool boundary¶
This is the effective lever, and it is NOT in the proxy:
- do not send 4,000 lines of
git logwhen an aggregate answers; - do not send the nominative list when "12 principals hold this role, including 3 service accounts" is enough;
- prefer
kubectl get pods -o name | wc -lto a full dump.
Status: not implemented in the MVP. It would be a typed tool broker (cf. AgentWall, plan §5 Phase 4, task 3), which aggregates before sending. To arbitrate with jo: it is the highest-yield project for privacy once the MVP is in service.
4. Other residual risks¶
| Risk | Status | Mitigation |
|---|---|---|
| Vault readable by the agent (answer §3.5: local, same user) | Assumed | PreToolUse hook (hooks/pretooluse_guard.py) refuses any access to the vault path and to the key; tested (test_pretooluse_hook.py). A dedicated Unix account would close the gap. |
| Channel 2 — Bash | Blocked, never pseudonymized | A command must reach the real cluster, so there is no return path. The hook refuses direct network egress and secret dumps before execution. Inherent; the definitive answer is D9 (deployment isolation). |
| Channel 2 — remote MCP, WebFetch | Pseudonymized under task forward |
They speak HTTP, so the forward proxy terminates TLS and rewrites JSON-RPC bodies both ways, with the same vault. Not the default path, and not yet exercised by a real session — treat the guarantee as untested until it has been. |
Remote MCP servers and connectors do not go through ANTHROPIC_BASE_URL |
Open | Measured: mcp-proxy.anthropic.com ×12, mcp.context7.com ×11, registry.npmjs.org ×4, api.githubcopilot.com ×2 in a single session — that is 4 destinations out of 5 outside the proxy. They do not carry the conversation, but carry requests formulated by the agent. To arbitrate: docs/d9-network-isolation.md. |
| Datadog telemetry of the Claude Code binary | Cut, content unknown | Observed at ~343 KB/session on 2026-08-01; absent on 2026-08-02 after adding CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 / DISABLE_TELEMETRY=1 / DISABLE_ERROR_REPORTING=1 in ~/.claude/settings.json (tests/datadog_probe.sh, 34 other flows captured at the same time). The cut is therefore effective. The CONTENT of the payloads has never been inspected: to do so one must launch the session with a temporary settings file without these variables — the script's environment is not enough to neutralize them. |
| Volume and rhythm of requests | Not mitigated | Even pseudonymized, the temporal PROFILE of activity is visible on the provider's side (number of turns, size of contexts, working hours). |
MCP tool names (payments_api__query) |
Open | The walker's SKIP_KEYS preserves name: a tool name encoding an internal service leaks. To be handled at the MCP server level (renaming in the middle would break correspondence on return). |
| Determinism per project (answer §3.1) | Assumed | A substitute is stable in the project: two sessions of the same project are correlatable with each other on the provider's side. This is the price of the prompt cache. Switching to session: removes the correlation and the cache. |
5. What remains to be done before real operation¶
- Annotate the real corpus and re-run
tests/corpus_eval.py --real(Phase 5). - Fix with jo the recall thresholds per identifier class.
- Decide the Datadog and
mcp-proxy.anthropic.compolicy. - Deploy in a containerized environment or under a sandbox: this is the
only form where D9 is held (jo's arbitration of 2026-08-02 — no firewall
on the workstation). As long as this is not done, the proxy reduces the
surface, it does not close it: cf.
docs/d9-network-isolation.md. - Legal / DPO review on the basis of the present document.
- Envelope encryption of the vault (KMS/HSM), key rotation, immutable access log — not implemented in the MVP.