Taintward follows sensitive data through every tool call your agent makes. The model gets placeholders, third parties get nothing, your own systems get the real values back. And you get a signed report to show your customers.
Masking everything to [PII] breaks the agent: it can no longer write to Alice about her tax form. Taintward swaps each value for a stable placeholder on the way to the LLM, and swaps the real value back on the way to your user or your own systems.
Write to Alice Lee (alice@acme.com) about the missing form for SSN 234-12-1234.
Write to <PERSON_1> (<EMAIL_1>) about the missing form for SSN <SSN_1>.
The real values never leave your process.
Hi Alice Lee, we are still missing one form for the account ending in 1234...
An external API or a log line only ever gets a one-way mask.
Every path writes standard OpenTelemetry spans, so you can view them in SigNoz, Jaeger, Grafana, Datadog or anything else that speaks OTLP.
LangChain, LangGraph, the OpenAI Agents SDK, or plain OpenAI and Anthropic clients. A few lines, no decorators on every tool.
pip install "taintward[langchain]"
# unknown tools are treated as external
ward = tw.init("support-bot", destinations={
"search_crm": "internal",
"send_email": "external",
})
tools = tw.wrap_langchain_tools(tools)
model = tw.protect_langchain_model(model)
Integration guide
Put it in front of any MCP server. No code changes. Per tool: redact, block, or only record.
# gateway.yaml
upstreams:
crm: { command: crm-mcp-server }
tools:
- match: "crm.get_*"
destination: internal
- match: "*.send_*"
destination: external
mode: block
Gateway guide
An OpenTelemetry collector with a Rego policy step. Scales out on Kubernetes, and edits to the policy roll out without a rebuild.
helm install taintward \ ./deploy/helm/taintward \ --set exporter.endpoint=signoz:4317 # the policy is a plain .rego file # your security team can readDeploy guide
One command turns your traces into a report: what data your agents touched, where it went, what was redacted or blocked, and a signed, tamper-evident log root anyone can verify.
Which tools and which services received personal data, and which calls crossed borders.
Every violation goes into an append-only Merkle log with signed checkpoints, so a deleted or edited record is detected.
The report only claims what was measured, and says how. Detection accuracy is published, not promised.
The sidecar collector evaluates this Rego policy on every span before it is stored. Hover a rule to see which tools it fires on in the breach run.
package taintward # Sinks where raw sensitive data must not land.sensitive_sinks := {"external", "llm", "log"}non_adequate_jurisdictions := {"us"} # Rule 1: sensitive data reaching an external / llm / log sink.violation contains {"rule": "pii_egress", ...} if { input.sensitive == true input.destination in sensitive_sinks} # Rule 2: PII to a non-adequate jurisdiction (GDPR Art. 44).transfer_violation contains {"rule": "transfer", ...} if { has_class(input.classes, "pii") input.jurisdiction in non_adequate_jurisdictions} # Always redact raw sensitive values from stored spans.redact if { input.sensitive == true}
Sensitive data headed to an LLM, an external API or a log is a violation.
PII sent to a non-adequate jurisdiction, the GDPR Art. 44 case. Fires on top of rule 1.
SigNoz never stores the raw value, even for tools that legitimately need it internally.
Each stage replaces a piece of the AgentRaft research pipeline with something that runs live on OpenTelemetry.
AgentRaft (Lin et al., 2026) found data over-exposure in 57% of the agent call chains it analyzed. Taintward keeps its formal model, D_OE = (D_trans \ (D_nec ∪ D_int)) ∩ D_total, and makes it enforceable.
gen_ai.* spans in SigNozStarts the collector, a demo agent that leaks an SSN and an AWS key, and a trace viewer. Open localhost:16686 and look for the raw values. You will not find them.
$ git clone https://github.com/DCchoudhury15/Taintward && cd Taintward $ docker compose up --build # open http://localhost:16686, service taintward-hr-bot