Isolating PII Redaction Pipelines in MicroVMs
A PII redaction pipeline has a peculiar job: its entire purpose is to take the most sensitive data in your system and make it safe enough to hand to something less trusted — a third-party analytics vendor, a non-production environment, an LLM prompt, a support ticket, a data lake with broader read access. To do that job, it needs unrestricted access to the raw, unredacted input. Which means that until the moment it finishes and you've verified its output, the redaction pipeline itself is holding exactly the data it exists to protect.
I'm Ajay, I build PandaStack. This post is about treating redaction and anonymization jobs as a distinct isolation problem — not because the code is untrusted in the way a customer's uploaded script is, but because the failure mode of under-redaction is silent, the failure mode of a network-connected redaction job is a self-inflicted exfiltration channel, and the inputs are frequently the messiest, most adversarial data in your whole pipeline.
The asymmetric failure: two ways to be wrong, only one you'll notice
Redaction has two failure directions and they are not equally visible. Over-redaction — scrubbing too aggressively — breaks the downstream consumer in an obvious way: a support ticket loses the customer name it needed, an analytics dashboard shows nulls where a segment used to be, an LLM prompt loses the context it needed to answer. Someone files a bug. Under-redaction is the opposite: a phone number slips through in a free-text field, a name survives inside a nested JSON blob the regex didn't walk into, a document's metadata carries an author field nobody scrubbed. Nothing breaks. The data moves downstream looking clean, and the first anyone hears about it is a disclosure.
The raw input is frequently adversarial, even when it isn't malicious
Redaction pipelines commonly run over user-generated content — uploaded documents, support-ticket attachments, scraped web pages, form submissions — which means the parsers involved (PDF extraction, OCR, document-format libraries, archive handling) are processing untrusted files by definition. This is the same surface that makes malware sandboxes and file-upload processors dangerous: a PDF parser has a much larger attack surface than the regex that runs after it, and a crafted file can exploit a parser bug well before your PII detector ever sees a token.
- A malformed or maliciously crafted document can crash or exploit the extraction library, independent of anything to do with PII.
- Archive and compression bombs target the ingestion step of a redaction pipeline exactly the way they target any file-upload processor — a small file that expands to gigabytes on decompression.
- OCR and NLP models used for PII detection are themselves attack surface if they're third-party or self-hosted, and a pipeline that grants broad filesystem or network access to code performing model inference is trusting that code more than the task requires.
- PII detection increasingly runs an LLM or NER model as a component, which means the raw, unredacted document — the thing you're trying to protect — gets sent to the model's context window. Where that model runs matters as much as how good it is at finding phone numbers.
Default-deny network egress is the single highest-leverage control
Most of what makes a redaction job dangerous isn't the code — it's the network. A redaction pipeline processing raw PII has no legitimate reason to reach the general internet. It reads input, writes redacted output, and should be able to do nothing else. If it's compromised by a parser exploit, or simply has a dependency with a supply-chain backdoor, unrestricted egress turns a contained processing bug into an active exfiltration channel — using your own infrastructure to move the exact data you were trying to protect.
Run each redaction job in a microVM with egress locked down to nothing, or to an explicit allowlist if the job genuinely needs to call an internal detection API. The guest gets the raw input on disk, does its work, writes redacted output to a location the orchestrator reads back, and is destroyed. No outbound connection the job didn't specifically need ever had a chance to open.
from pandastack import Sandbox
import json
def redact_document(doc_id: str, raw_bytes: bytes, doc_kind: str) -> dict:
"""Redact ONE document in a guest with no network egress at all.
Raw PII enters this VM and never leaves it except as the verified,
redacted output the orchestrator explicitly reads back.
"""
sbx = Sandbox.create(
template="code-interpreter",
ttl_seconds=300,
metadata={"doc": doc_id, "kind": "pii-redaction"},
)
try:
# Egress locked to nothing -- a parser exploit or a bad dependency
# has no path out even if it tries.
sbx.network.set_egress(default="deny", allow=[])
sbx.filesystem.write(f"/work/input.{doc_kind}", raw_bytes)
out = sbx.exec(
f"cd /work && python3 -m redact.run "
f"--in input.{doc_kind} --kind {doc_kind} --out redacted.json "
f"--report findings.json",
timeout_seconds=180,
)
if out.exit_code != 0:
raise RedactionFailed(doc_id, out.stderr)
redacted = json.loads(sbx.filesystem.read("/work/redacted.json"))
findings = json.loads(sbx.filesystem.read("/work/findings.json"))
return {"doc_id": doc_id, "redacted": redacted, "findings": findings}
finally:
sbx.kill() # the raw document is gone; only the redacted copy left this VMThat `set_egress(default="deny")` call is doing more work than almost anything else in this pipeline. It's the difference between "a parser bug is a crashed sandbox" and "a parser bug is an incident," and it costs nothing in normal operation because the job never needed to talk to the internet in the first place.
Verify the output before it leaves the boundary — don't trust the redactor's own report
A redaction job that reports success is not the same as a redaction job that succeeded. Treat its own findings report as a hint, not a guarantee, and run an independent check on the output before it's released to whatever consumer is waiting for it — a second-pass scanner with different detection logic, a set of canary values planted in the input specifically to confirm they were caught, or a stricter pattern set applied purely as a gate. This is the same trust posture as verifying any sandboxed computation's output rather than taking its exit code at face value: the sandbox proposes a result, a trusted process outside it decides whether that result is good enough to release.
- Run detection twice with different tools or models and diff the findings — agreement is a weak signal of correctness, disagreement is a strong signal to hold the output for review rather than release it.
- Plant canary PII in test batches (a known fake SSN, a known fake phone number) and fail the whole batch if a canary survives to the output.
- Gate release on the redaction job's own resource behavior too — a job that finished suspiciously fast on a large, complex document is worth a second look before its output ships.
- Keep the raw input retained only as long as your policy requires, and destroyed with the sandbox otherwise — the shortest-lived copy of raw PII is the one that was never written to durable storage in the first place.
Shared redaction service vs container-per-job vs microVM-per-job
- Blast radius of a parser exploit — Shared service: a crafted document can compromise a long-lived process handling many documents' raw PII in sequence, and the process typically has broad network access for legitimate reasons elsewhere in the app. Container per job: better isolation, but a shared kernel and often shared egress rules remain. MicroVM per job: a real kernel boundary, and egress denied by default, so a successful exploit still has nowhere to send anything.
- Under-redaction detection — Shared service: findings usually get logged inline with everything else, making a systematic miss (a pattern that never matches a certain field format) hard to notice in the noise. Container: similar, marginally better isolation of logs per job. MicroVM: each job is a discrete, attributable run, which makes independent second-pass verification and canary testing straightforward to wire per run.
- PII exposure to detection models — Shared service: if PII detection calls an LLM or NER model, the raw document typically transits the same network path as everything else the service does. Container: no structural improvement here specifically. MicroVM: egress can be scoped to exactly the detection endpoint needed and nothing else, with the boundary enforced at the network-namespace level rather than in application code.
- Malicious/malformed input handling — Shared service: parser bugs affect a process handling many documents, so a crash or resource exhaustion has broad blast radius. Container: cgroup limits bound resource use but the kernel is shared. MicroVM: fixed resource allocation per job and a hard TTL, so one hostile document can't degrade unrelated jobs.
- Cost of isolation — Shared service: effectively free per document, which is why redaction is so often left running inside a general-purpose data pipeline rather than pulled out. MicroVM: on PandaStack a create restores a baked snapshot in about 179ms p50 (~203ms p99) rather than cold-booting — cheap enough to do per document, not just per batch.
The summary
A redaction pipeline's job is inherently paradoxical: it needs full access to the data it exists to protect. That's not a reason to skip isolating it — it's the reason to isolate it more carefully than almost anything else you run. Deny network egress by default so a parser exploit or a supply-chain bug in a dependency can't turn into exfiltration. Treat the raw input as adversarial, because it frequently is, even without malicious intent. And verify the output independently before it's released, because a redaction job reporting success is not proof that it succeeded — under-redaction is silent by nature, and silent is exactly the failure mode you can't afford to trust to a self-report.
Frequently asked questions
Why does a PII redaction pipeline need stronger isolation if the code isn't untrusted?
Because the input is adversarial even when the code is trusted, and because the consequence of a miss is silent. The pipeline processes user-generated content (uploaded documents, scraped pages, free-text fields) that can exploit parsing libraries independent of any PII-specific bug, and it holds raw, unredacted personal data as a matter of its job description. A crash in a general-purpose data pipeline is an alert. A crash — or a quiet miss — in a redaction pipeline can be a disclosure that nobody notices until it's discovered downstream.
Why is network egress the highest-priority control here rather than, say, code review?
Because a redaction job has no legitimate reason to reach the general internet — it reads raw input, writes redacted output, and that's the whole job. If a parser exploit or a compromised dependency does something unexpected, unrestricted egress is what turns that into exfiltration. Denying egress by default costs nothing during normal operation, since the job was never going to make an outbound connection anyway, and it closes off the one channel that turns a contained bug into a breach.
How do I catch under-redaction if the pipeline itself reports success?
Don't trust the redactor's own report as a guarantee. Run an independent second-pass check with different detection logic or a stricter pattern set, plant canary PII values in test batches and fail the batch if a canary survives, and treat the redaction job's output the way you'd treat any sandboxed computation's result — proposed by an untrusted process, verified by a trusted one, before it's released to whatever's waiting for it.
Does this apply to redaction that happens right before sending data to an LLM?
Especially there. If PII detection itself uses an LLM or NER model, the raw unredacted document has to reach that model somehow, which means the model call is part of the trust boundary, not outside it. Scope the sandbox's egress to exactly the detection endpoint it needs — nothing broader — so the raw document's exposure is limited to the one call it actually requires, and the redacted result is what continues on to wherever it was headed next.
Is this overkill for a small internal tool that just masks emails in log files?
For a fixed, well-tested pattern on structured, low-risk fields, probably — proportionality matters. The isolation is worth the (small) cost when the input is unstructured or user-generated, when the output is headed somewhere with a different trust level than the source (a third-party vendor, a broader-access data lake, a non-production environment), or when a miss carries regulatory weight. Match the control to the sensitivity of what's actually flowing through the pipeline.
Keep reading
- Egress-controlled sandboxes on PandaStack — deny-all network by default, scoped allowlists when needed
- Controlling network egress for untrusted code
- Sandboxing untrusted file uploads and media processing
- MicroVMs for malware detonation sandboxes
- Zero-trust code execution architecture
49ms p50 cold start. Fork, snapshot, and scale to zero.