Redact before the provider call, not after
Once an LLM provider or model-controlled tool receives raw data, application-side masking is too late. The useful control point is the last local boundary where your code still owns the payload.
pyveil keeps the operation local and replaces supported values with scoped HMAC placeholders such as [EMAIL:12hex]. Stable placeholders let the model refer to the same hidden value during a run without receiving the original value.
Install and prove it works
pip install pyveil
python -m pyveil demo
The demo uses synthetic values and needs no secret. Application redaction does require your own HMAC secret so placeholders cannot be correlated by an unknown global key.
Protect a provider-neutral LLM call
from pyveil import Channel, Veil
veil = Veil.high(
secret=b"tenant-or-run-secret",
scope="tenant-123/session-456",
)
messages = [
{
"role": "user",
"content": "Email alice@example.com about account CUS-A1B2C3D4.",
}
]
safe = veil.redact_data(messages, channel=Channel.PROMPT_INPUT)
response = call_llm(safe.data) # OpenAI, Anthropic, Gemini, or your gateway
The list and dictionary shape is preserved. Only sensitive string values change. Use a CustomRule for identifiers or known values your application understands:
from pyveil import CustomRule
rules = [
CustomRule("CUSTOMER_ID", r"\bCUS-[A-Z0-9]{8}\b"),
CustomRule.exact("PERSON", ["Alice Kim", "Hong Gildong"]),
]
veil = Veil.high(secret=b"tenant-secret", rules=rules)
Use structured redaction for agent payloads
Do not stringify tool or model payloads just to mask them. redact_data recursively handles dictionaries, lists, tuples, and JSON while keeping booleans, numbers, and null values intact.
payload = {
"user": "alice@example.com",
"headers": {"Authorization": "Bearer DEMO_ONLY_CREDENTIAL_123456"},
"debug": True,
}
safe = veil.redact_data(payload, channel=Channel.TOOL_CALL_RESULT)
assert safe.data["debug"] is True
assert safe.data["user"].startswith("[EMAIL:")
assert safe.data["headers"]["Authorization"].startswith("[AUTH_HEADER:")
Block credentials before model-controlled tools execute
Tool arguments are a higher-risk boundary than an ordinary prompt. The default policy blocks credential-like findings on tool.call.arguments rather than silently forwarding them.
from pyveil import BlockedSensitiveData, Channel
try:
safe_args = veil.redact_data(
tool_args,
channel=Channel.TOOL_CALL_ARGUMENTS,
).data
except BlockedSensitiveData as exc:
audit(exc.summary()) # counts only; no raw secret
else:
run_tool(**safe_args)
Redact model input, tool results, MCP resources, memory, logs, and traces. Block credentials before model-controlled tool execution.
Keep large redaction work off the event loop
pyveil is synchronous and CPU-bound. For large payloads in FastAPI or another async server, run it in the default executor:
import asyncio
from functools import partial
redact = partial(
veil.redact_data,
payload,
channel=Channel.PROMPT_INPUT,
)
safe = await asyncio.get_running_loop().run_in_executor(None, redact)
For small request bodies the direct synchronous call is usually simpler. Keep max_input_chars enabled and enforce upstream request-size limits either way.
Test the boundary, not only the detector
def test_provider_never_receives_raw_email():
captured = {}
def fake_provider(messages):
captured["messages"] = messages
safe = veil.redact_data(
[{"role": "user", "content": "Email alice@example.com"}],
channel=Channel.PROMPT_INPUT,
)
fake_provider(safe.data)
assert "alice@example.com" not in str(captured)
assert "[EMAIL:" in str(captured)
The repository also ships a reproducible synthetic detector evaluation and runs it in CI.
Know what local regex redaction does not solve
| Need | Recommended layer |
|---|---|
| Known emails, phones, cards, credentials, domain IDs | pyveil at the final application boundary |
| Unknown people, organizations, locations, addresses | NER or Presidio upstream, then pyveil at the boundary |
| Documents, images, incident workflows, managed policy | Enterprise DLP |
| Prompt injection and model behavior controls | A dedicated guardrail layer |
Redaction reduces exposure. It does not prove GDPR, HIPAA, PCI, or any other compliance regime, and no detector has perfect recall.