Skip to main content

shadow-vault

A bi-directional, format-preserving reversible PII proxy for AI agents, LangChain/LangGraph, and Model Context Protocol (MCP) tools.

Tests Python Versions License Zero Dependencies


Stop breaking your AI Agent tools with [REDACTED]

Most PII masking libraries were designed for static document redaction, not autonomous AI agents. Replacing sensitive data with [REDACTED] or <EMAIL_1> completely breaks tool-calling workflows:

  1. Schema & Validator Rejection: When your AI agent attempts to invoke a Stripe API, PostgreSQL tool, or CRM integration, strict downstream validators (Pydantic, regex schemas, database constraints) immediately crash when handed [REDACTED_CARD] instead of a 16-digit numeric card string.
  2. One-Way Irreversibility: Once data is destroyed by one-way redaction, local tools cannot execute real actions. You cannot process a refund, send an email, or dispatch an SMS with [EMAIL]. Bi-directional pseudonymization securely restores real PII at the tool boundary and re-masks outputs before returning to the model.
  3. SSE Streaming Token Tearing: Naive regex replacements leak torn fragments when modern LLMs stream tokens across Server-Sent Events (SSE) chunks (e.g. streaming "u_8f", then "9a1@shadow", then ".vault"). shadow-vault provides a lookahead holdback buffer that reassembles split tokens seamlessly without token tearing.

Architecture Flow

                     +---------------------------------------+
                     |         END USER / CLIENT             |
                     +---------------------------------------+
                                  |                 ^
                1. Raw User PII   |                 |  8. Hydrated Real PII
                   (e.g. Card)    v                 |     (StreamHydrator SSE)
                     +---------------------------------------+
                     |         SHADOW-VAULT PROXY            |
                     +---------------------------------------+
                        |                          ^
      2. Dehydrated     |                          |  7. Dehydrated Tool
         Safe Prompt    v                          |     Return Value
+------------------------------------+    +------------------------------------+
|         CLOUD LLM / AGENT          |    |          LOCAL MCP TOOL            |
| (OpenAI / Anthropic / LangChain)   |    |      (Stripe / SQL / API)          |
+------------------------------------+    +------------------------------------+
           |                                                 ^
           |  3. Model Tool Call with                        |  4. @vault_guard
           |     Synthetic Tokens                            |     Reversible Hydration
           +-------------------------------------------------+

Quickstart (12 Lines)

Install the core library (zero heavy NLP dependencies):

pip install shadow-vault

Protect any Python function or agent tool in seconds:

from shadow_vault import ShadowVault, vault_guard

vault = ShadowVault()

# 1. Protect your internal tool: automatically receives real PII, returns masked PII
@vault_guard(vault=vault, session_id_arg="session_id")
def charge_customer(email: str, card: str, session_id: str = "default") -> dict:
    # Function executes with REAL PII: email='alice@example.com', card='4532 0151 1283 0366'
    return {"status": "PAID", "receipt": email, "backup_phone": "+91 9876543210"}

# 2. Dehydrate safe prompt for the LLM:
prompt = vault.dehydrate("Charge alice@example.com on card 4532 0151 1283 0366", session_id="s1")
# Prompt: "Charge u_c09753@shadow.vault on card 4000009995970590"

# 3. Model executes tool using synthetic tokens -> returns sanitized output back to agent state:
result = charge_customer(email="u_c09753@shadow.vault", card="4000009995970590", session_id="s1")
# Result: {'status': 'PAID', 'receipt': 'u_c09753@shadow.vault', 'backup_phone': '+919000597059'}

Interactive Zero-Key Terminal Demo

Run the self-contained 4-stage interactive walkthrough (requires NO API keys):

python examples/demo_agent_roundtrip.py

Supported Entities (Global + India)

shadow-vault uses zero-dependency regexes coupled with strict mathematical checksum algorithms so random numbers are never falsely flagged:

Entity Type Mathematical Checksum / Algorithm Real Input Example Format-Preserving Synthetic Output
CREDIT_CARD Luhn Checksum (mod 10) (13–19 digits) 4532 0151 1283 0366 4000009995970590 (passes Luhn!)
AADHAAR Verhoeff Dihedral $D_5$ Algorithm 3675 9834 6016 999905970594 (passes Verhoeff!)
PAN_CARD Indian Income Tax Format ([A-Z]{5}[0-9]{4}[A-Z]{1}) ABCDE1234F SHDWV7059Z
UPI_ID Evaluated before email (@okaxis, @okhdfcbank, @paytm, etc.) dhiraj@okaxis u_a81c25@okshadow
EMAIL RFC-5322 (ignores @shadow.vault & UPI handles) alice@example.com u_c09753@shadow.vault
PHONE E.164, Indian mobile (+91 [6-9]\d{9}), US/NANP +91 9876543210 +919000597059 (valid 10-digit mobile)
SSN US Social Security Number (Area/Group/Serial checks) 219-45-7890 900-99-7059 (unissued area code)
API_KEY OpenAI sk-..., GitHub ghp_..., Google AIza..., Bearer sk-abcdef1234567890abcdef... sk-sv-shadow-c0975300000000000000

Why shadow-vault?

Capability shadow-vault Microsoft Presidio LangChain PIIMiddleware
Format-Preserving Tokenization Yes (Pydantic & regex valid) No (replaces with <EMAIL>) No (replaces with [REDACTED])
Reversible Tool Calls (MCP & Python) Yes (auto-hydrates at boundary) Manual (requires custom glue) No (one-way irreversible)
SSE Stream Safe (Zero Token Tearing) Yes (lookahead holdback buffer) No (chunks break tokens) No
Zero Heavy NLP Dependencies Yes (~0 MB, pure Python + SQLite) No (requires spaCy / PyTorch ~500MB) Yes
Mathematical Checksum Validation Yes (Luhn & Verhoeff algorithms) Partial No
Thread-Safe Session SQLite Storage Yes (built-in with TTL expiration) Requires External DB None

Framework Integrations

1. LangChain & LangGraph

from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
from shadow_vault import ShadowVault, protect_tool, ShadowVaultChatWrapper

vault = ShadowVault()
session_id = "agent_session"

# Protect Tools: Inputs hydrated before execution, outputs dehydrated before returning
@tool
def lookup_db(card_number: str) -> dict:
    """Fetch record from database."""
    return {"card": card_number, "balance": 1500}

safe_tool = protect_tool(lookup_db, vault=vault, session_id=session_id)

# Protect ChatModel: Prompts dehydrated before cloud LLM, tool-call arguments kept synthetic
llm = ChatOpenAI(model="gpt-4o")
safe_llm = ShadowVaultChatWrapper(llm=llm, vault=vault, session_id=session_id)

2. Model Context Protocol (MCP)

Intercept MCP tools/call JSON-RPC requests and responses:

from shadow_vault import ShadowVault, MCPVaultInterceptor

vault = ShadowVault()
interceptor = MCPVaultInterceptor(vault=vault)

# 1. Intercept incoming request from LLM:
safe_request = interceptor.intercept_request(mcp_request, session_id="user_session")

# 2. Intercept outgoing tool response before it reaches LLM:
safe_response = interceptor.intercept_response(mcp_response, session_id="user_session")

3. OpenAI-Compatible FastAPI Proxy Server

Deploy a drop-in proxy for any OpenAI SDK client:

import uvicorn
from shadow_vault import ShadowVault, create_proxy_app

vault = ShadowVault()
app = create_proxy_app(vault=vault, upstream_base_url="https://api.openai.com/v1")

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000)

Point your client to http://localhost:8000/v1:

from openai import OpenAI

client = OpenAI(
    base_url="http://localhost:8000/v1",
    api_key="your-api-key",
    default_headers={"X-Session-ID": "session_42"}
)

# Streaming SSE response with real-time token re-hydration:
stream = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "Send bill to alice@example.com"}],
    stream=True
)

for chunk in stream:
    if chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)

Benchmark & Performance Profile (1,000 Iterations)

Measured on a realistic 2,000-token prompt containing 12 distinct PII entities, structured MCP tool-call dictionaries, and 500-chunk SSE streams using benchmarks/run_benchmarks.py:

Latency Profile

Workload / Operation Input Payload Size P50 Latency P95 Latency P99 Latency Target Met
Dehydrate Prompt (12 Entities) ~2,000 tokens (4.6 KB) 2.876 ms 4.043 ms 4.531 ms Sub-5ms
Hydrate MCP Tool Arguments 5 Synthetic PII Args 26.6 μs (0.0266 ms) 45.9 μs 73.4 μs < 0.1 ms
StreamHydrator (Per Chunk) 500 Token Chunks 50.0 μs (0.0500 ms) 63.0 μs 72.3 μs < 0.1 ms
Full 500-Chunk Stream Total 500 SSE Token Chunks 25.00 ms 31.49 ms 36.16 ms Real-Time

Memory Overhead (tracemalloc)

Metric Measured Footprint Comparison vs. Heavy NLP (spaCy / Presidio)
Memory per Active Session 0.028 KB (28 bytes) ~12,500x lighter (~500 MB model load vs. 0.04 KB)
Peak Heap Overhead (500 Active Sessions) 28.56 KB Zero GPU / Torch dependencies required
Storage Engine SQLite In-Memory / WAL Built-in TTL expiration prunes inactive sessions

Checksum Accuracy & False-Positive Rejection

Test Scenario Sample Size False-Positive Rejection Rate Theoretical Guarantee
Random 16-Digit IDs (Order & Tracking Numbers) 10,000 90.03% Modulo 10 Luhn algorithm rejects non-card numbers
Random 12-Digit IDs (Transaction References) 10,000 89.95% Verhoeff dihedral group $D_5$ check rejects non-IDs
Single-Digit Substitution & Transposition Errors Exhaustive 100.0% 100% mathematical error detection guarantee

To re-run the benchmark suite locally:

python benchmarks/run_benchmarks.py

Running Tests

pytest -v
# 54 passed in 1.37s (100% pass rate)

Contributing

Contributions are welcome! Read CONTRIBUTING.md to see how you can add a new regional PII detector in under 20 lines of code.


License

Distributed under the MIT License. See LICENSE for details.

Metadata

Release files for shadow-vault 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for shadow-vault 0.3.0
File Size Uploaded Provenance Actions
shadow_vault-0.3.0.tar.gz 51.5 kB With provenance Details for shadow_vault-0.3.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for shadow-vault 0.3.0
File Interpreter ABI Platform Actions
shadow_vault-0.3.0-py3-none-any.whl Python 3 none any Details for shadow_vault-0.3.0-py3-none-any.whl

Total release size: 80.4 kB

Release files / shadow_vault-0.3.0.tar.gz

Download URL shadow_vault-0.3.0.tar.gz
Size 51.5 kB
Tags Source
SHA-256 checksum
How to use checksums
5f38c0d7e2808f088b1976fc3aacecd9294dcb1c67b6eba116a348ee2231cdb2
BLAKE2b-256 checksum
How to use checksums
0f5453737ffcd3a990b46a0d5b54d52936abe10ae3a446b300b930ee62b8158c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.

Transparency log

Release files / shadow_vault-0.3.0-py3-none-any.whl

Download URL shadow_vault-0.3.0-py3-none-any.whl
Size 28.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
11886cf9ee7ec976cce5c8d1b55223c48959e15d3cc7e1eafc7540792fe4133e
BLAKE2b-256 checksum
How to use checksums
f984cd9032e6cbb5ae6c105e75b90561b1b09f7bc6f629dac7f2fdbd17a2d970
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.

Transparency log

Security for shadow-vault 0.3.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for shadow_vault-0.3.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page