# Shadow documentation > Shadow is an open-source privacy layer for AI: it swaps names, emails, customer data and secrets for realistic, reversible fakes before prompts reach the APIs behind ChatGPT, Claude and other LLMs, then restores them in the answer. Source: https://getshadow.dev/ This file concatenates every page of the Shadow documentation as Markdown. Each page starts with a level-1 heading and its source URL. --- # Shadow: open-source PII redaction for ChatGPT, Claude and every LLM Source: https://getshadow.dev/ > Stop sensitive data leaking to ChatGPT, Claude and any LLM. Shadow swaps PII and secrets for realistic, reversible fakes on your machine. Free, Apache-2.0. **Shadow is an open-source privacy layer that sits between your apps and AI providers such as OpenAI, Anthropic and Google Gemini.** It finds personal data, customer names, credentials and internal identifiers in every outbound prompt, replaces them with realistic, format-preserving fakes, and translates the fakes back in the response. It is PII redaction and AI data loss prevention that keeps ChatGPT-style assistants, Claude, coding agents and your own LLM apps useful. It runs as a local HTTPS proxy, a gateway base URL, a Docker sidecar or the `shadow-core` Rust library, and the mapping between real and fake values never leaves the machine. ## Frequently asked questions ### How do I stop employees pasting customer data into ChatGPT? Give people an AI tool that is safe to paste into instead of one they have to avoid. Route your API-based AI clients (internal chat apps, SDK-based tools, coding assistants, agents) through Shadow, and customer names, emails, IDs and secrets are replaced with fakes before the request leaves the machine, while the answer still comes back with the real values. Shadow intercepts provider APIs such as `api.openai.com` and `api.anthropic.com`, not consumer web apps like chatgpt.com, so pair it with a policy that points staff at an approved API-backed client. See [use cases](https://getshadow.dev/use-cases/). ### Is Shadow a Presidio alternative? It is a complement more than a replacement. Microsoft Presidio is a detector and anonymizer library that replaces values with placeholders or hashes. Shadow adds what sits around detection: consistent, format-preserving fakes from one persona, an encrypted mapping, restoration of responses, streams and tool calls, and a ready-made HTTPS proxy and gateway. Shadow can call Presidio as an extra detector. See [Shadow vs Presidio](https://getshadow.dev/compare/presidio/). ### Does Shadow work with Claude Code, Cursor or GitHub Copilot? Claude Code and other tools that accept a base URL (`ANTHROPIC_BASE_URL`, `OPENAI_BASE_URL`) or honour `HTTPS_PROXY` work through [gateway mode](https://getshadow.dev/integrate/gateway-mode/) or [proxy mode](https://getshadow.dev/integrate/proxy-mode/), and the `code` intent keeps identifiers readable. Cursor and GitHub Copilot send traffic to their vendors' own backends, which are not on Shadow's built-in host list; you can add hosts with `extra_hosts`, but check the request log before relying on it, and clients that pin certificates cannot be intercepted. See [integrating any endpoint](https://getshadow.dev/integrate/any-endpoint/). ### Is Shadow GDPR or HIPAA compliant? No tool makes an organisation compliant on its own. Shadow helps you meet data-minimisation, confidentiality and transfer requirements under GDPR and HIPAA by replacing the personal data and PHI it detects before requests reach AI providers, and its mapping never leaves your machine. Detection is best effort, and an action the provider runs itself (a hosted tool) is sent the real contact details it needs, each one logged. Whether you are compliant still depends on your whole programme: contracts, policies, access control and what else you send. See [industries](https://getshadow.dev/industries/). ### Does Shadow work offline and self-hosted? Yes. Shadow is a single binary that runs on your laptops, servers or containers, with no cloud service, account or telemetry. Detection, the persona and the encrypted vault are all local, and optional detectors (a NER model, Presidio) run on addresses you choose. The only outbound traffic is the masked request you were already sending, and you can point it at a self-hosted model endpoint instead of a cloud provider. ### Can Shadow unmask responses and tool calls? Yes. Every fake in the response is turned back into the real value, including streamed SSE and NDJSON responses where a fake is split across events. Tool-call arguments are restored on your machine before your tools run. When you ask the model to write to or act on someone, that recipient is passed through as written (and logged), so an agent's email reaches the real address and greets the right person. Tools hosted in the provider's cloud are handled by an explicit policy. See [tool calls and agents](https://getshadow.dev/concepts/tool-calls/). ### What does Shadow cost? Nothing. Shadow is free and open source under the Apache-2.0 licence, with no account, licence key or usage fees. You keep paying your AI provider as before. ### How accurate is Shadow's PII detection? Detection combines checksummed patterns (cards, IBANs, national IDs), secrets and machine identifiers, your own dictionary of names and codenames, and an optional local NER model or Presidio for free-text names. Results depend on your data, so Shadow publishes its methodology and measurements on the [benchmarks](https://getshadow.dev/benchmarks/) page, and `shadow scan` shows what would be masked in your own files before you rely on it. ### Will masking make the AI's answers worse? Usually not, because the model still sees coherent data: a fake email is still an email, two different people stay two different people, and a fake card still passes Luhn. That is the main difference from placeholder redaction, which breaks the model's ability to reason about the values. For bulk reports Shadow switches to typed tokens such as `[PERSON_0001]`, which are restored in the answer. ### Which AI providers does Shadow support? OpenAI, Anthropic, Google Gemini and Vertex AI, Azure OpenAI, AWS Bedrock, OpenRouter, Mistral, Groq, Together AI, DeepSeek, xAI, Cohere, Perplexity and more, plus any custom or self-hosted HTTPS model API you add. See [supported providers](https://getshadow.dev/reference/providers/). --- # Get started with Shadow, the open-source privacy layer for AI Source: https://getshadow.dev/getting-started/ > Install Shadow and mask PII and secrets in OpenAI (ChatGPT models), Anthropic (Claude) and other LLM API traffic in five minutes. Introduction, quick start and installation for macOS, Linux, Windows and Docker. Shadow replaces names, emails, customer data and secrets with realistic, reversible fakes before a request reaches an AI provider, and restores them in the answer. Start with the introduction, or go straight to the five-minute quick start. --- # Introduction Source: https://getshadow.dev/getting-started/introduction/ > What Shadow is: an open-source privacy layer that masks PII and secrets in prompts to ChatGPT, Claude and other LLMs with reversible fakes, unlike blocking DLP. Shadow is an endpoint privacy layer for AI traffic. It sits between your apps and AI providers such as OpenAI, Anthropic, Google Gemini, Azure OpenAI, OpenRouter, Mistral and Groq. On the way out, it finds sensitive values in each request and replaces them with realistic, format-preserving fakes. On the way back, it turns the fakes into the real values again, streamed responses and tool calls included. ```text you ── "Summarise this thread: jane@acme.com asks when Project Falcon ships to Acme GmbH" ──▶ Shadow Shadow ── "Summarise this thread: hui.zhao@zhilan.cn asks when Velora Quilent ships to Zhilan Technology Co., Ltd." ──▶ provider provider ── "hui.zhao@zhilan.cn wants a ship date for Velora Quilent…" ──▶ Shadow Shadow ── "jane@acme.com wants a ship date for Project Falcon…" ──▶ you ``` The provider gets a coherent prompt about a company that does not exist. You get an answer about yours. When the goal is to write to or act on someone ("Draft a reply to jane@acme.com about Project Falcon"), Shadow passes the recipient through as written, so the model greets and addresses the right person, and logs the keep; Project Falcon and everything else that is not the recipient are still masked. Set `[policy.act] targets = "surrogate"` if the recipient must stay hidden from the provider too. See [intent-aware policy](https://getshadow.dev/concepts/intent-policy/). ## The problem Prompts carry far more than the question: - names, emails and phone numbers of colleagues and customers; - customer lists, invoices with IBANs, contracts with addresses; - internal hostnames, IPs and file paths that contain your username; - API keys and tokens pasted along with a config file; - the name of your company and its unreleased projects. Once a request reaches a provider, that content can end up in logs, abuse-monitoring queues, support tooling, training sets or a breach. Policies tell people not to paste sensitive data, and people paste it anyway, because the tool is useful. ## The approach: translate, don't block Most data-loss-prevention gateways **block** a request when they see something sensitive. Blocking breaks the tool, so people route around it. Plain redaction replaces values with `[REDACTED]` or ``, which loses meaning: the model can no longer tell two people apart, write an email to the right address, or keep a table consistent. Shadow **translates**. Every sensitive value maps to a stable fake of the same shape, drawn from a persistent fake identity for your machine called the [persona](https://getshadow.dev/concepts/personas/): | Real | What the provider sees | Property kept | |---|---|---| | `jane@acme.com` | `hui.zhao@zhilan.cn` | valid email, persona domain | | `4111 1111 1111 1111` | `4837 2191 9471 3480` | 16 digits, valid Luhn check | | `DE89 3704 0044 0532 0130 00` | another German IBAN | passes mod-97 | | `Acme GmbH` (your organisation) | `Zhilan Technology Co., Ltd.` | a company name | | `/Users/jdoe/src/app` | `/Users/huizhao/src/app` | a path that still works in code | The same real value always gets the same fake, and no two real values share a fake, so the reverse translation is unambiguous. Restoration works on whole fakes, and on a fake person's first or last name on its own ("Dear Hui," becomes "Dear Jane,"). Other fragments, such as the last four digits of a card or the domain of an email, stay fake. The mapping lives in an [encrypted vault](https://getshadow.dev/concepts/vault/) on your machine and never leaves it. ## What Shadow is not - **Not an anonymiser.** Providers still see your IP address and the account behind your API key. Shadow protects the *content* of requests. See the [security model](https://getshadow.dev/reference/security/). - **Not a perfect detector.** Detection combines regular expressions, checksums, a dictionary of your own terms, local machine facts and optional NER or [Presidio](https://getshadow.dev/concepts/detection/#presidio). It is best effort, and you can test it with `shadow scan`. - **Not a cloud service.** There is no account, no hosted component and no telemetry. - **Not a browser or SaaS filter.** Shadow covers apps that call AI provider APIs such as `api.openai.com` and `api.anthropic.com` (see [supported providers](https://getshadow.dev/reference/providers/)). It does not see what people type into chatgpt.com, claude.ai or gemini.google.com, or into AI features built into other SaaS products. ## See what your AI tools send The activity dashboard shows how much traffic went to which provider (OpenAI, Anthropic, Gemini and others), what was masked by data type, what was blocked, and which app sent it. The same view is available from the menu-bar app on Mac and Windows and as `shadow top` in the terminal. ## Three ways to run it | Mode | Use it when | Setup | |---|---|---| | [Proxy](https://getshadow.dev/integrate/proxy-mode/) | You want every tool on a machine that calls a supported AI API covered | `HTTPS_PROXY=http://127.0.0.1:8877` and trust a local CA | | [Gateway](https://getshadow.dev/integrate/gateway-mode/) | The tool lets you set a base URL | `base_url=http://127.0.0.1:8877/gw/openai/v1`, no CA | | [Library](https://getshadow.dev/integrate/rust/) | You build a Rust service that calls models | `shadow-core` crate, masking in-process | Meridian, a compliance platform, embeds `shadow-core` so every model call it makes is masked in-process. ## Next steps - [Quick start](https://getshadow.dev/getting-started/quick-start/): protect your first request in five minutes. - [How it works](https://getshadow.dev/concepts/how-it-works/): the request and response lifecycle. - [Intent-aware policy](https://getshadow.dev/concepts/intent-policy/): how actions, reports and agents are handled. --- # Quick start Source: https://getshadow.dev/getting-started/quick-start/ > Install Shadow, create a persona, start the local proxy and send your first PII-masked request to OpenAI or Anthropic in about five minutes. This guide takes about five minutes. You will install the `shadow` binary, create a persona for your machine, start Shadow in the foreground and send a request through it. 1. **Install Shadow** **macOS / Linux** ```sh curl -fsSL https://raw.githubusercontent.com/Tiny-Bubble-Company/shadow/main/packaging/install.sh | sh ``` **Windows** ```powershell irm https://raw.githubusercontent.com/Tiny-Bubble-Company/shadow/main/packaging/install.ps1 | iex ``` **Cargo** ```sh cargo install --locked --git https://github.com/Tiny-Bubble-Company/shadow shadow-cli ``` The install script downloads a release binary when one exists for your platform and otherwise builds from source with `cargo`. It never changes system settings. See [Installation](https://getshadow.dev/getting-started/installation/) for Docker and service setups. 2. **Create the persona, vault key and local CA** ```sh shadow init --org "Acme GmbH" ``` `--org` names your organisation. Shadow protects it (and the bare name `Acme`) and maps it to the persona's company. You can pass `--org` several times, and `--region JP` to choose where the persona lives. ```text ◆ Persona — what AI providers see company Xinghe Technology Co., Ltd. (xinghe.cn) person Ying Chen location Chengdu, China · Asia/Shanghai · zh-CN user id user-941ea3e72322 username yingchen hostname xinghe-srv-11 colleagues Tao Zhu, Hui Yang, Lei Li, Yong Xu ``` Your persona will differ: it is generated randomly once and then kept. 3. **Try masking offline** ```sh echo "Ticket from jane@acme.com (Acme GmbH): card 4111 1111 1111 1111 declined, logs in /Users/jdoe/src" | shadow mask ``` ```text Ticket from tsun@xinghe.cn (Xinghe Technology Co., Ltd.): card 4261 8002 0172 9224 declined, logs in /Users/jwang/src ``` Pipe the output into `shadow unmask` and you get the original back. Nothing is sent anywhere by these commands. 4. **Start Shadow** ```sh shadow run ``` Shadow listens on `127.0.0.1:8877` as both an HTTPS proxy and a gateway. Leave it running and open a second terminal. 5. **Send a protected request** The quickest path is **gateway mode**, which needs no certificate setup. Point the OpenAI base URL at Shadow: ```sh curl http://127.0.0.1:8877/gw/openai/v1/chat/completions \ -H "Authorization: Bearer $OPENAI_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"Summarise in one line: jane@acme.com from Acme GmbH says her card was declined."}]}' ``` The provider receives the persona's email and company. The response you get back contains `jane@acme.com` and `Acme GmbH`. With `shadow run` in the foreground, the log shows how many values were replaced for each request, never the values themselves. Most SDKs read a base URL from the environment, so existing tools often need just one variable: ```sh export OPENAI_BASE_URL=http://127.0.0.1:8877/gw/openai/v1 export ANTHROPIC_BASE_URL=http://127.0.0.1:8877/gw/anthropic ``` 6. **Verify** ```sh shadow status # proxy health, persona, vault size, CA fingerprint shadow identity # the persona providers see shadow vault stats # how many values are protected, by kind shadow scan prompt.txt # what Shadow would replace in a file, without changing it ``` ## Cover more tools: proxy mode Gateway mode works for anything that accepts a base URL. To cover tools that don't, such as IDE extensions, desktop apps and scripts that call a [supported AI API](https://getshadow.dev/reference/providers/) and honour `HTTPS_PROXY`, use Shadow as an HTTPS proxy. That needs the local CA to be trusted once: ```sh shadow install # dry run: prints every step it would take shadow install --apply # background service + CA trust + env file ``` Then add the printed environment (also written to `/env.sh`) to your shell profile: ```sh export HTTPS_PROXY=http://127.0.0.1:8877 export NO_PROXY=localhost,127.0.0.1 ``` Only known AI hosts are decrypted. Everything else passes through a blind tunnel, which also means AI web apps such as chatgpt.com in a browser are not masked. Details in [Proxy mode](https://getshadow.dev/integrate/proxy-mode/). > **Protect your own vocabulary** Detectors find structured values on their own. Add the names they cannot know about, such as codenames, customers and people: ```sh shadow terms add "Project Falcon" "org:Globex" "person:Jane Doe" ``` ## What next - [Choose an integration mode](https://getshadow.dev/integrate/overview/) for each of your tools. - Read about [intent-aware policy](https://getshadow.dev/concepts/intent-policy/) before using agents with tools. - Review the [configuration reference](https://getshadow.dev/reference/configuration/). --- # Installation Source: https://getshadow.dev/getting-started/installation/ > Install the Shadow binary on macOS, Linux or Windows: one-line install script, Cargo from source, Docker, or a background service that starts at login. Shadow is a single static binary called `shadow`. It has no runtime dependencies: no OpenSSL, no Python, no Node. Building from source needs Rust 1.88 or newer. ## Install script **macOS / Linux** ```sh curl -fsSL https://raw.githubusercontent.com/Tiny-Bubble-Company/shadow/main/packaging/install.sh | sh ``` The script: 1. detects your platform (`aarch64` or `x86_64`, `apple-darwin` or `unknown-linux-gnu`); 2. downloads `shadow-.tar.gz` from the latest GitHub release if one exists, otherwise builds from source with `cargo install`; 3. installs the binary to `~/.local/bin/shadow` (override with `SHADOW_BIN_DIR`); 4. runs `shadow init` to create the persona, vault key and CA. It never changes system settings. That is what `shadow install --apply` is for. **Windows** ```powershell irm https://raw.githubusercontent.com/Tiny-Bubble-Company/shadow/main/packaging/install.ps1 | iex ``` The script installs `shadow.exe` to `%LOCALAPPDATA%\Shadow`, adds that folder to your user `PATH`, and runs `shadow init`. It downloads a release zip when available and falls back to `cargo install`. Only 64-bit Windows is supported. Prefer to read a script before running it? Download it first, inspect it, then run it with `sh install.sh`. Set `SHADOW_REPO` to install from a fork or mirror. ## From source with Cargo ```sh cargo install --locked --git https://github.com/Tiny-Bubble-Company/shadow shadow-cli shadow init ``` Or from a clone: ```sh git clone https://github.com/Tiny-Bubble-Company/shadow cd shadow cargo build --release -p shadow-cli ./target/release/shadow --version ``` ## Docker The repository ships a two-stage `Dockerfile` that builds Shadow and runs it as an unprivileged user (UID 10001) with its data in `/data`. ```sh docker build -f packaging/Dockerfile -t shadow . docker run -d --name shadow \ -p 127.0.0.1:8877:8877 \ -v shadow-data:/data \ shadow ``` The container runs `shadow run --listen 0.0.0.0:8877`, has a health check based on `shadow status --quiet`, and keeps the persona, vault and CA in the `/data` volume. Keep that volume: it holds the mapping needed to restore responses. See [Docker and Kubernetes](https://getshadow.dev/integrate/docker-kubernetes/) for sidecar patterns. ## Run as a background service `shadow install` sets Shadow up to start at login and trusts the local CA. It is a **dry run by default**: it prints every file it would write and every command it would run. Add `--apply` to perform the steps. ```sh shadow install # show the plan shadow install --apply # do it ``` What it does per platform: | Platform | Service | CA trust | |---|---|---| | macOS | LaunchAgent `~/Library/LaunchAgents/dev.shadow.proxy.plist`, loaded with `launchctl bootstrap gui/` | `security add-trusted-cert -r trustRoot` into your login keychain | | Linux | systemd user unit `~/.config/systemd/user/shadow.service`, `systemctl --user enable --now` | Copies the CA to `/usr/local/share/ca-certificates` and runs `update-ca-certificates` (Debian, Ubuntu), or to `/etc/pki/ca-trust/source/anchors` and runs `update-ca-trust` (Fedora, RHEL). Uses `sudo`. | | Windows | Scheduled Task `Shadow` that runs at logon with limited rights | `certutil -user -addstore Root`; also sets `SHADOW_HOME` for your user | With `--apply`, it also writes `env.sh` (or `env.ps1` on Windows) to the data directory with the proxy variables for your shell profile. To remove everything: ```sh shadow uninstall # dry run shadow uninstall --apply ``` The systemd unit is hardened (`NoNewPrivileges`, `ProtectSystem=strict`, write access only to the data directory). For a system-wide server install, copy `packaging/linux/shadow.service` to `/etc/systemd/system/`, add a `User=` line and point `SHADOW_HOME` at a directory that user owns. On Windows, `packaging/windows/install-service.ps1` registers the same Scheduled Task from PowerShell; wrap `shadow.exe run` with NSSM or WinSW if you need a machine-wide Windows service. ## Data directory Everything Shadow keeps lives in one directory: | OS | Default | |---|---| | macOS | `~/Library/Application Support/shadow` | | Linux | `~/.local/share/shadow` | | Windows | `%APPDATA%\shadow` | Override it with `SHADOW_HOME`, `--data-dir`, or `data_dir` in `shadow.toml`. | File | Contents | |---|---| | `shadow.toml` | Configuration ([reference](https://getshadow.dev/reference/configuration/)) | | `persona.json` | The fake identity of this machine | | `vault.bin` | Encrypted real↔fake mapping | | `vault.key` | 32-byte vault key, mode `0600` | | `terms.json` | Terms added with `shadow terms add` | | `ca/ca.pem`, `ca/ca.key` | Local CA certificate and key (`0600`) | | `ca/bundle.pem` | System roots plus the Shadow CA, for runtimes that replace their trust store | ## Verify the install ```sh shadow --version shadow status ``` `shadow status` reports whether the proxy answers on its listen address, the persona, the vault size, the CA fingerprint, whether the model detector is on, and whether an upstream proxy is configured. ## Optional components - **NER sidecar.** Open-weight models (OpenAI Privacy Filter and GLiNER) or a small trainable PyTorch model in `ml/` that find free-text names and addresses. See [Detection](https://getshadow.dev/concepts/detection/#local-ner-sidecar). - **Presidio.** Microsoft's PII analyzer as an extra detector. See [Detection](https://getshadow.dev/concepts/detection/#presidio). --- # Integrate Shadow: proxy, gateway, Docker, Kubernetes or Rust Source: https://getshadow.dev/integrate/ > Ways to put Shadow in front of AI traffic: a local HTTPS proxy for desktop tools, a gateway base URL for SDKs, a Docker or Kubernetes sidecar, any HTTP endpoint, or the Rust library. Pick the integration that matches how your traffic reaches the model. Most desktop tools use proxy mode; SDKs and servers usually use the gateway base URL. --- # Integration modes Source: https://getshadow.dev/integrate/overview/ > Choose how to run Shadow: HTTPS proxy for AI tools on a machine, gateway base URL for SDKs, or the embedded Rust library. How each mode works and when to use it. Shadow runs the same masking engine in three ways. You can mix them on one machine: the proxy and the gateway are served from the same port, and they share the persona and vault. | | Proxy | Gateway | Library | |---|---|---|---| | **How you connect** | `HTTPS_PROXY=http://127.0.0.1:8877`, or a PAC file | Base URL `http://127.0.0.1:8877/gw//...` | `shadow-core` crate in your Rust process | | **CA trust needed** | Yes, once | No | No | | **Covers** | Any tool that honours proxy settings and calls an [intercepted AI API host](https://getshadow.dev/reference/providers/) | Tools and SDKs that accept a base URL | Your own code | | **Non-AI traffic** | Tunnelled without decryption | Not involved | Not involved | | **Typical users** | Developer laptops, IDEs, desktop apps, CI runners | SDK-based apps, services, containers | Platforms that call models from Rust | | **Guide** | [Proxy mode](https://getshadow.dev/integrate/proxy-mode/) | [Gateway mode](https://getshadow.dev/integrate/gateway-mode/) | [Embed in Rust](https://getshadow.dev/integrate/rust/) | ## Choosing **Start with the gateway** if the tool lets you set a base URL. It is explicit, it needs no certificate, and you can see in the configuration that requests go through Shadow. **Use the proxy** when you want every AI API client on a machine covered, including tools you don't control. Shadow decrypts only the [known AI hosts](https://getshadow.dev/reference/providers/) and tunnels everything else byte for byte, so banking, email and package downloads are never touched. **Embed the library** when you are building the application yourself in Rust. There is no extra hop, you control exactly which strings are masked, and you can use [sessions](https://getshadow.dev/integrate/rust/#sessions-and-intent) to apply the intent policy per request. ## Deployment patterns | Where | Pattern | |---|---| | Laptop or workstation | `shadow install --apply`: background service, CA trusted, proxy env in your shell | | Server or VM | systemd unit, gateway URLs in the app's environment | | Container | Sidecar container on `localhost:8877`, gateway mode. See [Docker and Kubernetes](https://getshadow.dev/integrate/docker-kubernetes/) | | Rust service | `shadow-core` in-process, data directory on a persistent volume | | Anything else that talks to an LLM | See [Integrating any endpoint](https://getshadow.dev/integrate/any-endpoint/) | ## What every mode does Whatever the mode, a request goes through the same steps: 1. **Classify** the request's intent and pick a treatment per value ([intent policy](https://getshadow.dev/concepts/intent-policy/)). 2. **Detect** sensitive values in every string of the JSON body, plus uploaded [files](https://getshadow.dev/concepts/file-masking/). 3. **Translate** each value to its fake or token, recorded in the vault. 4. **Scrub** identifying fields and headers (proxy and gateway only). 5. **Restore** the response: JSON, server-sent events, NDJSON streams and tool-call arguments. See [How it works](https://getshadow.dev/concepts/how-it-works/) for the full lifecycle. --- # Proxy mode Source: https://getshadow.dev/integrate/proxy-mode/ > Run Shadow as an HTTPS forward proxy that masks PII in the AI API traffic of every tool on a machine: trusting the local CA, intercepted hosts, bypass rules and PAC files. In proxy mode, tools send their traffic to Shadow with the standard `HTTPS_PROXY` setting. For each connection, Shadow looks at the destination host: - **Known AI host** (for example `api.openai.com`): Shadow terminates TLS with a certificate from its local CA, masks the request, forwards it to the real host over a fresh TLS connection, and restores the response. - **Any other host**: Shadow opens a plain TCP tunnel and copies bytes in both directions. It never sees the content, because it never holds a certificate for that host. TLS is terminated only on your machine, before the request is encrypted again for the provider. ## 1. Start Shadow ```sh shadow run # foreground # or, as a background service with the CA trusted: shadow install --apply ``` ## 2. Trust the local CA `shadow init` creates a CA at `/ca/ca.pem`. Its private key is created with mode `0600` and never leaves the machine. The CA is constrained (path length 0) and valid for 10 years. Leaf certificates are minted in memory per host. `shadow install --apply` adds the CA to the OS trust store. To do it by hand: **macOS** ```sh security add-trusted-cert -r trustRoot \ -k ~/Library/Keychains/login.keychain-db \ ~/Library/Application\ Support/shadow/ca/ca.pem ``` **Debian / Ubuntu** ```sh sudo cp ~/.local/share/shadow/ca/ca.pem /usr/local/share/ca-certificates/shadow-local-ca.crt sudo update-ca-certificates ``` **Fedora / RHEL** ```sh sudo cp ~/.local/share/shadow/ca/ca.pem /etc/pki/ca-trust/source/anchors/shadow-local-ca.pem sudo update-ca-trust ``` **Windows** ```powershell certutil -user -addstore Root "$env:APPDATA\shadow\ca\ca.pem" ``` The running proxy also serves the CA at `http://127.0.0.1:8877/shadow/ca.pem`, which is handy in containers. ### Runtimes with their own trust store Several runtimes ignore the OS trust store. Point them at Shadow's files: | Runtime | Variable | Value | |---|---|---| | Node.js | `NODE_EXTRA_CA_CERTS` | `ca/ca.pem` (adds to the built-in roots) | | Python `requests` | `REQUESTS_CA_BUNDLE` | `ca/bundle.pem` | | Python `httpx`, OpenSSL-based tools | `SSL_CERT_FILE` | `ca/bundle.pem` | | curl | `CURL_CA_BUNDLE` | `ca/bundle.pem` | `bundle.pem` contains the system roots plus the Shadow CA, because these variables *replace* the trust store rather than extend it. ## 3. Set the proxy environment `shadow install` prints the exact lines for your data directory, and `--apply` writes them to `/env.sh` (or `env.ps1`): **macOS / Linux** ```sh export HTTPS_PROXY=http://127.0.0.1:8877 export NO_PROXY=localhost,127.0.0.1 export NODE_EXTRA_CA_CERTS="$HOME/Library/Application Support/shadow/ca/ca.pem" export REQUESTS_CA_BUNDLE="$HOME/Library/Application Support/shadow/ca/bundle.pem" export SSL_CERT_FILE="$HOME/Library/Application Support/shadow/ca/bundle.pem" export CURL_CA_BUNDLE="$HOME/Library/Application Support/shadow/ca/bundle.pem" ``` On Linux the paths are under `~/.local/share/shadow`. **Windows** ```powershell $env:HTTPS_PROXY = "http://127.0.0.1:8877" $env:NO_PROXY = "localhost,127.0.0.1" $env:NODE_EXTRA_CA_CERTS = "$env:APPDATA\shadow\ca\ca.pem" $env:REQUESTS_CA_BUNDLE = "$env:APPDATA\shadow\ca\bundle.pem" $env:SSL_CERT_FILE = "$env:APPDATA\shadow\ca\bundle.pem" $env:CURL_CA_BUNDLE = "$env:APPDATA\shadow\ca\bundle.pem" ``` Add `source "/env.sh"` to `~/.zshrc` or `~/.bashrc` to make it permanent. ## System-wide with a PAC file Apps that use the OS proxy settings (browsers, many desktop apps) can use the PAC file Shadow serves at `/proxy.pac`. It sends only the intercepted hosts through Shadow and everything else direct. In a browser that means AI web apps such as chatgpt.com, claude.ai and gemini.google.com go direct and are not masked: ```sh # macOS networksetup -setautoproxyurl "Wi-Fi" http://127.0.0.1:8877/proxy.pac ``` On Windows, set it under *Settings → Network → Proxy → Use setup script*. On GNOME, under *Settings → Network → Network Proxy → Automatic*. ## Which hosts are intercepted Run `shadow hosts` to list the patterns in effect. The built-in list covers OpenAI, Anthropic, OpenRouter, Google Gemini and Vertex AI, Azure OpenAI and Azure AI services, Mistral, Groq, Together, DeepSeek, xAI, AWS Bedrock runtime, Cohere, Perplexity, Fireworks, Cerebras and Voyage AI. The full list is in [Supported providers](https://getshadow.dev/reference/providers/). Add hosts, such as a self-hosted LLM gateway, with `extra_hosts`. Exclude hosts with `bypass_hosts`. Both accept `*` globs: ```toml [proxy] extra_hosts = ["llm.internal.example.com", "*.inference.example.net"] bypass_hosts = ["api.cohere.com"] ``` A bypassed host is tunnelled like any non-AI host: never decrypted, never masked. ## Plain HTTP Absolute-form HTTP requests (`http://host/...` sent to the proxy) are forwarded too. They are masked only if the host is on the intercept list. ## Egress through a relay Translation hides content, not your IP address. To make AI traffic leave from somewhere else, for example a relay in the persona's region, set an upstream proxy. It applies to intercepted traffic only: ```toml [proxy] upstream_proxy = "socks5h://relay.example.net:1080" # http://, socks5:// or socks5h:// ``` ## Bypassing Shadow If Shadow is stopped, tools configured with `HTTPS_PROXY` fail to connect. To bypass it temporarily, unset the variable for one command: ```sh env -u HTTPS_PROXY your-tool ``` > **Certificate pinning** Apps that pin their TLS certificates reject any CA they don't expect, including Shadow's. They fail in proxy mode. Use gateway mode for those tools where possible, or add their host to `bypass_hosts`. --- # Gateway mode Source: https://getshadow.dev/integrate/gateway-mode/ > Mask PII in OpenAI, Anthropic, OpenRouter and Gemini calls by pointing your SDK's base URL at Shadow. Examples in Python, Node.js and curl. No CA needed. In gateway mode, Shadow acts as an explicit reverse proxy. You change the base URL of your SDK or tool, and nothing else: ```text http://127.0.0.1:8877/gw// → / ``` For example, `http://127.0.0.1:8877/gw/openai/v1/chat/completions` is forwarded to `https://api.openai.com/v1/chat/completions`. Query strings are kept. Your API key travels in the usual header and is passed through to the provider. No certificate is involved: the hop from your app to Shadow is plain HTTP on loopback, and Shadow connects to the provider over TLS. ## Built-in routes | Route | Upstream base | Base URL for OpenAI-style SDKs | |---|---|---| | `openai` | `https://api.openai.com` | `http://127.0.0.1:8877/gw/openai/v1` | | `anthropic` | `https://api.anthropic.com` | `http://127.0.0.1:8877/gw/anthropic` (Anthropic SDK) | | `openrouter` | `https://openrouter.ai/api` | `http://127.0.0.1:8877/gw/openrouter/v1` | | `gemini`, `google` | `https://generativelanguage.googleapis.com` | `http://127.0.0.1:8877/gw/gemini` | | `mistral` | `https://api.mistral.ai` | `http://127.0.0.1:8877/gw/mistral/v1` | | `groq` | `https://api.groq.com/openai` | `http://127.0.0.1:8877/gw/groq/v1` | | `together` | `https://api.together.xyz` | `http://127.0.0.1:8877/gw/together/v1` | | `deepseek` | `https://api.deepseek.com` | `http://127.0.0.1:8877/gw/deepseek/v1` | | `xai` | `https://api.x.ai` | `http://127.0.0.1:8877/gw/xai/v1` | | `cohere` | `https://api.cohere.com` | `http://127.0.0.1:8877/gw/cohere` | | `perplexity` | `https://api.perplexity.ai` | `http://127.0.0.1:8877/gw/perplexity` | | `fireworks` | `https://api.fireworks.ai/inference` | `http://127.0.0.1:8877/gw/fireworks/v1` | | `cerebras` | `https://api.cerebras.ai` | `http://127.0.0.1:8877/gw/cerebras/v1` | `shadow hosts` prints the routes in effect, including your own. ## Custom routes Add routes for Azure OpenAI, AWS Bedrock (API-key auth), a self-hosted model server or an internal LLM gateway under `[proxy.routes]`. A custom route with the same name as a built-in overrides it. ```toml [proxy.routes] azure = "https://my-resource.openai.azure.com" bedrock = "https://bedrock-runtime.eu-central-1.amazonaws.com" llm = "https://llm.internal.example.com" ``` `http://127.0.0.1:8877/gw/azure/openai/deployments/gpt-4o/chat/completions?api-version=2024-10-21` then goes to your Azure resource. ## Examples ### OpenAI **Python** ```python from openai import OpenAI client = OpenAI(base_url="http://127.0.0.1:8877/gw/openai/v1") # api_key from OPENAI_API_KEY reply = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "Summarise this ticket from jane@acme.com: card declined twice"}], ) print(reply.choices[0].message.content) # mentions jane@acme.com, the provider never saw it ``` **Node.js** ```ts import OpenAI from "openai"; const client = new OpenAI({ baseURL: "http://127.0.0.1:8877/gw/openai/v1" }); const stream = await client.chat.completions.create({ model: "gpt-4o-mini", messages: [{ role: "user", content: "Summarise the ticket from Jane Doe at Acme GmbH" }], stream: true, }); for await (const chunk of stream) process.stdout.write(chunk.choices[0]?.delta?.content ?? ""); ``` **curl** ```sh curl http://127.0.0.1:8877/gw/openai/v1/chat/completions \ -H "Authorization: Bearer $OPENAI_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"Hello from jane@acme.com"}]}' ``` **Environment** ```sh # Read by the official OpenAI SDKs and many tools built on them export OPENAI_BASE_URL=http://127.0.0.1:8877/gw/openai/v1 ``` ### Anthropic The Anthropic SDKs append `/v1/messages` themselves, so the base URL stops at the route. **Python** ```python import anthropic client = anthropic.Anthropic(base_url="http://127.0.0.1:8877/gw/anthropic") msg = client.messages.create( model="claude-sonnet-4-5", max_tokens=512, messages=[{"role": "user", "content": "Write a short note to Jane Doe about Project Falcon"}], ) print(msg.content[0].text) ``` **Node.js** ```ts import Anthropic from "@anthropic-ai/sdk"; const client = new Anthropic({ baseURL: "http://127.0.0.1:8877/gw/anthropic" }); const msg = await client.messages.create({ model: "claude-sonnet-4-5", max_tokens: 512, messages: [{ role: "user", content: "Write a short note to Jane Doe about Project Falcon" }], }); ``` **curl** ```sh curl http://127.0.0.1:8877/gw/anthropic/v1/messages \ -H "x-api-key: $ANTHROPIC_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "Content-Type: application/json" \ -d '{"model":"claude-sonnet-4-5","max_tokens":256,"messages":[{"role":"user","content":"Hi, I am Jane Doe"}]}' ``` **Environment** ```sh export ANTHROPIC_BASE_URL=http://127.0.0.1:8877/gw/anthropic ``` ### OpenRouter OpenRouter speaks the OpenAI protocol, so use the OpenAI SDK with the `openrouter` route: **Python** ```python from openai import OpenAI import os client = OpenAI( base_url="http://127.0.0.1:8877/gw/openrouter/v1", api_key=os.environ["OPENROUTER_API_KEY"], ) ``` **Node.js** ```ts const client = new OpenAI({ baseURL: "http://127.0.0.1:8877/gw/openrouter/v1", apiKey: process.env.OPENROUTER_API_KEY, }); ``` **curl** ```sh curl http://127.0.0.1:8877/gw/openrouter/v1/chat/completions \ -H "Authorization: Bearer $OPENROUTER_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model":"openai/gpt-4o-mini","messages":[{"role":"user","content":"Hi from jane@acme.com"}]}' ``` ### Google Gemini ```sh curl "http://127.0.0.1:8877/gw/gemini/v1beta/models/gemini-2.5-flash:generateContent" \ -H "x-goog-api-key: $GEMINI_API_KEY" \ -H "Content-Type: application/json" \ -d '{"contents":[{"parts":[{"text":"Write to jane@acme.com about the Acme GmbH renewal"}]}]}' ``` With the Google Gen AI SDK, set the base URL through its HTTP options (`http_options={"base_url": "http://127.0.0.1:8877/gw/gemini"}` in Python). ## Gateway endpoints Besides `/gw/...`, the same port serves: | Path | Returns | |---|---| | `/` or `/shadow/status` | JSON status: version, persona region/country/company/hostname, vault stats, routes, CA fingerprint | | `/healthz` | `ok` | | `/proxy.pac` | PAC file for [proxy mode](https://getshadow.dev/integrate/proxy-mode/#system-wide-with-a-pac-file) | | `/shadow/ca.pem` | The local CA certificate | Errors from Shadow itself, such as an unknown route, are returned as OpenAI-style JSON (`{"error": {"message": ..., "type": "shadow_error"}}`), so SDKs surface them cleanly. > **Keep the gateway on loopback** Anyone who can reach the gateway can use it to talk to providers with their own keys through your persona, and the responses are unmasked with your vault. Shadow warns when you listen on a non-loopback address. In containers, publish the port only to `127.0.0.1` or keep it inside the pod. --- # Embed in Rust Source: https://getshadow.dev/integrate/rust/ > Embed the shadow-core Rust crate to mask and restore LLM calls in-process: sessions, intent policy, JSON and streaming helpers, no proxy hop. `shadow-core` is the engine behind the `shadow` binary: detection, persona, vault, policy and translation. Embed it when your own Rust service calls models and you want masking in-process, with no proxy hop. Meridian, a compliance platform, uses it this way so that every model call it makes is masked before it leaves. ## Add the dependency ```toml [dependencies] shadow-core = { git = "https://github.com/Tiny-Bubble-Company/shadow" } serde_json = "1" ``` ## Open an instance ```rust use shadow_core::{Shadow, ShadowConfig}; let config = ShadowConfig::from_dir("/var/lib/myapp/shadow") .with_organization(vec!["Acme GmbH".into()]) .with_terms(vec!["Project Falcon".into(), "person:Jane Doe".into()]) .with_region("JP"); let shadow = Shadow::open(config)?; ``` `Shadow::open` creates or loads the persona, the vault and its key in the data directory. Use `ShadowConfig::load(path)` to read a `shadow.toml` instead. `Shadow` is cheap to clone and safe to share across threads and tasks. Keep the data directory on persistent storage. It holds the mapping that restores responses, and the persona that keeps fakes stable over time. ## Mask and restore text ```rust let masked = shadow.mask("Ping jane@acme.com about Acme GmbH"); // masked.text -> "Ping hui.zhao@zhilan.cn about Zhilan Technology Co., Ltd." // masked.replacements -> 2 // masked.kinds -> ["email", "org"] let answer = call_llm(&masked.text)?; let restored = shadow.unmask(&answer); ``` `shadow.mask` applies the default treatment (realistic surrogates for everything). It is the simplest API and fine for single-purpose prompts. For requests that carry tools or bulk data, use a [session](#sessions-and-intent). ## Mask a JSON request body ```rust let mut body = serde_json::json!({ "model": "gpt-4o", "messages": [{"role": "user", "content": "Write to jane@acme.com"}] }); let n = shadow.mask_json(&mut body); // masks string leaves in place shadow.scrub_identifiers(&mut body); // OpenAI `user`/`safety_identifier`, Anthropic `metadata.user_id` let mut response: serde_json::Value = send(&body)?; shadow.unmask_json(&mut response); ``` `mask_json` skips protocol keys whose values must not change: `model`, `role`, `type`, `object`, `id`, `tool_call_id`, `tool_use_id`, `finish_reason`, `stop_reason`, `encoding_format`, `media_type`, `mime_type`, `detail`, `service_tier`, `reasoning_effort`, `voice`, `format`, `tool_choice` and `stop_sequence`. ## Restore a stream Models stream text in small pieces, so a fake like `Wei Zhang` can arrive as `"Wei"` and `" Zhang"`. A `StreamUnmasker` holds back just enough text to restore values split across chunks: ```rust let mut s = shadow.stream_unmasker(); let mut out = String::new(); for chunk in stream { out.push_str(&s.push(&chunk)); // text that is safe to emit now } out.push_str(&s.finish()); // flush the held-back tail ``` The concatenated output is identical to unmasking the whole text at once. It holds back at most the length of the longest fake minus one byte. ## Sessions and intent A session applies the [intent-aware policy](https://getshadow.dev/concepts/intent-policy/) to one request: it classifies what the request is for, chooses a treatment per value (surrogate, token, keep or redact), and remembers the request's tokens so the response can be restored. ```rust // From a provider request body: reads the instruction, the tools and their kind. let session = shadow.session_for(&request_json); // Or from parts you already have. use shadow_core::RequestContext; let session = shadow.session(RequestContext { instruction: "Build a churn report from this customer list".into(), tools: vec![], // the tools offered to the model, if any }); let masked = session.mask(&prompt); // Masked { text, replacements, kinds } let n = session.mask_json(&mut body); // usize let text = session.unmask(&answer); session.unmask_json(&mut response); let mut s = session.stream_unmasker(); let c = session.classification(); // intent, remote_actions, reasons let sum = session.summary(); // counts: surrogates, tokens, kept, redacted ``` Typed tokens look like `[PERSON_0001]` and `[EMAIL_0002]`. They are reversible within the session and restored by `session.unmask`. Use one session per model call (or per agent turn). Tool-call arguments that come back from the model are restored with `session.unmask_json`, so a local tool receives real values. See [Tool calls and agents](https://getshadow.dev/concepts/tool-calls/). ## Inspect and manage ```rust let findings = shadow.scan(text); // Vec: start, end, kind, score, detector let persona = shadow.persona(); // the fake identity let stats = shadow.stats(); // entries by kind shadow.add_terms(&["Nightjar".into()]); // protect more terms at runtime shadow.forget("jane@acme.com")?; // drop a mapping (real or fake) ``` ## Custom detectors Implement `shadow_core::Detector` and pass it to `Shadow::open_with_detectors`: ```rust use shadow_core::{Detector, Finding, Kind}; struct TicketIds; impl Detector for TicketIds { fn name(&self) -> &'static str { "ticket" } fn detect(&self, text: &str) -> Vec { // Return byte ranges with a kind; Shadow resolves overlaps with other detectors. find_ticket_ids(text) .map(|(start, end)| Finding::new(start, end, Kind::Term, "ticket")) .collect() } } let shadow = Shadow::open_with_detectors(config, vec![Box::new(TicketIds)])?; ``` ## Concurrency and multiple processes - One `Shadow` can be shared across threads; masking takes a short write lock only when a new value is added to the vault. - Several processes can share a data directory. Each save merges the other processes' entries first, so no mapping is lost, and readers refresh when the file changes. ## Using the proxy as a library `shadow-proxy` exposes the proxy and gateway (`Proxy::new(shadow)`, `Proxy::run(addr)`) if you want to host them inside your own binary, for example with a custom listener or tests that redirect a host with `with_upstream_override`. --- # Docker and Kubernetes Source: https://getshadow.dev/integrate/docker-kubernetes/ > Run Shadow as a sidecar container in Docker or Kubernetes to mask PII in your service's LLM calls, with persistent state and controlled egress. In containers, Shadow works best as a **sidecar**: one Shadow instance next to each application, reachable on `localhost`, using [gateway mode](https://getshadow.dev/integrate/gateway-mode/). No CA has to be installed in the application image. ## Build the image ```sh docker build -f packaging/Dockerfile -t shadow:local . ``` The image is based on `debian:bookworm-slim`, runs as the unprivileged user `shadow` (UID 10001), stores its state in `/data` (`SHADOW_HOME=/data`), exposes port 8877, and has a `HEALTHCHECK` that runs `shadow status --quiet`. The default command is `shadow run --listen 0.0.0.0:8877`. Configuration goes in `/data/shadow.toml`. Mount it from a config map or a file, or let Shadow use its defaults. ## Docker Compose ```yaml services: app: image: my-app:latest environment: OPENAI_BASE_URL: http://shadow:8877/gw/openai/v1 ANTHROPIC_BASE_URL: http://shadow:8877/gw/anthropic depends_on: [shadow] shadow: image: shadow:local volumes: - shadow-data:/data - ./shadow.toml:/data/shadow.toml:ro # No published ports: only containers on this network can reach it. volumes: shadow-data: ``` ## Kubernetes sidecar Containers in one pod share a network namespace, so the app reaches Shadow on `127.0.0.1` and Shadow can listen on loopback only: ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: my-app spec: replicas: 1 selector: { matchLabels: { app: my-app } } template: metadata: { labels: { app: my-app } } spec: containers: - name: app image: my-app:latest env: - name: OPENAI_BASE_URL value: http://127.0.0.1:8877/gw/openai/v1 - name: shadow image: registry.example.com/shadow:0.1.0 args: ["run", "--listen", "127.0.0.1:8877"] volumeMounts: - { name: shadow-data, mountPath: /data } - { name: shadow-config, mountPath: /data/shadow.toml, subPath: shadow.toml } readinessProbe: exec: { command: ["shadow", "status", "--quiet"] } securityContext: runAsNonRoot: true allowPrivilegeEscalation: false readOnlyRootFilesystem: true volumes: - name: shadow-data persistentVolumeClaim: { claimName: my-app-shadow } - name: shadow-config configMap: { name: my-app-shadow } ``` ### State and replicas The data directory holds the persona, the vault and its key. It matters in two ways: - **Restoring responses** needs the mapping that masked the request. Within one request this is always in memory, so a lost volume never breaks an in-flight call. - **Stable fakes over time** (the same customer always appears under the same fake name, conversation history stays consistent) need the vault to persist. For one replica, a `PersistentVolumeClaim` is enough. For several replicas that should present one identity, either give them a shared `ReadWriteMany` volume (Shadow merges concurrent writers on save), or provision the same `persona.json`, `vault.key` and an initial `vault.bin` from a secret into each pod. Treat `vault.key` as a secret. ## Proxy mode in containers If the application cannot set a base URL, use proxy mode inside the pod: set `HTTPS_PROXY=http://127.0.0.1:8877` and make the application trust Shadow's CA. Fetch it from the running sidecar at `http://127.0.0.1:8877/shadow/ca.pem`, or mount `ca/ca.pem` from the shared volume. Provision the CA ahead of time (run `shadow init` once and store `ca/` as a secret) so that it is stable across restarts. ## Egress control The sidecar is the only component that needs to reach AI providers. A `NetworkPolicy` applies to the whole pod, so it cannot stop the application container from bypassing a sidecar in the same pod. To enforce that, route egress through a proxy or firewall that only accepts provider traffic from Shadow (point Shadow at it with `upstream_proxy`), or run Shadow as a separate gateway deployment and allow provider egress only from its pods. Set `[proxy] upstream_proxy` too if outbound traffic must go through a corporate proxy or a relay. ## Without containers On a VM, the same pattern is a systemd unit (see [Installation](https://getshadow.dev/getting-started/installation/#run-as-a-background-service)) and gateway URLs in the application's environment. --- # Integrating any endpoint Source: https://getshadow.dev/integrate/any-endpoint/ > Put Shadow in front of anything that calls an LLM: Claude Code and coding assistants, IDEs, agents, desktop apps, custom APIs and self-hosted gateways. Shadow is not tied to a list of applications. If something sends HTTP requests to a model API, Shadow can sit in front of it. This page shows how to decide where to plug it in. ## Decision guide 1. **Can you set a base URL?** Use [gateway mode](https://getshadow.dev/integrate/gateway-mode/). Most SDKs, CLIs and frameworks (LangChain, LlamaIndex, Vercel AI SDK, LiteLLM, the OpenAI and Anthropic SDKs) accept one, often from `OPENAI_BASE_URL` or `ANTHROPIC_BASE_URL`. 2. **Does it honour `HTTPS_PROXY` or the system proxy?** Use [proxy mode](https://getshadow.dev/integrate/proxy-mode/). This covers most IDE extensions, Electron apps, Python and Node scripts, and CI jobs, as long as they call an [intercepted AI host](https://getshadow.dev/reference/providers/). Tools that talk to their vendor's own backend instead (GitHub Copilot, Cursor) are not masked unless you add that host with `extra_hosts`, and then only if Shadow can parse their request format. 3. **Is it your own Rust code?** [Embed `shadow-core`](https://getshadow.dev/integrate/rust/). 4. **Is the endpoint not a built-in provider?** Add a route or an intercepted host for it (below). ## A custom or self-hosted model API Any HTTPS endpoint that accepts JSON prompts can be protected: an internal LLM gateway, a vLLM or TGI server on another machine, a vendor API that wraps a model. ```toml [proxy] # proxy mode: decrypt and mask traffic to these hosts too extra_hosts = ["llm.internal.example.com"] [proxy.routes] # gateway mode: http://127.0.0.1:8877/gw/internal/... -> https://llm.internal.example.com/... internal = "https://llm.internal.example.com" ``` Shadow masks every string value in a JSON request body, except protocol keys such as `model`, `role` and `type`, so it works with request shapes it has never seen. Responses are restored in full JSON bodies and in SSE or NDJSON streams. Streams of unknown formats are still restored string by string; only the cross-event reassembly of split fakes is format-specific (OpenAI Chat Completions and Responses, Anthropic, Gemini, Cohere and Ollama-style NDJSON). Non-JSON request bodies are forwarded as they are, except for [file uploads](https://getshadow.dev/concepts/file-masking/), which are masked. ## Agents and tool calls Agents are where privacy and usefulness collide: the model has to *act* on real values. Shadow handles this with the [intent-aware policy](https://getshadow.dev/concepts/intent-policy/): - The person the instruction asks the agent to contact or act on is the **recipient** and passes through as written, logged, so the model addresses the right person (`[policy.act] targets`, default `keep`; `surrogate` fakes it). - When the agent's tools run **on your side** (your code executes the tool call), the model sees fakes for everything else and writes tool calls with them. Shadow restores the real values in the tool-call arguments before they reach your code. - When tools run **in the provider's cloud** (hosted MCP servers, server-side tools), Shadow cannot restore values there. By default it keeps real what the action needs (the contact details, people, companies, addresses, usernames and IBANs named in the instruction, and for a follow-up that names no email or phone, the emails, phones and people anywhere in the request), keeps secrets, cards and IDs masked, and logs each keep. You can change this. Details and examples in [Tool calls and agents](https://getshadow.dev/concepts/tool-calls/). ## IDEs and coding assistants Coding assistants send file contents, paths, hostnames and pasted configuration, which is exactly what Shadow is good at protecting. Usernames in paths (`/Users/jdoe/...`), internal hostnames, IPs and API keys are masked, and the `code` intent keeps identifiers readable. - **VS Code and JetBrains extensions.** Most use the IDE's proxy setting or `HTTPS_PROXY`. Set the IDE's HTTP proxy to `http://127.0.0.1:8877` and make sure the CA is trusted by the OS (VS Code uses the OS store; JetBrains IDEs can be pointed at it in *Settings → Tools → Server Certificates*). - **Terminal assistants and CLIs.** Start them from a shell where `env.sh` is sourced, or set their base URL variable. - **Desktop chat apps.** Use the [PAC file](https://getshadow.dev/integrate/proxy-mode/#system-wide-with-a-pac-file) or system proxy for apps that call a provider API with your key. The ChatGPT and Claude desktop apps talk to their consumer backends (chatgpt.com, claude.ai), which are not intercepted. Apps that pin certificates will refuse the local CA; there is no workaround for those besides using a different client. ## Backend services in any language Point the service's model client at the gateway URL through configuration, and run Shadow next to it as a service or [sidecar](https://getshadow.dev/integrate/docker-kubernetes/). No code change is required beyond the base URL. ```sh # Python, Node, Go, Java: any OpenAI-compatible client OPENAI_BASE_URL=http://127.0.0.1:8877/gw/openai/v1 ./my-service ``` ## Checklist - Run `shadow scan` on a few representative prompts and documents to see what is detected before you rely on it. - Add your organisation names, codenames and customers with `organization`, `terms` or `shadow terms add`. - Decide how actions in provider-hosted tools are handled (`[policy.act] remote_tool_targets`). - Keep Shadow on loopback, or inside the pod. - Check the log line per request (`masked N value(s)`) during rollout. --- # How Shadow works: detection, vault, personas and policy Source: https://getshadow.dev/concepts/ > Concepts behind Shadow's PII masking for LLMs: how sensitive values are detected, stored in the encrypted vault, replaced with persona-consistent fakes, and restored in streams, tool calls and files. These pages explain what Shadow does to each request and why: how it finds sensitive values, how fakes stay consistent, and how they are restored in responses. --- # How it works Source: https://getshadow.dev/concepts/how-it-works/ > How Shadow masks AI traffic end to end: interception, detection, intent policy, reversible translation and restoration of streamed responses. ![Shadow architecture: apps send requests through Shadow on the endpoint, which detects, applies policy and translates before the provider; responses are restored on the way back; other hosts are tunnelled untouched.](../../../assets/architecture.svg) Shadow has three parts: - **`shadow-core`** (library): detection, persona, vault, intent policy and translation. Everything that touches real values. - **`shadow-proxy`** (library): the HTTP(S) forward proxy, the `/gw` gateway, the local CA, file handling and the streaming translator. - **`shadow-cli`** (the `shadow` binary): setup, service installation, inspection tools. ## Life of a request ### 1. Intercept Shadow listens on `127.0.0.1:8877`. - **Gateway**: the app sends `POST /gw/openai/v1/chat/completions` in plain HTTP on loopback. Shadow maps the route to `https://api.openai.com/v1/chat/completions`. - **Proxy**: the app sends `CONNECT api.openai.com:443`. Because the host is on the intercept list, Shadow completes the TLS handshake itself with a leaf certificate minted by the local CA, then reads the HTTP request inside. For any host not on the list, Shadow opens a raw TCP tunnel instead and never sees the plaintext. ### 2. Prepare the body Shadow decodes `gzip` or `deflate` request bodies, drops hop-by-hop headers and `accept-encoding` (so the response arrives uncompressed and can be translated), and looks at the content type: - **JSON** is parsed and handled field by field. - **`multipart/form-data`** uploads, and base64 files embedded in JSON, go through [file masking](https://getshadow.dev/concepts/file-masking/). - **Requests signed with AWS SigV4** are forwarded unmodified with a warning, because changing the body would break the signature. ### 3. Classify intent With `[policy] intent = true` (the default), Shadow classifies the request as `act`, `report`, `code` or `converse` from the instruction, the tools offered and the shape of the data. The class decides the treatment of each detected value: a surrogate, a typed token, the real value, or a redaction. See [Intent-aware policy](https://getshadow.dev/concepts/intent-policy/). ### 4. Detect Every string leaf of the JSON body is scanned, apart from protocol keys (`model`, `role`, `type`, `id`, `tool_call_id`, and similar). Each string goes through every enabled detector: - built-in regex and checksum detectors (emails, phones, cards, IBANs, national IDs, IPs, MACs, hostnames, secrets, paths, names, addresses); - the dictionary of your organisation names and terms; - local machine facts (OS user, hostname, git identity); - optional: the local NER sidecar and [Presidio](https://getshadow.dev/concepts/detection/#presidio). Overlapping findings are resolved: the longest span wins, then the higher-priority kind (secrets first, then organisation terms, emails, financial IDs, names). Values on `allow_terms`, and values that are already fakes, are skipped. ### 5. Translate Each finding gets the treatment chosen by the policy. The default is a **surrogate**: a format-preserving fake from the [persona](https://getshadow.dev/concepts/personas/), stored in the [vault](https://getshadow.dev/concepts/vault/). The same real value always gets the same fake, and a fake is never reused for a different real value, so reversal is exact. ### 6. Scrub identifiers - JSON fields `user`, `safety_identifier` (OpenAI) and `metadata.user_id` (Anthropic) are replaced with the persona's user id. - Headers in `strip_headers` are removed: by default `OpenAI-Organization`, `X-Forwarded-For`, `X-Real-IP`, `Forwarded`, `Via`, `X-Client-IP` and the `x-stainless-*` OS and runtime headers. - Headers in `persona_headers` are rewritten when present, for example `X-Request-Origin` becomes the persona hostname. ### 7. Forward The masked request goes to the provider over TLS (rustls), directly or through `upstream_proxy`. Shadow logs one line per request with the method, URL and number of values masked. It never logs values. ### 8. Restore - **JSON responses**: every string leaf is unmasked, including tool-call arguments. - **Streams** (`text/event-stream` and NDJSON): see below. - **Other content types** pass through unchanged. ## Streaming hold-back LLM APIs stream text as many small JSON events. A fake such as `Wei Zhang` can arrive as `"Wei"` in one event and `" Zhang"` in the next, so byte-level replacement would miss it. Shadow parses each event and routes the *delta text fields* of known formats (OpenAI Chat Completions and Responses, Anthropic Messages, Gemini, Cohere, Ollama-style NDJSON) through a per-channel `StreamUnmasker`. A channel is one content stream, such as one choice's text or one tool call's arguments. The unmasker holds back at most *longest fake − 1* bytes, the minimum needed to recognise a fake that is still arriving, and emits everything before that immediately. When the channel ends, the held text is flushed into the closing event (or a synthesized copy of the channel's last event). The effect is that tokens keep streaming at the same pace, delayed by at most a few characters, and the concatenated output is exactly what unmasking the whole text would produce. Other string fields in events are unmasked directly. ## What is not processed - Non-AI hosts in proxy mode: tunnelled, never decrypted. - Images (passed through by default; no OCR), audio, WebSocket and Realtime APIs, and gRPC. - SigV4-signed requests (AWS Bedrock with IAM signing). Bedrock with bearer API keys is masked normally. See the [security model](https://getshadow.dev/reference/security/) for the full list of limitations. --- # Personas Source: https://getshadow.dev/concepts/personas/ > Personas give Shadow one coherent fake identity per machine (company, people, domain, hostnames) so masked prompts stay realistic and consistent. A persona is a coherent fake identity for one machine: a company, its domain, a city, a primary person, colleagues, a username, a user id and a hostname. Shadow creates it once, on `shadow init` or the first `Shadow::open`, stores it in `persona.json`, and draws every fake from it. ```text $ shadow identity ◆ Persona — what AI providers see company Xinghe Technology Co., Ltd. (xinghe.cn) person Ying Chen location Chengdu, China · Asia/Shanghai · zh-CN user id user-941ea3e72322 username yingchen hostname xinghe-srv-11 colleagues Tao Zhu, Hui Yang, Lei Li, Yong Xu ``` `shadow identity --json` prints the full record, including the region code, locale, dial code and creation time. ## Why a persona instead of random fakes Random, unrelated fakes make prompts incoherent: an email at one fake company, a signature from another, a phone number from a third country. Models notice, and answers get worse. A persona keeps fakes **consistent with each other**: - your organisation maps to the persona's company, and emails at your domain map to addresses at the persona's domain; - your own git identity and OS user map to the persona's primary person and username; - phone numbers use the persona region's dial code; names come from the region's naming conventions; - your machine's hostname maps to the persona hostname. And because the persona is persistent, the same customer shows up under the same fake name tomorrow, which keeps conversation history and long-running agents consistent. ## Regions By default the persona's region is chosen **far from the real one**, based on the machine's timezone. A machine in Europe gets a persona in, for example, China, Japan or Brazil. The idea is that the fake identity should not be a near miss of the real one. Available regions: | Code | Country | Code | Country | |---|---|---|---| | `CN` | China | `IN` | India | | `JP` | Japan | `AU` | Australia | | `KR` | South Korea | `MX` | Mexico | | `BR` | Brazil | `ZA` | South Africa | | `US` | United States | `CA` | Canada | | `DE` | Germany | | | Pin a region in configuration or at init: ```toml [persona] region = "JP" # or "auto" (default) ``` ```sh shadow init --region JP ``` The region matters for answers. A persona in Shenzhen gets advice about Chinese public holidays and phone formats. If your prompts depend on locale (tax rules, address formats, legal context), pick a region that suits your work. ## Overrides You can choose the company name and domain yourself: ```toml [persona] company = "Northwind Trading Ltd." domain = "northwind.example" ``` Everything else is still generated. ## How fakes are generated | Kind | Fake | |---|---| | Email | A name from the persona region at the faked domain: your organisation's domain becomes the persona domain, webmail domains such as `gmail.com` are kept. Separators, digits and case follow the original | | Person | A name from the persona region's first and last names | | Organisation | The persona company for your primary organisation (and its short name for the bare name); other organisations get a generated name that keeps the original legal form (`Globex Ltd` becomes, for example, `Zephyr Ltd`) | | Term (codename, project) | A generated codeword such as `Velora Quilent` | | Phone | Same digit layout with the persona's dial code | | Credit card | Same length and grouping, valid Luhn check digit | | IBAN | Same country and length, valid mod-97 check digits | | National ID | Same format | | IP / MAC | Same family and format | | Hostname | A generated domain with the persona region's TLD; internal TLDs (`.internal`, `.corp`, `.lan`, `.local`) and generic labels such as `www` or `api` are kept | | Username (in paths) | A username in the persona region's style; your own OS user maps to the persona username | | Secret | Same prefix and shape (`sk-proj-…` stays `sk-proj-…`) with a random body; PEM blocks keep their header and footer. Opt in to [redaction](https://getshadow.dev/concepts/intent-policy/#treatments) instead | | Address | A house number and a street from the persona region | Every fake is checked against the vault so that no two real values share one. ## Changing or resetting the persona `shadow init --force` deletes `persona.json` and `vault.bin` and starts over with a new identity. The old mapping is gone, so responses to old prompts that mention old fakes are no longer restored. To move the same identity to another machine, copy `persona.json`, `vault.bin` and `vault.key` (see [The vault](https://getshadow.dev/concepts/vault/)). --- # The vault Source: https://getshadow.dev/concepts/vault/ > How Shadow's local vault stores the real-to-fake mapping: ChaCha20-Poly1305 encryption at rest, multi-process sharing, export and forgetting values. The vault is the table of real↔fake pairs. It is what makes translation reversible, and it never leaves your machine. ## Format and encryption | File | Contents | |---|---| | `vault.bin` | `SHV1` magic, a 12-byte nonce, then the JSON list of entries encrypted with **ChaCha20-Poly1305** | | `vault.key` | The 32-byte key, hex encoded, created with mode `0600` | Each entry records the kind (`email`, `person`, `secret`, ...), the real value, the fake, and when it was created. A fresh nonce is used for every save. A wrong key or a modified file fails authentication and is reported as an error rather than silently producing garbage. Typed tokens used by the [intent policy](https://getshadow.dev/concepts/intent-policy/) (`[PERSON_0001]`) are numbered per session and restored from that session. Redacted values are irreversible by design: the provider receives `[REDACTED_SECRET]` and nothing maps it back. ## Writes and concurrency - **Atomic.** Every save writes a temporary file and renames it over `vault.bin`, so a crash never leaves a half-written vault. - **Thread-safe.** One `Shadow` instance is shared by all connections. Lookups take a read lock; adding a new value takes a short write lock. - **Multi-process.** Several processes can use one data directory, for example the background proxy and a `shadow mask` in your terminal. Before each save, the on-disk vault is merged in (a union of pairs), so no process loses another's entries. Running processes pick up changes from disk when the file changes. ## Inspecting the vault ```sh shadow vault stats ``` ```text ◆ Vault — 13 entries (created 2026-10-31T12:31:29Z) KIND ENTRIES credit_card 1 email 2 hostname 2 org 2 person 2 secret 1 term 1 username 2 ``` To see the actual pairs: ```sh shadow vault export --i-understand ``` This prints real values in clear text, which is why it needs the flag. Don't paste the output into an AI tool. ## Forgetting a value ```sh shadow vault forget "jane@acme.com" # by real value shadow vault forget "hui.zhao@zhilan.cn" # or by fake ``` After forgetting, the next time the value appears it gets a new fake. Responses that still mention the old fake will no longer be restored. ## Backups and moving machines The vault, its key and the persona belong together. To move an identity to another machine, or to give several machines one identity, copy: ```text persona.json vault.bin vault.key ``` Without `vault.key`, `vault.bin` cannot be read. Without `vault.bin`, old fakes cannot be restored, but new requests work normally. ## Protecting the vault The vault is only as safe as the account that owns it. Anyone who can read files as your user can read `vault.key` and decrypt the mapping. Use full-disk encryption, keep the data directory private, and treat `vault.key` like any other credential. On servers, run Shadow as a dedicated user and restrict the data directory to it. See the [security model](https://getshadow.dev/reference/security/). --- # Detection Source: https://getshadow.dev/concepts/detection/ > How Shadow detects PII and secrets: checksummed patterns, secrets, dictionary terms, local machine facts, an optional NER model and Microsoft Presidio. Detection decides *what* is sensitive. Shadow layers several sources and merges their findings into one set of non-overlapping spans. Test detection on your own content at any time. Nothing is changed or sent: ```sh shadow scan notes.txt # redacted previews shadow scan notes.txt --reveal # full values shadow scan notes.txt --json # kind, line, column, detector, score, value ``` ## Built-in detectors All built-in detectors are on by default. Turn individual ones off under `[detectors]`. | Toggle | Finds | How | |---|---|---| | `email` | Email addresses | Pattern | | `phone` | International numbers (`+49 30 1234567`) and US formats (`(415) 555-0100`, `415-555-0100`) | Pattern | | `person` | Person names: a known first name followed by a capitalised surname, or an honorific (`Mr`, `Ms`, `Dr`, `Prof`...) plus a name | Pattern + name list | | `address` | Street addresses in English (`221 Baker Street`) and German (`Hauptstraße 5`) styles | Pattern | | `iban` | IBANs | Pattern + **mod-97** check | | `credit_card` | Payment card numbers, 13 to 19 digits with optional separators | Pattern + **Luhn** check | | `national_id` | US SSN and ITIN, UK National Insurance and NHS numbers, Indian Aadhaar and PAN, Australian TFN and ABN | Pattern and checksum | | `ip` | IPv4 and full IPv6 addresses | Pattern | | `mac` | MAC addresses | Pattern | | `hostname` | Hostnames in URLs and bare domains that are not well-known public services; anything under `internal_domains` or an internal TLD (`.internal`, `.corp`, `.lan`, `.local`) | Pattern + allow list | | `secret` | OpenAI, Anthropic, OpenRouter, GitHub, GitLab, Slack, AWS, Google, Groq, xAI, Hugging Face, npm and Stripe live keys; JWTs; PEM private keys; bearer tokens; `password=`, `api_key:`, `client_secret=`-style assignments | Pattern; placeholders like `your-key`, `changeme`, `${VAR}` are ignored | | `path` | Usernames inside home paths: `/Users/`, `/home/`, `C:\Users\` | Pattern | | `local_facts` | This machine's OS user name, hostname, git `user.name` and `user.email`, and local domain | Discovered at startup | Public domains (well-known services such as `github.com` or `google.com`) are on a built-in allow list so that ordinary URLs in prompts are left alone. Add your own public domains with `allow_domains`. ## Dictionary terms Detectors cannot know your company's codenames, customers or colleagues. Tell Shadow: ```toml organization = ["Acme GmbH"] # first one maps to the persona company terms = ["Project Falcon", "Nightjar", "org:Globex", "person:Jane Doe"] internal_domains = ["acme.com", "acme.internal"] ``` or at runtime (stored in `terms.json`): ```sh shadow terms add "Project Falcon" "org:Globex" "person:Jane Doe" "hostname:build01" shadow terms list ``` An optional prefix sets the kind, which decides the shape of the fake: `person:`, `org:`, `term:`, `hostname:`, `email:`, `username:`. Without a prefix, a term that ends in a legal suffix (`GmbH`, `Ltd`, `Inc`...) is treated as an organisation, and anything else as a term. For organisations, the bare name without the suffix (`Acme`) is protected too. Dictionary matching is case-insensitive and respects word boundaries. ## Allow lists ```toml allow_domains = ["docs.mycompany-public.io"] # never masked as hostnames allow_terms = ["Jane Austen"] # never masked, whatever detects them ``` ## Local NER sidecar Regex and dictionaries are precise for structured values but miss free-text names, addresses and organisations they have not seen. The `ml/` directory contains an optional sidecar that adds a named-entity model. It is a standard-library HTTP server on loopback, and there are two ways to run it. ### Open-weight models (recommended) `serve_hf.py` runs open-weight models, all Apache-2.0, downloaded from Hugging Face on first start: | Backend | Model | Finds | | --- | --- | --- | | `privacy-filter` | [OpenAI Privacy Filter](https://huggingface.co/openai/privacy-filter) | people, addresses, private dates, emails, phones, URLs, account numbers, secrets | | `gliner` | [GLiNER multi PII](https://huggingface.co/urchade/gliner_multi_pii-v1) | multilingual names, organisations, addresses, IDs, usernames | | `ensemble` (default) | both, merged | the union of the two | ```sh cd ml python -m venv .venv && . .venv/bin/activate pip install -r requirements-hf.txt python serve_hf.py --backend ensemble --host 127.0.0.1 --port 8878 ``` It uses CUDA or Apple MPS when available. If MPS crashes on your macOS version, pass `--device cpu`. On CPU the ensemble takes roughly a second per prompt, so raise `timeout_ms` to match. Shadow applies its public-name allowlist to whatever the model returns, so "London" or "Microsoft" stay readable. ### Small from-scratch model `serve.py` runs a tiny pure-PyTorch model (a BiLSTM with a character CNN, no downloaded weights) that you train yourself. It recognises `PERSON`, `ORG`, `LOC` and `PROJECT`, is fast on CPU, and suits air-gapped machines: ```sh pip install -r requirements.txt python train.py --out model.pt --epochs 8 --synthetic 20000 python serve.py --model model.pt --host 127.0.0.1 --port 8878 ``` You can fine-tune it on your own JSONL data with `--data`. ### Enable it in Shadow ```toml [model_detector] enabled = true url = "http://127.0.0.1:8878/detect" # must be loopback timeout_ms = 300 # raise to ~2000 for the open-weight models on CPU min_score = 0.6 ``` Both servers refuse to bind a non-loopback address unless you pass `--allow-remote`, and they never log request bodies. Shadow **fails open** on the sidecar: if it is down or slow, the built-in detectors still run. See `ml/README.md` for the HTTP contract, and the [benchmarks](https://getshadow.dev/benchmarks/) for what each option adds. ## Presidio [Microsoft Presidio](https://microsoft.github.io/presidio/) is the leading open-source PII framework. Its Analyzer has more than 50 recognizers, including spaCy and transformer-based NER and many national ID formats. Shadow can call a Presidio Analyzer as an additional detector. Presidio only **detects**. Shadow still does everything else: the persona, the fakes, the vault, the policy and the restoration. You get Presidio's recall with Shadow's reversible, format-preserving translation. Run the analyzer locally: ```sh docker run -d --name presidio -p 127.0.0.1:5002:3000 mcr.microsoft.com/presidio-analyzer ``` or with Compose next to Shadow: ```yaml services: presidio: image: mcr.microsoft.com/presidio-analyzer ports: ["127.0.0.1:5002:3000"] ``` Enable it: ```toml [presidio] enabled = true url = "http://127.0.0.1:5002/analyze" language = "en" min_score = 0.5 entities = [] # empty = all entities Presidio supports; or e.g. ["PERSON", "IBAN_CODE", "US_PASSPORT"] ``` Behaviour: - Presidio's entity types are mapped to the closest Shadow kind (for example `PERSON` → person, `EMAIL_ADDRESS` → email, `PHONE_NUMBER` → phone, `IBAN_CODE` → iban), so the fake keeps the right shape. Findings below `min_score` are dropped. - Its findings are merged with Shadow's own. Where spans overlap, the highest-confidence span wins and the result is a non-overlapping set. - If Presidio is unreachable or returns an error, Shadow **fails open**: it logs a warning and continues with its built-in detectors. - Keep Presidio on loopback or inside the same pod. Shadow sends it the plaintext of each prompt. ## How findings are merged All sources produce findings (byte range, kind, score, detector). Shadow then: 1. drops findings that are on `allow_terms`, whitespace-only, or already a known fake; 2. resolves overlaps into a non-overlapping set, preferring longer spans and higher-priority kinds (secrets first, then organisation terms, emails, financial IDs, names), and confidence where sources disagree; 3. hands the result to the [policy](https://getshadow.dev/concepts/intent-policy/) to choose a treatment per finding. ## Custom detectors In Rust, implement the `Detector` trait and pass it to `Shadow::open_with_detectors`. See [Embed in Rust](https://getshadow.dev/integrate/rust/#custom-detectors). --- # Intent-aware policy Source: https://getshadow.dev/concepts/intent-policy/ > Shadow classifies each AI request as act, report, code or converse and picks a fake, token, real value or redaction for every sensitive value. One treatment does not fit every request. A chat about a customer works best with realistic fakes. A report over a thousand customer rows works best with opaque IDs. An email has to reach the real person, so the model has to know who that person is. Shadow's policy looks at what a request is *for*, then picks a treatment for each detected value, so masking never gets in the way of the goal. The rule behind every decision: **a value is masked unless masking it would change the goal of the request.** If you ask the model to write to or act on a person, faking that person makes it greet the wrong name, research the wrong company or address the wrong inbox. So the recipient of a message or action passes through as written, and every such value is logged. Everything else in the request (other people, health data, IDs, cards, secrets, hostnames) is still masked, and text the model writes comes back through Shadow, which restores those values before you see it. ## Treatments | Treatment | What the provider sees | Reversible | Use | |---|---|---|---| | `surrogate` | A realistic, format-preserving fake from the persona: `hui.zhao@zhilan.cn` | Yes | **Default.** Keeps prompts natural so answers stay good | | `token` | A typed placeholder: `[PERSON_0007]`, `[EMAIL_0002]` | Yes | Bulk data in reports, where the model should treat values as opaque IDs | | `keep` | The real value | n/a | Only for the recipient of a message or action: the person you ask the model to write to or act on (default), and what a provider-hosted action needs. Every keep is logged | | `redact` | `[REDACTED_SECRET]` | **No** | Opt-in, for values that must never leave and never need to come back | Surrogates and tokens are both restored in the response. The difference is how the model treats them: a surrogate reads like real data, so the model writes naturally about it; a token is visibly a placeholder, so the model copies it through without inventing details about it. ## Intents With `[policy] intent = true` (the default), every request is classified into one of four intents. The classifier is local and rule-based. It looks at the latest instruction, the tools offered to the model, and the shape of the data. | Intent | Signals | Example | |---|---|---| | `act` | The model is asked to contact or *do* something to someone: send, email, message, call, invite, book, pay, reply, remind. The verb can open a clause or follow "to", "and", "then", "you" or "please" ("I need you to email…", "write it and send it"). Composing a message counts too: draft, write, compose, prepare or create followed within six words by email, letter, message, reply, note, reminder, invitation and similar | "Email jane@acme.com about the renewal", "Draft a renewal reminder to Jane Doe" | | `report` | Report, summarise, analyse, export or aggregate over data; or many PII values or tabular data in the request | "Build a churn report from this customer list" | | `code` | Source code, diffs, stack traces, configuration | "Why does this config fail to connect?" | | `converse` | Everything else | "What should I say in a renewal negotiation?" | In Rust, `session.classification()` returns the intent, whether any actions run remotely (`remote_actions`), and the reasons for the decision. The proxy logs the intent and a per-treatment summary for each request. ## Decision table | Value | `converse` | `code` | `report` | `act` | |---|---|---|---|---| | Person, email, phone, address | surrogate | surrogate | **token** | surrogate | | Recipient of the message or action (names, emails, phones, companies, addresses, IBANs, usernames in the recipient phrase) | | | | **keep** (default; set `targets = "surrogate"` for a stand-in restored in the answer and tool calls) | | What a provider-hosted action needs: recipient, people, companies, addresses, usernames and IBANs in the instruction (and, for a follow-up like "now send her the invite", emails, phones and people anywhere in the request) | | | | **keep** (configurable) | | Organisation, terms, hostnames, IDs | surrogate | surrogate | surrogate | surrogate | | Secrets | surrogate | surrogate | surrogate | surrogate | | Anything listed under `[policy.always]` | that treatment | that treatment | that treatment | that treatment | Defaults are shown. Every cell is configurable. ## How the recipient is found For an `act` request, Shadow reads the **recipient phrase**: the words after "to", "for" or "at" that follow the verb, or the words right after a verb that takes the person as its object ("Email jane@acme.com", "Call Jo"). The phrase ends at "about", "regarding", "that", "saying", "telling", "asking", "to say/tell/ask", "with the/a/our", "explaining", "to confirm", "to let", "so that", "because", "if", "when" or the end of the sentence. Names, emails, phones, companies, addresses, IBANs and usernames inside that phrase are kept; everything after it is masked as usual. ## Example: an action with a local tool > "Email jane@acme.com about the renewal" Your app offers the model a `send_email` tool that **your code** executes. 1. Shadow classifies the request as `act`. `jane@acme.com` is the recipient. 2. The recipient is **kept** (and logged). The provider sees *"Email jane@acme.com about the renewal"*. 3. The model answers with a tool call: `send_email(to="jane@acme.com", ...)`. 4. Your code sends the email to Jane. Anything else in the request, such as a colleague's name or a card number in the body, is still masked and restored in the tool-call arguments on your machine, also when they are streamed in pieces. If the recipient must stay hidden from the provider and your own code sends the message, set `targets = "surrogate"`. The provider then sees *"Email hui.zhao@zhilan.cn about the renewal"*, the model calls `send_email(to="hui.zhao@zhilan.cn", ...)`, and Shadow restores `jane@acme.com` in the arguments before your code runs. The trade-off: the model writes to a stand-in, so it may greet or research the wrong person. ## Example: an action with a provider-hosted tool Same instruction, but the tool is a hosted MCP server or another server-side tool that the **provider** executes. Shadow cannot restore values inside the provider's cloud: if it sent a fake, the email would go to `hui.zhao@zhilan.cn`. So by default what the action needs is **kept**: the recipient, and the contact details, people, companies, addresses, usernames and IBANs named in the instruction, because they end up in the email Jane receives. For a follow-up that names no email address or phone number ("now send her the invite"), the email, phone and person values anywhere in the request (earlier messages, pasted documents, tool results) are kept too, because that is where the recipient is. Secrets, card numbers, national IDs, hostnames and IPs are still masked, other values in earlier messages and files stay masked, and every keep is logged. If you would rather the action fail than reveal those values, set `remote_tool_targets = "surrogate"`. ## Example: drafting text you will send yourself > "Draft a renewal reminder to Jane Doe (jane@acme.com) at Acme GmbH. Mention that Max Weber approved it and card 4111 1111 1111 1111 will be charged." No tool is involved, but the goal is still to write to Jane, so the request is `act`. Jane Doe, her email and Acme GmbH are the recipient phrase and pass through as written, so the model greets the right person and writes about the right company. Max Weber and the card number are not the recipient: the provider sees a fake name and a fake card, and Shadow restores the real ones in the draft you read and send. This means the recipient's details reach the provider whenever you ask the model to write to them. To hide them, set `targets = "surrogate"`: the provider then writes to a stand-in and Shadow restores the real name and address in the draft (fake first and last names are restored on their own, so "Dear Hui" becomes "Dear Jane"). Requests that are *about* people but do not contact them ("summarise this thread", "analyse these complaints") are not `act`, so everyone in them stays masked. ## Example: a report > "Build a churn report from this customer list" (followed by 400 rows of names, emails and phones) 1. Shadow classifies the request as `report`. 2. People, emails, phones and addresses become **typed tokens**: `[PERSON_0001]`, `[EMAIL_0001]`, ... Organisation names and other values still get surrogates (`default = "surrogate"`). 3. The model treats the tokens as opaque IDs. It groups, counts and ranks them without inventing facts about fake people. 4. The answer comes back with every token replaced by the real value: a churn report about your real customers. ## Example: a secret with redaction turned on > "Why does this fail? `OPENAI_API_KEY=sk-proj-…` …" By default a secret is a surrogate like everything else: the provider sees a fake key of the same shape, and if the model quotes the config back, you see your real key restored locally. If you would rather secrets never leave in any form, opt in to redaction: ```toml [policy.always] secret = "redact" ``` The provider then sees `[REDACTED_SECRET]` and the value cannot be restored. ## Configuration ```toml [policy] intent = true # classify each request (default true) [policy.act] targets = "keep" # keep | surrogate: the recipient of a message or action remote_tool_targets = "keep" # surrogate | keep: when the action runs in the provider's cloud [policy.report] person = "token" email = "token" phone = "token" address = "token" default = "surrogate" # every other kind in a report [policy.always] # empty by default; applies regardless of intent # secret = "redact" # national_id = "token" ``` Keys under `[policy.report]` and `[policy.always]` are detector kinds: `email`, `phone`, `person`, `org`, `term`, `address`, `iban`, `credit_card`, `national_id`, `ip`, `mac`, `hostname`, `secret`, `username`. Values are `surrogate`, `token`, `keep` or `redact`. With `intent = false`, every request is treated as `converse`: surrogates everywhere, plus whatever `[policy.always]` says. ## Observability - The proxy logs, per request, the intent and the count of each treatment (surrogates, tokens, kept, redacted). It never logs values. - Every `keep` is logged with the value's kind and the reason (message recipient or remote tool target), so you can audit what left in clear text. - In Rust, `session.summary()` returns the same counts. --- # File masking Source: https://getshadow.dev/concepts/file-masking/ > How Shadow masks PII in files sent to LLMs: multipart uploads, base64 and data URIs in text, CSV, JSON, DOCX, XLSX, PPTX and PDF formats. Prompts are not the only way data reaches a model. Files uploaded for retrieval, attached to a message or embedded as base64 often carry far more sensitive content than the prompt itself. Shadow masks them before they leave the machine. ## Where files are found | Transport | Examples | |---|---| | `multipart/form-data` uploads | OpenAI `POST /v1/files`, `POST /v1/uploads` parts | | Base64 files inside JSON | OpenAI `input_file.file_data`, Anthropic `document` blocks with a `base64` source, Gemini `inline_data` | | `data:` URIs | `data:text/csv;base64,...` anywhere in a JSON string | The format is recognised from the declared MIME type and the file name. ## Formats | Format | Treatment | Result | |---|---|---| | Plain text, Markdown | Masked as text | Same file, masked | | CSV | Masked as text, cell by cell | Same columns and rows | | JSON | Masked as JSON (string values) | Valid JSON | | HTML, XML | Masked as text | Markup intact | | DOCX | Masked in place: the document's text runs | A valid Word document | | XLSX | Masked in place: shared strings | A valid workbook; numbers and formulas untouched | | PPTX | Masked in place: text runs on slides | A valid presentation | | PDF | Text is extracted, masked and sent **as text** (`pdf = "text"`) | The model receives the masked text instead of the PDF | | Images | Passed through unchanged by default (`images = "pass"`) | No OCR yet | | Anything else | Passed through | | Masking uses the same detectors, persona, vault and [intent policy](https://getshadow.dev/concepts/intent-policy/) as prompts, so a customer named in the prompt and in an attached spreadsheet gets the same fake in both. A customer list attached to a report request gets typed tokens. Office documents are edited inside their ZIP container, so styles, tables and layout survive. A value split across differently formatted runs (for example a name that is half bold) may not be recognised. ## PDF options PDFs cannot be safely edited in place, so Shadow offers three choices: | `pdf =` | Behaviour | |---|---| | `"text"` (default) | Extract the text, mask it, send it as a text document. Layout, images and scanned pages are lost | | `"block"` | Refuse the request with an error, so a PDF never leaves unmasked | | `"pass"` | Send the PDF unchanged (not masked) | ## Images Shadow does not run OCR yet. Images, including screenshots and scanned documents, are passed through unchanged by default. If images may contain sensitive data, block them: ```toml [files] images = "block" ``` ## Configuration ```toml [files] enabled = true # mask uploaded files (default true) pdf = "text" # text | block | pass images = "pass" # pass | block max_bytes = 52428800 # 50 MiB; larger files are not masked ``` ## Limitations - **No OCR.** Text inside images and scanned PDFs is not detected. - **PDF layout is not preserved** in `text` mode. Questions about the visual layout of a PDF will get worse answers. - **Files above `max_bytes`** are forwarded unmasked with a warning, or refused when `block_oversize = true`. - **Uploads by reference.** A file already uploaded to the provider earlier (by `file_id`) is not re-scanned. Upload files through Shadow from the start. - **Embedded objects** in Office files (charts' cached values, embedded workbooks, images) are not masked. - **Archives** (`.zip`, `.tar`) other than Office formats are passed through. Check what Shadow finds in a document's text with `shadow scan` on an exported text version before you rely on it. --- # Tool calls and agents Source: https://getshadow.dev/concepts/tool-calls/ > How Shadow keeps AI agents working on masked data: tool-call arguments are restored locally before tools run, and provider-hosted tools follow policy. Agents turn model output into actions: sending email, querying databases, creating tickets, running shell commands. That raises a question plain masking cannot answer: if the model only ever sees fakes, how does the action reach the real target? Shadow's answer by default: the recipient of the action is passed through as written (and logged), and everything else is masked and restored. ## The round trip In a typical agent loop, the model proposes a tool call, **your code** runs it, and the result goes back to the model: ```text app ──(prompt + tool schemas)──▶ Shadow ──(masked)──▶ model app ◀──(tool call, real args)── Shadow ◀──(tool call, fake args)── model app runs the tool with real values app ──(tool result)──▶ Shadow ──(result masked)──▶ model ``` Shadow handles each leg: 1. **Request.** Prompts, prior messages and previous tool results are masked like any other text. Tool *schemas* are scanned like any other strings. Protocol keys such as `type` are never touched, and tool names and parameter definitions rarely contain anything a detector matches, so they normally arrive intact. 2. **Model's tool call.** The model writes arguments using the values it saw. The recipient named in the instruction is kept by default, so `{"to": "jane@acme.com"}` arrives as is; other values are fakes, such as a colleague `{"cc": "hui.zhao@zhilan.cn"}`. 3. **Restoration.** Shadow restores the fakes in the arguments before your code sees them: `{"cc": "max.weber@acme.com"}`. With `targets = "surrogate"` the recipient is a fake too and is restored the same way. This works for complete JSON responses and for streamed arguments, where a fake may be split across many deltas. 4. **Tool result.** When your code sends the result back, it contains real values again, and Shadow masks them with the same fakes as before, because the vault maps them consistently. The model reasons about the real recipient and a coherent fake world around them. Your tools operate on the real one. Supported tool-call shapes include OpenAI Chat Completions `tool_calls`, the OpenAI Responses API function calls, Anthropic `tool_use` blocks and Gemini `functionCall` parts. ## Local tools versus provider-hosted tools The round trip above requires the tool to run on your side of Shadow. Some tools run inside the provider's cloud instead: - hosted MCP servers that the provider connects to on your behalf; - server-side tools such as web search, code execution or connectors executed by the provider. For these, the arguments never come back through Shadow before the action happens, so any fake would be used as is: an email to a fake recipient would go to `hui.zhao@zhilan.cn`. The [intent policy](https://getshadow.dev/concepts/intent-policy/) handles this explicitly: ```toml [policy.act] targets = "keep" # the recipient of the message or action: pass through, logged remote_tool_targets = "keep" # provider-hosted tools: send the real target ``` - With `keep` (default), only what the action needs is sent in clear text: the contact details, people, companies, addresses, usernames and IBANs named in the latest instruction, and, for a follow-up such as "now send her the invite" that names no email or phone, the email, phone and person values anywhere in the request (earlier messages, pasted documents, tool results). Secrets, card numbers, national IDs, hostnames and IPs stay masked. Each keep is logged with its kind and reason, never the value. - With `surrogate`, the provider never sees the target, and the hosted action operates on a fake. Use this when exposure matters more than the action succeeding. ## Practical guidance - Prefer **local tool execution** for actions on personal data. With `targets = "surrogate"` it is the only way to hide the recipient from the provider and still have a working action; the model then writes to a stand-in, so check greetings and wording before sending. - Keep tool **descriptions** free of personal data. Anything sensitive in them is masked like other text, which can make the description harder for the model to follow. - Use typed **report** handling for bulk data an agent reads (exports, query results): tokens are cheaper for the model to carry through than realistic fakes, and less likely to be embellished. - When a tool returns data that the model must quote exactly (IDs, account numbers), surrogates still round-trip correctly as long as the model quotes the whole value. A fake person's first or last name on its own is restored, but other fragments (the last four digits of a card, part of an email) are not, so ask for full values in tool arguments. Test with `shadow mask` and `shadow unmask` if in doubt. - In Rust, use one [session](https://getshadow.dev/integrate/rust/#sessions-and-intent) per model call, and `session.unmask_json` on the response to restore tool-call arguments. --- # Activity dashboard Source: https://getshadow.dev/concepts/activity-dashboard/ > See what Shadow is doing on your machine. A web dashboard, a terminal view and a menu-bar app show the AI traffic Shadow checked, the sensitive data it masked, and the requests it blocked, by data type, destination and app. Shadow works quietly in the background, so it can be hard to tell that it is doing anything. The activity feed answers three questions at a glance: - **How much AI traffic went through Shadow?** Requests monitored, by destination (OpenAI, Anthropic, Google Gemini, Azure OpenAI, ...) and by the app that sent them (Claude Code, Codex CLI, the OpenAI Python SDK, curl, ...). Only traffic to [intercepted AI API hosts](https://getshadow.dev/reference/providers/) is counted; AI web apps used in a browser are not. - **What did it protect?** Values masked before they left the machine, by data type (email addresses, names, secrets and API keys, hostnames, card numbers, ...), and how many were restored in the answers. - **What did it stop?** Requests blocked by policy, such as an image upload when `files.images = "block"`, with the reason. You can look at the same feed in three places: a [web dashboard](#web-dashboard), a [terminal view](#terminal-shadow-top) and a [menu-bar app](#menu-bar-and-tray-app). ## What is recorded, and what never is Every request that Shadow translates produces one small event: | Field | Example | |---|---| | Time | `2026-10-31T14:02:11Z` | | Destination | provider `anthropic`, host `api.anthropic.com` | | App | `Claude Code` (from the `User-Agent` header) | | Endpoint kind | `chat`, `embeddings`, `files` or `other` | | Route | HTTPS proxy or `/gw` gateway | | Action | `masked`, `blocked` or `clean` | | Counts per data type | `{"email": 2, "secret": 1}` | | Detectors used | `email`, `secret`, `dictionary`, ... | | Values restored in the response | `3` | | Block reason | a fixed category such as `image_blocked` | | Request intent | `code`, `report`, `act` or `converse` | | Size, status, latency, streamed | `5.1 KB`, `200`, `840 ms`, `true` | **Never recorded:** the real values, the fakes or tokens sent in their place, prompts, responses, request paths and query strings, headers, API keys, file names, and the text of block messages (which can contain a file name). Unknown apps are named by the first product token of their `User-Agent` only (`MyTool/1.2 (build-host; jane@acme.com)` becomes `MyTool`), so the comment part, where hostnames and user names tend to appear, is dropped. Blind tunnels to hosts that Shadow does not intercept are not recorded at all: Shadow cannot see inside them. ## Web dashboard With Shadow running, open: ```sh shadow dashboard # or browse to http://127.0.0.1:8877/shadow/ui ``` The page shows the headline numbers, a traffic timeline (masked, clean and blocked requests), breakdowns by data type, destination and app, and the most recent incidents. Click an incident to see everything recorded about it. Pick a window of 1 hour, 24 hours, 7 days or 30 days (keys `1` to `4`); the page refreshes every few seconds and follows your system's light or dark theme. The page is built into the `shadow` binary. It loads nothing from the internet, runs under a strict Content-Security-Policy (`default-src 'none'`, scripts and styles from the page's own origin only) and talks only to the activity API below. Useful URL parameters: | Parameter | Effect | |---|---| | `?demo=1` | Synthetic demo data, clearly labelled "Demo data". Good for trying the UI or taking screenshots. | | `?theme=dark` / `?theme=light` | Force a theme. | | `?w=1h` / `24h` / `7d` / `30d` | Initial time window. | | `?embed=1` | Compact layout used by the tray app. | ## Terminal: `shadow top` ```sh shadow top # live view of the running proxy shadow top --demo # synthetic data ``` A full-screen view with the totals, a timeline, bars by data type, destination and app, and a live incident list. Keys: `1`-`4` change the window, `Tab` or arrow keys switch between Overview, Incidents and Breakdown, `↑`/`↓` and `Enter` open an incident, `r` refreshes, `q` quits. If the proxy is not running, `shadow top` says so, shows the history saved on disk, and keeps retrying. For servers and scripts, `shadow activity` prints the same summary as plain tables: ```sh shadow activity # last 24 hours shadow activity --window 7d shadow activity --json # the summary object from the API below ``` ## Menu-bar and tray app `apps/shadow-tray` is a small companion app for macOS (menu bar) and Windows (system tray). Its icon shows whether Shadow is protecting the machine (a filled crescent) or not running (an outline). The menu lists today's requests, values protected and requests blocked, and has **Open dashboard** (a popover with the web dashboard), **Open in browser**, **Docs** and **Quit**. On macOS it has no Dock icon; on Windows, left-click opens the dashboard and right-click opens the menu. ```sh cd apps/shadow-tray cargo run --release ``` It is a separate Cargo workspace built on the system webview (WKWebView, WebView2), so building Shadow itself never needs webview libraries. ## HTTP API All endpoints are on the proxy's listen address and return JSON. | Endpoint | Returns | |---|---| | `GET /shadow/activity?since=&limit=&action=masked,blocked` | Recent events, newest first. `action=incidents` is short for masked and blocked. | | `GET /shadow/activity/summary?window=1h\|24h\|7d\|30d\|today` | Totals, a timeline, and breakdowns by data type (`kinds`), destination (`providers`), app (`clients`), endpoint and block reason. | | `GET /shadow/activity/stream` | Server-Sent Events: one `activity` event per new request. | | `GET /shadow/ui` | The dashboard. | ```sh curl -s 'http://127.0.0.1:8877/shadow/activity/summary?window=24h' | jq .totals ``` ```json { "requests": 573, "masked_requests": 349, "blocked": 11, "clean": 213, "values_protected": 981, "values_restored": 629, "providers": 8, "clients": 9, "avg_latency_ms": 1840 } ``` ### Who can reach it The dashboard and the activity API answer only connections from the same machine, and only when the `Host` header names a loopback address (`127.0.0.1`, `localhost`, `[::1]`). The second check stops a web page you visit from reading the API through DNS rebinding. Other paths on the proxy are unaffected. When Shadow runs in Docker and listens on `0.0.0.0`, connections from the host arrive from the Docker bridge and are refused. Publish the port on loopback only (`-p 127.0.0.1:8877:8877`) and set `allow_remote = true`, or read the summary from inside the container. See [Docker and Kubernetes](https://getshadow.dev/integrate/docker-kubernetes/). ## Configuration ```toml [activity] enabled = true # record activity events at all persist = true # keep history in /activity/activity.jsonl retention_days = 30 # drop older events max_file_mb = 16 # rotate the log past this size (one previous file is kept) allow_remote = false # serve the dashboard and API to non-loopback clients ``` Recording adds one short in-memory update per request; the file is written from a background thread. With `persist = false` the dashboard still works but starts empty after each restart. See the [configuration reference](https://getshadow.dev/reference/configuration/) for every other option. ## Frequently asked questions ### Does the dashboard store my prompts or the values Shadow masked? No. Shadow records counts and categories only, such as "2 email addresses and 1 API key masked in a request from Claude Code to Anthropic". Real values, the fakes sent in their place, prompts, responses, file names and headers are never recorded. ### Can someone else on my network see the dashboard? No. The dashboard and the activity API only answer connections from the same machine, and only when the Host header names a loopback address. You can opt in to remote access with activity.allow_remote. ### How do I turn activity recording off? Set enabled = false in the [activity] section of shadow.toml. To keep the dashboard but write nothing to disk, set persist = false. ### Where is the activity history stored? In activity/activity.jsonl inside the Shadow data directory, created with mode 0600 in a 0700 directory. It rotates at 16 MB and drops events older than 30 days by default. --- # PII redaction benchmark: Shadow vs Presidio, GLiNER, OpenAI Privacy Filter, LLM Guard Source: https://getshadow.dev/benchmarks/ > Reproducible PII masking benchmark for LLM prompts. Leak rate, over-masking, precision, recall, round trip and latency for Shadow, Microsoft Presidio, GLiNER, OpenAI Privacy Filter, LLM Guard and scrubadub. **Shadow is an open-source privacy layer that masks PII and secrets in LLM prompts with reversible fakes. This page measures how much sensitive data it and five other open-source tools let through, how much harmless text they destroy, and how fast they are, on three labelled datasets.** Everything here is reproducible from the [`bench/`](https://github.com/Tiny-Bubble-Company/shadow/tree/main/bench) folder. :::note[TL;DR] - On the **held-out test set** (1,500 documents, 7,800 sensitive values), Shadow's built-in rules leak **3.08%** of sensitive values at **7.5 ms** per document. Adding the Presidio detector brings that to **1.35%**; adding the open-weight NER sidecar brings it to **0.08%**. The best standalone tool, OpenAI Privacy Filter, leaks **11.01%**; Presidio leaks **20.74%**. - Shadow's rules changed **no public names** (company, city and public-figure names that should stay readable) and restored the original text exactly in **100%** of samples. - On the **third-party Gretel finance set**, which Shadow was never tuned on, every tool does worse. GLiNER alone (17.61%) beats Shadow's rules alone (31%), mainly on company and person names in non-English text. ::: ## Why leak rate is the headline number When a prompt goes to a hosted model, a missed value is a value disclosed. So the main metric counts **sensitive values still readable in the output**, after normalising case, spacing and punctuation, rather than span-level F1, which rewards finding *most* of a value. A tool that masks "Jane" but leaves "Okafor" in the text has leaked a surname; F1 calls that a partial hit. Leak rate alone would reward masking everything, so the benchmark also measures: - **Public names changed**: labelled names that should stay readable (Microsoft, Berlin, Taylor Swift). Masking them makes answers worse. - **Text kept**: ordinary words outside any label that survive. Low values mean the model gets a mangled prompt. - **Precision, recall, F1**: span detection, a gold span counts as found when at least half of it is covered. - **Round trip**: for tools that can reverse their masking, whether unmasking gives back the original exactly. - **Latency**: median time per document on one CPU, after warm-up. ## Held-out test set 1,500 documents, 7,800 labelled sensitive values, ten locales and eleven scenarios. Generated with a different seed from the development set and never used for tuning. Lower is better for the first three columns. | Tool | Leak rate ↓ | Partial name leak ↓ | Public names changed ↓ | Text kept ↑ | Precision | Recall | F1 | Round trip | p50 latency | | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | | **Shadow + open-weight NER sidecar** | **0.08%** | 0.45% | 2.06% | 86.48% | 78.53% | 99.62% | 87.83 | 99.27% | 1003 ms | | **Shadow + Presidio detector** | 1.35% | 4.87% | 0% | 94.78% | 95.64% | 97.77% | 96.69 | 100% | 22.8 ms | | **Shadow (built-in rules)** | 3.08% | 9.8% | **0%** | **98.13%** | **99.97%** | 96.46% | **98.19** | **100%** | 7.5 ms | | OpenAI Privacy Filter | 11.01% | 4.57% | 0.61% | 93.12% | 86.55% | 85.87% | 86.21 | not reversible | 227 ms | | GLiNER multi PII | 13.28% | 1.36% | 49.28% | 88.88% | 74.6% | 86.58% | 80.14 | not reversible | 405 ms | | Microsoft Presidio | 20.74% | 22.76% | 26.01% | 90.21% | 79.39% | 72.64% | 75.87 | not reversible | 8.8 ms | | LLM Guard (Anonymize) | 29.26% | 15.73% | 2.29% | 97.87% | n/a | n/a | n/a | 100% | 110 ms | | scrubadub | 67.38% | 92.36% | 0% | 98.14% | 100% | 34.37% | 51.16 | not reversible | 0.4 ms | What stands out: - **Structured identifiers are solved by rules.** Shadow leaked no emails, phone numbers, IBANs, card numbers, national IDs, IP addresses, URLs, hostnames, usernames or secrets. ML-only tools miss many of these: GLiNER leaked 89% of national IDs and 80% of URLs; Privacy Filter leaked 59% of IP addresses and 69% of usernames; Presidio leaked 74% of secrets. - **Names are where tools differ.** Shadow's rules still leak 21.6% of company names and 5.6% of person names, mostly names in free text with no surrounding cue. The NER sidecar closes that gap (1.1% and 0%), at the cost of precision and some over-masking. - **GLiNER over-masks.** It changed 49% of public names, so prompts about real companies or places lose their meaning. - LLM Guard reports no spans, so precision and recall are not available for it. ### Leak rate by type of value (held-out) | Value | Shadow | Shadow + Presidio | Shadow + NER sidecar | Presidio | Privacy Filter | GLiNER | LLM Guard | scrubadub | | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | | address | 2.15% | 0.59% | 0% | 14.48% | 3.13% | 16.83% | 92.56% | 99.61% | | credit card | 0% | 0% | 0% | 2.8% | 0% | 0% | 18.18% | 23.78% | | date of birth | 0% | 0% | 0% | 0% | 0% | 0% | 100% | 100% | | email | 0% | 0% | 0% | 0% | 0.28% | 5.8% | 0% | 0% | | hostname | 0% | 0% | 0% | 0% | 1.42% | 46.81% | 0% | 0% | | iban | 0% | 0% | 0% | 6.23% | 0.33% | 0% | 9.18% | 98.69% | | ip | 0% | 0% | 0% | 0% | 58.87% | 21.99% | 0% | 100% | | national id | 0% | 0% | 0% | 22.92% | 0% | 88.89% | 88.19% | 88.89% | | org | 21.61% | 10.62% | 1.1% | 61.17% | 55.68% | 3.66% | 81.87% | 100% | | person | 5.58% | 2.21% | 0% | 22.86% | 4.02% | 0.1% | 1.31% | 100% | | phone | 0% | 0% | 0% | 3.68% | 0.49% | 13.82% | 16.19% | 54.7% | | secret | 0% | 0% | 0% | 74.17% | 0% | 1.28% | 51.15% | 98.98% | | url | 0% | 0% | 0% | 0% | 30.83% | 80.45% | 100% | 0% | | username | 0% | 0% | 0% | 89.43% | 69.04% | 56.76% | 85.26% | 100% | ### Leak rate by kind of text (held-out) | Scenario | Shadow | Shadow + Presidio | Shadow + NER sidecar | Presidio | Privacy Filter | GLiNER | LLM Guard | scrubadub | | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | | chat request | 7.28% | 2.43% | 0.13% | 21.33% | 16.99% | 1.66% | 32.69% | 76.12% | | code | 0% | 0% | 0% | 49.28% | 5.44% | 26.56% | 56.32% | 59.36% | | CSV export | 0.38% | 0.14% | 0% | 8.73% | 0.58% | 4.99% | 9.88% | 52.09% | | DevOps log | 0% | 0% | 0% | 40.9% | 49.05% | 49.29% | 54.49% | 66.67% | | HR record | 0.42% | 0.14% | 0% | 10.56% | 0.14% | 26.94% | 57.22% | 89.44% | | meeting notes | 15.93% | 8.14% | 0.18% | 33.98% | 18.76% | 0.18% | 17.52% | 80% | | multilingual | 1.46% | 0.49% | 0% | 12.34% | 3.41% | 1.14% | 20.13% | 68.18% | | payment | 9.01% | 4.8% | 0.9% | 34.23% | 15.32% | 3.3% | 37.24% | 100% | | email signature | 6.56% | 2.6% | 0.15% | 18.02% | 12.82% | 18.78% | 31.76% | 72.37% | | support ticket | 0% | 0% | 0% | 6.82% | 0.35% | 0% | 6.99% | 55.24% | ## Third-party test set: Gretel synthetic PII finance 1,000 documents sampled from the test split of [gretelai/synthetic_pii_finance_multilingual](https://huggingface.co/datasets/gretelai/synthetic_pii_finance_multilingual) (Apache-2.0), with 5,077 sensitive values in seven languages: loan agreements, SWIFT messages, customer records, privacy notices and similar financial documents. It was written by a third party and **never used to tune Shadow**; detector rules were developed only on samples of its separate train split. Gretel does not label public names, so over-masking is not measured here. This is the honest picture of out-of-distribution performance: - **Every tool leaks more** on these documents than on the synthetic sets. Long legal and financial prose, company names in many languages and names without cues are harder for everyone. - **GLiNER alone beats Shadow's rules alone** on overall leak rate (17.61% vs 31%), driven by company names (6% vs 47%) and person names. It pays for that with 35% precision: almost two in three things it masks are not sensitive. It also took 2.3 seconds per document on CPU. - **Shadow's rules stay ahead on identifiers** (phones 4%, IP 4.5%, IBAN 5%) and keep the most text intact (98.8%) with 89% precision. - Part of Shadow's company-name leak is deliberate: its public allowlist keeps well-known company names readable, and Gretel labels every company as sensitive. ### Gretel leak rate by language ## Development set The development set (1,500 documents, 7,637 values, seed 20261031) is what Shadow's rules were tuned against, so its numbers flatter Shadow and are shown only for completeness. They match the held-out set closely (Shadow rules 3.16%, + Presidio 1.31%, + NER sidecar 0.1%; Privacy Filter 11.27%, GLiNER 13.75%, Presidio 20.65%, LLM Guard 29.8%, scrubadub 68.08%), which suggests the rules are not overfitted to individual examples, though they share the generator's style. ## Methodology ### Datasets | Dataset | Documents | Sensitive values | Source | Use | | --- | ---: | ---: | --- | --- | | `heldout-v1` | 1,500 | 7,800 | Shadow generator, seed 7 | test, never tuned on | | `gretel-finance-1000` | 1,000 | 5,077 | Gretel.ai, test split, Apache-2.0 | test, never tuned on | | `synthetic-v1` | 1,500 | 7,637 | Shadow generator, seed 20261031 | development | The Shadow generator writes original text with [Faker](https://faker.readthedocs.io/) in ten locales and eleven scenarios: chat requests to an assistant, support tickets, CSV exports, HR records, DevOps logs, source code, meeting notes, email signatures, payments and multilingual messages. Every sensitive value is labelled, and so are public names a good tool should leave alone. No real personal data is used anywhere. Gretel labels are mapped onto the benchmark's: `name`, `first_name` and `last_name` to person, `company` to org, `street_address` to address, `ssn`, `passport_number` and `driver_license_number` to national ID, `password` and `api_key` to secret, and so on. Labels with no counterpart (times, plain dates, internal customer IDs, BIC codes) become *ignore* spans: masking them is not a false positive and leaving them is not a leak. The full mapping is in [`bench/datasets/gretel.py`](https://github.com/Tiny-Bubble-Company/shadow/blob/main/bench/datasets/gretel.py). ### Tools and configuration | Tool | Version | Configuration | | --- | --- | --- | | Shadow (built-in rules) | `main` at publication | default config: regex, checksums, dictionaries, names, fields, addresses, public allowlist | | Shadow + Presidio detector | Presidio Analyzer 2.2.362 container | Shadow's built-in rules plus Presidio over HTTP | | Shadow + open-weight NER sidecar | `ml/serve_hf.py --backend ensemble` | built-in rules plus OpenAI Privacy Filter and GLiNER multi PII v1 | | Microsoft Presidio | presidio-analyzer 2.2.358, spaCy `en_core_web_lg` | default recognisers, `replace` operator | | OpenAI Privacy Filter | `openai/privacy-filter` | sidecar `--backend privacy-filter`, all predicted spans masked | | GLiNER multi PII | `urchade/gliner_multi_pii-v1`, gliner 0.2.21 | PII labels, threshold 0.5 | | LLM Guard | llm-guard 0.3.16 | `Anonymize` scanner with its default vault, `Deanonymize` for round trip | | scrubadub | 2.0.1 | default detectors | Every tool ran with its defaults or its documented recommended settings. No tool was given the gold labels. Each tool was run on the same text and scored by the same code. ### Hardware One Apple M3 (8 cores), CPU only, PyTorch 2.15 and transformers 4.51. Apple's GPU backend crashed on this macOS version, so all models ran on CPU; on a GPU the model-based tools, and Shadow with its sidecar, are many times faster. Latency is the median per document after warm-up, measured inside each tool's process and including Shadow's unmask check. ### Limitations - The synthetic sets come from Shadow's own generator. Their formats and phrasing are the ones Shadow's rules were written for, which favours Shadow. Treat the Gretel numbers as the fairer comparison. - Gretel's documents are synthetic too, generated by an LLM, and only financial. - Leak rate checks whether the exact value survives. A tool that changes one character of a value is counted as having masked it. - Shadow + NER sidecar does not round-trip every sample (99.27% held-out): an ML span occasionally overlaps text a fake collides with. Built-in rules alone round-trip 100%. - Commercial services (Private AI, Nightfall, AWS Comprehend, Google Sensitive Data Protection, Azure AI Language) are not included because their terms or pricing make public, reproducible runs impractical. See the [comparison pages](https://getshadow.dev/compare/) for what their documentation says. ## Reproduce ```sh git clone https://github.com/Tiny-Bubble-Company/shadow && cd shadow/bench python -m venv .venv && . .venv/bin/activate pip install presidio-analyzer presidio-anonymizer gliner scrubadub llm-guard faker huggingface_hub pyarrow python -m spacy download en_core_web_lg python run.py --data datasets/heldout-v1.jsonl --out results-heldout \ --tools shadow,presidio,gliner,scrubadub,llm_guard python report.py results-heldout ``` `shadow+presidio` needs a Presidio Analyzer on `127.0.0.1:5002`; `shadow+ner` and `privacy_filter` need the [open-weight sidecar](https://getshadow.dev/concepts/detection/) on ports 8878 and 8879. The [bench README](https://github.com/Tiny-Bubble-Company/shadow/blob/main/bench/README.md) has the details, and `summary.json` for each run is committed next to the code. ## Related - [How Shadow detects sensitive data](https://getshadow.dev/concepts/detection/) - [Shadow vs Presidio](https://getshadow.dev/compare/presidio/), [vs GLiNER](https://getshadow.dev/compare/gliner-pii/), [vs OpenAI Privacy Filter](https://getshadow.dev/compare/openai-privacy-filter/), [vs LLM Guard](https://getshadow.dev/compare/llm-guard/) - [Open-source PII redaction tools compared](https://getshadow.dev/compare/open-source-pii-redaction-tools/) ## Frequently asked questions ### Which open-source tool leaks the least PII in LLM prompts? On the held-out test set, Shadow with its open-weight NER sidecar leaked 0.08% of sensitive values, Shadow with the Presidio detector 1.35%, and Shadow's built-in rules alone 3.08%. The best standalone tools were OpenAI Privacy Filter (11.01%), GLiNER multi PII (13.28%) and Microsoft Presidio (20.74%). On the third-party Gretel finance set every tool leaks more; see the Gretel section for those numbers. ### Is Shadow more accurate than Microsoft Presidio? On all three datasets in this benchmark Shadow's built-in rules leak fewer sensitive values than Presidio's default analyzer (3.08% vs 20.74% held-out, 31% vs 43.39% on Gretel), with higher precision and no public names changed. Shadow can also run Presidio as an extra detector, which lowers its leak rate further. ### How fast is Shadow compared with other PII tools? Shadow's built-in detectors took 7.5 ms per sample at the median on one Apple M3 CPU, including masking and the round-trip check. Presidio took about 9 ms, LLM Guard about 110 ms, OpenAI Privacy Filter about 230 ms and GLiNER about 400 ms on the same CPU. Adding the open-weight NER sidecar makes Shadow about 1 second per sample on CPU; a GPU cuts that sharply. ### Are these benchmark results biased towards Shadow? Partly, and the page says where. The synthetic test sets come from Shadow's own generator, and its rules were tuned on a separate development set from the same generator, so it has a home advantage there. That is why the page also reports a third-party dataset (Gretel synthetic PII finance, Apache-2.0) that was never used for tuning. On that set, GLiNER alone leaks less than Shadow's rules alone. ### Can I reproduce the benchmark? Yes. The datasets, label mappings, runner and report script are in the bench folder of the Shadow repository on GitHub, under Apache-2.0. One command runs every tool and writes the tables on this page. ### Which tools can restore the original values after the LLM answers? Only Shadow and LLM Guard's Anonymize/Deanonymize pair round-trip in this benchmark. Shadow restored the exact original text in 100% of samples with its built-in rules. Presidio, GLiNER, OpenAI Privacy Filter and scrubadub replace values one way. --- # Shadow vs alternatives: the AI data-leak prevention landscape Source: https://getshadow.dev/compare/ > Compare Shadow with Presidio, LLM Guard, LiteLLM, Nightfall, Netskope, Skyflow and 25 more tools that stop sensitive data leaking into LLM prompts. **Shadow is an open-source (Apache-2.0) privacy layer for AI traffic.** It replaces names, emails, customers, secrets and IDs in prompts with realistic, reversible fakes before a request reaches OpenAI, Anthropic, Gemini or another provider, and restores the real values in the response, including streamed responses and tool calls. This page maps the other tools people use to stop sensitive data leaking into large language models, and links to a detailed, fair comparison with each one. :::note[How we compare] Every comparison is based on public documentation, checked on the review date shown on each page. Where a vendor's documentation does not say something, we say "not documented" rather than guess. Detection accuracy is compared on the [benchmarks page](https://getshadow.dev/benchmarks/), with code and label mappings published, not on this page. ::: ## What kinds of tools prevent AI data leaks? Tools in this space fall into five groups. They overlap, and many teams combine two. | Category | What it does | Typical action on sensitive data | Examples | |---|---|---|---| | **Detector libraries and models** | Find PII spans in text. You build the rest | Placeholder, mask or hash, usually one way | Presidio, GLiNER-PII, OpenAI Privacy Filter, DataFog, cloud PII APIs | | **Guardrail frameworks and AI gateways** | Sit in front of model APIs, route traffic and run checks | Block, or redact with placeholders; a few restore | LLM Guard, NeMo Guardrails, LiteLLM, Portkey, Kong, Cloudflare, Bedrock Guardrails | | **Enterprise GenAI DLP** | Watch employees' use of AI apps in the browser, on the endpoint or in the SASE layer | Block, warn, coach, sometimes redact | Netskope, Zscaler, Palo Alto, Microsoft Purview, Nightfall, Harmonic, Prompt Security, Lasso, Strac | | **Privacy vaults and secure-AI platforms** | Tokenise or pseudonymise data and re-identify under policy | Reversible tokens, usually as SaaS | Skyflow, Protecto, Private AI, Tonic Textual, Wald.ai | | **Local reversible proxies** | Mask on the endpoint, restore in the response | Reversible placeholders or fakes | **Shadow**, Rehydra, a few small projects | Shadow belongs to the last group. Its distinguishing choices are realistic, format-preserving fakes drawn from a persistent [persona](https://getshadow.dev/concepts/personas/), restoration in [streams and tool calls](https://getshadow.dev/concepts/tool-calls/), an [intent-aware policy](https://getshadow.dev/concepts/intent-policy/), [file masking](https://getshadow.dev/concepts/file-masking/) for Office documents, and a local, single-binary deployment as an [HTTPS proxy](https://getshadow.dev/integrate/proxy-mode/), a [gateway](https://getshadow.dev/integrate/gateway-mode/) or a [Rust library](https://getshadow.dev/integrate/rust/). ## The comparison table Legend: **Block** = request refused; **Redact** = `[REDACTED]` or `` placeholder; **Token** = vault-backed token; **Fakes** = realistic synthetic values; **FPE** = format-preserving encryption. "Not documented" means public docs do not mention it, not that the feature is missing. | Tool | Open source / licence | Deployment | Protection method | Restores responses | Streaming | Tool calls | Files | Self-hosted | Price model | |---|---|---|---|---|---|---|---|---|---| | **Shadow** | Yes, Apache-2.0 | Endpoint proxy, gateway, Rust library | Reversible realistic fakes; typed tokens for reports; optional redact | Yes | Yes (SSE, NDJSON) | Yes, intent-aware | DOCX/XLSX/PPTX in place, PDF to text | Yes | Free | | [Presidio](https://getshadow.dev/compare/presidio/) | Yes, MIT | Library, REST containers | Redact, mask, hash, encrypt, custom | Only `encrypt` → `decrypt` | No | No | Image redactor (OCR), DICOM | Yes | Free | | [LLM Guard](https://getshadow.dev/compare/llm-guard/) | Yes, MIT | Library, API container | Placeholders, optional Faker values | Yes (Vault + Deanonymize) | Not documented | No | No | Yes | Free | | [NeMo Guardrails](https://getshadow.dev/compare/nemo-guardrails/) | Yes, Apache-2.0 | Guardrail framework | Block or label mask | No | Output rails, no unmask | Retrieval rails | No | Yes | Free | | [LiteLLM](https://getshadow.dev/compare/litellm/) | Core MIT, enterprise features paid | Self-hosted gateway | Block or redact via Presidio and others | Placeholders (`output_parse_pii`) | Reported issues on some paths | No | No | Yes | Free core; Enterprise paid | | [Portkey](https://getshadow.dev/compare/portkey/) | Gateway MIT; guardrails in cloud | SaaS, hybrid, OSS gateway | Redact via partner guardrails | No (docs: one way) | Not documented | No | No | Gateway only | SaaS tiers | | [Kong AI Gateway](https://getshadow.dev/compare/kong-ai-gateway/) | Sanitizer is Enterprise-only | Self-hosted gateway plugin | Placeholders or synthetic values | Yes (`restore`) | Not documented | No | No | Yes | Enterprise licence | | [Cloudflare AI Gateway](https://getshadow.dev/compare/cloudflare-ai-gateway/) | No | SaaS gateway | Flag or block | No | Buffers response to scan | No | No | No | Plan-based | | [AWS Comprehend / Bedrock Guardrails](https://getshadow.dev/compare/aws-comprehend-bedrock-guardrails/) | No | AWS service | Detect; block or `{NAME}` tags | No | Not verified | No | Async batch (Comprehend) | No | Usage-based | | [Azure AI Language PII](https://getshadow.dev/compare/azure-ai-language-pii/) | No | Azure service | Character mask, entity mask, synthetic (preview) | No | No | No | Document PII API | Containers (not verified) | Usage-based | | [Google Sensitive Data Protection](https://getshadow.dev/compare/google-sensitive-data-protection/) | No | GCP service | Mask, replace, FPE, deterministic encryption | Yes (`reidentifyContent`) | No | No | Images, storage jobs | No | Per GB | | [OpenAI Privacy Filter](https://getshadow.dev/compare/openai-privacy-filter/) | Yes, Apache-2.0 (model) | Local model | Detector only | n/a | n/a | n/a | n/a | Yes | Free | | [GLiNER PII models](https://getshadow.dev/compare/gliner-pii/) | Apache-2.0 or NVIDIA Open Model License | Local model | Detector only | n/a | n/a | n/a | n/a | Yes | Free | | [DataFog](https://getshadow.dev/compare/datafog/) | Yes, MIT | Library, LiteLLM guardrail, hook | Redact, replace, hash, block | Not documented | Not documented | No | Not documented | Yes | Free | | [LangChain PIIMiddleware](https://getshadow.dev/compare/langchain-pii-middleware/) | Yes, MIT | In-app agent middleware | Redact, mask, hash, block | No | n/a | Yes (tool results) | No | Yes | Free | | [Rehydra](https://getshadow.dev/compare/rehydra/) | Yes, MIT | SDK and local proxy (Node/Bun) | Reversible placeholders | Yes | Yes (sentence-buffered) | Yes | `.env` scrubbing | Yes | Free | | [Private AI](https://getshadow.dev/compare/private-ai/) | No | Container API, cloud | Placeholders, mask, synthetic | Yes (re-identify) | Not documented | Not documented | Many file types (vendor) | Yes (container) | Quote | | [Skyflow](https://getshadow.dev/compare/skyflow/) | No | Vault SaaS | Vault tokens | Yes (policy-governed) | Not documented | Not documented | Images, PDF, audio, Office | No | Quote | | [Protecto](https://getshadow.dev/compare/protecto/) | No | SaaS or VPC | Reversible tokens, FPE for databases | Yes (unmask API) | Not documented | Not documented | Not documented | VPC option | Quote | | [Tonic Textual](https://getshadow.dev/compare/tonic-textual/) | No | SaaS or self-hosted | Redact or consistent synthesis | Not as a proxy | No | No | Files in original format | Yes (Docker, Helm) | Pay-as-you-go or quote | | [Wald.ai](https://getshadow.dev/compare/wald-ai/) | No | SaaS chat and API | Contextual substitution | Yes | Not documented | Not documented | Not documented | No | Per user | | [Strac](https://getshadow.dev/compare/strac/) | No | SaaS and browser extension | Alert, block, redact, pseudonymise | Not documented | Not documented | No | SaaS stores | No | Quote | | [Nightfall](https://getshadow.dev/compare/nightfall/) | No | SaaS, browser extension, endpoint agent | Block or redact | No | n/a | No | Uploads | No | Quote | | [Lakera Guard](https://getshadow.dev/compare/lakera/) | No | API (Check Point) | Detect and flag | No | Not documented | Agent security | No | Enterprise only | Free tier; Enterprise quote | | [Prompt Security](https://getshadow.dev/compare/prompt-security/) | No | Extension and gateway (SentinelOne) | Block or filter | No | Not documented | Not documented | Not documented | Not documented | Quote | | [Harmonic Security](https://getshadow.dev/compare/harmonic-security/) | No | Browser extension | Warn, block, redirect; redact (vendor) | No | n/a | n/a | Uploads | No | Quote | | [Lasso Security](https://getshadow.dev/compare/lasso-security/) | No | Extension and gateway | Mask or alert | Not documented | Not documented | Not documented | Not documented | Not documented | Quote | | [Netskope One](https://getshadow.dev/compare/netskope/) | No | SASE inline | Block, coach, redact (mask, FPE, hash) | No | Not for SSE | Not for MCP | Not for file content | No | Quote | | [Zscaler](https://getshadow.dev/compare/zscaler/) | No | SASE inline | Block, isolate, allow, log | No | n/a | n/a | Uploads | No | Quote | | [Palo Alto AI Access](https://getshadow.dev/compare/palo-alto-ai-access/) | No | SASE and Prisma Browser | Block or allow | No | n/a | n/a | Not documented | No | Licence bundle | | [Microsoft Purview](https://getshadow.dev/compare/microsoft-purview/) | No | Microsoft 365, Edge, endpoint DLP | Block, warn, audit | No | n/a | n/a | Cannot scan files uploaded into prompts | No | Microsoft 365 E5-class licensing | The table is a summary. Each linked page explains the details and the caveats, including where the other tool is the better choice. ## How do I choose an AI data-leak prevention tool? Start from three questions: **who sends the data**, **what must happen to it**, and **who has to run the tool**. 1. **Employees pasting into ChatGPT, Gemini or Copilot in the browser, across a whole company?** You need visibility, policy and coaching across many managed devices. Choose an enterprise GenAI DLP or SASE product ([Netskope](https://getshadow.dev/compare/netskope/), [Zscaler](https://getshadow.dev/compare/zscaler/), [Microsoft Purview](https://getshadow.dev/compare/microsoft-purview/), [Nightfall](https://getshadow.dev/compare/nightfall/), [Harmonic](https://getshadow.dev/compare/harmonic-security/)). These mostly block or warn. 2. **Developers, agents and apps calling model APIs, where answers must stay useful?** Blocking breaks the tool and plain redaction loses meaning. Choose a reversible layer that restores values in responses and tool calls: Shadow (local, open source), or a vault service such as [Skyflow](https://getshadow.dev/compare/skyflow/) or [Protecto](https://getshadow.dev/compare/protecto/) if you prefer a managed service. 3. **Building your own pipeline and only need detection?** Use a detector: [Presidio](https://getshadow.dev/compare/presidio/), [GLiNER](https://getshadow.dev/compare/gliner-pii/), [OpenAI Privacy Filter](https://getshadow.dev/compare/openai-privacy-filter/) or a cloud PII API. Shadow can use Presidio and both open-weight models as additional detectors. 4. **Already standardised on an AI gateway?** Check its PII features first: [LiteLLM](https://getshadow.dev/compare/litellm/), [Kong](https://getshadow.dev/compare/kong-ai-gateway/), [Portkey](https://getshadow.dev/compare/portkey/), [Cloudflare](https://getshadow.dev/compare/cloudflare-ai-gateway/). Shadow can sit in front of a gateway as a sidecar, so masking happens before traffic leaves the host. 5. **Need de-identified datasets for training or analytics, not live traffic?** Use a batch tool such as [Tonic Textual](https://getshadow.dev/compare/tonic-textual/) or [Google Sensitive Data Protection](https://getshadow.dev/compare/google-sensitive-data-protection/). 6. **Must the protection tool itself run inside your network, and be auditable?** Prefer open-source, self-hosted tools: Shadow, Presidio, LLM Guard, LiteLLM, NeMo Guardrails, Rehydra. :::tip[A common combination] Enterprise DLP for browser chat on managed devices, plus Shadow on developer machines, CI runners and services that call model APIs. The DLP product sees what employees type into web apps; Shadow keeps API, agent and file traffic useful while masked. ::: ## What Shadow does not do Being honest about the gaps helps you choose: - **No central fleet console.** There is no admin dashboard, SIEM export or user coaching. Configuration is a file per machine or container. - **No OCR.** Text inside images and scanned PDFs is not detected. See [file masking](https://getshadow.dev/concepts/file-masking/#limitations). - **Only AI API traffic.** Proxy mode intercepts a [list of AI API hosts](https://getshadow.dev/reference/providers/) (plus any you add). It does not see what people type into chatgpt.com, claude.ai or gemini.google.com in a browser, phone apps, Microsoft 365 Copilot or GitHub Copilot. - **Detection is best effort.** Regex, checksums, dictionaries and optional NER models miss things: the built-in rules leak 3.08% of sensitive values on the held-out [benchmark](https://getshadow.dev/benchmarks/), 0.08% with the open-weight NER sidecar. - **Recipients and provider-hosted actions see real targets.** When you ask the model to write to or act on someone, Shadow passes that recipient through as written so the model addresses the right person; when the provider itself runs a tool (a hosted MCP server, web search), it keeps real the contact details the action needs. Both are defaults, configurable, and each keep is logged. See [intent policy](https://getshadow.dev/concepts/intent-policy/). - **It protects content, not identity.** Providers still see your IP address and the account behind your API key. See the [security model](https://getshadow.dev/reference/security/). ## Category guides - [Best open-source PII redaction tools for LLMs](https://getshadow.dev/compare/open-source-pii-redaction-tools/): Presidio, LLM Guard, GLiNER, OpenAI Privacy Filter, Rehydra, Shadow and more. - [AI DLP tools compared](https://getshadow.dev/compare/ai-dlp-tools/): Block vs redact vs pseudonymise: Netskope, Zscaler, Purview, Nightfall, Harmonic and others. - [LLM gateways with PII masking](https://getshadow.dev/compare/llm-gateways-with-pii-masking/): LiteLLM, Portkey, Kong, Cloudflare, Bedrock Guardrails and where Shadow fits. - [Privacy vaults for LLMs](https://getshadow.dev/compare/privacy-vaults-for-llms/): Skyflow, Protecto, Private AI, Tonic Textual, Wald.ai and a local alternative. ## All comparisons **Detector libraries and models:** [Presidio](https://getshadow.dev/compare/presidio/) · [OpenAI Privacy Filter](https://getshadow.dev/compare/openai-privacy-filter/) · [GLiNER PII](https://getshadow.dev/compare/gliner-pii/) · [DataFog](https://getshadow.dev/compare/datafog/) · [AWS Comprehend and Bedrock Guardrails](https://getshadow.dev/compare/aws-comprehend-bedrock-guardrails/) · [Azure AI Language PII](https://getshadow.dev/compare/azure-ai-language-pii/) · [Google Sensitive Data Protection](https://getshadow.dev/compare/google-sensitive-data-protection/) **Guardrails and gateways:** [LLM Guard](https://getshadow.dev/compare/llm-guard/) · [NeMo Guardrails](https://getshadow.dev/compare/nemo-guardrails/) · [LangChain PIIMiddleware](https://getshadow.dev/compare/langchain-pii-middleware/) · [LiteLLM](https://getshadow.dev/compare/litellm/) · [Portkey](https://getshadow.dev/compare/portkey/) · [Kong AI Gateway](https://getshadow.dev/compare/kong-ai-gateway/) · [Cloudflare AI Gateway](https://getshadow.dev/compare/cloudflare-ai-gateway/) · [Lakera Guard](https://getshadow.dev/compare/lakera/) **Enterprise GenAI DLP:** [Netskope](https://getshadow.dev/compare/netskope/) · [Zscaler](https://getshadow.dev/compare/zscaler/) · [Palo Alto AI Access](https://getshadow.dev/compare/palo-alto-ai-access/) · [Microsoft Purview](https://getshadow.dev/compare/microsoft-purview/) · [Nightfall](https://getshadow.dev/compare/nightfall/) · [Harmonic Security](https://getshadow.dev/compare/harmonic-security/) · [Prompt Security](https://getshadow.dev/compare/prompt-security/) · [Lasso Security](https://getshadow.dev/compare/lasso-security/) · [Strac](https://getshadow.dev/compare/strac/) **Privacy vaults and secure-AI platforms:** [Skyflow](https://getshadow.dev/compare/skyflow/) · [Protecto](https://getshadow.dev/compare/protecto/) · [Private AI](https://getshadow.dev/compare/private-ai/) · [Tonic Textual](https://getshadow.dev/compare/tonic-textual/) · [Wald.ai](https://getshadow.dev/compare/wald-ai/) **Local reversible proxies:** [Rehydra](https://getshadow.dev/compare/rehydra/) ## Try Shadow - [Quick start](https://getshadow.dev/getting-started/quick-start/): Install Shadow and protect your first request in five minutes. - [How it works](https://getshadow.dev/concepts/how-it-works/): Interception, detection, translation and streamed restoration. --- *Last reviewed: 2026-10-31. All product names are trademarks of their respective owners. These comparisons are based on public documentation as of the review date. If something is out of date, please [open an issue](https://github.com/Tiny-Bubble-Company/shadow/issues).* ## Frequently asked questions ### What is the best tool to stop sensitive data leaking into ChatGPT or Claude? It depends on where the data flows. To block or warn users in web chat UIs across a company, enterprise GenAI DLP (Netskope, Zscaler, Purview, Nightfall, Harmonic) fits. To mask data in API and agent traffic while keeping answers useful, a reversible pseudonymisation layer such as Shadow, Skyflow or Protecto fits. To build your own pipeline, detector libraries such as Presidio, GLiNER or OpenAI Privacy Filter are the building blocks. ### What is the difference between redaction, tokenisation and reversible pseudonymisation? Redaction replaces a value with [REDACTED] or and cannot be undone. Tokenisation replaces it with an opaque token that a vault can map back. Reversible pseudonymisation, which Shadow uses by default, replaces it with a realistic fake of the same format (a valid email, a Luhn-valid card) and maps it back in the response, so the model can still reason about the data. ### Is Shadow free and open source? Yes. Shadow is licensed under Apache-2.0, runs on your own machines or servers, needs no account and sends no telemetry. ### Does Shadow replace a DLP platform? Not entirely. Shadow protects the content of AI API traffic on the machines where it runs. It does not offer a central admin console, user coaching, SIEM export or coverage of devices it is not installed on. Many teams run it alongside a DLP or SASE product. ### Does Shadow make us GDPR or HIPAA compliant? No tool makes you compliant by itself. Shadow helps meet data-minimisation and confidentiality requirements by replacing detected personal data with fakes before requests reach AI providers, but compliance depends on your whole programme. ### How accurate is Shadow compared with these tools? Detection accuracy depends heavily on dataset and label mapping. Shadow publishes its methodology and results on the benchmarks page rather than quoting single numbers here. --- # Best open-source PII redaction tools for LLMs in 2026 Source: https://getshadow.dev/compare/open-source-pii-redaction-tools/ > An honest guide to open-source PII redaction tools for LLM prompts in 2026, including Presidio, LLM Guard, GLiNER, OpenAI Privacy Filter, Rehydra and Shadow. **The strongest open-source options for keeping PII out of LLM prompts in 2026 are Presidio for detection, OpenAI Privacy Filter and GLiNER for model-based detection, LLM Guard and Rehydra for reversible placeholders, and Shadow for reversible realistic fakes with streaming, tool-call and file support.** They solve different parts of the problem, and the best setups often combine a detector with a layer that handles the round trip. This guide is written by the Shadow maintainers, so read it with that in mind. We have tried to be fair, and Shadow is not the right answer in every row. ## What to look for Detection is only half the job when the text goes to an LLM. Ask: 1. **Recall and precision.** Does it find what matters in your data, without masking public names the model needs? 2. **What replaces the value?** `[REDACTED]`, a placeholder like ``, or a realistic fake? Placeholders are safe but can degrade answers. 3. **Is it reversible?** Can the response, streamed or not, be turned back into real values? 4. **Agents and tools.** Are tool-call arguments restored before tools run? 5. **Files.** Are uploaded documents covered? 6. **Licence.** Is the code *and* every model checkpoint usable commercially? 7. **Operational fit.** Python library, container, Node SDK, or a single binary? ## The shortlist | Tool | Type | Licence | Replacement | Reversible | Streaming | Tool calls | Best for | |---|---|---|---|---|---|---|---| | [Presidio](https://getshadow.dev/compare/presidio/) | Detector + anonymizer library | MIT | Placeholder, mask, hash, encrypt | Only `encrypt` | No | No | Pipelines, OCR, custom recognizers | | [OpenAI Privacy Filter](https://getshadow.dev/compare/openai-privacy-filter/) | Detection model | Apache-2.0 | Detector only | n/a | n/a | n/a | Strong English detection, local | | [GLiNER PII](https://getshadow.dev/compare/gliner-pii/) | Detection models | Apache-2.0 or NVIDIA Open Model License | Detector only | n/a | n/a | n/a | Zero-shot labels, multilingual | | [LLM Guard](https://getshadow.dev/compare/llm-guard/) | Scanner library | MIT | Placeholders, optional Faker | Yes | Not documented | No | Python apps wanting many scanners | | [NeMo Guardrails](https://getshadow.dev/compare/nemo-guardrails/) | Guardrail framework | Apache-2.0 | Label mask or block | No | Output rails | Retrieval rails | Conversational rails | | [LangChain PIIMiddleware](https://getshadow.dev/compare/langchain-pii-middleware/) | Agent middleware | MIT | Redact, mask, hash, block | No | n/a | Tool results | LangChain agents | | [DataFog](https://getshadow.dev/compare/datafog/) | Library + hooks | MIT | Redact, replace, hash | Not documented | Not documented | No | Fast regex-first scrubbing | | [Rehydra](https://getshadow.dev/compare/rehydra/) | SDK + local proxy | MIT | Reversible placeholders | Yes | Yes | Yes | TypeScript and browser apps | | **Shadow** | Proxy, gateway, Rust library | Apache-2.0 | Reversible realistic fakes; tokens for reports | Yes | Yes | Yes, intent-aware | Endpoint and service traffic to supported AI providers | ## 1. Presidio: the reference detector Presidio has the broadest recognizer set, a pluggable NLP engine (spaCy, Stanza, transformers), an image redactor with OCR, and a large ecosystem; many gateways use it as their PII backend. Its anonymizer is built for one-way transformations; only `encrypt` can be reversed, and there is no LLM-specific streaming logic. The repository now lives at `data-privacy-stack/presidio`, still MIT. **Choose it for** data pipelines and as a detector inside other tools. **Pair it with** Shadow if you need the round trip: Shadow can call Presidio's Analyzer as an extra detector. [Shadow vs Presidio](https://getshadow.dev/compare/presidio/). ## 2. OpenAI Privacy Filter: a strong free model Released in April 2026 under Apache-2.0, it is a token-classification model with eight labels (person, address, email, phone, URL, date, account number, secret). On Shadow's held-out benchmark it leaked 11.01% of sensitive values on its own, against 3.08% for Shadow's built-in rules. It is primarily English and is a detector only. **Shadow's optional NER sidecar can run it**, alone or in an ensemble with GLiNER. [Shadow vs OpenAI Privacy Filter](https://getshadow.dev/compare/openai-privacy-filter/). ## 3. GLiNER PII models: zero-shot and multilingual GLiNER models take entity labels at inference time, so you can ask for "passport number" or "IBAN" without retraining. `urchade/gliner_multi_pii-v1` is Apache-2.0 and multilingual; `nvidia/gliner-PII` uses the NVIDIA Open Model License. Published scores vary a lot by dataset. Shadow's sidecar uses the Apache-2.0 multilingual checkpoint. [Shadow vs GLiNER](https://getshadow.dev/compare/gliner-pii/). ## 4. LLM Guard: scanners with a reversible vault LLM Guard's Anonymize and Deanonymize scanners share a `Vault`, so placeholders can be restored. It also offers many non-PII scanners such as prompt-injection detection. Streaming is not documented, detection is English-focused, and one optional recognizer model is CC-BY-NC-4.0, so check licences before commercial use. [Shadow vs LLM Guard](https://getshadow.dev/compare/llm-guard/). ## 5. NeMo Guardrails: rails, not round trips NVIDIA's framework runs input, output and retrieval rails using Presidio or the GLiNER-PII NIM. It masks with labels or blocks, and does not restore. Excellent for conversational policy; less so when answers must contain the real values. [Shadow vs NeMo Guardrails](https://getshadow.dev/compare/nemo-guardrails/). ## 6. LangChain PIIMiddleware: native to LangChain agents It applies redact, mask, hash or block strategies to inputs, outputs and tool results inside a LangChain agent. It is convenient if you are already on LangChain, but it is not reversible. [Shadow vs LangChain PIIMiddleware](https://getshadow.dev/compare/langchain-pii-middleware/). ## 7. DataFog: lightweight scrubbing DataFog offers regex, spaCy and GLiNER engines, a LiteLLM guardrail and a Claude Code hook. Its modes are redact, replace and hash; reversibility is not documented. [Shadow vs DataFog](https://getshadow.dev/compare/datafog/). ## 8. Rehydra: the closest open-source analogue Rehydra is a TypeScript SDK and local proxy with regex plus ONNX NER, an AES-256-GCM session map, sentence-buffered streaming rehydration and tool-call argument rehydration. It uses XML-style placeholders rather than realistic fakes and is English and UK-centric. If you work in Node, Bun or the browser, it is a natural fit. [Shadow vs Rehydra](https://getshadow.dev/compare/rehydra/). ## 9. Shadow: reversible realistic fakes at the endpoint Shadow replaces values with realistic, format-preserving fakes from a persistent [persona](https://getshadow.dev/concepts/personas/), stores the mapping in an [encrypted local vault](https://getshadow.dev/concepts/vault/), and restores responses including [SSE streams and tool calls](https://getshadow.dev/concepts/tool-calls/). Its [intent policy](https://getshadow.dev/concepts/intent-policy/) switches to typed tokens for reports and handles provider-hosted tools explicitly. It masks [DOCX, XLSX and PPTX uploads in place](https://getshadow.dev/concepts/file-masking/). It runs as an [HTTPS proxy](https://getshadow.dev/integrate/proxy-mode/), a [gateway](https://getshadow.dev/integrate/gateway-mode/) or a [Rust library](https://getshadow.dev/integrate/rust/). **Where it is weaker:** no OCR, no Python API, multilingual recall still being measured, and detection that depends on regex, dictionaries and optional models. For the most recall, enable [Presidio and the NER sidecar](https://getshadow.dev/concepts/detection/). ## Recommended combinations | Situation | Suggested stack | |---|---| | Python data pipeline, one-way anonymisation | Presidio (+ GLiNER or Privacy Filter recognizers) | | Live LLM traffic, answers must stay useful | Shadow, with Presidio and/or the NER sidecar for recall | | TypeScript app or browser extension | Rehydra | | LangChain agent, values never needed back | LangChain PIIMiddleware | | Need many safety scanners in one Python library | LLM Guard | | Conversational policy and topical rails | NeMo Guardrails, with Shadow in front for the round trip | ## How accurate are they? Published numbers for the same tool vary by up to five times between datasets because of label mapping and annotation style, so headline F1 scores rarely transfer. The Shadow [benchmark](https://getshadow.dev/benchmarks/) runs Presidio, LLM Guard, GLiNER, OpenAI Privacy Filter and scrubadub side by side with Shadow on a licence-clean held-out set, and measures leak rate, over-masking, round-trip exactness and latency, with the mapping and code published. Leak rates there range from 0.08% (Shadow with the NER sidecar) and 3.08% (Shadow's built-in rules) to 20.74% for Presidio and 29.26% for LLM Guard. ## Sources - [Presidio](https://github.com/data-privacy-stack/presidio) - [OpenAI Privacy Filter model card](https://huggingface.co/openai/privacy-filter) - [urchade/gliner_multi_pii-v1](https://huggingface.co/urchade/gliner_multi_pii-v1), [nvidia/gliner-PII](https://huggingface.co/nvidia/gliner-PII) - [LLM Guard Anonymize](https://protectai.github.io/llm-guard/input_scanners/anonymize/) - [NeMo Guardrails PII detection](https://docs.nvidia.com/nemo/guardrails/latest/configure-guardrails/guardrail-catalog/pii-detection) - [LangChain PIIMiddleware](https://reference.langchain.com/python/langchain/agents/middleware/pii) - [DataFog](https://github.com/DataFog/datafog-python) - [Rehydra SDK](https://github.com/rehydra-ai/rehydra-sdk) --- *Last reviewed: 2026-10-31. All product names are trademarks of their respective owners; this guide is based on public documentation as of the review date.* ## Frequently asked questions ### What is the best open-source PII redaction tool for LLMs? There is no single best tool. Presidio is the most established detector library. OpenAI Privacy Filter and GLiNER PII models are strong free detection models. LLM Guard and Rehydra add reversible placeholders for LLM calls. Shadow adds realistic reversible fakes, streaming and tool-call restoration, file masking and a local proxy, and can use Presidio and the open models as detectors. ### Which open-source tools can restore PII in the LLM response? LLM Guard (Anonymize and Deanonymize with a shared vault), Rehydra (placeholders with an encrypted session map) and Shadow (realistic fakes with an encrypted vault) restore values. Presidio can reverse only its encrypt operator. NeMo Guardrails, LangChain PIIMiddleware and DataFog do not document restoration. ### Are open-source PII models licensed for commercial use? Check each checkpoint. OpenAI Privacy Filter and urchade/gliner_multi_pii-v1 are Apache-2.0. nvidia/gliner-PII uses the NVIDIA Open Model License. Some community models trained on older ai4privacy datasets carry non-commercial licences, including one optional LLM Guard recognizer that is CC-BY-NC-4.0. ### Do open-source PII tools support languages other than English? Some do. Presidio supports any language you have a spaCy, Stanza or transformer model for. GLiNER multi-PII is multilingual. OpenAI Privacy Filter and LLM Guard are primarily English. Shadow's multilingual recall is being measured on the benchmarks page. ### Is Shadow free? Yes. Shadow is Apache-2.0, self-hosted, with no account and no telemetry. --- # AI DLP tools compared: block vs redact vs pseudonymise Source: https://getshadow.dev/compare/ai-dlp-tools/ > AI DLP tools compared by what they do to sensitive data: block, redact or pseudonymise. Netskope, Zscaler, Purview, Nightfall, Harmonic and Shadow. **AI DLP tools differ most in what they do when they find sensitive data: block the request, redact the value, or replace it with a reversible pseudonym.** Enterprise GenAI DLP products from Netskope, Zscaler, Palo Alto, Microsoft, Nightfall and Harmonic mainly block, warn or coach employees using AI apps. Shadow takes the third approach for API, agent and file traffic: it sends realistic fakes and restores the real values in the response. ## Three ways to stop an AI data leak | Approach | What the model sees | User experience | Main risk | |---|---|---|---| | **Block** | Nothing; the request is refused | Task fails; user may be coached or asked to justify | People route around it (personal accounts, phones) | | **Redact** | `[REDACTED]`, `` or a masked value | Task runs with less information | Answers degrade; values can't be restored | | **Pseudonymise (reversible)** | A realistic stand-in: `hui.zhao@zhilan.cn` | Task runs normally; answer shows the real values | Depends on detection recall and vault security | None of these is always right. Blocking is correct for data that must never be processed by an external AI at all. Redaction suits logs and analytics. Pseudonymisation suits everyday work where the answer needs to mention the real customer, colleague or account. ## The main AI DLP tools | Tool | Where it runs | Block | Redact | Reversible | Streams | Files | Fleet management | |---|---|---|---|---|---|---|---| | [Netskope One](https://getshadow.dev/compare/netskope/) | SASE inline | Yes | Yes (mask, FPE, hash) | No | Redaction not for SSE | Not file content | Yes | | [Zscaler](https://getshadow.dev/compare/zscaler/) | SASE inline | Yes (also isolate) | Not documented | No | n/a | Uploads controlled | Yes | | [Palo Alto AI Access](https://getshadow.dev/compare/palo-alto-ai-access/) | SASE, Prisma Browser | Yes | Only in a community blog | No | n/a | Not documented | Yes | | [Microsoft Purview](https://getshadow.dev/compare/microsoft-purview/) | Microsoft 365, Edge, endpoint | Yes (warn, audit) | No | No | n/a | Cannot scan files uploaded into prompts | Yes | | [Nightfall](https://getshadow.dev/compare/nightfall/) | Browser extension, endpoint agent | Yes | Yes | No | n/a | Uploads | Yes | | [Harmonic Security](https://getshadow.dev/compare/harmonic-security/) | Browser extension | Yes (warn, redirect) | Vendor-stated | No | n/a | Uploads | Yes | | [Prompt Security](https://getshadow.dev/compare/prompt-security/) | Extension, gateway | Yes | Not verified | No | Not documented | Not documented | Yes | | [Lasso Security](https://getshadow.dev/compare/lasso-security/) | Extension, gateway | Alert | Mask | Not documented | Not documented | Not documented | Yes | | [Strac](https://getshadow.dev/compare/strac/) | Extension, SaaS | Yes | Yes | Not documented | Not documented | SaaS stores | Yes | | **Shadow** | Endpoint proxy, gateway, library | Optional (`block` for PDFs and images; secrets can be redacted) | Opt-in | **Yes** | **Yes** | DOCX/XLSX/PPTX in place, PDF to text | No central console | ## Where enterprise AI DLP is strongest - **Coverage of web chat.** Browser extensions and SASE inspection see what employees type into ChatGPT, Gemini, Claude and Copilot web apps, including on unmanaged AI sites. - **Visibility and shadow-AI discovery.** Which AI apps are in use, by whom, with what data. - **Central policy and reporting.** One policy across thousands of devices, with audit logs, SIEM integration and incident workflows. - **User coaching.** A pop-up that explains why a paste is risky changes behaviour over time. - **Existing investment.** If you already run Netskope, Zscaler, Palo Alto or Microsoft 365 E5, adding GenAI controls may cost little extra. Shadow does none of these. It has no console and no browser extension, and it does not see what people type into AI web apps such as chatgpt.com or claude.ai. ## Where blocking and redaction fall short - **API and agent traffic.** Developers, CI jobs and services call model APIs directly. Inline DLP built for browsers often does not inspect these well, and Netskope's documentation explicitly excludes SSE streaming responses and MCP from redaction. - **Agents need real values back.** If an agent's tool call contains a redacted address, the action fails. Shadow restores tool-call arguments on your machine before the tool runs. See [tool calls](https://getshadow.dev/concepts/tool-calls/). - **Files.** Several DLP products can block an upload but not transform it. Shadow masks [Office documents in place](https://getshadow.dev/concepts/file-masking/) so the model still gets the content. - **Answer quality.** Placeholders strip meaning. Realistic, format-preserving fakes from a consistent [persona](https://getshadow.dev/concepts/personas/) keep prompts coherent. ## How do I choose an AI DLP tool? 1. **Mostly employees in web chat, many managed devices?** Start with your SASE or Microsoft 365 vendor, or a browser-first tool like Nightfall or Harmonic. 2. **Mostly developers, agents and services calling model APIs?** Add a reversible layer like Shadow on those machines and containers. 3. **Both?** Run both. Exclude the AI API hosts that Shadow handles from your SASE TLS inspection, or chain Shadow's outbound traffic through your corporate proxy with `upstream_proxy`, so the two do not fight over TLS. 4. **Strict "never leaves" data classes?** Use blocking for those, in either tool. In Shadow, `[policy.always] secret = "redact"` and `[files] pdf = "block"` give hard stops. ## Is Shadow a free alternative to enterprise AI DLP? For API, agent, developer and file traffic on machines you control, Shadow is a free, open-source way to replace detected sensitive values before they reach AI providers while keeping answers useful. It is not a replacement for fleet-wide browser DLP, central reporting or user coaching. See the [overview](https://getshadow.dev/compare/) for the full landscape. ## Sources - [Netskope: redact sensitive data](https://docs.netskope.com/en/redact-sensitive-data/) - [Zscaler: making generative AI safe](https://www.zscaler.com/blogs/product-insights/make-generative-ai-tools-chatgpt-safe-and-secure-zscaler) - [Prisma Browser: GenAI prompt data controls](https://docs.paloaltonetworks.com/prisma-browser/administration/manage-prisma-browser-policy-profiles/configure-prisma-browser-data-controls/configure-data-leak-prevention/genai-prompt) - [Nightfall DLP for browsers](https://addons.mozilla.org/addon/nightfall-dlp-for-browsers/) - [Harmonic Security: browser-based GenAI security](https://www.harmonic.security/solutions/browser-based-genai-security) - [Lasso Security: contextual data protection](https://www.lasso.security/press/lasso-security-launches-contextual-data-protection-tool-for-genai-applications) - [Strac: ChatGPT DLP](https://strac.io/integration/chatgpt-dlp) --- *Last reviewed: 2026-10-31. All product names are trademarks of their respective owners; this guide is based on public documentation as of the review date.* ## Frequently asked questions ### What is AI DLP? AI data loss prevention (AI DLP or GenAI DLP) stops sensitive data such as personal data, customer records, source code and secrets from reaching AI tools and model providers. Products do this by blocking the request, redacting the sensitive values, or replacing them with reversible pseudonyms. ### Should an AI DLP tool block or redact? Blocking is the simplest and safest for clearly prohibited data, but it breaks the task and encourages workarounds. Redaction lets the request through without the value but can make answers worse. Reversible pseudonymisation keeps answers useful by sending realistic stand-ins and restoring the real values in the response. ### Which AI DLP tools can redact rather than block? Netskope documents inline redaction with mask, format-preserving encryption or hash, excluding streamed responses, MCP and file content. Nightfall and Strac redact in their browser extensions. Harmonic and Lasso describe masking or redaction in vendor material. Shadow pseudonymises reversibly. ### Is there an open-source AI DLP tool? Shadow is an open-source (Apache-2.0) privacy layer for AI API traffic. It is not a full DLP suite: it has no central console, coaching or SIEM export. Presidio, LLM Guard and LiteLLM are open-source building blocks. ### Does AI DLP make us compliant? No tool makes an organisation compliant on its own. AI DLP helps meet requirements such as data minimisation and confidentiality, as part of a wider programme. --- # LLM gateways with PII masking compared (2026) Source: https://getshadow.dev/compare/llm-gateways-with-pii-masking/ > Which LLM and AI gateways mask PII, and which restore it? LiteLLM, Portkey, Kong, Cloudflare, Bedrock Guardrails and Shadow compared. **Most LLM gateways can redact or block PII, but few restore it in the response, and fewer handle streaming and tool calls.** LiteLLM and Kong can restore placeholders or synthetic values; Portkey, Bedrock Guardrails and Cloudflare are one way. Shadow is not a routing gateway, but it sits in the same position for privacy: it masks with realistic fakes and restores responses, streams and tool calls, and it works alongside any gateway. ## What an AI gateway does, and where PII fits AI gateways centralise model access: one endpoint for many providers, API keys, rate limits, budgets, caching, fallbacks, logging and guardrails. PII handling is usually one guardrail among many, often delegated to Presidio or a cloud API. That matters because the gateway is typically a **shared service**. Prompts travel in clear text from every client to the gateway, and the gateway's logs may hold them. Masking at the gateway protects the provider hop, not the hop before it. ## Comparison | Gateway | Licence | PII action | Restores | Streaming restore | Tool calls | Self-hosted | |---|---|---|---|---|---|---| | [LiteLLM](https://getshadow.dev/compare/litellm/) | MIT core; enterprise features paid | Block or redact via Presidio, Lakera, Bedrock and others | Placeholders (`output_parse_pii`) | Reported gaps on some paths ([#22821](https://github.com/BerriAI/litellm/issues/22821)) | No | Yes | | [Portkey](https://getshadow.dev/compare/portkey/) | Gateway MIT; guardrails in cloud | Redact via partner guardrails | No (docs: one way) | Not documented | No | Gateway only | | [Kong AI Gateway](https://getshadow.dev/compare/kong-ai-gateway/) | `ai-sanitizer` is Enterprise-only | Placeholder or synthetic | Yes (`restore`) | Not documented | No | Yes | | [Cloudflare AI Gateway](https://getshadow.dev/compare/cloudflare-ai-gateway/) | Proprietary | Flag or block | No | Buffers response to scan | No | No | | [Bedrock Guardrails](https://getshadow.dev/compare/aws-comprehend-bedrock-guardrails/) | Proprietary | Block or `{NAME}` tags | No | Not verified | No | No | | Agent Router (formerly Envoy AI Gateway) | Apache-2.0 | Guardrail hooks only in OSS; PII in Tetrate's enterprise offering | n/a | n/a | MCP routing | Yes | | **Shadow** | Apache-2.0 | Realistic reversible fakes; tokens for reports; optional redact | Yes | Yes | Yes, intent-aware | Yes | ## Gateway by gateway ### LiteLLM The most popular open-source gateway. Its Presidio guardrail masks prompts and, with `output_parse_pii`, restores placeholders. An issue reported that unmasking was missing on Anthropic's native path and in SSE streaming; it was closed in June 2026, and the status of the replacement fixes is not verified. Great for routing and budgets. [Shadow vs LiteLLM](https://getshadow.dev/compare/litellm/). ### Portkey A popular gateway with an MIT core. PII redaction comes from partner guardrails (Pangea, Bedrock, Azure, Patronus), and Portkey's own docs describe the transformation as one way. Palo Alto Networks announced its acquisition in April 2026. [Shadow vs Portkey](https://getshadow.dev/compare/portkey/). ### Kong AI Gateway Kong's Enterprise `ai-sanitizer` plugin is the closest commercial analogue to Shadow: it can replace PII with placeholders or synthetic values and restore them. It requires Gateway 3.10 or later (3.12 for response sanitization) and a private PII service image supporting nine languages. Streaming behaviour is not documented. Kong also offers a Skyflow plugin for vault-backed tokens. [Shadow vs Kong](https://getshadow.dev/compare/kong-ai-gateway/). ### Cloudflare AI Gateway Cloudflare's DLP for AI Gateway can pass, flag or block based on Cloudflare One DLP profiles; it does not redact. Scanning responses buffers the stream, which adds time to first token. Ideal if you already use Cloudflare and want visibility and blocking. [Shadow vs Cloudflare](https://getshadow.dev/compare/cloudflare-ai-gateway/). ### Amazon Bedrock Guardrails Native to Bedrock: block or anonymise PII into type tags, plus custom regex. No restoration. Note that Shadow forwards SigV4-signed Bedrock requests unmasked because changing the body would break the signature, so Guardrails remain the right control for IAM-signed Bedrock traffic. [Shadow vs AWS](https://getshadow.dev/compare/aws-comprehend-bedrock-guardrails/). ## How does Shadow fit with a gateway? Shadow is not a router: it has no budgets, caching, fallbacks or multi-tenant key management. It is a privacy layer that can sit **in front of** a gateway on each client, or **between** the gateway and providers. | Pattern | How | Benefit | |---|---|---| | Shadow on clients, gateway central | Clients use Shadow's [proxy](https://getshadow.dev/integrate/proxy-mode/) or [gateway mode](https://getshadow.dev/integrate/gateway-mode/) with a custom route to your gateway | Detected values are masked before they leave the endpoint, so gateway logs hold fakes instead | | Shadow as a sidecar next to the gateway | Gateway's upstream points at Shadow's `/gw//` routes in the same pod | One masking point for all services; see [Docker and Kubernetes](https://getshadow.dev/integrate/docker-kubernetes/) | | Shadow inside your service | Embed `shadow-core` in a [Rust service](https://getshadow.dev/integrate/rust/) | No extra hop | In the first pattern, the gateway's own PII guardrail becomes a second line of defence. ## How to choose - **You need routing, budgets and observability first:** pick a gateway (LiteLLM or Kong self-hosted, Portkey or Cloudflare managed), then decide on PII. - **You need answers that mention the real values, including streams and agents:** add Shadow, or use Kong's `ai-sanitizer` if you are on Kong Enterprise and do not need streaming guarantees. - **You only need to block known-bad data:** Cloudflare DLP or Bedrock Guardrails may be enough. Detection accuracy and round-trip exactness are measured on the [benchmarks page](https://getshadow.dev/benchmarks/): on the held-out set, Shadow's built-in rules leaked 3.08% of sensitive values and round-tripped 100% of documents exactly. The gateways themselves are not benchmarked there. ## Sources - [LiteLLM Presidio PII masking tutorial](https://docs.litellm.ai/docs/tutorials/presidio_pii_masking) - [Portkey PII redaction](https://portkey.ai/docs/product/guardrails/pii-redaction) - [Kong ai-sanitizer plugin](https://developer.konghq.com/plugins/ai-sanitizer/) - [Cloudflare AI Gateway DLP](https://developers.cloudflare.com/ai-gateway/features/dlp/) - [Amazon Bedrock Guardrails components](https://docs.aws.amazon.com/bedrock/latest/userguide/guardrails-components.html) - [Agent Router](https://github.com/theagentrouter/agent-router) --- *Last reviewed: 2026-10-31. All product names are trademarks of their respective owners; this guide is based on public documentation as of the review date.* ## Frequently asked questions ### Which AI gateways can mask PII in prompts? LiteLLM (via Presidio, Lakera, Bedrock and other guardrails), Portkey (via partner guardrails), Kong AI Gateway (ai-sanitizer, Enterprise), Amazon Bedrock Guardrails (anonymize) and Shadow mask PII. Cloudflare AI Gateway DLP flags or blocks rather than redacts. ### Which gateways restore PII in the response? Kong's ai-sanitizer has a restore option, LiteLLM's Presidio guardrail can restore placeholders with output_parse_pii, and Shadow restores realistic fakes in JSON, streams and tool calls. Portkey's docs describe its PII redaction as one way, and Bedrock Guardrails and Cloudflare do not restore. ### Do gateway PII features work with streaming? It varies. Cloudflare buffers the full response to scan it. A LiteLLM issue reported missing unmasking on Anthropic's native streaming path. Kong and Portkey do not document streaming behaviour for PII. Shadow restores SSE and NDJSON streams with a hold-back of a few characters. ### Can I use Shadow with my existing LLM gateway? Yes. Run Shadow on the client or as a sidecar so traffic is masked before it reaches the gateway, or point the gateway's upstream at Shadow's /gw/ routes. Keep the gateway for routing, keys, budgets and observability. ### Is there a free open-source gateway with reversible PII masking? LiteLLM's open-source proxy with the Presidio guardrail restores placeholders. Shadow is Apache-2.0 and restores realistic fakes, but it is a privacy layer rather than a full routing gateway. --- # Privacy vaults for LLMs: Skyflow, Protecto, Private AI and alternatives Source: https://getshadow.dev/compare/privacy-vaults-for-llms/ > Privacy vaults for LLMs compared: Skyflow, Protecto, Private AI, Tonic Textual, Wald.ai and Shadow's local encrypted vault. Tokens, fakes and re-identification. **A privacy vault for LLMs swaps sensitive values for tokens or pseudonyms before a prompt reaches the model, and re-identifies them in the response.** Skyflow, Protecto, Private AI, Tonic Textual and Wald.ai offer this as commercial platforms or containers. Shadow offers it as open-source software with a local encrypted vault and realistic, format-preserving fakes instead of opaque tokens. ## Why a vault and not plain redaction? Redaction throws information away. A vault keeps a mapping, so the model works on stand-ins and your users still see real values in the answer. The design questions are: - **Where does the mapping live?** In a vendor's cloud, in your VPC, or on each endpoint? - **What does the model see?** Opaque tokens, labelled placeholders, or realistic fakes? - **Who can re-identify?** Policy-governed per user, or whoever runs the process? - **What else does it cover?** Files, databases, analytics, non-AI systems? ## Comparison | Product | Deployment | Licence | What the model sees | Re-identification | Streaming | Files | Price model | |---|---|---|---|---|---|---|---| | [Skyflow](https://getshadow.dev/compare/skyflow/) | Vault SaaS; Kong plugin | Proprietary | Vault tokens | Policy-governed | Not documented | Images, PDF, audio, Office (Detect) | Quote | | [Protecto](https://getshadow.dev/compare/protecto/) | SaaS or VPC | Proprietary | Consistent "semantic" tokens; FPE for databases | Unmask API under policy | Not documented | Not documented | Quote | | [Private AI](https://getshadow.dev/compare/private-ai/) | Self-hosted container or cloud | Proprietary | Placeholders, masks or synthetic values | Re-identify (PrivateGPT Headless) | Not documented | Many types (vendor) | Quote; limited free tier (third-party report) | | [Tonic Textual](https://getshadow.dev/compare/tonic-textual/) | SaaS or self-hosted | Proprietary | Redaction or consistent synthesis | Not as a live proxy | No | Files in original format | Pay-as-you-go or quote | | [Wald.ai](https://getshadow.dev/compare/wald-ai/) | SaaS chat and API | Proprietary | Contextual substitution | Yes | Not documented | Not documented | Per user | | **Shadow** | Endpoint proxy, gateway, Rust library | Apache-2.0 | Realistic, format-preserving fakes; typed tokens for reports | Automatic for the local process | Yes (SSE, NDJSON) | DOCX/XLSX/PPTX in place, PDF to text | Free | ## The vendors in brief ### Skyflow A data privacy vault used well beyond AI: payments, PII storage and analytics, with an LLM Privacy Vault and Detect APIs for files across images, PDF, audio and Office formats. Re-identification is governed by fine-grained policy. Strong if you want one vault for all systems. [Shadow vs Skyflow](https://getshadow.dev/compare/skyflow/). ### Protecto Consistent tokens across systems, an unmask API under policy, and format-preserving encryption for databases; available as SaaS or in your VPC. Protecto publishes a vendor-run benchmark white paper against Presidio and Comprehend. [Shadow vs Protecto](https://getshadow.dev/compare/protecto/). ### Private AI A self-hosted container with broad multilingual detection (the vendor cites 50+ entity types in 52 languages), placeholders, masks or synthetic values, and re-identification via PrivateGPT Headless. The strongest choice on documented language coverage. [Shadow vs Private AI](https://getshadow.dev/compare/private-ai/). ### Tonic Textual Primarily batch de-identification and synthesis for datasets, with consistent synthetic replacement and files redacted in their original format. Tonic also publishes PrivacyBench, which scores replacement quality. A better fit for training and analytics data than for live traffic. [Shadow vs Tonic Textual](https://getshadow.dev/compare/tonic-textual/). ### Wald.ai A hosted secure-AI chat and API that substitutes sensitive values contextually and restores them, with a consistent mapping across a conversation. Conceptually close to Shadow, but cloud-hosted. [Shadow vs Wald.ai](https://getshadow.dev/compare/wald-ai/). ## Shadow's local vault Shadow's [vault](https://getshadow.dev/concepts/vault/) is a ChaCha20-Poly1305 encrypted file on the machine that runs Shadow, with atomic writes and safe concurrent use by several processes. Each value maps to a realistic fake from a persistent [persona](https://getshadow.dev/concepts/personas/): the same customer gets the same fake name tomorrow, and no two real values share a fake. Restoration covers JSON responses, [streams and tool calls](https://getshadow.dev/concepts/tool-calls/). The [intent policy](https://getshadow.dev/concepts/intent-policy/) switches to typed tokens like `[PERSON_0007]` for bulk reports. **What it does not have:** per-user re-identification policy, a central shared vault service, database or analytics integrations, OCR, or an admin console. Anyone who can read the data directory can decrypt the mapping, so protect it like any credential. See the [security model](https://getshadow.dev/reference/security/). ## How to choose | If you need... | Consider | |---|---| | One governed vault for AI, payments and analytics | Skyflow | | Consistent tokens across many data systems, in your VPC | Protecto | | Broad multilingual detection in a self-hosted container | Private AI | | De-identified datasets for training or analytics | Tonic Textual | | A hosted secure chat for non-technical staff | Wald.ai | | Free, open-source, local masking of API, agent and file traffic with streaming and tool-call restoration | Shadow | These can coexist. For example, Tonic Textual for training data and Shadow for live developer and agent traffic. ## Accuracy Vendors in this category mostly publish their own benchmarks; treat those as vendor-run. The Shadow [benchmark](https://getshadow.dev/benchmarks/) measures leak rate, over-masking and round-trip exactness with published code; the commercial vaults above are not included. On its held-out set, Shadow's built-in rules leaked 3.08% of sensitive values and round-tripped 100% of documents exactly. ## Sources - [Skyflow Detect SDK sample](https://raw.githubusercontent.com/skyflowapi/skyflow-node-sdk/HEAD/samples/detect-api/get-detect-run.ts) - [Protecto: technology](https://protecto.ai/tech-behind-protecto/) - [Private AI: PrivateGPT Headless](https://docs.private-ai.com/pgpt-headless) - [Tonic Textual benchmark](https://www.tonic.ai/ai-model-benchmarks/textual-benchmark) and [PrivacyBench](https://www.tonic.ai/ai-model-benchmarks/privacy-bench) - [Wald.ai data security for AI](https://wald.ai/datasecurity-for-ai) --- *Last reviewed: 2026-10-31. All product names are trademarks of their respective owners; this guide is based on public documentation as of the review date.* ## Frequently asked questions ### What is a privacy vault for LLMs? A privacy vault stores sensitive values and gives the rest of the system tokens or pseudonyms instead. For LLMs, the vault replaces personal data in prompts before they reach the model and re-identifies the values in responses for authorised users. ### Do I need a hosted privacy vault? A hosted vault suits organisations that want central policy, shared tokens across many systems and a managed service. If you only need to protect AI traffic from specific machines or services, a local vault such as Shadow's keeps the mapping on your own infrastructure with no extra service. ### What is the difference between vault tokens and realistic fakes? Vault tokens are opaque or semi-opaque strings that clearly are not real data. Realistic fakes look like real data of the same type, such as a valid email or a Luhn-valid card number. Fakes keep prompts natural for the model; tokens make it obvious which values are protected. Shadow uses fakes by default and typed tokens for reports. ### Is there an open-source privacy vault for LLMs? Shadow is open source (Apache-2.0) and keeps its mapping in a ChaCha20-Poly1305 encrypted vault on the local machine. LLM Guard and Rehydra also keep reversible mappings. Skyflow, Protecto, Private AI, Tonic Textual and Wald.ai are commercial. ### Can a privacy vault be shared across machines? Hosted vaults are shared by design. Shadow's vault can be shared by copying persona.json, vault.bin and vault.key, or by pointing several processes at one data directory. --- # Shadow vs AWS Comprehend and Bedrock Guardrails — a self-hosted alternative Source: https://getshadow.dev/compare/aws-comprehend-bedrock-guardrails/ > Shadow vs AWS Comprehend PII and Amazon Bedrock Guardrails. AWS detects, blocks or tags PII; Shadow masks with reversible fakes and restores answers. **AWS offers two PII services relevant to LLMs: Amazon Comprehend detects and redacts PII in text, and Amazon Bedrock Guardrails can block or anonymise PII in prompts and responses for Bedrock models. Shadow is an open-source privacy layer that replaces sensitive values with realistic, reversible fakes before a request reaches any provider, and restores them in the answer.** If you run everything on Bedrock and want managed guardrails, AWS is the natural fit. If you call several providers, need the real values back in responses, or want data masked before it leaves your machine, Shadow covers that. :::note[TL;DR] Comprehend is a cloud PII detector; Bedrock Guardrails blocks or replaces PII with type tags like `{NAME}` and does not restore them. Shadow masks with consistent fakes on your own machine and restores values in responses, streams and tool calls, across many providers. Bedrock Guardrails also handles content safety, which Shadow does not. ::: ## At a glance | | Shadow | AWS Comprehend / Bedrock Guardrails | |---|---|---| | **Open source / licence** | Yes, Apache-2.0 | No, proprietary AWS services | | **Deployment** | Endpoint HTTPS proxy, gateway (`/gw//`), Rust library | AWS-managed APIs; Guardrails attach to Bedrock calls or the `ApplyGuardrail` API | | **Protection method** | Reversible realistic fakes; typed tokens for reports; optional redaction | Comprehend: detect and redact. Guardrails: BLOCK or ANONYMIZE with type tags, plus custom regex | | **Restores responses** | Yes, automatically | No | | **Streaming** | Yes, SSE and NDJSON | Guardrails streaming behaviour not verified here; Comprehend is not a stream processor | | **Tool calls** | Yes, restored locally; intent-aware | No PII-specific tool-call handling documented | | **Files** | DOCX, XLSX, PPTX in place; PDF to text | Comprehend async batch jobs on documents in S3 | | **Self-hosted** | Yes | No | | **Price model** | Free | Usage-based (per text unit) | ## What AWS does well - **Managed and integrated.** No servers to run. Guardrails attach directly to Bedrock model invocations and agents, with IAM, CloudWatch and CloudTrail around them. - **More than PII.** Bedrock Guardrails also offers content filters, denied topics, word filters and contextual grounding checks. Shadow only handles sensitive values. - **Strong generic detection.** In Tonic's vendor-run benchmark (October 2025), Comprehend scored highest of the generic detectors tested, ahead of several open-source options. Treat vendor benchmarks with care, but it is a strong signal. - **Batch processing.** Comprehend runs asynchronous PII jobs over documents stored in S3, which suits dataset preparation. - **Custom patterns.** Guardrails accept custom regex for organisation-specific identifiers; AWS documents regex filters as not charged separately (per a third-party pricing guide). ## Where Shadow is different ### Does Bedrock Guardrails give the real values back? No. ANONYMIZE replaces values with type tags, so a prompt about Jane Doe reaches the model as `{NAME}`, and the answer mentions `{NAME}` too. That is safe, but the user gets a less useful answer and has to fill in the blanks. Shadow sends `Hui Zhao` instead, from a persistent [persona](https://getshadow.dev/concepts/personas/), and turns it back into `Jane Doe` before your app sees the response. ### Realistic fakes keep the model working A Shadow fake email is a valid email at a consistent fake domain; a fake card number passes Luhn; an IBAN passes mod-97. The model reasons about a coherent fake world. Type tags lose the distinction between two people of the same type and break tasks like drafting an email or filling a table. ### Works with many providers, not only Bedrock Shadow masks traffic to OpenAI, Anthropic, Gemini, Azure OpenAI, Mistral, OpenRouter and more, as a [proxy](https://getshadow.dev/integrate/proxy-mode/) or [gateway](https://getshadow.dev/integrate/gateway-mode/). Bedrock Guardrails protect Bedrock calls (and text you pass to `ApplyGuardrail`). ### Data never reaches a third-party detector With Comprehend, the plaintext you want to protect is sent to an AWS API to be scanned. Shadow detects on the machine that sends the request. For organisations that already trust AWS with the data, this matters less. ### Streams, tool calls and intent Shadow restores fakes split across SSE events and inside streamed tool-call arguments, and its [intent policy](https://getshadow.dev/concepts/intent-policy/) chooses fakes, typed tokens like `[PERSON_0007]`, or keeps real the recipient of a message and what a provider-hosted action needs (each keep is logged). See [tool calls](https://getshadow.dev/concepts/tool-calls/). ### Files in prompts Shadow masks DOCX, XLSX and PPTX uploads in place, and base64 or multipart files in requests. See [file masking](https://getshadow.dev/concepts/file-masking/). ## A limitation to know about: SigV4 Shadow cannot mask Bedrock requests signed with AWS SigV4, because changing the body breaks the signature. It forwards them unmasked with a warning. Bedrock calls authenticated with bearer API keys are masked normally. If your Bedrock usage is IAM-signed, Guardrails are the practical option for that traffic. ## When should you choose AWS? - Your AI workloads run on Bedrock and you want a managed, IAM-governed guardrail. - You also need content filters, denied topics or grounding checks. - You need batch PII detection on documents in S3. - Your requests are SigV4-signed. - Placeholder output is acceptable for your use case. ## When should you choose Shadow? - You call several model providers, or providers outside AWS. - You need the real values back in responses, streams and agent tool calls. - You want sensitive data masked before it leaves the machine, not scanned by a cloud API. - You want a free, open-source tool you can audit and run anywhere. ## Can you use them together? Yes. Shadow can mask requests to Bedrock (with API keys) before they arrive, and a Bedrock Guardrail can still apply content filters and denied topics to the masked request. For SigV4-signed traffic, keep Guardrails as the PII control. ## Migrating from Bedrock Guardrails PII filters 1. Keep your guardrail for content filters and topics; consider turning off PII ANONYMIZE once Shadow masks the traffic, so the model sees fakes rather than tags. 2. Switch Bedrock clients to API-key authentication, or add a [custom route](https://getshadow.dev/integrate/gateway-mode/#custom-routes) for your Bedrock endpoint. 3. Port custom regex patterns to Shadow [dictionary terms](https://getshadow.dev/concepts/detection/#dictionary-terms) or a custom detector. 4. Run `shadow scan` on representative prompts before switching traffic. ## How do they compare on accuracy? Comprehend performed well in Tonic's vendor-run benchmark, and the language support is ambiguous in AWS's own documentation (the API reference lists 12 language codes while other AWS pages say English). Shadow's [benchmark](https://getshadow.dev/benchmarks/) measures leak rate, over-masking and round-trip exactness with published code and mappings; commercial cloud APIs such as Comprehend are not included, because their terms and pricing make public, reproducible runs impractical. ## Sources - [Amazon Comprehend DetectPiiEntities API reference](https://docs.aws.amazon.com/comprehend/latest/APIReference/API_DetectPiiEntities.html) - [Amazon Bedrock Guardrails components](https://docs.aws.amazon.com/bedrock/latest/userguide/guardrails-components.html) - [Amazon Comprehend pricing](https://aws.amazon.com/comprehend/pricing/) - [Amazon Bedrock pricing](https://aws.amazon.com/bedrock/pricing/) - [Tonic Textual benchmark (vendor-run)](https://www.tonic.ai/ai-model-benchmarks/textual-benchmark) - [Shadow supported providers](https://getshadow.dev/reference/providers/) --- *Last reviewed: 2026-10-31. AWS, Amazon Comprehend and Amazon Bedrock are trademarks of their owner; this comparison is based on public documentation as of the review date.* ## Frequently asked questions ### Is Shadow an alternative to Amazon Bedrock Guardrails for PII? For the PII part, yes. Bedrock Guardrails can block a request or anonymise PII with type tags such as {NAME}, and does not restore the original values. Shadow replaces values with realistic, reversible fakes and restores them in the response. Bedrock Guardrails also covers content filters, denied topics and grounding checks, which Shadow does not. ### Can Bedrock Guardrails restore masked PII in the model's response? Public AWS documentation describes BLOCK and ANONYMIZE actions for sensitive information filters. It does not describe restoring anonymised values in the response, so the user sees tags like {NAME} rather than the real name. ### Does Shadow work with Amazon Bedrock? Yes when Bedrock is called with bearer API keys. Requests signed with AWS SigV4 (IAM) cannot be modified without breaking the signature, so Shadow forwards them unmasked with a warning. ### How much does AWS Comprehend PII detection cost? Comprehend is billed per 100-character unit with a minimum per request; a third-party pricing guide lists about $0.0001 per unit. Check the AWS pricing page for current rates. Shadow is free and runs locally. ### Which is more accurate, AWS Comprehend or Shadow? Comprehend scored well in a vendor-run benchmark by Tonic. Results depend heavily on the dataset and label mapping. Comprehend is not in the Shadow benchmark, because commercial cloud APIs are impractical to run publicly and reproducibly. --- # Shadow vs Azure AI Language PII — open-source Azure PII alternative for LLMs Source: https://getshadow.dev/compare/azure-ai-language-pii/ > Shadow vs Azure AI Language PII detection. Azure masks or synthesises PII one way; Shadow uses reversible fakes and restores LLM responses locally. **Azure AI Language PII detection is a managed Microsoft API that finds personal data in text and returns a redacted version. Shadow is an open-source privacy layer that replaces sensitive values in AI requests with realistic, reversible fakes on your own machine, then restores them in the model's answer.** Azure suits teams that want a managed, multilingual detector inside Azure. Shadow suits teams that want prompts to any provider masked and answers restored without building that pipeline. :::note[TL;DR] Azure detects PII and redacts it one way, with masks, entity labels or (in preview) synthetic values. Shadow detects locally, uses consistent reversible fakes from a persona, and restores them in responses, streams and tool calls. Use Azure for managed batch redaction; use Shadow in front of live LLM traffic. ::: ## At a glance | | Shadow | Azure AI Language PII | |---|---|---| | **Open source / licence** | Yes, Apache-2.0 | No, proprietary Azure service | | **Deployment** | Endpoint HTTPS proxy, gateway (`/gw//`), Rust library | Azure cloud API; container availability not verified here | | **Protection method** | Reversible realistic fakes; typed tokens for reports; optional redaction | `characterMask`, `entityMask`, `syntheticReplacement` (preview) | | **Restores responses** | Yes, automatically | No | | **Streaming** | Yes, SSE and NDJSON | No, request/response API | | **Tool calls** | Yes, restored locally; intent-aware | No | | **Files** | DOCX, XLSX, PPTX in place; PDF to text | Document PII API | | **Self-hosted** | Yes | Not verified (Azure documents containers for some Language features) | | **Price model** | Free | Usage-based, per text record | ## What Azure AI Language PII does well - **Managed and multilingual.** No models to host, many entity categories and languages, and Microsoft-run updates. - **Several redaction styles.** Character masking (`****`), entity masking (`[PERSON]`) and, since the `2025-11-15-preview` API version, synthetic replacement with realistic values, which is closer to how Shadow works. - **Document support.** A document PII API handles whole files, which suits batch redaction workflows. - **Azure governance.** It fits into Azure identity, networking, monitoring and regional data residency choices that many enterprises already use. - **Conversation PII.** Azure also offers PII detection for conversation transcripts, useful in contact-centre scenarios. ## Where Shadow is different ### Can Azure put the real values back in the answer? No re-identification is documented for the redaction policies. If you send a redacted prompt to a model, the answer refers to `[PERSON]` or a synthetic name, and connecting it back to the original is your job. Shadow keeps an [encrypted vault](https://getshadow.dev/concepts/vault/) of real-to-fake pairs and restores them in JSON, [streamed responses](https://getshadow.dev/concepts/how-it-works/#streaming-hold-back) and [tool-call arguments](https://getshadow.dev/concepts/tool-calls/). ### Consistent fakes across requests Synthetic replacement in a one-off API call is not documented as consistent between calls. Shadow's [persona](https://getshadow.dev/concepts/personas/) gives the same customer the same fake name today and next week, so long conversations and agents stay coherent. Fakes are format-preserving: emails stay emails at a consistent domain, cards pass Luhn, IBANs pass mod-97. ### Detection happens on your machine With Azure, the plaintext is sent to an Azure endpoint to be scanned. Shadow detects locally before the request leaves. If Azure is already your trusted processor, this difference matters less. ### One step, not two round trips A pipeline built on Azure PII calls the Language API, then the model, then your own restore code. Shadow is a single local hop as a [proxy](https://getshadow.dev/integrate/proxy-mode/) or [gateway](https://getshadow.dev/integrate/gateway-mode/), and adds an [intent policy](https://getshadow.dev/concepts/intent-policy/) that picks fakes, typed tokens or keep per value. ### Office files in uploads Shadow masks DOCX, XLSX and PPTX attachments in place inside requests, plus base64 and multipart uploads. See [file masking](https://getshadow.dev/concepts/file-masking/). ## When should you choose Azure AI Language PII? - You want a managed, multilingual detector inside Azure. - You redact documents or transcripts in batch and don't need the originals back. - You need Azure's compliance attestations and regional hosting for the detector itself. - Your team prefers to buy rather than run software. ## When should you choose Shadow? - You want live LLM traffic masked and responses restored automatically. - You use streaming, agents or tool calls. - You want consistent fakes across sessions. - You want detection to happen locally, with a free, open-source tool. ## Can you use them together? Shadow doesn't call Azure AI Language as a detector out of the box. You can implement Shadow's `Detector` trait in Rust to call any detection API ([custom detectors](https://getshadow.dev/integrate/rust/#custom-detectors)), but sending plaintext to a cloud detector undoes part of the point of local masking. A more common split: Azure PII for batch redaction of stored documents, Shadow for live traffic to Azure OpenAI and other providers. ## Migrating from an Azure PII pipeline in front of an LLM 1. Route your model client through Shadow, by base URL ([gateway mode](https://getshadow.dev/integrate/gateway-mode/)) or `HTTPS_PROXY` ([proxy mode](https://getshadow.dev/integrate/proxy-mode/)). Add Azure OpenAI deployments under `[proxy.routes]`. 2. Remove the pre-call redaction and any post-call substitution code. 3. Add organisation names, customers and codenames as [dictionary terms](https://getshadow.dev/concepts/detection/#dictionary-terms). 4. For extra recall on names, enable the [NER sidecar or Presidio](https://getshadow.dev/concepts/detection/). 5. Compare with `shadow scan` on representative content before switching. ## How do they compare on accuracy? Tonic's vendor-run benchmark (October 2025) includes Azure. Numbers swing widely with datasets and label mapping, so treat any single figure with care. Shadow's [benchmark](https://getshadow.dev/benchmarks/) publishes its code and mappings; commercial cloud APIs such as Azure are not included, because their terms and pricing make public, reproducible runs impractical. ## Sources - [Azure AI Language: redact text PII](https://docs.azure.cn/en-us/ai-services/language-service/personally-identifiable-information/how-to/redact-text-pii) - [Azure AI Language PII overview](https://learn.microsoft.com/azure/ai-services/language-service/personally-identifiable-information/overview) - [Azure AI Language pricing](https://azure.microsoft.com/pricing/details/cognitive-services/language-service/) - [Tonic Textual benchmark (vendor-run)](https://www.tonic.ai/ai-model-benchmarks/textual-benchmark) --- *Last reviewed: 2026-10-31. Azure and Microsoft are trademarks of their owner; this comparison is based on public documentation as of the review date.* ## Frequently asked questions ### Is Shadow an alternative to Azure AI Language PII detection? For LLM traffic, yes. Azure AI Language detects PII and returns redacted text using a character mask, an entity mask or synthetic replacement (preview). It does not map values back. Shadow masks prompts with reversible fakes and restores the real values in responses. ### Can Azure PII redaction be reversed? Public Azure documentation does not describe re-identification for the PII redaction policies. characterMask, entityMask and syntheticReplacement are one-way. If you need the originals back, you keep your own mapping. ### Does Azure syntheticReplacement work like Shadow's fakes? Both produce realistic-looking values instead of placeholders. Azure's synthetic replacement is a preview feature and is not documented as reversible or consistent across requests. Shadow's fakes come from a persistent persona, stay consistent across sessions and are restored in the response. ### Does Shadow work with Azure OpenAI? Yes. Shadow intercepts *.openai.azure.com and Azure AI services hosts in proxy mode, and you can add your Azure endpoint as a custom gateway route. ### Which is more accurate, Azure PII or Shadow? Azure AI Language is not in the Shadow benchmark, because commercial cloud APIs are impractical to run publicly and reproducibly. Vendor-run benchmarks such as Tonic's include it, but results vary with dataset and label mapping. --- # Shadow vs Cloudflare AI Gateway DLP — alternative that masks instead of blocking Source: https://getshadow.dev/compare/cloudflare-ai-gateway/ > Shadow vs Cloudflare AI Gateway DLP. Cloudflare flags or blocks prompts with sensitive data; Shadow masks with reversible fakes and restores answers. **Cloudflare AI Gateway is a hosted gateway that sits between your application and model providers, adding caching, rate limiting, analytics and logging, plus a DLP feature that can flag or block prompts and responses containing sensitive data. Shadow is an open-source privacy layer that replaces sensitive values with realistic, reversible fakes before requests leave your machine, and restores them in the answer.** Cloudflare's DLP decides whether a request may pass; Shadow changes the request so it can pass safely. :::note[TL;DR] Cloudflare AI Gateway DLP uses Cloudflare One DLP profiles to flag or block, and buffers responses to scan them. Shadow masks values with consistent fakes, restores them in responses, streams and tool calls, and runs locally with no account. Use Cloudflare for edge analytics, caching and a blocking backstop; use Shadow so useful requests are not blocked in the first place. ::: ## At a glance | | Shadow | Cloudflare AI Gateway DLP | |---|---|---| | **Open source / licence** | Yes, Apache-2.0 | No, proprietary service | | **Deployment** | Endpoint HTTPS proxy, gateway (`/gw//`), Rust library | Hosted on Cloudflare's network | | **Protection method** | Reversible realistic fakes from a persona; typed tokens for reports; optional redaction | Flag or block, based on DLP profiles | | **Restores responses** | Yes, automatically | No (nothing is replaced) | | **Streaming** | Yes, SSE and NDJSON, incremental | Response scanning buffers the stream | | **Tool calls** | Yes, arguments restored locally; intent-aware for provider-hosted tools | No | | **Files** | DOCX, XLSX, PPTX masked in place; PDF to text; CSV, JSON, HTML | Not documented | | **Self-hosted** | Yes, single binary, no account | No | | **Price model** | Free | Cloudflare plan-based (DLP plan requirements not verified) | ## What Cloudflare AI Gateway does well - **Zero infrastructure.** Change a base URL and traffic flows through Cloudflare's network, with no servers to run. - **Gateway features.** Caching, rate limiting, retries, model fallbacks, analytics, cost tracking and request logs in one dashboard. - **Shared DLP profiles.** [AI Gateway DLP](https://developers.cloudflare.com/ai-gateway/features/dlp/) reuses Cloudflare One DLP detection, so organisations already using Cloudflare One get consistent policies across web, SaaS and AI traffic. A "PII Record" profile was [added in April 2026](https://developers.cloudflare.com/changelog/post/2026-04-28-pii-record-profile/). - **Both directions.** Prompts and responses can be checked, and matches can be flagged for review rather than blocked. - **Scale.** Cloudflare's edge network handles high volume without capacity planning. ## Where Shadow is different ### Does blocking stop the leak or stop the work? A blocked request protects the data but leaves the user without an answer, and people tend to work around tools that block often. Shadow translates the request: `jane@acme.com` becomes `hui.zhao@zhilan.cn`, the provider answers about the fake, and Shadow restores the real address in the response. See [introduction](https://getshadow.dev/getting-started/introduction/) for the translate-don't-block approach. ### Where is sensitive data processed? With a hosted gateway, prompts travel to the gateway in clear text before they are inspected. Shadow masks on the endpoint or in the same pod, so the provider and any gateway downstream see fakes for every value Shadow detects. The exceptions, kept real by default and logged, are the recipient when you ask the model to write to or act on someone, and the contact details a provider-hosted action (a hosted MCP server or server-side tool) needs; see [intent policy](https://getshadow.dev/concepts/intent-policy/). No account, no telemetry. ### Streaming without buffering Cloudflare's docs note that scanning responses buffers the full stream, which raises time to first token. Shadow restores [streams incrementally](https://getshadow.dev/concepts/how-it-works/#streaming-hold-back), holding back at most the length of the longest fake. ### Agents, intent and files Shadow restores [tool-call arguments](https://getshadow.dev/concepts/tool-calls/) locally, uses an [intent policy](https://getshadow.dev/concepts/intent-policy/) to choose fakes, tokens, keep or redact per value, and masks [uploaded Office documents](https://getshadow.dev/concepts/file-masking/) in place. ## When should you choose Cloudflare AI Gateway? - You want a hosted gateway with caching, analytics and rate limits and no infrastructure. - Flagging or blocking is the right response to sensitive data in your traffic. - You already use Cloudflare One DLP and want one policy set across channels. ## When should you choose Shadow? - Requests containing personal data should still succeed, with answers restored to real values. - Sensitive data must be masked before it leaves your machine or network. - You stream responses and want no added time to first token from buffering. - You need agent tool calls and file uploads handled. ## Can you use Cloudflare AI Gateway with Shadow? Yes, and the combination is natural. Add a [custom route](https://getshadow.dev/integrate/gateway-mode/#custom-routes) in Shadow pointing at your Cloudflare AI Gateway URL, or run your client behind Shadow's [proxy](https://getshadow.dev/integrate/proxy-mode/) with the gateway host added to `extra_hosts`. Shadow masks first; Cloudflare caches, logs and analyses masked traffic, and its DLP profiles act as a backstop for anything Shadow's detectors miss. Logs stored at the gateway then contain fakes rather than the values Shadow detected. ## How do they compare on accuracy? Cloudflare's DLP profiles are not benchmarked publicly for AI Gateway. Shadow's [benchmark](https://getshadow.dev/benchmarks/) publishes its leak-rate and round-trip methodology and code; hosted services without independent access are compared on documented capabilities only. ## Sources - [Cloudflare AI Gateway DLP](https://developers.cloudflare.com/ai-gateway/features/dlp/) - [PII Record DLP profile changelog](https://developers.cloudflare.com/changelog/post/2026-04-28-pii-record-profile/) - [Cloudflare AI Gateway overview](https://developers.cloudflare.com/ai-gateway/) - [Shadow streaming hold-back](https://getshadow.dev/concepts/how-it-works/#streaming-hold-back) --- *Last reviewed: 2026-10-31. Cloudflare is a trademark of its owner; this comparison is based on public documentation as of the review date.* ## Frequently asked questions ### Can Cloudflare AI Gateway redact PII? Cloudflare AI Gateway's DLP feature can flag or block requests and responses that match DLP profiles. Its documentation describes no redaction or replacement action. Shadow replaces sensitive values with reversible fakes so the request still goes through. ### Is Shadow an alternative to Cloudflare AI Gateway? For data protection, yes; as a gateway, no. Cloudflare AI Gateway also provides caching, rate limiting, analytics and logging at Cloudflare's edge. Shadow focuses on masking and restoring sensitive values and runs on your own machines. ### Does Cloudflare AI Gateway DLP slow down streaming? Cloudflare's documentation notes that scanning responses requires buffering the full response, which increases time to first token. Shadow restores streams incrementally, holding back only a few characters. ### How much does Cloudflare AI Gateway DLP cost? AI Gateway's core features are available on Cloudflare plans; DLP profiles come from Cloudflare One. Which plans include DLP for AI Gateway was not verified at review time, so check Cloudflare's pricing. Shadow is free. ### Can I use Shadow with Cloudflare AI Gateway? Yes. Route requests through Shadow first, then to your Cloudflare AI Gateway URL. Cloudflare then sees already-masked prompts, and its DLP profiles act as a second check. --- # Shadow vs DataFog — open-source DataFog alternative for LLM privacy Source: https://getshadow.dev/compare/datafog/ > Shadow vs DataFog for LLM PII protection. DataFog redacts, replaces or hashes PII; Shadow uses reversible fakes and restores streamed responses. **DataFog is an open-source (MIT) Python library for detecting and redacting PII, with integrations for LiteLLM and Claude Code. Shadow is an open-source (Apache-2.0) privacy layer for AI traffic that replaces sensitive values with realistic, reversible fakes and restores them in the model's response.** DataFog is a light, flexible choice when one-way redaction is enough. Shadow is built for the full round trip, where the answer has to come back with the real names, emails and IDs. :::note[TL;DR] DataFog redacts, replaces or hashes PII before it reaches a model, with a choice of regex, spaCy or GLiNER engines. Shadow translates values into consistent fakes and restores them in responses, streams and tool calls, as a proxy, gateway or Rust library. ::: ## At a glance | | Shadow | DataFog | |---|---|---| | **Open source / licence** | Yes, Apache-2.0 | Yes, MIT | | **Deployment** | Endpoint HTTPS proxy, gateway (`/gw//`), Rust library | Python library; LiteLLM guardrail; Claude Code hook | | **Protection method** | Reversible realistic fakes; typed tokens for reports; optional redaction | Redact (`[EMAIL_1]`-style), replace, hash, block | | **Restores responses** | Yes, automatically | Not documented | | **Streaming** | Yes, SSE and NDJSON | Not documented | | **Tool calls** | Yes, restored locally; intent-aware | Not documented | | **Files** | DOCX, XLSX, PPTX in place; PDF to text | Not documented for LLM uploads | | **Self-hosted** | Yes | Yes | | **Price model** | Free | Free | ## What DataFog does well - **Engine choice.** Regex for speed, spaCy or GLiNER for named entities, and a "smart" cascade that combines them. You pick the trade-off between latency and recall. - **Fast regex path.** DataFog publishes low vendor latency figures for its regex mode and its Claude Code hook. - **Integrations where developers are.** A LiteLLM guardrail and a Claude Code hook make it easy to add PII checks to an existing gateway or coding agent. - **Simple Python API.** Easy to drop into scripts, notebooks and data pipelines. - **Permissive licence.** MIT, active releases (v4.9.0 at the time of review). ## Where Shadow is different ### Does DataFog give you the real values back? Its documented modes are redact, replace and hash. A prompt about `jane@acme.com` reaches the model as `[EMAIL_1]` or a replacement, and the answer refers to that. Shadow restores the real value automatically using an [encrypted local vault](https://getshadow.dev/concepts/vault/), in JSON responses, [server-sent event streams](https://getshadow.dev/concepts/how-it-works/#streaming-hold-back) and [tool-call arguments](https://getshadow.dev/concepts/tool-calls/). ### Realistic, consistent fakes Shadow replaces values with format-preserving fakes from a persistent [persona](https://getshadow.dev/concepts/personas/): a plausible name, a valid email at a consistent fake domain, a Luhn-valid card, a mod-97-valid IBAN. The model reads natural text, and the same customer gets the same fake across sessions. ### Covering many tools on a machine DataFog integrates through LiteLLM or a Claude Code hook. Shadow intercepts at the network layer, so any tool that honours `HTTPS_PROXY` or accepts a base URL is covered when it calls a [supported AI API host](https://getshadow.dev/reference/providers/), including desktop apps and SDKs you didn't configure individually. Web chat in a browser (chatgpt.com, claude.ai) is not covered. See [proxy mode](https://getshadow.dev/integrate/proxy-mode/) and [gateway mode](https://getshadow.dev/integrate/gateway-mode/). ### Intent policy and files Shadow's [intent policy](https://getshadow.dev/concepts/intent-policy/) chooses fakes for conversation, typed tokens such as `[PERSON_0007]` for bulk reports, and keep for message recipients and provider-hosted action targets. [File masking](https://getshadow.dev/concepts/file-masking/) covers DOCX, XLSX and PPTX uploads in place. ### No Python runtime Shadow is a single Rust binary for macOS, Linux and Windows. That matters when you roll it out to many developer machines or CI runners. ## When should you choose DataFog? - One-way redaction is enough, for example for logs, analytics or a compliance pre-check. - You want a Python library to call from your own code. - You already run LiteLLM and want a lightweight guardrail there. - You want to experiment with regex, spaCy and GLiNER engines side by side. ## When should you choose Shadow? - The model's answer must contain the real values. - You use streaming, agents and tool calls. - You want one control that covers every tool on a machine that calls a supported AI API. - You want Office uploads masked before they leave. ## Can you use them together? There is no built-in integration. You can run both on the same traffic, for example DataFog in a LiteLLM gateway and Shadow on developer machines, but masking twice is rarely useful. If you mainly like DataFog's GLiNER engine, Shadow's [NER sidecar](https://getshadow.dev/concepts/detection/#local-ner-sidecar) runs GLiNER and OpenAI Privacy Filter as detectors. ## Migrating from DataFog 1. Route model calls through Shadow's [gateway](https://getshadow.dev/integrate/gateway-mode/) or [proxy](https://getshadow.dev/integrate/proxy-mode/), and remove the DataFog guardrail or hook for that traffic. 2. Port any custom regex patterns to [dictionary terms](https://getshadow.dev/concepts/detection/#dictionary-terms) or a Rust [custom detector](https://getshadow.dev/integrate/rust/#custom-detectors). 3. Enable the NER sidecar if you relied on DataFog's spaCy or GLiNER engines. 4. If you used hashing for values that must never leave, set those kinds to `redact` under `[policy.always]`. 5. Compare coverage with `shadow scan` on representative prompts. ## How do they compare on accuracy and speed? DataFog's published figures are latency claims from the vendor, not accuracy results on public datasets. DataFog is not in Shadow's [benchmark](https://getshadow.dev/benchmarks/), which measures leak rate, over-masking, round-trip exactness and latency for Shadow and other open-source tools, with code published. Its GLiNER engine is represented there by the GLiNER baseline. ## Sources - [DataFog on GitHub](https://github.com/DataFog/datafog-python) - [DataFog on PyPI](https://pypi.org/project/datafog/) - [Shadow detection](https://getshadow.dev/concepts/detection/) --- *Last reviewed: 2026-10-31. DataFog is a trademark of its owner; this comparison is based on public documentation as of the review date.* ## Frequently asked questions ### Is Shadow an alternative to DataFog? For protecting LLM traffic, yes. DataFog is an MIT-licensed Python library that detects PII with regex, spaCy or GLiNER and redacts, replaces or hashes it, with a LiteLLM guardrail and a Claude Code hook. Shadow is a Rust privacy layer that replaces values with reversible fakes and restores them in responses. ### Can DataFog restore redacted values in the response? Public DataFog documentation does not describe restoring values in model responses. Its modes are redact, replace and hash. Shadow restores values automatically, including in streams and tool calls. ### Does DataFog work with Claude Code? Yes, DataFog ships a Claude Code hook. Shadow covers Claude Code through its HTTPS proxy or gateway, so the whole request, including file contents the agent reads, is masked and the response restored. ### Is DataFog free? Yes. DataFog is open source under the MIT licence. Shadow is open source under Apache-2.0. Both are free to self-host. ### Which is faster, DataFog or Shadow? DataFog publishes its own latency figures for its regex mode and hook. Shadow's built-in rules take about 7.5 ms per document on the held-out benchmark. DataFog itself is not in that benchmark. --- # Shadow vs GLiNER PII — zero-shot PII models inside an LLM privacy proxy Source: https://getshadow.dev/compare/gliner-pii/ > Shadow vs GLiNER PII models (urchade, NVIDIA, Knowledgator). GLiNER detects PII spans; Shadow runs it as a detector and adds reversible masking. **GLiNER PII models are small, open-weight named-entity models that detect personal data for whatever labels you ask for. Shadow is an open-source privacy layer for AI traffic that detects sensitive values, replaces them with realistic, reversible fakes, and restores them in the response.** They are complementary: Shadow's optional NER sidecar runs `urchade/gliner_multi_pii-v1` (Apache-2.0) to improve recall on names, organisations and addresses, especially outside English. :::note[TL;DR] GLiNER finds spans; it doesn't mask traffic or restore answers. Shadow does the masking, vault, intent policy, streaming and tool-call restoration, and can use GLiNER as a detector. Pick the GLiNER checkpoint by licence as much as by accuracy. ::: ## At a glance | | Shadow | GLiNER PII models | |---|---|---| | **Open source / licence** | Yes, Apache-2.0 | urchade and Knowledgator checkpoints Apache-2.0; `nvidia/gliner-PII` NVIDIA Open Model License | | **Deployment** | Endpoint HTTPS proxy, gateway (`/gw//`), Rust library | Python library (`gliner`) and model weights; NVIDIA also offers a NIM | | **Protection method** | Reversible realistic fakes; typed tokens for reports; optional redaction | Detector only: returns labelled spans | | **Restores responses** | Yes, automatically | n/a | | **Streaming** | Yes, SSE and NDJSON | n/a | | **Tool calls** | Yes, restored locally; intent-aware | n/a | | **Files** | DOCX, XLSX, PPTX in place; PDF to text | n/a | | **Self-hosted** | Yes | Yes | | **Price model** | Free | Free (NIM access needs an NVIDIA NGC key) | ## What GLiNER does well - **Zero-shot labels.** You pass the entity labels at inference time ("person", "organization", "passport number"), so you can add a new type without retraining. - **Multilingual checkpoints.** `urchade/gliner_multi_pii-v1` covers several languages, which helps where English-centric detectors miss names. - **Small and fast.** Compact encoders run on a CPU, and much faster on a GPU, without a large language model. - **Organisations and free text.** NER models catch names and companies that regex and dictionaries can't know about. - **Ecosystem.** GLiNER is used by NVIDIA NeMo Guardrails (via the GLiNER-PII NIM), DataFog and others, and there are several PII-tuned checkpoints to choose from. ## Where Shadow is different ### What happens after detection? GLiNER returns spans and stops. To protect an LLM prompt you still need replacements, a mapping to restore the answer, stream handling for values split across events, tool-call restoration, and interception of tools you didn't write. Shadow provides all of these: consistent, format-preserving fakes from a [persona](https://getshadow.dev/concepts/personas/), an [encrypted vault](https://getshadow.dev/concepts/vault/), [streaming restore](https://getshadow.dev/concepts/how-it-works/#streaming-hold-back) and [tool-call](https://getshadow.dev/concepts/tool-calls/) handling. ### Structured identifiers need more than NER Card numbers, IBANs, IPs and API keys are better caught by patterns with checksums. Shadow's built-in detectors handle those, a dictionary covers your own codenames and customers, and local machine facts cover usernames and hostnames. GLiNER adds recall on free-text entities. See [detection](https://getshadow.dev/concepts/detection/). ### Policy and files Shadow's [intent policy](https://getshadow.dev/concepts/intent-policy/) decides between a fake, a typed token like `[PERSON_0007]`, keeping a value, or redaction. [File masking](https://getshadow.dev/concepts/file-masking/) applies the same detectors to DOCX, XLSX and PPTX uploads. ### Public names Zero-shot NER tends to flag well-known names such as "Microsoft" or "London". Shadow applies a public-name allow list after the sidecar, so these are not masked unnecessarily. ## When should you use GLiNER on its own? - You are building a custom pipeline (logs, datasets, search indexing) and need flexible entity types. - You need a detector inside a Python app with labels that change often. - You only need to find entities, not mask live AI traffic. ## When should you choose Shadow? - You want AI requests masked and responses restored with no code changes. - You use streaming, agents and file uploads. - You want structured identifiers, your own terms and NER combined in one place. ## How do you run GLiNER inside Shadow? Shadow's open-weight sidecar, `ml/serve_hf.py`, has three backends: `gliner` (`urchade/gliner_multi_pii-v1`), `privacy-filter` (OpenAI Privacy Filter) and `ensemble` (both, the default). All bundled models are Apache-2.0. ```sh cd ml pip install -r requirements-hf.txt python serve_hf.py --backend gliner --host 127.0.0.1 --port 8878 ``` ```toml [model_detector] enabled = true url = "http://127.0.0.1:8878/detect" # must be loopback timeout_ms = 300 min_score = 0.6 ``` The sidecar maps GLiNER labels to Shadow kinds (person, org, address, email, phone, national ID, IBAN, credit card and others). If it is slow or down, Shadow fails open to its built-in detectors. See [local NER sidecar](https://getshadow.dev/concepts/detection/#local-ner-sidecar) and [Shadow vs OpenAI Privacy Filter](https://getshadow.dev/compare/openai-privacy-filter/). :::caution[Check the checkpoint licence] The bundled sidecar uses Apache-2.0 weights. If you swap in `nvidia/gliner-PII` or a model trained on restricted ai4privacy datasets, review its licence first; some PII models carry non-commercial terms. ::: ## Migrating from a GLiNER-based redaction script 1. Start the sidecar with `--backend gliner` or `ensemble`, and enable `[model_detector]`. 2. Remove your own replacement and restoration code around model calls. 3. Route model traffic through Shadow's [gateway](https://getshadow.dev/integrate/gateway-mode/) or [proxy](https://getshadow.dev/integrate/proxy-mode/). 4. Move any custom labels you relied on into the sidecar's label map or Shadow [dictionary terms](https://getshadow.dev/concepts/detection/#dictionary-terms). 5. Compare findings with `shadow scan --json`. ## How do they compare on accuracy? Published GLiNER figures vary widely by checkpoint and dataset, and vendor figures are often measured on a model's own training distribution. Shadow's [benchmark](https://getshadow.dev/benchmarks/) evaluates GLiNER as a baseline and as part of Shadow: on the held-out set, GLiNER multi PII alone leaked 13.28% of sensitive values, Shadow's built-in rules 3.08%, and Shadow with the open-weight NER sidecar 0.08%. ## Sources - [urchade/gliner_multi_pii-v1](https://huggingface.co/urchade/gliner_multi_pii-v1) - [nvidia/gliner-PII](https://huggingface.co/nvidia/gliner-PII) - [knowledgator/gliner-pii-base-v1.0](https://huggingface.co/knowledgator/gliner-pii-base-v1.0) - [GLiNER on GitHub](https://github.com/urchade/GLiNER) - [Tonic Textual benchmark (vendor-run)](https://www.tonic.ai/ai-model-benchmarks/textual-benchmark) --- *Last reviewed: 2026-10-31. GLiNER and NVIDIA are trademarks of their respective owners; this comparison is based on public documentation as of the review date.* ## Frequently asked questions ### What is GLiNER PII? GLiNER is a family of compact named-entity models that find entities for labels you supply at run time (zero-shot). Several checkpoints are tuned for PII, including urchade/gliner_multi_pii-v1 (Apache-2.0), knowledgator/gliner-pii-base-v1.0 (Apache-2.0) and nvidia/gliner-PII (NVIDIA Open Model License). ### Is Shadow an alternative to GLiNER? Not directly. GLiNER is a detector; it returns spans. Shadow is a privacy layer that intercepts AI traffic, replaces values with reversible fakes and restores them in responses. Shadow's optional NER sidecar runs urchade/gliner_multi_pii-v1 as one of its detectors. ### Can I use GLiNER PII commercially? It depends on the checkpoint. urchade/gliner_multi_pii-v1 and knowledgator/gliner-pii-base-v1.0 are Apache-2.0. nvidia/gliner-PII uses the NVIDIA Open Model License, which has its own terms. Read the model card and licence of the exact checkpoint you deploy. ### How do I use GLiNER with Shadow? Run Shadow's sidecar (ml/serve_hf.py) with the gliner or ensemble backend on 127.0.0.1:8878 and enable [model_detector]. Shadow merges GLiNER's spans with its own detectors and handles masking and restoration. ### How accurate is GLiNER for PII? It varies a lot by checkpoint and dataset. On Shadow's held-out benchmark, GLiNER multi PII leaked 13.28% of sensitive values and changed many public names, against 3.08% for Shadow's built-in rules and 0.08% for Shadow with the open-weight NER sidecar, which includes GLiNER. See the Shadow benchmarks page. --- # Shadow vs Google Sensitive Data Protection — Cloud DLP alternative for LLMs Source: https://getshadow.dev/compare/google-sensitive-data-protection/ > Shadow vs Google Cloud Sensitive Data Protection (Cloud DLP). Google offers reversible FPE; Shadow adds realistic fakes, a local proxy and streaming restore. **Google Cloud Sensitive Data Protection (formerly Cloud DLP) is a managed API that inspects, classifies and de-identifies sensitive data, and it is the only hyperscaler PII service with documented reversible, format-preserving transforms. Shadow is an open-source privacy layer that sits in front of AI providers, replaces values with realistic, reversible fakes, and restores them in responses, streams and tool calls.** Google is a strong choice for data pipelines and storage scanning on GCP. Shadow is built specifically for live LLM traffic and runs on your own machines. :::note[TL;DR] Google SDP can de-identify text with format-preserving encryption and re-identify it later with `reidentifyContent`, which is genuinely reversible. It is a cloud API you call, not an LLM proxy, and FPE output looks random rather than realistic. Shadow does the round trip automatically, with realistic fakes, on the endpoint. ::: ## At a glance | | Shadow | Google Sensitive Data Protection | |---|---|---| | **Open source / licence** | Yes, Apache-2.0 | No, proprietary Google Cloud service | | **Deployment** | Endpoint HTTPS proxy, gateway (`/gw//`), Rust library | Google Cloud API | | **Protection method** | Reversible realistic fakes; typed tokens for reports; optional redaction | Mask, replace, bucketing, FPE (FF1), deterministic encryption with surrogate, crypto hash | | **Restores responses** | Yes, automatically | Yes, via `reidentifyContent`, which you call | | **Streaming** | Yes, SSE and NDJSON | No, request/response API | | **Tool calls** | Yes, restored locally; intent-aware | No | | **Files** | DOCX, XLSX, PPTX in place; PDF to text | Image redaction; inspection jobs over Cloud Storage, BigQuery and Datastore | | **Self-hosted** | Yes | No | | **Price model** | Free | Per GB inspected and transformed | ## What Google Sensitive Data Protection does well - **True reversibility.** `CryptoReplaceFfxFpeConfig` (FF1) and deterministic encryption with a surrogate infoType can be reversed with `reidentifyContent`, using keys you manage in Cloud KMS. Among AWS, Azure and Google PII services, only Google documents this. - **Format preservation.** FPE keeps the length and character set, so a 16-digit number stays 16 digits, which keeps schemas and validators happy. - **Scale and storage scanning.** Inspection jobs over Cloud Storage, BigQuery and Datastore, data profiling, and discovery make it a strong data-governance tool, not just a text API. - **Many infoTypes.** A large built-in catalogue of detectors across many countries, plus custom infoTypes, dictionaries and regex. - **Images.** Image redaction for text found in images, which Shadow does not do (no OCR). - **Clear pricing.** Published per-GB prices, with the first GB of inspection each month free. ## Where Shadow is different ### Realistic fakes versus encrypted surrogates Google FPE turns `Jane Doe` into a same-alphabet string that looks random, often prefixed with a surrogate infoType so it can be found again. A model reading it sees noise, which can hurt answers that depend on names, emails or addresses. Shadow sends a realistic value from a persistent [persona](https://getshadow.dev/concepts/personas/): `Hui Zhao`, `hui.zhao@zhilan.cn`, a Luhn-valid card, a mod-97-valid IBAN. The model writes naturally, and Shadow maps it back. ### Is Google DLP an LLM proxy? No. You call `deidentifyContent` before the model and `reidentifyContent` after it, and handle the plumbing yourself. That plumbing includes [streamed responses](https://getshadow.dev/concepts/how-it-works/#streaming-hold-back) where a value can be split across events, and [tool-call arguments](https://getshadow.dev/concepts/tool-calls/) that must be restored before your code acts. Shadow does all of this as a [proxy](https://getshadow.dev/integrate/proxy-mode/) or [gateway](https://getshadow.dev/integrate/gateway-mode/) with no code changes. ### Where detection runs With Google, plaintext is sent to a Google Cloud API for inspection. Shadow detects locally before anything leaves the machine. If your data already lives in GCP, this matters less. ### Intent-aware treatment Shadow's [intent policy](https://getshadow.dev/concepts/intent-policy/) uses realistic fakes for conversation, typed tokens like `[PERSON_0007]` for bulk reports, and keeps real the recipient of a message and what an action run by a provider-hosted tool needs, logging each keep. Google gives you the transforms; choosing per request is up to you. ### Office files inside requests Shadow masks DOCX, XLSX and PPTX uploads in place inside AI requests. See [file masking](https://getshadow.dev/concepts/file-masking/). ### Cost Shadow is free. Google charges per GB, which is modest for prompts but adds up for large document uploads, and adds a network round trip per call. ## When should you choose Google Sensitive Data Protection? - You need to discover and de-identify data across Cloud Storage, BigQuery or Datastore. - You need cryptographic, key-managed reversibility with Cloud KMS for audit reasons. - You need image redaction. - Your data and models already run on Google Cloud and you want a managed service. ## When should you choose Shadow? - You want live LLM traffic masked and restored with no code changes. - Answer quality matters and you want realistic fakes rather than encrypted strings. - You use streaming, agents and tool calls. - You want detection to happen on the endpoint, with a free, open-source tool. ## Can you use them together? Yes, for different jobs. Use Google SDP to scan and de-identify data at rest in GCP, and Shadow for live traffic from developer machines, agents and services to Gemini, Vertex AI or any other provider. Shadow intercepts the Gemini API and Vertex AI hosts in proxy mode. A Rust [custom detector](https://getshadow.dev/integrate/rust/#custom-detectors) could call Google's inspection API, but that sends plaintext to the cloud, which most Shadow users want to avoid. ## Migrating from a Cloud DLP de-identify / re-identify wrapper 1. Route your model client through Shadow by base URL or `HTTPS_PROXY`. For Vertex AI, add your regional endpoint under `[proxy.routes]` or rely on the intercepted `*-aiplatform.googleapis.com` hosts. 2. Remove the `deidentifyContent` and `reidentifyContent` calls around the model. 3. Port custom infoTypes and dictionaries to Shadow [dictionary terms](https://getshadow.dev/concepts/detection/#dictionary-terms) or a custom detector. 4. Keep Google SDP for storage scanning if you use it. 5. Check coverage with `shadow scan` on representative prompts. ## How do they compare on accuracy? Tonic's vendor-run benchmark (October 2025) includes Google DLP. As with every tool here, results depend heavily on dataset and label mapping. Shadow's [benchmark](https://getshadow.dev/benchmarks/) publishes its code and mappings; commercial cloud APIs such as Google SDP are not included, because their terms and pricing make public, reproducible runs impractical. ## Sources - [Re-identify free text with FPE using a surrogate (Google sample)](https://cloud.google.com/sensitive-data-protection/docs/samples/dlp-reidentify-free-text-with-fpe-using-surrogate) - [Sensitive Data Protection pricing](https://cloud.google.com/sensitive-data-protection/pricing) - [Sensitive Data Protection documentation](https://cloud.google.com/sensitive-data-protection/docs) - [Tonic Textual benchmark (vendor-run)](https://www.tonic.ai/ai-model-benchmarks/textual-benchmark) --- *Last reviewed: 2026-10-31. Google Cloud and Sensitive Data Protection are trademarks of their owner; this comparison is based on public documentation as of the review date.* ## Frequently asked questions ### Is Shadow an alternative to Google Cloud DLP for LLM prompts? Yes for live LLM traffic. Google Sensitive Data Protection (formerly Cloud DLP) is a cloud API for inspecting and de-identifying data, including reversible format-preserving encryption. Shadow runs locally in front of AI providers, replaces values with realistic fakes and restores them in responses automatically. ### Can Google Sensitive Data Protection re-identify data? Yes. With format-preserving encryption (FF1) or deterministic encryption with a surrogate infoType, the reidentifyContent method can restore the original values. Google is the only hyperscaler PII service in this comparison with documented reversible, format-preserving transforms. ### What is the difference between Google FPE and Shadow's fakes? Google FPE output keeps the alphabet and length but looks random, for example a name becomes a string of random characters. Shadow produces realistic values, such as a plausible name or a valid email at a consistent fake domain, so the model reads natural text. ### How much does Google Sensitive Data Protection cost? Google's pricing page lists content inspection at $3 per GB beyond the first 1 GB per month, and transformation at about $2 per GB. Shadow is free and open source. ### Which is more accurate, Google DLP or Shadow? Google DLP is not in the Shadow benchmark, because commercial cloud APIs are impractical to run publicly and reproducibly. Vendor-run benchmarks such as Tonic's include it, but results vary by dataset and label mapping. --- # Shadow vs Harmonic Security — open-source Harmonic alternative for AI data protection Source: https://getshadow.dev/compare/harmonic-security/ > Shadow vs Harmonic Security. Harmonic's browser extension warns, blocks or redirects AI use; Shadow masks API prompts with reversible fakes. **Harmonic Security is a browser extension, deployed through device management, that uses small language models to spot sensitive data and risky intent in employee AI use, then warns, blocks or redirects users to approved tools. Shadow is an open-source privacy layer that translates sensitive values in AI API requests into realistic, reversible fakes and restores them in the response.** Harmonic shapes how people use AI web apps; Shadow protects the content of API, agent and file traffic while the tools keep working. :::note[TL;DR] Harmonic is a user-facing control in the browser: AI app visibility, contextual detection with small models, and nudges that change behaviour. Shadow is a traffic-level control on the endpoint: reversible, format-preserving fakes, restored in streams and tool calls. Different layers, often complementary. ::: ## At a glance | | Shadow | Harmonic Security | |---|---|---| | **Open source / licence** | Yes, Apache-2.0 | No, commercial | | **Deployment** | Endpoint HTTPS proxy, gateway (`/gw//`), Rust library | Browser extension deployed via MDM, with a cloud console | | **Protection method** | Reversible realistic fakes from a persona; typed tokens for reports; optional redaction | Warn, block, redirect; redaction per vendor | | **Restores responses** | Yes, automatically | No | | **Streaming** | Yes, SSE and NDJSON with split-fake reassembly | Not applicable (acts before submission) | | **Tool calls** | Yes, arguments restored locally; intent-aware for provider-hosted tools | Not applicable | | **Files** | DOCX, XLSX, PPTX masked in place; PDF to text; CSV, JSON, HTML | Upload monitoring and control | | **Self-hosted** | Yes, single binary, no account | No | | **Price model** | Free | Quote | ## What Harmonic does well - **Context-aware detection.** Small language models judge what is being shared rather than relying only on patterns, which can catch things like unreleased financials or source code that regex misses. Harmonic claims it is "96% more accurate than regex" (vendor claim). - **Low friction for users.** Warnings and redirects explain the risk and point people to sanctioned tools, which tends to reduce workarounds compared with hard blocks. - **Shadow-AI visibility.** Security teams see which AI apps are used across the organisation, including new ones. - **Fast deployment.** An extension pushed via MDM covers managed browsers without network changes. - **Upload awareness.** File uploads to AI apps are covered as well as typed prompts. - **Low latency claims.** The vendor states under 200 ms per check. ## Where Shadow is different ### What happens after sensitive data is detected? Harmonic mostly decides whether to warn, block or redirect. Shadow decides how to transform: every sensitive value it detects becomes a realistic fake from a persistent [persona](https://getshadow.dev/concepts/personas/), the request proceeds, and the real values are restored in the answer. Users don't have to choose between privacy and getting the work done. ### Traffic a browser extension cannot see IDE assistants, CLIs, agents, CI jobs and back-end services call model APIs directly. Shadow covers them when they call a [supported AI API host](https://getshadow.dev/reference/providers/) through [proxy mode](https://getshadow.dev/integrate/proxy-mode/), [gateway mode](https://getshadow.dev/integrate/gateway-mode/) or the [Rust library](https://getshadow.dev/integrate/rust/), and restores [streams](https://getshadow.dev/concepts/how-it-works/#streaming-hold-back) and [tool-call arguments](https://getshadow.dev/concepts/tool-calls/). ### Intent that changes the transformation Harmonic's intent detection decides whether to flag. Shadow's [intent-aware policy](https://getshadow.dev/concepts/intent-policy/) decides how to transform: typed tokens for bulk reports, kept recipients and provider-hosted action targets, fakes elsewhere. ### Files masked in place Shadow masks DOCX, XLSX and PPTX uploads in place and turns PDFs into masked text. See [file masking](https://getshadow.dev/concepts/file-masking/). ### Open source and local No account, no telemetry, auditable code. ## When should you choose Harmonic? - Your priority is employee behaviour in AI web apps across managed browsers. - You want contextual detection beyond regex with coaching and redirects. - You need visibility into which AI tools staff use. - You want a fast, extension-based rollout. ## When should you choose Shadow? - You need to protect model API traffic from tools, agents and services. - Users must keep working with sensitive data and get answers with real values. - You need streaming, tool-call and Office-file support. - You want an open-source, self-hosted component. ## Using Harmonic and Shadow together 1. Use Harmonic in managed browsers for web-chat AI use and coaching. 2. Install Shadow on developer machines, CI runners and services that call model APIs. 3. Use Harmonic's console for organisation-wide visibility, and Shadow's per-request logs (counts and kinds, never values) for API traffic. 4. Optionally strengthen Shadow's detection with the open-weight NER sidecar or Presidio. See [detection](https://getshadow.dev/concepts/detection/). ## How do they compare on accuracy? Harmonic's accuracy figures are vendor claims without a public dataset we could reproduce. Shadow publishes its methodology and code on the [benchmarks page](https://getshadow.dev/benchmarks/). ## Sources - [Harmonic Security: browser-based GenAI security](https://www.harmonic.security/solutions/browser-based-genai-security) - [Harmonic Security](https://www.harmonic.security/) - [Shadow: intent-aware policy](https://getshadow.dev/concepts/intent-policy/) --- *Last reviewed: 2026-10-31. Harmonic Security is a trademark of its owner; this comparison is based on public documentation as of the review date.* ## Frequently asked questions ### Is Shadow a free alternative to Harmonic Security? For API and agent traffic, partly. Shadow is free and open source and masks prompts to model APIs with reversible fakes. Harmonic is a commercial browser-extension product that watches employee use of AI apps with small language models and warns, blocks or redirects. Shadow does not provide browser-level visibility or user coaching. ### How does Harmonic Security detect sensitive data? Harmonic says it uses small language models in a browser extension to judge the intent and sensitivity of what users share. It claims under 200 ms latency and 96% better accuracy than regex; these are vendor claims and we have not seen independent results. ### Does Harmonic restore redacted values in AI responses? Not that public documentation describes. Harmonic focuses on warning, blocking, redirecting to approved tools and, per the vendor, redaction. Shadow restores its fakes in responses, streams and tool calls. ### How much does Harmonic Security cost? Harmonic pricing is by quote. Shadow is free under Apache-2.0. ### Can Harmonic and Shadow be used together? Yes. Harmonic can cover employee use of AI web apps on managed browsers while Shadow masks traffic from developer tools, agents and services that a browser extension does not see. --- # Shadow vs Kong AI Gateway — open-source alternative to Kong ai-sanitizer Source: https://getshadow.dev/compare/kong-ai-gateway/ > Shadow vs Kong AI Gateway's ai-sanitizer plugin. Both can use synthetic values and restore; Shadow is open source, local and streams restores. **Kong AI Gateway is Kong's set of AI plugins for its API gateway, and its Enterprise `ai-sanitizer` plugin replaces PII in prompts with placeholders or synthetic values and can restore them. Shadow is an open-source privacy layer that does the same kind of reversible masking with realistic, format-preserving fakes, on the endpoint or as a sidecar, and restores values in streams and tool calls.** Kong's approach is the closest commercial analogue to Shadow's; the main differences are where it runs, the licence, and documented streaming support. :::note[TL;DR] If you already run Kong Enterprise and want PII handling centrally in the gateway, `ai-sanitizer` fits naturally. If you want reversible synthetic masking without an Enterprise licence, on developer machines as well as servers, with documented streaming and tool-call restoration, Shadow fits. Shadow can also sit in front of Kong. ::: ## At a glance | | Shadow | Kong AI Gateway (`ai-sanitizer`) | |---|---|---| | **Open source / licence** | Yes, Apache-2.0 | Kong Gateway OSS exists; `ai-sanitizer` is Enterprise-only | | **Deployment** | Endpoint HTTPS proxy, gateway (`/gw//`), Rust library | Self-hosted Kong Gateway plugin plus a private `kong/ai-pii-service` container | | **Protection method** | Reversible realistic fakes from a persona; typed tokens for reports; optional redaction | Placeholder or synthetic mode; credentials replaced with `#` | | **Restores responses** | Yes, automatically | Yes, with `restore` (requests sanitized on input) | | **Streaming** | Yes, SSE and NDJSON with split-fake reassembly | Not documented | | **Tool calls** | Yes, arguments restored locally; intent-aware for provider-hosted tools | Not documented | | **Files** | DOCX, XLSX, PPTX masked in place; PDF to text; CSV, JSON, HTML | Not documented | | **Self-hosted** | Yes, single binary, no account | Yes | | **Price model** | Free | Kong Enterprise licence | ## What Kong AI Gateway does well - **A full API gateway.** Kong brings routing, authentication, rate limiting, AI-specific plugins (prompt guard, prompt templates, semantic caching, multi-LLM routing) and a mature plugin ecosystem. - **Synthetic mode with restore.** The [`ai-sanitizer`](https://developer.konghq.com/plugins/ai-sanitizer/) plugin can replace values with synthetic data rather than placeholders and restore them afterwards, the same principle Shadow uses. - **Multilingual images.** The PII service ships images for nine languages: English, Spanish, French, German, Italian, Japanese, Korean, Portuguese and Turkish. - **Central policy.** Platform teams configure sanitization once in the gateway for every consumer. - **Vault option.** A separate [Skyflow plugin](https://developer.konghq.com/plugins/skyflow-ai-data-control/) adds vault-backed tokenisation and re-identification for teams that want a managed vault. ## Where Shadow is different ### Open source and no Enterprise licence `ai-sanitizer` requires Kong Enterprise, Gateway 3.10 or later (3.12 for response sanitization) and a private PII-service image that Kong documents as needing at least 600 MB of RAM. Shadow is Apache-2.0, a single binary, with no account. ### Does Kong restore values in streams and tool calls? Kong's plugin docs do not describe restoring sanitized values in streamed responses or tool-call arguments. Shadow restores fakes in [SSE and NDJSON streams](https://getshadow.dev/concepts/how-it-works/#streaming-hold-back) for OpenAI, Anthropic, Gemini and other formats, even when a fake is split across events, and restores [tool-call arguments](https://getshadow.dev/concepts/tool-calls/) before your code runs the tool. ### Format-preserving, persona-consistent fakes Shadow's fakes are drawn from a persistent [persona](https://getshadow.dev/concepts/personas/): card numbers keep a valid Luhn digit, IBANs pass mod-97, emails use the persona's domain, and the same value gets the same fake across sessions. Kong's documentation describes synthetic values but does not document checksum validity or cross-session consistency. ### Endpoint coverage A gateway only sees traffic routed to it. Shadow also runs on laptops as an [HTTPS proxy](https://getshadow.dev/integrate/proxy-mode/), so IDE agents and desktop apps calling a [supported provider API](https://getshadow.dev/reference/providers/) directly are covered. It masks [Office uploads](https://getshadow.dev/concepts/file-masking/) and applies an [intent policy](https://getshadow.dev/concepts/intent-policy/) per request. ## When should you choose Kong AI Gateway? - You already run Kong Enterprise and want PII handling in the same control plane as routing and auth. - You need language-specific detection in one of Kong's nine supported languages today. - Central gateway policy matters more than endpoint coverage. ## When should you choose Shadow? - You want reversible synthetic masking without an Enterprise licence. - Streaming and agent tool calls must be restored. - Developer machines and tools that call providers directly need coverage. ## Can you use Kong with Shadow? Yes. Run Shadow as a sidecar in front of Kong, or point Kong's AI upstreams at Shadow's gateway routes so Shadow masks just before traffic leaves your network. Kong keeps routing, auth and rate limits. Enable PII sanitization in only one of the two layers, otherwise the second sees synthetic values it did not create. See [Docker and Kubernetes](https://getshadow.dev/integrate/docker-kubernetes/). ## Migrating from Kong ai-sanitizer 1. Keep Kong for routing and other AI plugins. 2. Disable `ai-sanitizer` on the routes Shadow will cover. 3. Deploy Shadow as a sidecar and point the AI upstream URLs at `http://127.0.0.1:8877/gw//...`, with custom routes for Azure or Bedrock ([gateway mode](https://getshadow.dev/integrate/gateway-mode/)). 4. Add your organisation names and codenames as [dictionary terms](https://getshadow.dev/concepts/detection/#dictionary-terms). 5. Test streaming, tool calls and non-English prompts with `shadow scan` before switching. ## How do they compare on accuracy? Kong does not publish detection accuracy for its PII service. Shadow's [benchmark](https://getshadow.dev/benchmarks/) publishes its methodology, code and results; Kong's Enterprise-only plugin was not available for independent testing. ## Sources - [Kong ai-sanitizer plugin](https://developer.konghq.com/plugins/ai-sanitizer/) - [Kong Skyflow AI data control plugin](https://developer.konghq.com/plugins/skyflow-ai-data-control/) - [Kong AI Gateway](https://konghq.com/products/kong-ai-gateway) - [Shadow file masking](https://getshadow.dev/concepts/file-masking/) --- *Last reviewed: 2026-10-31. Kong is a trademark of its owner; this comparison is based on public documentation as of the review date.* ## Frequently asked questions ### Is Shadow an alternative to Kong's ai-sanitizer plugin? For PII masking, yes. Kong's ai-sanitizer is an Enterprise plugin that replaces PII with placeholders or synthetic values and can restore them. Shadow does the same job as an open-source endpoint proxy, gateway or Rust library, with streaming and tool-call restoration. Kong AI Gateway also provides routing, rate limiting and other gateway features that Shadow does not. ### Is Kong ai-sanitizer free? No. The ai-sanitizer plugin is available in Kong Enterprise and depends on a private kong/ai-pii-service container image. Shadow is free under Apache-2.0. ### Can Kong ai-sanitizer restore the original values? Yes. With the restore option, values sanitized in the request are put back in the response. Kong's docs require Gateway 3.10 or later, and sanitizing responses needs 3.12 or later. ### Does Kong ai-sanitizer work with streaming? Kong's plugin documentation does not describe restoring sanitized values in streamed responses. Shadow restores SSE and NDJSON streams, including fakes split across events. ### Which languages does Kong ai-sanitizer support? The PII service ships language-specific images for English, Spanish, French, German, Italian, Japanese, Korean, Portuguese and Turkish. Shadow's multilingual recall depends on its detectors and is measured on the benchmarks page. --- # Shadow vs Lakera Guard — Lakera alternative for PII masking in LLM prompts Source: https://getshadow.dev/compare/lakera/ > Shadow vs Lakera Guard (Check Point AI Guardrails). Lakera detects PII and prompt attacks; Shadow masks PII with reversible fakes and restores answers. **Lakera Guard, now part of Check Point, is an API that screens LLM inputs and outputs for prompt injection, jailbreaks, PII, secrets and other risks, and returns flags your application uses to block or allow. Shadow is an open-source privacy layer that replaces sensitive values with realistic, reversible fakes before requests reach the provider, and restores them in responses, streams and tool calls.** Lakera answers "is this request dangerous?"; Shadow answers "how do we send this request without exposing the data in it?". :::note[TL;DR] Lakera Guard is strongest at attack detection (prompt injection, jailbreaks) and also flags PII and secrets. It does not document masking-and-restore. Shadow masks PII and secrets with consistent fakes and restores answers locally, but does not detect prompt injection. Many teams will want both. ::: ## At a glance | | Shadow | Lakera Guard | |---|---|---| | **Open source / licence** | Yes, Apache-2.0 | No, proprietary | | **Deployment** | Endpoint HTTPS proxy, gateway (`/gw//`), Rust library | SaaS API; self-hosted for Enterprise | | **Protection method** | Reversible realistic fakes from a persona; typed tokens for reports; optional redaction | Detect and flag (PII, secrets, custom patterns, prompt attacks); your app blocks | | **Restores responses** | Yes, automatically | No (nothing is replaced) | | **Streaming** | Yes, SSE and NDJSON with split-fake reassembly | Not documented | | **Tool calls** | Yes, arguments restored locally; intent-aware for provider-hosted tools | Agent-security features for attack detection | | **Files** | DOCX, XLSX, PPTX masked in place; PDF to text; CSV, JSON, HTML | Not documented | | **Self-hosted** | Yes, single binary, no account | Enterprise only | | **Price model** | Free | Free community tier (per third-party sources); Enterprise quote | ## What Lakera Guard does well - **Prompt-injection and jailbreak detection.** This is Lakera's core strength, backed by a large stream of adversarial data from its public red-teaming games. Shadow does not attempt it. - **One API for many risks.** PII, secrets, custom regex, content moderation and malicious links are screened in one call, with results your app can act on. - **Low integration effort.** A single HTTP call before and after the model, with SDKs and policy configuration in a dashboard. - **Enterprise backing.** Following Check Point's [acquisition](https://www.itpro.com/business/acquisition/check-point-buys-lakera-to-secure-the-full-enterprise-ai-lifecycle), Lakera fits into a larger security portfolio, which suits organisations standardising on Check Point. - **Agent security.** Lakera positions its product for agentic applications, screening tool inputs and outputs for attacks. ## Where Shadow is different ### Flagging versus masking When Lakera flags PII, your application has to decide what to do, usually block or ask the user to edit. Shadow transforms the request instead: names, emails, IDs and secrets become consistent fakes from a persistent [persona](https://getshadow.dev/concepts/personas/), the request goes through, and the response comes back with the real values. The user gets an answer and the provider never sees the values Shadow detected. ### Where does the prompt go? Screening with a SaaS API means sending the unmasked prompt to that API. Shadow detects locally, with no account and no telemetry, and only the masked request leaves the machine. ### Restoration in streams and tool calls Shadow restores fakes in [SSE and NDJSON streams](https://getshadow.dev/concepts/how-it-works/#streaming-hold-back) and in [tool-call arguments](https://getshadow.dev/concepts/tool-calls/), and applies an [intent policy](https://getshadow.dev/concepts/intent-policy/) so bulk reports get typed tokens and provider-hosted actions are handled explicitly. ### Coverage without code Lakera is called from your code. Shadow can run as an [HTTPS proxy](https://getshadow.dev/integrate/proxy-mode/) that covers every tool on a machine that calls a [supported AI API](https://getshadow.dev/reference/providers/), including ones you didn't write, and masks [uploaded Office files](https://getshadow.dev/concepts/file-masking/). ## When should you choose Lakera Guard? - Your main risk is prompt injection, jailbreaks or harmful content in a customer-facing LLM app. - You want a managed API with a dashboard and vendor support. - Flagging PII for your app to block or review is enough. ## When should you choose Shadow? - You need to send prompts that contain personal or confidential data and still get useful answers. - Data must not leave your machine or network unmasked, including to a screening API. - You need open source, self-hosting and no per-request cost. ## Can you use Lakera Guard with Shadow? Yes. A layered setup works well: Shadow masks the request first, then your application (or gateway) calls Lakera Guard on the masked text for prompt-injection and content checks. Lakera then never receives the personal data Shadow detected, and Shadow restores values only after Lakera has screened the response. Expect Lakera's PII detector to fire less often on masked text, though realistic fakes may still look like PII to it; you may want to rely on Shadow for PII and on Lakera for attacks. ## How do they compare on accuracy? They mostly measure different things: Lakera focuses on attack detection, Shadow on keeping sensitive values out of requests. Shadow's [benchmark](https://getshadow.dev/benchmarks/) covers PII leak rate, over-masking and round-trip exactness with published code. Lakera was not independently benchmarked for PII. ## Sources - [Lakera Guard](https://www.lakera.ai/lakera-guard) - [Lakera documentation](https://docs.lakera.ai/) - [Check Point acquires Lakera (ITPro)](https://www.itpro.com/business/acquisition/check-point-buys-lakera-to-secure-the-full-enterprise-ai-lifecycle) - [Shadow security model](https://getshadow.dev/reference/security/) --- *Last reviewed: 2026-10-31. Lakera and Check Point are trademarks of their owners; this comparison is based on public documentation as of the review date.* ## Frequently asked questions ### Is Shadow an alternative to Lakera Guard? For PII, partly. Lakera Guard is an API that screens prompts and responses for prompt injection, jailbreaks, PII, secrets and unsafe content, and returns flags your application acts on. Shadow does not detect prompt injection; it masks sensitive values with reversible fakes and restores them. They complement each other. ### Does Lakera Guard redact PII? Lakera Guard's public documentation describes detecting and flagging PII, secrets and custom patterns. It does not describe replacing values and restoring them in the response. Shadow does both. ### Who owns Lakera now? Check Point announced the acquisition of Lakera in September 2025. Lakera Guard is being offered as part of Check Point's AI security portfolio, referred to as Check Point AI Guardrails. ### How much does Lakera Guard cost? Third-party sources describe a free community tier of around 10,000 requests per month, with Enterprise plans on quote. Check Lakera's or Check Point's site for current pricing. Shadow is free and open source. ### Can Lakera Guard be self-hosted? Lakera describes a self-hosted option for Enterprise customers. The standard offering is a SaaS API. Shadow is always self-hosted. --- # Shadow vs LangChain PIIMiddleware — reversible PII masking for agents Source: https://getshadow.dev/compare/langchain-pii-middleware/ > Shadow vs LangChain PIIMiddleware for agent PII protection. LangChain redacts, masks or hashes one way; Shadow restores values in tool calls and streams. **LangChain's PIIMiddleware is an in-application agent middleware that redacts, masks, hashes or blocks PII in an agent's input, output and tool results. Shadow is an open-source privacy layer for AI traffic that replaces sensitive values with realistic, reversible fakes before a request leaves the machine, and restores them in responses and tool calls.** PIIMiddleware is the quickest way to add one-way PII handling to a LangChain agent. Shadow is the choice when the agent must still act on real data, such as emailing the right person. :::note[TL;DR] PIIMiddleware runs inside LangChain and is one way: redact, mask, hash or block. Shadow runs outside the app, works with any framework or language, uses reversible realistic fakes, and restores values in streams and tool-call arguments so tools act on the real data. ::: ## At a glance | | Shadow | LangChain PIIMiddleware | |---|---|---| | **Open source / licence** | Yes, Apache-2.0 | Yes, MIT | | **Deployment** | Endpoint HTTPS proxy, gateway (`/gw//`), Rust library | Python middleware inside a LangChain agent | | **Protection method** | Reversible realistic fakes; typed tokens for reports; optional redaction | Redact, mask, hash, block | | **Restores responses** | Yes, automatically | No | | **Streaming** | Yes, SSE and NDJSON with split-fake reassembly | n/a (no restoration) | | **Tool calls** | Yes, arguments restored locally; intent-aware | Yes, can apply to tool results (one way) | | **Files** | DOCX, XLSX, PPTX in place; PDF to text | No | | **Self-hosted** | Yes | Yes (in your app) | | **Price model** | Free | Free | ## What PIIMiddleware does well - **Zero extra infrastructure.** It is a few lines in your agent definition; no proxy, sidecar or certificate. - **Agent-aware placement.** It can apply to the user input, the model output and, notably, tool results, which is where a lot of PII enters an agent (database rows, CRM records, emails). - **Clear strategies.** Redact, mask, hash or block per PII type, so you can block secrets and mask emails in the same agent. - **Custom detectors.** Built-in regex detectors cover common types, and you can add your own. - **Native to LangChain.** It fits naturally into LangChain and LangGraph agent code, tracing and testing. ## Where Shadow is different ### Can the agent still act on real data? With a one-way strategy, the model sees `[REDACTED_EMAIL]` or a hash, so a tool call it writes contains that too. Your `send_email` tool then gets a value it can't use. Shadow passes the recipient the instruction names through as written (and logs it), so the model writes `send_email(to="jane@acme.com")` and greets the right person; everything else in the request gets a realistic fake that Shadow restores in the arguments before your tool runs. With `[policy.act] targets = "surrogate"` the recipient is a fake too, such as `hui.zhao@zhilan.cn`, restored to `jane@acme.com` locally, and the provider never sees the address. See [tool calls and agents](https://getshadow.dev/concepts/tool-calls/). ### Provider-hosted tools Some tools run in the provider's cloud, where nothing can restore a fake. Shadow's [intent policy](https://getshadow.dev/concepts/intent-policy/) handles this explicitly: by default it keeps real only what the action needs (the contact details, people, companies and addresses named in the instruction) while secrets, cards and IDs stay masked, and logs each keep; or you can choose to send a fake and accept the action failing. ### Streaming restoration Shadow restores fakes that arrive split across streamed events, including streamed tool-call argument deltas. See [streaming hold-back](https://getshadow.dev/concepts/how-it-works/#streaming-hold-back). ### Framework- and language-independent PIIMiddleware protects LangChain agents. Shadow protects any client that talks to a [supported model API](https://getshadow.dev/reference/providers/) through it: other frameworks, other languages, IDE assistants, CLIs and desktop apps. See [integration modes](https://getshadow.dev/integrate/overview/). ### Consistent fakes and files Shadow's [persona](https://getshadow.dev/concepts/personas/) keeps fakes consistent across sessions, so long-running agents and memory stay coherent. [File masking](https://getshadow.dev/concepts/file-masking/) covers DOCX, XLSX and PPTX uploads. ### Detection breadth Shadow adds checksum-validated financial IDs, secrets in many key formats, local machine facts, a dictionary for your own codenames, and optional NER or Presidio. See [detection](https://getshadow.dev/concepts/detection/). ## When should you choose PIIMiddleware? - Your agent is built on LangChain and one-way handling is acceptable. - You want to block or hash specific PII types in tool results with no extra infrastructure. - You need a quick guard for a prototype. ## When should you choose Shadow? - Agents must act on real people, addresses and IDs through tools. - You use several frameworks, languages or off-the-shelf tools. - You stream responses and tool calls. - You want consistent fakes across sessions and Office uploads masked. ## Can you use them together? Yes. A common split is Shadow for everything that leaves the machine, and PIIMiddleware with the `block` strategy for types you never want in a tool result at all. Be aware that if both mask the same values, the model sees PIIMiddleware's redaction rather than Shadow's fakes, and those values can't be restored. ## Migrating from PIIMiddleware 1. Point your chat model's base URL at Shadow, for example `http://127.0.0.1:8877/gw/openai/v1` ([gateway mode](https://getshadow.dev/integrate/gateway-mode/)), or set `HTTPS_PROXY` ([proxy mode](https://getshadow.dev/integrate/proxy-mode/)). 2. Remove PIIMiddleware, or keep it only for strategies you want to stay one way (`block`). 3. Map hash or redact choices to Shadow's `[policy.always]`, for example `secret = "redact"`. 4. Add your organisation's names and codenames as [dictionary terms](https://getshadow.dev/concepts/detection/#dictionary-terms). 5. Run your agent's tests: tools should now receive real values in their arguments. ## How do they compare on accuracy? PIIMiddleware's built-in detectors are regex-based and no accuracy figures are published. Shadow's [benchmark](https://getshadow.dev/benchmarks/) measures leak rate, over-masking, round-trip exactness and latency, with code published. On the held-out set, Shadow's built-in rules leaked 3.08% of sensitive values and restored 100% of documents exactly. ## Sources - [LangChain PIIMiddleware reference](https://reference.langchain.com/python/langchain/agents/middleware/pii) - [LangChain on GitHub](https://github.com/langchain-ai/langchain) - [Shadow tool calls and agents](https://getshadow.dev/concepts/tool-calls/) --- *Last reviewed: 2026-10-31. LangChain is a trademark of its owner; this comparison is based on public documentation as of the review date.* ## Frequently asked questions ### What is LangChain PIIMiddleware? An agent middleware in LangChain that detects PII and applies a strategy (redact, mask, hash or block) to the agent's input, output and tool results. It is MIT-licensed and runs inside your LangChain application. ### Is Shadow an alternative to LangChain PIIMiddleware? Yes, if you need the originals back. PIIMiddleware's strategies are one-way, so the agent's final answer and tool calls see redacted or hashed values. Shadow masks with reversible fakes and restores them in responses and tool-call arguments before your tools run. ### Does Shadow work with LangChain and LangGraph? Yes. Point the model client's base URL at Shadow's gateway, or set HTTPS_PROXY. Shadow masks every request the agent makes and restores tool-call arguments, so your LangChain tools receive real values. ### Can PIIMiddleware restore PII in tool calls? Public LangChain documentation does not describe restoring values. A tool that needs a real email address would receive the redacted, masked or hashed form. ### Is LangChain PIIMiddleware free? Yes. It is part of the MIT-licensed LangChain library. Shadow is open source under Apache-2.0. --- # Shadow vs Lasso Security — open-source Lasso alternative for GenAI data protection Source: https://getshadow.dev/compare/lasso-security/ > Shadow vs Lasso Security. Lasso masks or alerts with plain-English contextual policies; Shadow masks with reversible fakes and restores answers. **Lasso Security is a GenAI security platform with a browser extension and gateway; its contextual data protection lets teams describe policies in plain English and then masks sensitive data or raises alerts. Shadow is an open-source privacy layer that translates sensitive values in AI API requests into realistic, reversible fakes and restores them in the response.** Lasso offers policy-driven governance across people and apps; Shadow focuses on keeping values out of provider requests without breaking answers, streams or agents. :::note[TL;DR] Lasso brings natural-language, contextual policies and central visibility to GenAI use, with masking or alerting. Whether it restores masked values in responses is not publicly documented. Shadow uses consistent, format-preserving fakes and restores them in streams, tool calls and files, locally and for free. ::: ## At a glance | | Shadow | Lasso Security | |---|---|---| | **Open source / licence** | Yes, Apache-2.0 | No, commercial | | **Deployment** | Endpoint HTTPS proxy, gateway (`/gw//`), Rust library | Browser extension, gateway, SaaS console | | **Protection method** | Reversible realistic fakes from a persona; typed tokens for reports; optional redaction | Mask or alert, driven by contextual policies | | **Restores responses** | Yes, automatically | Not documented | | **Streaming** | Yes, SSE and NDJSON with split-fake reassembly | Not documented | | **Tool calls** | Yes, arguments restored locally; intent-aware for provider-hosted tools | Not documented | | **Files** | DOCX, XLSX, PPTX masked in place; PDF to text; CSV, JSON, HTML | Not documented | | **Self-hosted** | Yes, single binary, no account | Not documented | | **Price model** | Free | Quote | ## What Lasso does well - **Plain-English policies.** Security teams can describe what must not be shared ("pricing for unreleased products", "customer health data") instead of building regexes, which suits data that patterns cannot capture. - **Context over patterns.** Contextual evaluation can catch sensitive business information that has no fixed format. - **People and applications.** An extension for employee use and a gateway for applications give two enforcement points. - **Central visibility.** Security teams get a view of GenAI use and policy events across the organisation. - **Masking as well as alerting.** Masking lets work continue rather than stopping it outright. ## Where Shadow is different ### Masks that the model can still use A mask such as `****` or `[NAME]` removes meaning. Shadow replaces each value with a realistic fake from a persistent [persona](https://getshadow.dev/concepts/personas/): `jane@acme.com` becomes `hui.zhao@zhilan.cn`, IBANs pass mod-97, codenames become other plausible codenames. The same real value always gets the same fake, so multi-turn conversations stay coherent. ### Can Lasso restore values in responses, streams and tool calls? Public documentation we checked does not describe restoration. Shadow restores fakes in JSON responses, [SSE and NDJSON streams](https://getshadow.dev/concepts/how-it-works/#streaming-hold-back) and [tool-call arguments](https://getshadow.dev/concepts/tool-calls/) on your machine, so agents act on real targets. ### Intent-aware policy Shadow's policy is less flexible in wording than Lasso's natural-language rules, but it decides how to transform each value based on intent: typed tokens for reports, kept recipients and provider-hosted action targets, fakes elsewhere. See [intent-aware policy](https://getshadow.dev/concepts/intent-policy/). ### Files Shadow masks DOCX, XLSX and PPTX uploads in place and turns PDFs into masked text. See [file masking](https://getshadow.dev/concepts/file-masking/). ### Open source and local No account, no telemetry, and no plaintext sent to a vendor cloud. ## When should you choose Lasso? - You want to express data policies in plain English for information without a fixed format. - You need central visibility and governance over GenAI use by people and apps. - Masking or alerting, without restoration, fits your workflows. - You want a commercially supported platform. ## When should you choose Shadow? - You need masked values restored in answers, streams and tool calls. - Developers, agents and services call model APIs directly. - You want Office uploads masked before they leave. - You want an open-source, self-hosted control with no licence cost. ## Using Lasso and Shadow together 1. Use Lasso for employee browser use, contextual policies and central visibility. 2. Install Shadow on developer machines, CI runners and services that call model APIs ([proxy mode](https://getshadow.dev/integrate/proxy-mode/), [gateway mode](https://getshadow.dev/integrate/gateway-mode/)). 3. Mirror the most important Lasso policies in Shadow as [dictionary terms](https://getshadow.dev/concepts/detection/#dictionary-terms) for names, customers and codenames. 4. If both sit in the API path, put Shadow nearest the application so downstream tools only see masked content. ## How do they compare on accuracy? Lasso does not publish detection results on public PII datasets that we could find. Shadow publishes its methodology on the [benchmarks page](https://getshadow.dev/benchmarks/). ## Sources - [Lasso Security launches contextual data protection for GenAI applications](https://www.lasso.security/press/lasso-security-launches-contextual-data-protection-tool-for-genai-applications) - [Lasso Security](https://www.lasso.security/) - [Shadow: personas](https://getshadow.dev/concepts/personas/) --- *Last reviewed: 2026-10-31. Lasso Security is a trademark of its owner; this comparison is based on public documentation as of the review date.* ## Frequently asked questions ### Is Shadow a free alternative to Lasso Security? For masking data in AI API traffic, partly. Shadow is free and open source and replaces sensitive values with reversible fakes on the machines where it runs. Lasso Security is a commercial GenAI security platform with a browser extension, gateway and central policies that Shadow does not replicate. ### What is Lasso's contextual data protection? Lasso lets security teams write data-protection policies in plain English, which are applied contextually rather than as fixed patterns. Its extension can mask sensitive data or alert, according to the company's launch announcement. ### Does Lasso restore masked values in AI responses? Public documentation we checked does not say. Shadow restores its fakes in responses, SSE streams and tool-call arguments locally. ### How much does Lasso Security cost? Pricing is by quote. Shadow is free under Apache-2.0. ### Can Lasso and Shadow be used together? Yes. Lasso can govern employee and application AI use with contextual policies while Shadow masks the content of API, agent and developer traffic on the endpoint. --- # Shadow vs LiteLLM — LiteLLM PII masking alternative with streaming restore Source: https://getshadow.dev/compare/litellm/ > Shadow vs LiteLLM proxy for PII masking. LiteLLM routes models and runs guardrails; Shadow adds reversible fakes and streaming, tool-call restore. **LiteLLM is an open-source AI gateway and SDK that gives one OpenAI-compatible API over many model providers, with keys, budgets, fallbacks and guardrail hooks. Shadow is an open-source privacy layer that replaces sensitive values with realistic, reversible fakes before a request leaves, and restores them in responses, streams and tool calls.** They are different products that overlap on PII masking. Many teams will keep LiteLLM for routing and use Shadow for masking. :::note[TL;DR] LiteLLM masks PII through guardrail integrations, most often Presidio, with placeholders and optional unmasking via `output_parse_pii`. Shadow uses consistent, format-preserving fakes and restores them in JSON, SSE streams and tool-call arguments. Keep LiteLLM as your gateway and put Shadow in front of it, or use Shadow's own gateway if you don't need routing. ::: ## At a glance | | Shadow | LiteLLM | |---|---|---| | **Open source / licence** | Yes, Apache-2.0 | Core MIT; enterprise directory under a commercial licence | | **Deployment** | Endpoint HTTPS proxy, gateway (`/gw//`), Rust library | Self-hosted Python proxy server; Python SDK | | **Protection method** | Reversible realistic fakes from a persona; typed tokens for reports; optional redaction | Block or redact via guardrail providers (Presidio, Lakera, Bedrock and others) | | **Restores responses** | Yes, automatically | With Presidio and `output_parse_pii` (placeholders) | | **Streaming** | Yes, SSE and NDJSON with split-fake reassembly | Reported gaps on some paths ([#22821](https://github.com/BerriAI/litellm/issues/22821)); current status not verified | | **Tool calls** | Yes, arguments restored locally; intent-aware for provider-hosted tools | Not documented for PII | | **Files** | DOCX, XLSX, PPTX masked in place; PDF to text; CSV, JSON, HTML | Not documented for PII | | **Self-hosted** | Yes, single binary, no account | Yes | | **Price model** | Free | Free core; Enterprise paid | ## What LiteLLM does well - **Unified API over many providers.** One OpenAI-compatible endpoint for OpenAI, Anthropic, Gemini, Bedrock, Azure, Vertex, local models and many more. Shadow does not translate between APIs. - **Platform features.** Virtual keys, per-team budgets, rate limits, fallbacks, retries, load balancing, caching and spend tracking. These are what most teams adopt LiteLLM for. - **Guardrail plug-ins.** LiteLLM can chain [Presidio](https://docs.litellm.ai/docs/tutorials/presidio_pii_masking), Lakera, Bedrock Guardrails, Aporia and others, configured per model or per key. - **Observability.** Callbacks to Langfuse, Helicone, OpenTelemetry and other tools. - **Large community.** It is one of the most widely deployed open-source AI gateways and moves quickly. ## Where Shadow is different ### Does LiteLLM restore PII in streamed responses? LiteLLM's Presidio integration can unmask placeholders in responses when `output_parse_pii` is enabled. Issue [#22821](https://github.com/BerriAI/litellm/issues/22821) reported that this did not happen on the Anthropic native path and in SSE streaming. It was closed in June 2026; whether every path is fixed was not verified at the review date, so test your own clients. Shadow restores fakes in [SSE and NDJSON streams](https://getshadow.dev/concepts/how-it-works/#streaming-hold-back) for OpenAI, Anthropic, Gemini, Cohere and Ollama-style formats, including fakes split across events. ### Placeholders or realistic fakes? With Presidio, the model sees `` or similar placeholders. Shadow sends realistic, consistent fakes from a [persona](https://getshadow.dev/concepts/personas/): a valid email at a fake company domain, a Luhn-valid card, a German IBAN that passes mod-97. The model reasons about plausible data, so answers stay useful, and the same customer keeps the same fake across sessions. ### Tool calls and intent Shadow restores [tool-call arguments](https://getshadow.dev/concepts/tool-calls/) locally, and its [intent policy](https://getshadow.dev/concepts/intent-policy/) handles bulk reports with typed tokens and message recipients and provider-hosted actions with an explicit, logged `keep`. ### Where masking happens LiteLLM is usually a shared server, so prompts travel to it in clear text before masking. Shadow can run on the endpoint itself, so data is masked before it leaves the machine, or as a sidecar next to a service. It also masks [Office uploads](https://getshadow.dev/concepts/file-masking/) in place. ## When should you choose LiteLLM? - You need one API over many model providers, with budgets, keys, fallbacks and spend tracking. - You want to chain several guardrail vendors centrally. - Block or placeholder redaction is enough for your traffic. ## When should you choose Shadow? - Masked values must be restored reliably, including in streams and tool calls. - You want realistic fakes so answer quality stays high. - Data should be masked on the endpoint, before it reaches any shared server. - You don't need multi-provider routing and want a single binary with no database. ## Can you use LiteLLM with Shadow? Yes. Two common layouts: 1. **Shadow behind LiteLLM.** Set each LiteLLM provider's `api_base` to Shadow's gateway (`http://127.0.0.1:8877/gw/openai/v1`, `/gw/anthropic`, and so on) or run the LiteLLM container with `HTTPS_PROXY` pointing at a Shadow sidecar. LiteLLM keeps routing, keys and budgets; Shadow masks what leaves and restores what comes back. 2. **Shadow on the client.** Developers run Shadow locally and point their tools at LiteLLM through Shadow, so prompts are masked before they reach the shared gateway. In either case, disable LiteLLM's Presidio masking for those routes so the two layers don't fight. See [integrating any endpoint](https://getshadow.dev/integrate/any-endpoint/) and [Docker and Kubernetes](https://getshadow.dev/integrate/docker-kubernetes/). ## Migrating from LiteLLM's Presidio guardrail 1. Keep LiteLLM for routing. 2. Remove the Presidio guardrail and `output_parse_pii` from the affected models. 3. Point those providers' `api_base` at Shadow's gateway routes, or add custom routes for Azure, Bedrock and self-hosted endpoints ([gateway mode](https://getshadow.dev/integrate/gateway-mode/)). 4. If you had custom Presidio recognizers, point Shadow's `[presidio]` section at your Analyzer. 5. Test streaming and tool-call flows end to end before switching production traffic. ## How do they compare on accuracy? With the Presidio guardrail, LiteLLM's detection accuracy is essentially Presidio's. Shadow's [benchmark](https://getshadow.dev/benchmarks/) does not test LiteLLM itself, but on the held-out set Microsoft Presidio leaked 20.74% of sensitive values, against 3.08% for Shadow's built-in rules and 1.35% for Shadow using Presidio as an extra detector. ## Sources - [LiteLLM Presidio PII masking tutorial](https://docs.litellm.ai/docs/tutorials/presidio_pii_masking) - [LiteLLM issue #22821](https://github.com/BerriAI/litellm/issues/22821) - [LiteLLM on GitHub](https://github.com/BerriAI/litellm) - [Shadow gateway mode](https://getshadow.dev/integrate/gateway-mode/) --- *Last reviewed: 2026-10-31. LiteLLM is a trademark of its owner; this comparison is based on public documentation as of the review date.* ## Frequently asked questions ### Is Shadow an alternative to LiteLLM? Not as a gateway. LiteLLM routes requests to 100+ model providers with keys, budgets, fallbacks and logging. Shadow does not route or budget; it masks sensitive values with reversible fakes and restores them. For PII masking specifically, Shadow can replace LiteLLM's Presidio guardrail, or sit in front of LiteLLM. ### Can LiteLLM unmask PII in responses? Yes, partly. With the Presidio guardrail and output_parse_pii enabled, LiteLLM replaces placeholders in the response with the original values. A GitHub issue (#22821) reported that unmasking was missing on the Anthropic native path and in SSE streaming; it was closed in June 2026, and whether a fix has shipped for every path was not verified. ### Does Shadow work with LiteLLM? Yes. Point LiteLLM's provider base URLs at Shadow's gateway, or run LiteLLM behind Shadow's HTTPS proxy. Shadow masks the request before it leaves the host and restores the response before LiteLLM returns it. ### Is LiteLLM free? The LiteLLM proxy core is open source under MIT. Some features live in an enterprise directory under a commercial licence. Shadow is free and open source under Apache-2.0. ### Which guardrails does LiteLLM support for PII? LiteLLM can call several guardrail providers, including Presidio, Lakera, Bedrock Guardrails and others. Most of them block or redact; restoration is only available with the Presidio integration. --- # Shadow vs LLM Guard — open-source LLM Guard alternative for PII masking Source: https://getshadow.dev/compare/llm-guard/ > Shadow vs LLM Guard (Protect AI). Both reverse PII in LLM responses; Shadow adds realistic fakes, streaming restore, tool calls and a proxy. **LLM Guard, from Protect AI, is an open-source Python toolkit of scanners that check LLM inputs and outputs, including an Anonymize/Deanonymize pair for PII. Shadow is an open-source privacy layer that does one job: it replaces sensitive values with realistic, reversible fakes before requests reach the provider and restores them in responses, streams and tool calls.** If you want a broad guardrail toolkit in Python, LLM Guard is a strong option. If you want PII masking with restoration for every tool on a machine that calls a supported AI API, without changing application code, Shadow is built for that. :::note[TL;DR] LLM Guard and Shadow share the same core idea for PII: replace on the way out, restore on the way back. LLM Guard does it inside your Python app with placeholders by default and also scans for prompt injection, toxicity and more. Shadow does it at the network layer with persona-based fakes, restores streamed responses and tool-call arguments, and masks Office uploads. ::: ## At a glance | | Shadow | LLM Guard | |---|---|---| | **Open source / licence** | Yes, Apache-2.0 | Yes, MIT (check licences of downloaded models) | | **Deployment** | Endpoint HTTPS proxy, gateway (`/gw//`), Rust library | Python library; API container (`laiyer/llm-guard-api`) | | **Protection method** | Reversible realistic fakes from a persona; typed tokens for reports; optional redaction | Placeholders by default; optional Faker values (`use_faker`) | | **Restores responses** | Yes, automatically | Yes, with the Deanonymize output scanner and a shared Vault | | **Streaming** | Yes, SSE and NDJSON with split-fake reassembly | Not documented | | **Tool calls** | Yes, arguments restored locally; intent-aware for provider-hosted tools | Not documented | | **Files** | DOCX, XLSX, PPTX masked in place; PDF to text; CSV, JSON, HTML | Not documented | | **Self-hosted** | Yes, single binary, no account | Yes | | **Price model** | Free | Free | ## What LLM Guard does well - **Breadth of scanners.** Beyond PII, LLM Guard has input scanners for prompt injection, secrets, banned topics, toxicity, token limits and more, and output scanners for things like relevance, bias and malicious URLs. Shadow covers only sensitive data. - **Reversible anonymisation in the same library.** The [Anonymize](https://protectai.github.io/llm-guard/input_scanners/anonymize/) and [Deanonymize](https://protectai.github.io/llm-guard/output_scanners/deanonymize/) scanners share a `Vault`, so a Python app can mask a prompt and restore the answer with a few lines of code. - **Transformer-based NER.** The Anonymize scanner combines regex patterns with NER models, which helps with free-text names that pattern matching misses. - **Published latency.** The project documents per-scanner latency on CPU and GPU, with ONNX options to speed things up. - **Easy to try.** `pip install llm-guard` or run the API container, and you have a working scanner pipeline. ## Where Shadow is different ### Placeholders or realistic fakes? LLM Guard's default replaces a name with a placeholder like `[REDACTED_PERSON_1]`. Its optional `use_faker` mode substitutes a fake value, but each value is generated independently. Shadow draws every fake from a persistent [persona](https://getshadow.dev/concepts/personas/): your company maps to one fake company, emails at your domain map to its domain, card numbers stay Luhn-valid and IBANs pass mod-97. Prompts stay coherent, and the same customer gets the same fake tomorrow. ### Does it restore streamed responses and tool calls? Most agent and chat clients stream. Shadow parses SSE and NDJSON events for OpenAI, Anthropic, Gemini, Cohere and Ollama-style APIs and restores fakes even when they are split across events, with a hold-back of only a few characters. See [streaming hold-back](https://getshadow.dev/concepts/how-it-works/#streaming-hold-back). Tool-call arguments are restored before your code runs the tool ([tool calls](https://getshadow.dev/concepts/tool-calls/)). LLM Guard's public docs do not describe either case. ### Intent-aware policy Shadow classifies each request as act, report, code or converse and picks a treatment per value: fakes for conversation, typed tokens such as `[PERSON_0007]` for bulk reports, and an explicit, logged `keep` for the recipient of a message or action and what a provider-hosted action needs. See [intent-aware policy](https://getshadow.dev/concepts/intent-policy/). ### No code changes LLM Guard runs where you call it. Shadow runs as an [HTTPS proxy](https://getshadow.dev/integrate/proxy-mode/) or [gateway](https://getshadow.dev/integrate/gateway-mode/), so IDE assistants, CLIs and desktop apps you don't control are covered too, when they call a [supported provider API](https://getshadow.dev/reference/providers/). ### Files and runtime Shadow masks DOCX, XLSX and PPTX uploads in place ([file masking](https://getshadow.dev/concepts/file-masking/)) and ships as a single Rust binary with an [encrypted vault](https://getshadow.dev/concepts/vault/). There is no Python runtime or model download required for the built-in detectors. ## When should you choose LLM Guard? - You need prompt-injection, toxicity or topic scanning as well as PII handling, in one Python toolkit. - You are building a Python application and want masking inside your own code path. - You want transformer NER for names without running a separate sidecar. ## When should you choose Shadow? - You need masking for tools you didn't write: IDE agents, CLIs, desktop apps, other teams' services. - Responses are streamed or agents call tools, and the answers must come back with real values. - You want realistic, consistent fakes rather than placeholders. - You need Office uploads masked before they leave. ## Can you use LLM Guard with Shadow? Yes. They solve overlapping but different problems. A common split is LLM Guard inside your application for prompt-injection and output-safety checks, with Shadow at the network edge handling PII masking and restoration for every outbound model call. If both anonymise, let only one of them do it, otherwise the second sees already-masked text. Shadow skips values that are already its own fakes, but it does not know LLM Guard's placeholders. ## Migrating from LLM Guard's Anonymize/Deanonymize 1. Keep any non-PII LLM Guard scanners you rely on. 2. Remove the `Anonymize` and `Deanonymize` scanners and their `Vault` from your pipeline. 3. Point your SDK at Shadow's gateway (`http://127.0.0.1:8877/gw//...`) or set `HTTPS_PROXY` ([integration modes](https://getshadow.dev/integrate/overview/)). 4. Add organisation names, customers and codenames as [dictionary terms](https://getshadow.dev/concepts/detection/#dictionary-terms); optionally enable the NER sidecar or Presidio for free-text names. 5. Compare coverage with `shadow scan` on a sample of real prompts before switching. ## How do they compare on accuracy? Both tools' detection depends on configuration and models. Shadow's [benchmark](https://getshadow.dev/benchmarks/) measures leak rate, over-masking, round-trip exactness and latency on a held-out test set, with published code and label mappings. On it, LLM Guard's Anonymize scanner leaked 29.26% of sensitive values, against 3.08% for Shadow's built-in rules (about 7.5 ms per document). LLM Guard's own documentation publishes latency figures; check them against your hardware. ## Sources - [LLM Guard documentation](https://protectai.github.io/llm-guard/) - [Anonymize input scanner](https://protectai.github.io/llm-guard/input_scanners/anonymize/) - [Deanonymize output scanner](https://protectai.github.io/llm-guard/output_scanners/deanonymize/) - [LLM Guard on GitHub](https://github.com/protectai/llm-guard) - [Shadow intent-aware policy](https://getshadow.dev/concepts/intent-policy/) --- *Last reviewed: 2026-10-31. LLM Guard and Protect AI are trademarks of their owner; this comparison is based on public documentation as of the review date.* ## Frequently asked questions ### Is Shadow an alternative to LLM Guard? For PII protection, yes. LLM Guard is a Python toolkit of input and output scanners, including Anonymize and Deanonymize. Shadow focuses only on sensitive data: it masks prompts with realistic, reversible fakes and restores them in responses, streams and tool calls, as a proxy, gateway or Rust library. LLM Guard also covers prompt injection, toxicity and other risks that Shadow does not. ### Can LLM Guard restore anonymised values in the response? Yes. The Anonymize input scanner stores originals in a Vault and the Deanonymize output scanner puts them back. By default the model sees placeholders; an optional use_faker setting substitutes fake values instead. ### Does LLM Guard support streaming responses? Public documentation does not describe deanonymising streamed responses, and streaming has appeared as a roadmap item. Shadow restores SSE and NDJSON streams on the fly, including fakes split across events. ### Is LLM Guard free for commercial use? LLM Guard itself is MIT-licensed. Check the licences of the models it downloads: one optional recognizer model used by the Anonymize scanner is published under CC-BY-NC-4.0, which restricts commercial use. ### Which is faster, LLM Guard or Shadow? LLM Guard publishes latency figures for its scanners, which depend on hardware and whether ONNX or a GPU is used. Shadow's built-in detectors are in-process Rust, about 7.5 ms per document on the held-out benchmark; optional NER models add their own time. See the Shadow benchmarks page for measured, reproducible numbers. --- # Shadow vs Microsoft Purview — open-source Purview DSPM for AI alternative Source: https://getshadow.dev/compare/microsoft-purview/ > Shadow vs Microsoft Purview DSPM for AI. Purview warns, blocks and audits Copilot and AI sites; Shadow masks API prompts and restores responses. **Microsoft Purview Data Security Posture Management (DSPM) for AI gives Microsoft 365 organisations visibility into AI use and applies DLP to Copilot and to third-party AI sites through Endpoint DLP and a browser extension, mainly by warning, blocking and auditing. Shadow is an open-source privacy layer that translates sensitive values in AI API requests into realistic, reversible fakes and restores them in the response.** If your AI use centres on Copilot and Microsoft-managed devices, Purview is the natural control. If developers, agents and services call model APIs directly, Shadow keeps that content private while answers stay useful. :::note[TL;DR] Purview is block-and-audit governance woven into Microsoft 365, with strong classification and reporting; its docs say it cannot scan files uploaded into prompts. Shadow masks prompts and files on the endpoint with reversible fakes and restores them in streams and tool calls. They are complementary. ::: ## At a glance | | Shadow | Microsoft Purview DSPM for AI | |---|---|---| | **Open source / licence** | Yes, Apache-2.0 | No, commercial | | **Deployment** | Endpoint HTTPS proxy, gateway (`/gw//`), Rust library | Microsoft 365 service, Endpoint DLP, browser extension (Edge, Chrome, Firefox) | | **Protection method** | Reversible realistic fakes from a persona; typed tokens for reports; optional redaction | Block, warn, audit | | **Restores responses** | Yes, automatically | No (block and audit model) | | **Streaming** | Yes, SSE and NDJSON with split-fake reassembly | Not applicable | | **Tool calls** | Yes, arguments restored locally; intent-aware for provider-hosted tools | Not applicable | | **Files** | DOCX, XLSX, PPTX masked in place; PDF to text; CSV, JSON, HTML | Cannot scan files uploaded into prompts (per docs) | | **Self-hosted** | Yes, single binary, no account | No, Microsoft cloud | | **Price model** | Free | Microsoft 365 E5-class or add-on licensing | ## What Purview does well - **Native Microsoft 365 coverage.** Purview understands Copilot interactions, sensitivity labels and Microsoft 365 content, which no third-party proxy can match. - **Classification and labels.** Sensitive information types, trainable classifiers and sensitivity labels are reused for AI policies, so classification work done for email and SharePoint pays off. - **Visibility into AI use.** DSPM for AI reports which AI apps are used and what kinds of sensitive data are involved, which helps prioritise risk. - **Endpoint and browser enforcement.** Endpoint DLP and the extension for Edge, Chrome and Firefox warn or block when users paste sensitive data into third-party AI sites. - **Audit, eDiscovery and compliance tooling.** Interactions can be audited and investigated with the same Purview tools used for the rest of Microsoft 365. - **Little extra infrastructure** for organisations already licensed and managed through Intune. ## Where Shadow is different ### Does Purview keep AI useful when data is sensitive? Purview's actions are block, warn and audit. That stops leaks but also stops the task. Shadow lets the request through with sensitive values translated: `jane@acme.com` becomes `hui.zhao@zhilan.cn`, a card number stays Luhn-valid, from a persistent [persona](https://getshadow.dev/concepts/personas/). The model answers, and Shadow restores the real values. ### Files uploaded into prompts Microsoft's documentation says Purview cannot scan files uploaded into prompts. Shadow masks DOCX, XLSX and PPTX uploads in place, keeps them valid documents, and turns PDFs into masked text. See [file masking](https://getshadow.dev/concepts/file-masking/). ### API, agent and developer traffic Purview's AI controls focus on Copilot and browser use, which Shadow does not cover. Shadow intercepts model API calls to [supported providers](https://getshadow.dev/reference/providers/) from any tool via [proxy mode](https://getshadow.dev/integrate/proxy-mode/) or [gateway mode](https://getshadow.dev/integrate/gateway-mode/), restores [streams](https://getshadow.dev/concepts/how-it-works/#streaming-hold-back) and [tool-call arguments](https://getshadow.dev/concepts/tool-calls/), and applies an [intent-aware policy](https://getshadow.dev/concepts/intent-policy/). ### Not tied to one vendor's stack Shadow runs on macOS, Linux and Windows, in containers and inside Rust services, with no account and no telemetry. ## When should you choose Purview? - Your organisation is standardised on Microsoft 365 and Copilot. - You need classification, labels, audit and eDiscovery that span AI and the rest of your content. - You manage devices with Intune and want warn-or-block on third-party AI sites. - You already hold the licences. ## When should you choose Shadow? - Developers, agents and services call OpenAI, Anthropic, Gemini or other APIs directly. - Users must keep working with sensitive data, with real values restored in answers. - Files attached to prompts must be masked, not just blocked. - You need an open-source, self-hosted control that works outside Microsoft-managed endpoints. ## Using Purview and Shadow together 1. Keep Purview for Copilot governance, classification, labels and audit. 2. Use Purview's Endpoint DLP and browser extension for web-chat AI on managed devices. 3. Install Shadow on developer machines, CI runners and services that call model APIs. 4. Reuse your Purview classification thinking: add organisation names, codenames and customer names to Shadow's [dictionary terms](https://getshadow.dev/concepts/detection/#dictionary-terms). ## How do they compare on accuracy? Microsoft does not publish Purview AI detection results on public PII datasets. Shadow publishes its methodology on the [benchmarks page](https://getshadow.dev/benchmarks/). ## Sources - [Microsoft Purview DSPM for AI](https://learn.microsoft.com/purview/ai-microsoft-purview) - [Message Center RM561892 (DLP for Copilot prompts)](https://mc.merill.net/message/RM561892) - [Shadow: file masking](https://getshadow.dev/concepts/file-masking/) - [Shadow: security model](https://getshadow.dev/reference/security/) --- *Last reviewed: 2026-10-31. Microsoft, Microsoft 365, Copilot and Purview are trademarks of their owner; this comparison is based on public documentation as of the review date.* ## Frequently asked questions ### Is Shadow an alternative to Microsoft Purview for AI? They cover different ground. Purview DSPM for AI governs Microsoft 365 Copilot and employee use of third-party AI sites in Microsoft-managed browsers and endpoints, mainly by warning, blocking and auditing. Shadow masks AI API traffic with reversible fakes on the machines where it runs. Shadow does not cover Copilot inside Microsoft 365. ### Can Purview scan files uploaded into ChatGPT prompts? Microsoft's documentation states that Purview cannot scan files uploaded into prompts on third-party AI sites. Shadow masks DOCX, XLSX and PPTX uploads in place and turns PDFs into masked text before they leave. ### Does Purview redact or block AI prompts? Purview's controls for AI are block, warn and audit, via Endpoint DLP and the browser extension for Edge, Chrome and Firefox. DLP for Copilot prompts was announced with a general-availability target of mid-2026 in a Message Center post; check Microsoft's current status. Reversible pseudonymisation is not documented. ### How much does Purview DSPM for AI cost? It is part of Microsoft 365 E5-class or Purview add-on licensing; exact entitlements vary by plan. Shadow is free under Apache-2.0. ### Does Shadow make us compliant with GDPR? No tool does that alone. Shadow helps meet data-minimisation requirements by replacing detected personal data with fakes before requests reach AI providers. Purview helps with discovery, classification and audit. Compliance depends on your whole programme. --- # Shadow vs NeMo Guardrails — NeMo Guardrails alternative for PII masking Source: https://getshadow.dev/compare/nemo-guardrails/ > Shadow vs NVIDIA NeMo Guardrails for PII. NeMo masks or blocks with labels; Shadow uses reversible fakes and restores responses and tool calls. **NeMo Guardrails, from NVIDIA, is an open-source framework for adding programmable rails to LLM applications: topical, safety, dialogue, retrieval and PII rails. Shadow is an open-source privacy layer that replaces sensitive values with realistic, reversible fakes before requests leave, and restores them in responses, streams and tool calls.** NeMo is the broader framework; Shadow goes deeper on one problem, keeping data out of provider requests without breaking answers. :::note[TL;DR] NeMo Guardrails detects PII (via Presidio or GLiNER-PII) and masks it with labels like `[EMAIL]` or blocks the message, with no restoration. Shadow translates PII into consistent fakes and turns them back into real values in the answer, including streamed text and tool-call arguments. Use NeMo for conversation and safety rails; use Shadow for reversible data masking. ::: ## At a glance | | Shadow | NeMo Guardrails | |---|---|---| | **Open source / licence** | Yes, Apache-2.0 | Yes, Apache-2.0 | | **Deployment** | Endpoint HTTPS proxy, gateway (`/gw//`), Rust library | Python framework or guardrails server in front of your app | | **Protection method** | Reversible realistic fakes from a persona; typed tokens for reports; optional redaction | Block, or mask with entity labels (`[EMAIL]`) | | **Restores responses** | Yes, automatically | No | | **Streaming** | Yes, SSE and NDJSON with split-fake reassembly | Output rails on streams; no unmasking | | **Tool calls** | Yes, arguments restored locally; intent-aware for provider-hosted tools | Not for PII; retrieval rails cover RAG chunks | | **Files** | DOCX, XLSX, PPTX masked in place; PDF to text; CSV, JSON, HTML | No | | **Self-hosted** | Yes, single binary, no account | Yes (GLiNER-PII NIM route needs an NGC key) | | **Price model** | Free | Free | ## What NeMo Guardrails does well - **A full guardrail framework.** Colang lets you define dialogue flows, topical boundaries, jailbreak and moderation checks, fact-checking and hallucination rails. PII is one rail among many. - **Three places to apply PII rails.** The [PII detection rail](https://docs.nvidia.com/nemo/guardrails/latest/configure-guardrails/guardrail-catalog/pii-detection) can run on user input, on model output and on retrieved chunks, which covers RAG leaks that a pure prompt proxy may not see in the same way. - **Pluggable detection.** You can choose Presidio or NVIDIA's GLiNER-PII model, and swap configuration per application. - **NVIDIA ecosystem.** Integrates with NIM microservices and NVIDIA's model catalog, which suits teams already standardised there. - **Open source and active.** The project lives at `NVIDIA-NeMo/Guardrails` on GitHub. ## Where Shadow is different ### What happens to masked values in the answer? NeMo replaces `jane@acme.com` with `[EMAIL]`. The model can no longer tell two addresses apart, and the answer comes back with labels instead of your data. Shadow replaces it with `hui.zhao@zhilan.cn`, a fake drawn from a persistent [persona](https://getshadow.dev/concepts/personas/), and restores the original in the response. Answers mention your real customers, not placeholders. ### Streaming and tool calls Shadow restores fakes in [server-sent event streams](https://getshadow.dev/concepts/how-it-works/#streaming-hold-back) even when they are split across events, and restores [tool-call arguments](https://getshadow.dev/concepts/tool-calls/) before your code runs the tool, so an agent's own `send_email` tool reaches the real address while the provider only saw a fake. ### Intent-aware policy Shadow decides per request whether values become fakes, typed tokens (`[PERSON_0007]` for bulk reports), a logged `keep` for message recipients and provider-hosted action targets, or a redaction. See [intent-aware policy](https://getshadow.dev/concepts/intent-policy/). ### Covers tools you don't control NeMo Guardrails wraps an application you build. Shadow sits at the network layer as an [HTTPS proxy](https://getshadow.dev/integrate/proxy-mode/) or [gateway](https://getshadow.dev/integrate/gateway-mode/), so IDE agents, CLIs and third-party apps that call a [supported provider API](https://getshadow.dev/reference/providers/) are covered too. It also masks [uploaded Office files](https://getshadow.dev/concepts/file-masking/) in place. ## When should you choose NeMo Guardrails? - You need dialogue control, topical rails, jailbreak checks or fact-checking, not only PII handling. - You want PII checks on retrieved RAG chunks as part of the same framework. - You are building a Python application and are comfortable with Colang configuration. - Masking with labels, with no restoration, is acceptable for your use case. ## When should you choose Shadow? - Answers must contain the real values, restored locally. - You rely on streaming or agent tool calls. - You need coverage for tools and services you did not write. - You want consistent fakes across sessions and Office uploads masked. ## Can you use NeMo Guardrails with Shadow? Yes. Put NeMo Guardrails in your application for dialogue and safety rails, and route its outbound model calls through Shadow's gateway. Shadow masks every request before it leaves and restores the answer before NeMo's output rails see it. If you do this, disable NeMo's PII masking on input, otherwise Shadow receives `[EMAIL]` labels instead of values it can translate and restore. Both can share detectors: Shadow can call [Presidio](https://getshadow.dev/concepts/detection/#presidio), and its optional open-weight sidecar runs OpenAI Privacy Filter and `urchade/gliner_multi_pii-v1`, both Apache-2.0. See [detection](https://getshadow.dev/concepts/detection/). ## Migrating NeMo PII rails to Shadow 1. Keep your non-PII rails. 2. Remove the PII detection rail from input (and output, if it only masked values you now want restored). 3. Configure your LLM provider's base URL to Shadow's gateway, for example `http://127.0.0.1:8877/gw/openai/v1` ([gateway mode](https://getshadow.dev/integrate/gateway-mode/)). 4. If you used Presidio with custom recognizers, point Shadow's `[presidio]` section at the same Analyzer. 5. Validate with `shadow scan` and the per-request log. ## How do they compare on accuracy? Detection accuracy mostly depends on the detector each tool is configured with. The GLiNER-PII model card reports its own F1 figures, and third-party results on other benchmarks differ. Shadow's [benchmark](https://getshadow.dev/benchmarks/) compares leak rate, over-masking and round-trip exactness with published code and label mappings. ## Sources - [NeMo Guardrails PII detection](https://docs.nvidia.com/nemo/guardrails/latest/configure-guardrails/guardrail-catalog/pii-detection) - [NeMo Guardrails on GitHub](https://github.com/NVIDIA-NeMo/Guardrails) - [nvidia/gliner-PII model card](https://huggingface.co/nvidia/gliner-PII) - [Shadow tool calls and agents](https://getshadow.dev/concepts/tool-calls/) --- *Last reviewed: 2026-10-31. NVIDIA and NeMo are trademarks of their owner; this comparison is based on public documentation as of the review date.* ## Frequently asked questions ### Is Shadow an alternative to NeMo Guardrails? For sensitive-data protection, partly. NeMo Guardrails is a general guardrail framework for dialogue, topical, safety and retrieval rails, with PII detection as one rail. Shadow does only data protection: it replaces sensitive values with reversible fakes and restores them in responses. Many teams could use both. ### Does NeMo Guardrails restore masked PII in the response? Its PII rails detect and either mask values with labels such as [EMAIL] or block the message. Public documentation does not describe restoring the original values in the model's answer. ### Which PII detectors does NeMo Guardrails use? The documented PII rails can use Microsoft Presidio or the NVIDIA GLiNER-PII model served as a NIM. Shadow can also use Presidio, and its optional open-weight sidecar uses OpenAI Privacy Filter and a GLiNER multilingual PII model. ### Is NeMo Guardrails free? The framework is open source and free to self-host. Using the hosted GLiNER-PII NIM requires an NVIDIA NGC key. Shadow is free and open source under Apache-2.0. ### Can NeMo Guardrails mask PII in streaming responses? NeMo Guardrails supports output rails on streamed responses, but those rails check or mask output; they do not restore earlier masked inputs. Shadow restores streamed responses on the fly. --- # Shadow vs Netskope — open-source Netskope GenAI DLP alternative for AI traffic Source: https://getshadow.dev/compare/netskope/ > Shadow vs Netskope One GenAI DLP. Netskope blocks, coaches and redacts inline; Shadow restores fakes in streams, tool calls and files. **Netskope One is a SASE platform with GenAI DLP: it sees which AI apps employees use, then blocks, coaches or redacts sensitive data inline. Shadow is an open-source privacy layer for AI API traffic: it swaps sensitive values for realistic, reversible fakes and restores them in responses, streams, tool calls and files.** If you need company-wide visibility and policy for AI use, Netskope is built for that. If you need developer tools, agents and services to keep working while their prompts are masked, Shadow covers the gaps Netskope's own docs list. :::note[TL;DR] Netskope governs AI use across a managed fleet and can redact with mask, FPE or hash, but its docs say redaction does not apply to SSE streaming responses, MCP or file content. Shadow translates values into consistent fakes, restores them in streams and tool calls, and masks Office uploads in place. Many organisations would run both. ::: ## At a glance | | Shadow | Netskope One GenAI DLP | |---|---|---| | **Open source / licence** | Yes, Apache-2.0 | No, commercial | | **Deployment** | Endpoint HTTPS proxy, gateway (`/gw//`), Rust library | SASE inline (cloud security service, steering client) | | **Protection method** | Reversible realistic fakes from a persona; typed tokens for reports; optional redaction | Block, coach, allow; redact with mask, FPE or hash per DLP entity | | **Restores responses** | Yes, automatically | Not documented | | **Streaming** | Yes, SSE and NDJSON with split-fake reassembly | Redaction not applied to SSE streaming responses (per docs) | | **Tool calls** | Yes, arguments restored locally; intent-aware for provider-hosted tools | Redaction not applied to MCP (per docs) | | **Files** | DOCX, XLSX, PPTX masked in place; PDF to text; CSV, JSON, HTML | Upload controls; redaction not applied to file content (per docs) | | **Self-hosted** | Yes, single binary, no account | No, delivered as a cloud service | | **Price model** | Free | Quote | ## What Netskope does well - **Fleet-wide visibility.** Netskope steers traffic from managed devices through its cloud, so security teams see which AI apps are used, by whom, and how often, including shadow AI that nobody approved. Shadow only sees traffic on machines where it is installed. - **One DLP policy for everything.** The same DLP profiles, identifiers and exact-data-match rules apply to web, SaaS, email and AI apps. Teams that already tuned Netskope DLP get AI coverage without a second rule set. - **Coaching.** Users can see a pop-up explaining why a prompt was blocked or what they should do instead, and can justify an exception. This changes behaviour, not just traffic. - **Inline redaction choices.** Per DLP entity, Netskope can mask, apply format-preserving encryption or hash, which is more than most SASE products document. - **Central administration and reporting.** Policy is set once in a console, incidents flow to the security operations workflow and SIEM, and reporting supports audits. - **Browser and web chat coverage.** Employees using ChatGPT, Gemini or Copilot in the browser are covered without per-app configuration. ## Where Shadow is different ### Does Netskope work with streaming AI responses and agents? Netskope's redaction documentation lists exclusions: file content, SSE streaming responses and MCP traffic. Most coding assistants and agent frameworks stream responses and call tools, so those flows fall outside inline redaction. Shadow restores fakes in [server-sent event streams](https://getshadow.dev/concepts/how-it-works/#streaming-hold-back), including fakes split across events, and restores [tool-call arguments](https://getshadow.dev/concepts/tool-calls/) locally before your tools run. ### Translate rather than block Blocking breaks the tool, and users route around it. Shadow replaces `jane@acme.com` with `hui.zhao@zhilan.cn` from a persistent [persona](https://getshadow.dev/concepts/personas/); card numbers stay Luhn-valid and IBANs pass mod-97. The model sees a coherent prompt and the answer comes back with real values. Netskope's FPE preserves format too, but its docs do not describe restoring values in the response. ### Intent-aware policy Shadow classifies each request as act, report, code or converse and picks a treatment per value: a fake, a typed token such as `[PERSON_0007]`, the real value for a message recipient or provider-hosted action target, or redaction. See [intent-aware policy](https://getshadow.dev/concepts/intent-policy/). ### Files inside AI requests Shadow masks DOCX, XLSX and PPTX uploads in place, extracts PDF text, and handles base64 and multipart payloads. See [file masking](https://getshadow.dev/concepts/file-masking/). ### Local, open source, free Shadow is a single Rust binary with no account and no telemetry. Detection runs locally, so plaintext is never sent to a security cloud for inspection. You can audit the code. ## When should you choose Netskope? - You need to see and govern AI use across hundreds or thousands of managed devices. - You want one DLP policy across web, SaaS and AI, with coaching and incident workflows. - Your main risk is employees pasting into web chat UIs. - You need central reporting for auditors and integration with your SOC. ## When should you choose Shadow? - Developers, agents and services call model APIs and rely on streaming and tool calls. - You want answers to stay useful, with real values restored, rather than blocked or one-way redacted. - You want Office files in AI requests masked before they leave. - You need a self-hosted, open-source control that does not send plaintext to a third-party inspection cloud. - You don't have a SASE budget, or you need coverage for machines outside the managed fleet, such as CI runners and containers. ## Using Netskope and Shadow together The two tools sit at different layers and can coexist: 1. Keep Netskope for browser, SaaS and web-chat AI use on managed devices. 2. Install Shadow on developer machines, CI runners and servers that call model APIs, in [proxy mode](https://getshadow.dev/integrate/proxy-mode/) or [gateway mode](https://getshadow.dev/integrate/gateway-mode/). 3. Decide which layer decrypts the AI API hosts Shadow intercepts. Either add those hosts to a Netskope SSL-inspection exception for the devices running Shadow, or point Shadow's `upstream_proxy` at the Netskope path so it sees already-masked traffic. 4. Use Netskope reporting for fleet visibility, and Shadow's per-request logs (counts and kinds, never values) for API traffic. ## How do they compare on accuracy? Netskope does not publish PII detection benchmarks on public datasets, and neither product can be measured fairly without a tenant. Shadow's [benchmark](https://getshadow.dev/benchmarks/) publishes its method, datasets and code so results can be reproduced. ## Sources - [Netskope: Redact sensitive data](https://docs.netskope.com/en/redact-sensitive-data/) - [Netskope One platform](https://www.netskope.com/netskope-one) - [Shadow: intent-aware policy](https://getshadow.dev/concepts/intent-policy/) - [Shadow: security model](https://getshadow.dev/reference/security/) --- *Last reviewed: 2026-10-31. Netskope is a trademark of its owner; this comparison is based on public documentation as of the review date.* ## Frequently asked questions ### Is Shadow a free alternative to Netskope? For AI API traffic, partly. Shadow is free and open source (Apache-2.0) and masks prompts to AI providers with reversible fakes on the machines where it runs. Netskope One is a commercial SASE platform that covers web, cloud apps and AI apps across a whole managed fleet, with central policy and reporting. Shadow does not replace that breadth. ### Can Netskope redact sensitive data in ChatGPT prompts? Yes. Netskope's documentation describes inline redaction that can mask, format-preserving encrypt or hash data per DLP entity. The same documentation says redaction does not apply to file content, server-sent event (SSE) streaming responses or MCP traffic. ### Does Netskope restore redacted values in the AI response? Public documentation does not describe restoring redacted values in responses. Shadow restores its fakes in JSON responses, SSE streams and tool-call arguments on the local machine. ### How much does Netskope cost? Netskope does not publish list prices; pricing is by quote and depends on the modules licensed. Shadow is free to self-host. ### Can Netskope and Shadow run together? Yes. A common pattern is Netskope for employee browser and SaaS traffic on managed devices, and Shadow on developer machines, CI runners and services that call model APIs. Exclude the AI API hosts Shadow handles from Netskope TLS inspection, or chain the two, so certificates are not intercepted twice. --- # Shadow vs Nightfall — open-source Nightfall AI alternative for LLM prompts Source: https://getshadow.dev/compare/nightfall/ > Shadow vs Nightfall AI DLP. Nightfall blocks or redacts prompts in the browser and on endpoints; Shadow masks with reversible fakes and restores answers. **Nightfall is a cloud DLP platform whose browser extension and endpoint agents redact or block sensitive data before employees submit it to ChatGPT, Claude and other AI tools, alongside DLP for SaaS apps. Shadow is an open-source privacy layer that translates sensitive values in AI API requests into realistic, reversible fakes and restores them in the response.** Nightfall protects what people type and upload in AI web apps; Shadow protects what tools, agents and services send through model APIs, without losing the meaning of the data. :::note[TL;DR] Nightfall is strong at employee-facing DLP: browser and endpoint coverage, SaaS integrations, central alerting. Its AI protection redacts or blocks, one way. Shadow uses consistent, format-preserving fakes, restores them in streams, tool calls and files, and runs locally for free. They cover different traffic. ::: ## At a glance | | Shadow | Nightfall | |---|---|---| | **Open source / licence** | Yes, Apache-2.0 | No, commercial | | **Deployment** | Endpoint HTTPS proxy, gateway (`/gw//`), Rust library | SaaS console, browser extension, macOS and Windows agents | | **Protection method** | Reversible realistic fakes from a persona; typed tokens for reports; optional redaction | Block or redact | | **Restores responses** | Yes, automatically | No | | **Streaming** | Yes, SSE and NDJSON with split-fake reassembly | Not applicable (acts before submission) | | **Tool calls** | Yes, arguments restored locally; intent-aware for provider-hosted tools | Not documented | | **Files** | DOCX, XLSX, PPTX masked in place; PDF to text; CSV, JSON, HTML | Upload inspection and blocking | | **Self-hosted** | Yes, single binary, no account | No, cloud service with local extension and agents | | **Price model** | Free | Quote | ## What Nightfall does well - **Covers the browser where people actually paste.** The extension acts before submission in AI web apps, which is where many leaks happen. - **Endpoint agents.** macOS and Windows agents extend coverage beyond the browser. - **Broad SaaS DLP.** Nightfall also scans SaaS tools such as chat, ticketing and code platforms, so AI is one part of a wider data-protection programme. - **Central policy and alerting.** Security teams manage detectors and policies in one console, review incidents and route alerts to existing workflows. - **Redaction as well as blocking.** Removing just the sensitive part lets users continue with the rest of a prompt, which is friendlier than a hard block. - **Fast to roll out.** An extension pushed through device management covers many users quickly. ## Where Shadow is different ### Is redaction enough to keep answers useful? Redaction replaces `Jane Doe` with a placeholder or removes it. The model can no longer tell two people apart or write to the right address, and the answer has gaps. Shadow replaces values with realistic fakes from a persistent [persona](https://getshadow.dev/concepts/personas/), so `jane@acme.com` becomes `hui.zhao@zhilan.cn` and a card number stays Luhn-valid. The model answers normally and Shadow puts the real values back. ### API, agent and developer traffic A browser extension does not see API calls from IDE assistants, CLIs, agents, CI jobs or back-end services. Shadow intercepts those calls to [supported AI APIs](https://getshadow.dev/reference/providers/) through [proxy mode](https://getshadow.dev/integrate/proxy-mode/) or [gateway mode](https://getshadow.dev/integrate/gateway-mode/) and restores [SSE streams](https://getshadow.dev/concepts/how-it-works/#streaming-hold-back) and [tool-call arguments](https://getshadow.dev/concepts/tool-calls/). ### Intent-aware policy Shadow distinguishes agent actions, bulk reports, code and conversation, and picks a fake, a typed token, a kept target or redaction per value. See [intent-aware policy](https://getshadow.dev/concepts/intent-policy/). ### Office files masked, not refused Shadow masks DOCX, XLSX and PPTX uploads in place and turns PDFs into masked text. See [file masking](https://getshadow.dev/concepts/file-masking/). ### Local and open source No account, no telemetry, no plaintext sent to a vendor cloud for inspection. ## When should you choose Nightfall? - Your main risk is employees pasting into AI web apps on managed devices. - You want one DLP platform for AI and SaaS apps with central alerting. - You need quick, low-touch rollout across many users. - One-way redaction or blocking fits your workflows. ## When should you choose Shadow? - Developers, agents and services call model APIs directly. - You need real values restored in answers, streams and tool calls. - You want Office uploads masked before they leave. - You prefer an open-source, self-hosted control with no per-seat cost. ## Using Nightfall and Shadow together 1. Keep Nightfall's extension and agents for web chat and SaaS apps. 2. Install Shadow on developer machines, CI runners and services that call model APIs. 3. Keep Nightfall's central incident view for employee behaviour; use Shadow's per-request logs (counts and kinds, never values) for API traffic. 4. Share your sensitive term lists: add customer names and codenames to Shadow's [dictionary terms](https://getshadow.dev/concepts/detection/#dictionary-terms). ## How do they compare on accuracy? Independent results for Nightfall on public PII datasets are not available; vendor comparisons exist but use old versions. Shadow publishes its methodology and code on the [benchmarks page](https://getshadow.dev/benchmarks/). ## Sources - [Nightfall DLP for browsers (Firefox add-on listing)](https://addons.mozilla.org/addon/nightfall-dlp-for-browsers/) - [Nightfall AI](https://www.nightfall.ai/) - [Shadow: tool calls and agents](https://getshadow.dev/concepts/tool-calls/) - [Shadow: file masking](https://getshadow.dev/concepts/file-masking/) --- *Last reviewed: 2026-10-31. Nightfall is a trademark of its owner; this comparison is based on public documentation as of the review date.* ## Frequently asked questions ### Is Shadow a free alternative to Nightfall? For AI API traffic, yes in part. Shadow is free and open source (Apache-2.0) and masks prompts to AI providers with reversible fakes. Nightfall is a commercial DLP platform that covers browsers, endpoints and SaaS apps with central policy and alerting, which Shadow does not offer. ### Does Nightfall redact data before it reaches ChatGPT? Yes. Nightfall's browser extension and its macOS and Windows agents can redact or block sensitive data before it is submitted to ChatGPT, Claude and other AI tools, according to its public listing. ### Does Nightfall restore redacted values in the response? Not that public documentation describes. Redaction in Nightfall is one way. Shadow replaces values with realistic fakes and restores the real values in responses, streams and tool calls. ### How much does Nightfall cost? Nightfall's enterprise pricing is by quote. Shadow is free to self-host. ### Can Nightfall and Shadow be used together? Yes. Nightfall can cover web chat and SaaS apps on employee devices while Shadow masks API, agent and developer traffic, which a browser extension does not see. --- # Shadow vs OpenAI Privacy Filter — using the open-weight PII model in a proxy Source: https://getshadow.dev/compare/openai-privacy-filter/ > Shadow vs OpenAI Privacy Filter. Privacy Filter is a free PII detection model; Shadow can run it as a detector and adds reversible masking and restore. **OpenAI Privacy Filter is a free, open-weight model (Apache-2.0) that detects personal data in text. Shadow is an open-source privacy layer for AI traffic that detects sensitive values, replaces them with realistic, reversible fakes, and restores them in responses.** The two fit together: Shadow's optional NER sidecar can run Privacy Filter, so you get its recall on names and addresses plus Shadow's masking, vault, policy and restoration. :::note[TL;DR] Privacy Filter answers "where is the PII in this text?" Shadow answers "how do I send this to a model without exposing it, and get a useful answer back?" Use Privacy Filter inside Shadow, not instead of it, unless you only need detection. ::: ## At a glance | | Shadow | OpenAI Privacy Filter | |---|---|---| | **Open source / licence** | Yes, Apache-2.0 | Yes, Apache-2.0 (model weights) | | **Deployment** | Endpoint HTTPS proxy, gateway (`/gw//`), Rust library | Model you run with Transformers (Python) or Transformers.js | | **Protection method** | Reversible realistic fakes; typed tokens for reports; optional redaction | Detector only: returns labelled spans | | **Restores responses** | Yes, automatically | n/a | | **Streaming** | Yes, SSE and NDJSON | n/a | | **Tool calls** | Yes, restored locally; intent-aware | n/a | | **Files** | DOCX, XLSX, PPTX in place; PDF to text | n/a (text in, spans out) | | **Self-hosted** | Yes | Yes | | **Price model** | Free | Free | ## What OpenAI Privacy Filter does well - **Strong reported accuracy.** OpenAI publishes high self-reported scores on the pii-masking-300k dataset family. On Shadow's held-out benchmark it was the strongest standalone detector tested, leaking 11.01% of sensitive values. - **Efficient architecture.** About 1.5B total parameters with roughly 50M active per token, so it is practical on a CPU for moderate volumes and fast on a GPU. - **Permissive licence.** Apache-2.0 weights, usable commercially without the non-commercial terms that affect some PII models trained on restricted datasets. - **Runs anywhere.** Python with Transformers, or in the browser and Node with Transformers.js. - **Useful labels.** Its eight labels include `secret` and `account_number`, not only classic named entities, which suits developer and finance content. ## Where Shadow is different ### Is a detector enough to protect LLM prompts? No. After detection you still need to decide what to replace each span with, keep a mapping so the answer can be restored, handle values split across streamed events, restore tool-call arguments before your code acts, and intercept traffic from tools you didn't write. Shadow does all of that. Privacy Filter is one possible input to it. ### Realistic, reversible replacements Shadow turns detected values into consistent, format-preserving fakes from a persistent [persona](https://getshadow.dev/concepts/personas/) and stores the pairs in an [encrypted vault](https://getshadow.dev/concepts/vault/). Responses, [streams](https://getshadow.dev/concepts/how-it-works/#streaming-hold-back) and [tool calls](https://getshadow.dev/concepts/tool-calls/) are restored automatically. ### Structured values and your own terms Regex and checksum detectors are more reliable than a language model for IBANs (mod-97), card numbers (Luhn), IPs, MACs and API key formats. Shadow's built-in detectors cover these, plus a dictionary for company names, customers and codenames no public model knows, and local machine facts like your OS username and hostname. See [detection](https://getshadow.dev/concepts/detection/). ### Intent and files Shadow's [intent policy](https://getshadow.dev/concepts/intent-policy/) picks fakes, typed tokens or keep per value; [file masking](https://getshadow.dev/concepts/file-masking/) covers DOCX, XLSX and PPTX uploads. ### Fail-open design If the sidecar is slow or down, Shadow keeps masking with its built-in detectors, capped by `timeout_ms`, so a model hiccup does not stop traffic or bypass masking entirely. ## When should you use OpenAI Privacy Filter on its own? - You are building your own redaction pipeline, for example for logs or datasets. - You need detection in a browser or Node app via Transformers.js. - You only need to flag or count PII, not mask live traffic. ## When should you choose Shadow? - You want prompts to AI providers masked and answers restored without writing that pipeline. - You need streaming, tool calls and file uploads handled. - You want structured identifiers and your own terms covered alongside NER. ## How do you run Privacy Filter inside Shadow? Shadow ships an optional open-weight sidecar, `ml/serve_hf.py`. Its backends are `privacy-filter` (OpenAI Privacy Filter), `gliner` (`urchade/gliner_multi_pii-v1`, Apache-2.0) and `ensemble` (both, the default). Models download on first run. ```sh cd ml pip install -r requirements-hf.txt python serve_hf.py --backend ensemble --host 127.0.0.1 --port 8878 ``` ```toml [model_detector] enabled = true url = "http://127.0.0.1:8878/detect" # must be loopback timeout_ms = 300 min_score = 0.6 ``` Shadow merges the sidecar's spans with its own, resolves overlaps and applies its public-name allow list, so over-eager spans on well-known names are dropped. The sidecar binds to loopback only. See [detection](https://getshadow.dev/concepts/detection/#local-ner-sidecar). :::tip Use `ensemble` when your content includes non-English names or organisation names: Privacy Filter is strongest on English, and GLiNER adds multilingual and organisation coverage. See [Shadow vs GLiNER PII](https://getshadow.dev/compare/gliner-pii/). ::: ## Migrating from a Privacy Filter-based redaction script 1. Start the sidecar with `--backend privacy-filter` (or `ensemble`) and enable `[model_detector]`. 2. Remove your own span-replacement and post-processing code around model calls. 3. Route model calls through Shadow's [gateway](https://getshadow.dev/integrate/gateway-mode/) or [proxy](https://getshadow.dev/integrate/proxy-mode/). 4. Add your organisation's terms to the [dictionary](https://getshadow.dev/concepts/detection/#dictionary-terms). 5. Check results with `shadow scan --json`, which shows which detector found each span. ## How do they compare on accuracy? Privacy Filter's published numbers are self-reported on a dataset family whose licence restricts redistribution, and it is primarily English. Shadow's [benchmark](https://getshadow.dev/benchmarks/) evaluates Privacy Filter as a baseline and Shadow with and without the sidecar on a licence-clean held-out set, with code and label mappings published: Privacy Filter alone leaked 11.01% of sensitive values, Shadow's built-in rules 3.08%, and Shadow with the sidecar 0.08%. ## Sources - [openai/privacy-filter on Hugging Face](https://huggingface.co/openai/privacy-filter) - [Introducing OpenAI Privacy Filter](https://openai.com/blog/introducing-openai-privacy-filter) - [Shadow detection: local NER sidecar](https://getshadow.dev/concepts/detection/#local-ner-sidecar) --- *Last reviewed: 2026-10-31. OpenAI is a trademark of its owner; this comparison is based on public documentation as of the review date.* ## Frequently asked questions ### What is OpenAI Privacy Filter? An open-weight PII detection model released by OpenAI in April 2026 under Apache-2.0. It labels eight kinds of span (account numbers, addresses, emails, people, phones, URLs, dates and secrets) and runs locally with Hugging Face Transformers or Transformers.js. ### Is Shadow an alternative to OpenAI Privacy Filter? They do different jobs. Privacy Filter is a detector: it finds spans and stops there. Shadow is a privacy layer that intercepts AI traffic, replaces values with reversible fakes and restores them in responses. Shadow's optional NER sidecar can run Privacy Filter as one of its detectors. ### How do I use OpenAI Privacy Filter with Shadow? Run Shadow's open-weight sidecar (ml/serve_hf.py) with the privacy-filter or ensemble backend on 127.0.0.1:8878, then enable [model_detector] in Shadow's configuration. Shadow merges its findings with the built-in detectors. ### Does OpenAI Privacy Filter send data to OpenAI? No. The weights are downloaded once and the model runs on your hardware. Inference does not call an OpenAI API. ### How accurate is OpenAI Privacy Filter? OpenAI publishes its own self-reported scores. On Shadow's held-out benchmark, Privacy Filter alone leaked 11.01% of sensitive values, against 3.08% for Shadow's built-in rules and 0.08% for Shadow with the open-weight NER sidecar, which includes Privacy Filter. It is primarily English. --- # Shadow vs Palo Alto AI Access Security — open-source alternative for AI APIs Source: https://getshadow.dev/compare/palo-alto-ai-access/ > Shadow vs Palo Alto Networks AI Access Security and Prisma Browser. Palo Alto blocks or allows AI apps; Shadow masks prompts with reversible fakes. **Palo Alto Networks AI Access Security, delivered through Prisma Access and Prisma Browser, discovers which AI apps employees use and applies data controls that block or allow prompts. Shadow is an open-source privacy layer that swaps sensitive values in AI API requests for realistic, reversible fakes and restores them in the response.** Palo Alto governs AI use across a managed fleet; Shadow keeps the content of developer, agent and service traffic private without stopping the work. :::note[TL;DR] Palo Alto AI Access Security gives security teams discovery, risk scoring and block-or-allow control over AI apps inside a SASE platform. Shadow translates values into consistent fakes and restores them in streams, tool calls and files on the endpoint. Use Palo Alto for fleet governance and Shadow where tools must keep working on sensitive data. ::: ## At a glance | | Shadow | Palo Alto AI Access Security / Prisma Browser | |---|---|---| | **Open source / licence** | Yes, Apache-2.0 | No, commercial | | **Deployment** | Endpoint HTTPS proxy, gateway (`/gw//`), Rust library | SASE (Prisma Access) and Prisma Browser | | **Protection method** | Reversible realistic fakes from a persona; typed tokens for reports; optional redaction | Block or allow per policy; redaction mentioned only in a community blog | | **Restores responses** | Yes, automatically | Not documented | | **Streaming** | Yes, SSE and NDJSON with split-fake reassembly | Not documented for transformation | | **Tool calls** | Yes, arguments restored locally; intent-aware for provider-hosted tools | Not documented | | **Files** | DOCX, XLSX, PPTX masked in place; PDF to text; CSV, JSON, HTML | Not documented in the sources checked | | **Self-hosted** | Yes, single binary, no account | No, cloud service and managed browser | | **Price model** | Free | Licence bundle | ## What Palo Alto does well - **AI app discovery and risk scoring.** Security teams get an inventory of AI apps in use, with risk attributes, so they can sanction some and restrict others. - **Enterprise browser control.** Prisma Browser enforces data controls at the point where people type and paste, including on unmanaged or contractor devices that use the browser. - **Consistent enterprise DLP.** The same DLP engine and data profiles used across Palo Alto's platform apply to AI prompts. - **Central policy, logging and SOC integration.** Decisions are logged and can feed the security operations stack, which helps with investigations and audit evidence. - **A broader AI security roadmap.** Palo Alto announced an agreement to acquire Portkey to build the Prisma AIRS AI Gateway, which signals investment in API-level AI traffic as well as browser use. ## Where Shadow is different ### Translate instead of block Blocking a prompt stops a leak and stops the work. Shadow sends the request with every sensitive value it detects replaced by a realistic fake from a persistent [persona](https://getshadow.dev/concepts/personas/): `Acme GmbH` becomes `Zhilan Technology Co., Ltd.`, an IBAN keeps a valid checksum. The model answers normally and Shadow restores the real values. ### Can Palo Alto restore values in streams and tool calls? Public documentation does not describe that. Shadow restores fakes in [SSE and NDJSON streams](https://getshadow.dev/concepts/how-it-works/#streaming-hold-back), even when split across events, and restores [tool-call arguments](https://getshadow.dev/concepts/tool-calls/) on your machine so agents act on real values. ### Intent-aware policy Shadow treats an agent action, a bulk report, a code question and a conversation differently, using fakes, typed tokens like `[PERSON_0007]`, kept recipients and provider-hosted action targets, or redaction. See [intent-aware policy](https://getshadow.dev/concepts/intent-policy/). ### Files inside requests Shadow masks DOCX, XLSX and PPTX uploads in place and turns PDFs into masked text. See [file masking](https://getshadow.dev/concepts/file-masking/). ### Open source, local, free No account and no telemetry. Detection runs locally, so plaintext prompts are not sent to a security cloud for inspection. ## When should you choose Palo Alto? - You already run Prisma Access or Strata and want AI controls in the same platform. - You need discovery and governance of AI apps across a managed fleet. - Your main concern is employees and contractors using AI in the browser. - You need central reporting and SOC workflows. ## When should you choose Shadow? - You need to protect API calls to [supported AI providers](https://getshadow.dev/reference/providers/) from developer tools, agents, CI runners and services. - People must keep using AI with sensitive data, and answers must come back with real values. - You need streaming, tool-call and Office-file support. - You want an auditable, self-hosted, open-source control. ## Using Palo Alto and Shadow together 1. Keep Palo Alto for AI app discovery and browser-level controls on managed devices. 2. Install Shadow on machines and services that call model APIs, using [proxy mode](https://getshadow.dev/integrate/proxy-mode/) or [gateway mode](https://getshadow.dev/integrate/gateway-mode/). 3. On those machines, exclude the AI API hosts Shadow intercepts from SSL decryption, or route Shadow's outbound traffic through Prisma Access with `upstream_proxy`. 4. Let Palo Alto decide which AI apps are allowed; let Shadow handle what goes into allowed requests. ## How do they compare on accuracy? Palo Alto does not publish PII detection benchmarks on public datasets. Shadow's [benchmark](https://getshadow.dev/benchmarks/) publishes its datasets, label mappings and code. ## Sources - [Prisma Browser: GenAI prompt data controls](https://docs.paloaltonetworks.com/prisma-browser/administration/manage-prisma-browser-policy-profiles/configure-prisma-browser-data-controls/configure-data-leak-prevention/genai-prompt) - [Palo Alto Networks AI Access Security](https://www.paloaltonetworks.com/sase/ai-access-security) - [Report on the Portkey acquisition announcement](https://pulse2.com/palo-alto-networks-to-acquire-portkey-to-establish-ai-gateway-as-control-plane-for-autonomous-enterprise-agents/amp/) - [Shadow vs Portkey](https://getshadow.dev/compare/portkey/) --- *Last reviewed: 2026-10-31. Palo Alto Networks, Prisma and AI Access Security are trademarks of their owner; this comparison is based on public documentation as of the review date.* ## Frequently asked questions ### Is Shadow a free alternative to Palo Alto AI Access Security? For AI API traffic, partly. Shadow is free and open source and masks prompts with reversible fakes on the machines where it runs. Palo Alto AI Access Security is part of a commercial SASE platform that discovers and controls AI app use across a managed fleet. Shadow does not offer that fleet-wide visibility. ### Can Palo Alto redact data in ChatGPT prompts? Palo Alto's official Prisma Browser documentation describes data controls for GenAI prompts that block or allow. Redaction is described only in a community blog post we found, not in official product documentation, so treat it as unverified. ### Does Palo Alto restore values in AI responses? Reversible pseudonymisation is not documented. Shadow restores its fakes in responses, SSE streams and tool-call arguments locally. ### How is Palo Alto AI Access Security priced? It is sold as part of Palo Alto Networks licence bundles; public list prices are not published. Shadow is free under Apache-2.0. ### Is Portkey part of Palo Alto now? Palo Alto Networks announced an agreement to acquire Portkey in April 2026, positioning it as the Prisma AIRS AI Gateway. The closing date was not confirmed in sources we checked. See the Shadow vs Portkey page. --- # Shadow vs Portkey — Portkey PII redaction alternative that restores responses Source: https://getshadow.dev/compare/portkey/ > Shadow vs Portkey AI gateway for PII. Portkey redacts one way via partner guardrails; Shadow uses reversible fakes and restores streams and tool calls. **Portkey is an AI gateway and control plane: an open-source gateway for routing to many model providers, plus a hosted platform for observability, prompt management and guardrails. Shadow is an open-source privacy layer that replaces sensitive values with realistic, reversible fakes before requests leave, and restores them in responses, streams and tool calls.** Portkey's PII redaction is one-way and comes from partner guardrails; Shadow's masking is reversible and runs locally. :::note[TL;DR] Choose Portkey for multi-provider routing, observability and a managed control plane. Choose Shadow when prompts must be masked before they leave your machine and the answer must come back with real values. They can run together: Shadow masks, Portkey routes and observes. ::: ## At a glance | | Shadow | Portkey | |---|---|---| | **Open source / licence** | Yes, Apache-2.0 | Gateway MIT; guardrails and platform in Portkey's cloud | | **Deployment** | Endpoint HTTPS proxy, gateway (`/gw//`), Rust library | SaaS, hybrid, or self-hosted OSS gateway | | **Protection method** | Reversible realistic fakes from a persona; typed tokens for reports; optional redaction | Redaction via partner guardrails (Pangea, Bedrock, Azure, Patronus) and regex checks | | **Restores responses** | Yes, automatically | No (docs describe redaction as one-way) | | **Streaming** | Yes, SSE and NDJSON with split-fake reassembly | Not documented for PII redaction | | **Tool calls** | Yes, arguments restored locally; intent-aware for provider-hosted tools | Not documented for PII | | **Files** | DOCX, XLSX, PPTX masked in place; PDF to text; CSV, JSON, HTML | Not documented | | **Self-hosted** | Yes, single binary, no account | Gateway yes; partner guardrails are hosted services | | **Price model** | Free | SaaS tiers and enterprise plans | ## What Portkey does well - **Routing and reliability.** One API over many providers with fallbacks, retries, load balancing, caching and conditional routing. - **Observability.** Request logs, traces, cost and latency analytics, and feedback in a hosted dashboard. - **Prompt management.** Versioned prompt templates and a playground for teams. - **Guardrail marketplace.** Portkey can call many guardrail partners, so you can combine PII, safety and content checks without integrating each vendor yourself. - **Open-source gateway.** The gateway itself is MIT-licensed and widely used, and can run in your own environment. - **Enterprise direction.** Palo Alto Networks [announced](https://pulse2.com/palo-alto-networks-to-acquire-portkey-to-establish-ai-gateway-as-control-plane-for-autonomous-enterprise-agents/amp/) its intent to acquire Portkey in April 2026 as part of Prisma AIRS, which may matter to buyers standardising on Palo Alto's platform. Closing was not verified at review time. ## Where Shadow is different ### Can Portkey put redacted values back in the answer? Portkey's [PII redaction docs](https://portkey.ai/docs/product/guardrails/pii-redaction) describe the transformation as one-way. That is safe, but the answer then contains placeholders, and an agent cannot act on a redacted email address. Shadow keeps a real-to-fake mapping in an [encrypted local vault](https://getshadow.dev/concepts/vault/) and restores its fakes in JSON, [streamed responses](https://getshadow.dev/concepts/how-it-works/#streaming-hold-back) and [tool-call arguments](https://getshadow.dev/concepts/tool-calls/). ### Where does the data go before it is masked? With hosted partner guardrails, the unmasked prompt is sent to a guardrail service to be checked. Shadow detects locally with built-in detectors and, optionally, local NER models or Presidio on loopback. Plaintext is never sent to a guardrail service; only the masked request leaves the host, apart from message recipients and the values a provider-hosted action needs, which are kept real by default and logged. No account, no telemetry. ### Realistic fakes and intent Shadow's fakes come from a persistent [persona](https://getshadow.dev/concepts/personas/), so prompts stay coherent and consistent across sessions. The [intent policy](https://getshadow.dev/concepts/intent-policy/) uses typed tokens for bulk reports and a logged `keep` for message recipients and provider-hosted action targets. ### Files Shadow masks DOCX, XLSX and PPTX uploads in place and PDF text before upload ([file masking](https://getshadow.dev/concepts/file-masking/)). ## When should you choose Portkey? - You need a managed control plane: routing, observability, prompt management and governance for many teams. - One-way redaction is acceptable and you want to buy guardrails from several vendors through one integration. - You are standardising on Palo Alto Networks' AI security stack. ## When should you choose Shadow? - Answers and agent actions need the real values back. - Data must be masked before it leaves the machine or the pod, without a hosted guardrail call. - You want an open-source, self-hosted tool with no account. ## Can you use Portkey with Shadow? Yes. Run Shadow as a sidecar or on developer machines and send traffic through Shadow before it reaches Portkey, or configure the self-hosted Portkey gateway to call providers through Shadow's HTTPS proxy. Portkey still sees request metadata, cost and latency, while the values Shadow detected are already masked in its logs, which also keeps them out of observability storage. Disable Portkey's PII redaction guardrail on those routes so values reach Shadow intact when Shadow sits downstream. See [integration modes](https://getshadow.dev/integrate/overview/). ## Migrating Portkey PII redaction to Shadow 1. Keep Portkey for routing and observability. 2. Turn off the PII redaction guardrail on the routes Shadow will cover. 3. Install Shadow next to the client or the self-hosted gateway ([Docker and Kubernetes](https://getshadow.dev/integrate/docker-kubernetes/)). 4. Route provider traffic through Shadow's [proxy](https://getshadow.dev/integrate/proxy-mode/) or [gateway](https://getshadow.dev/integrate/gateway-mode/). 5. Add company names and codenames as [dictionary terms](https://getshadow.dev/concepts/detection/#dictionary-terms) and check coverage with `shadow scan`. ## How do they compare on accuracy? Portkey's detection accuracy is whichever partner guardrail you choose. Shadow's [benchmark](https://getshadow.dev/benchmarks/) publishes leak rate, over-masking and round-trip results with code, against open-source baselines; Portkey and its partner guardrails are not included. ## Sources - [Portkey PII redaction](https://portkey.ai/docs/product/guardrails/pii-redaction) - [Portkey AI Gateway on GitHub](https://github.com/Portkey-AI/gateway) - [Portkey pricing](https://portkey.ai/pricing) - [Palo Alto Networks to acquire Portkey (Pulse 2.0)](https://pulse2.com/palo-alto-networks-to-acquire-portkey-to-establish-ai-gateway-as-control-plane-for-autonomous-enterprise-agents/amp/) --- *Last reviewed: 2026-10-31. Portkey is a trademark of its owner; this comparison is based on public documentation as of the review date.* ## Frequently asked questions ### Is Shadow an alternative to Portkey? Only for data protection. Portkey is an AI gateway and control plane with routing, observability, prompt management and guardrails. Shadow does not route or observe; it masks sensitive values with reversible fakes and restores them in responses. You can run Shadow alongside Portkey. ### Does Portkey restore redacted PII in the response? Portkey's PII redaction documentation describes the transformation as one-way: values are redacted before the request reaches the model and are not put back. Shadow restores its fakes in the response. ### Is Portkey open source? The Portkey AI Gateway is open source under MIT. The PII redaction guardrails run through Portkey's cloud and partner integrations such as Pangea, AWS Bedrock Guardrails, Azure and Patronus. Shadow is fully open source under Apache-2.0. ### Was Portkey acquired by Palo Alto Networks? Palo Alto Networks announced an agreement to acquire Portkey on 30 April 2026, with Portkey positioned as the Prisma AIRS AI Gateway. The closing date was not verified at the time of review. ### How much does Portkey cost? Portkey offers SaaS tiers and enterprise plans; check Portkey's pricing page for current figures. Shadow is free to self-host. --- # Shadow vs Presidio — open-source Presidio alternative for LLM traffic Source: https://getshadow.dev/compare/presidio/ > Shadow vs Microsoft Presidio for LLM prompts. Presidio detects and redacts PII; Shadow adds reversible fakes, streaming restore and a proxy. **Presidio is the reference open-source library for detecting and anonymising PII. Shadow is a privacy layer for AI traffic that uses detection as one step, then replaces values with realistic, reversible fakes and restores them in the model's response.** If you need a detector to build your own pipeline, Presidio is an excellent choice. If you need prompts to OpenAI, Anthropic or Gemini masked and answers restored without writing that pipeline, Shadow does it, and can call Presidio for extra recall. :::note[TL;DR] Presidio finds PII and replaces it, usually one way, with placeholders like ``. Shadow translates PII into consistent, format-preserving fakes, sends them to the provider, and restores the real values in responses, streams and tool calls. They are complementary: Shadow can run Presidio as a detector. ::: ## At a glance | | Shadow | Presidio | |---|---|---| | **Open source / licence** | Yes, Apache-2.0 | Yes, MIT | | **Deployment** | Endpoint HTTPS proxy, gateway (`/gw//`), Rust library | Python library; Analyzer and Anonymizer REST containers | | **Protection method** | Reversible realistic fakes from a persona; typed tokens for reports; optional redaction | Replace (placeholder), redact, mask, hash, encrypt, custom, keep, `surrogate_ahds` | | **Restores responses** | Yes, automatically | Only values protected with `encrypt` (via `decrypt`); you wire it up | | **Streaming** | Yes, SSE and NDJSON with split-fake reassembly | No built-in stream handling | | **Tool calls** | Yes, arguments restored locally; intent-aware for provider-hosted tools | No | | **Files** | DOCX, XLSX, PPTX masked in place; PDF to text; CSV, JSON, HTML | Image redactor with OCR; DICOM; structured data module | | **Self-hosted** | Yes, single binary, no account | Yes | | **Price model** | Free | Free | ## What Presidio does well - **Detection breadth.** The Analyzer ships dozens of recognizers, including many national ID formats, and can use spaCy, Stanza or Hugging Face transformer models for named-entity recognition. You can add custom recognizers with regex, deny lists or code. - **Language flexibility.** Because the NLP engine is pluggable, you can bring a model for your language rather than waiting for a vendor to support it. - **Images and medical imaging.** The image redactor runs OCR and blacks out PII in screenshots and scans; there is DICOM support for medical images. Shadow does not do OCR. - **Ecosystem.** Presidio is the default PII backend for many other tools, including LiteLLM, NeMo Guardrails and several gateways. There is a large community, many examples, and an evaluation toolkit (`presidio-research`) for measuring recall on your own data. - **Operator choice.** Replace, mask, hash, encrypt and custom operators let you pick the trade-off per entity type, which is useful for logs, analytics and dataset preparation. ## Where Shadow is different ### Does Presidio keep LLM answers useful? Presidio's default `replace` operator turns `Jane Doe` into ``. That is ideal for logs, but in a prompt it removes meaning: the model can't tell two people apart, can't write an email to the right address, and may refuse to complete a table. Shadow replaces `Jane Doe` with a realistic name such as `Hui Zhao`, and `jane@acme.com` with `hui.zhao@zhilan.cn`, drawn from a persistent [persona](https://getshadow.dev/concepts/personas/). Card numbers stay Luhn-valid, IBANs pass mod-97, paths still look like paths. The model reasons normally, and the answer stays useful. ### Can Presidio reverse anonymisation in a response? Presidio's only reversible operator is `encrypt`, which produces AES ciphertext. The model sees random-looking strings, and you must find them in the response and decrypt them yourself. Shadow keeps a real-to-fake mapping in an [encrypted local vault](https://getshadow.dev/concepts/vault/) and restores its fakes in JSON responses, [server-sent event streams](https://getshadow.dev/concepts/how-it-works/#streaming-hold-back) (even when a fake is split across events), and [tool-call arguments](https://getshadow.dev/concepts/tool-calls/). ### Intent-aware handling Shadow classifies each request as act, report, code or converse. A report over hundreds of customer rows gets typed tokens such as `[PERSON_0007]`; an agent action that runs in the provider's cloud keeps real the contact details it needs, so the action works, and each keep is logged. See [intent-aware policy](https://getshadow.dev/concepts/intent-policy/). Presidio leaves this logic to you. ### A proxy, not just a library Presidio is something you call from code. Shadow intercepts traffic: set `HTTPS_PROXY` or a base URL and every tool on the machine that calls a [supported AI API](https://getshadow.dev/reference/providers/), including ones you didn't write, is covered. See [proxy mode](https://getshadow.dev/integrate/proxy-mode/) and [gateway mode](https://getshadow.dev/integrate/gateway-mode/). ### Office files in uploads Shadow masks DOCX, XLSX and PPTX uploads in place and keeps them valid documents, plus base64 and multipart payloads. See [file masking](https://getshadow.dev/concepts/file-masking/). Presidio has no Office-file handler for LLM uploads. ## When should you choose Presidio? - You are building a data pipeline (logs, analytics, dataset preparation) and need one-way anonymisation. - You need OCR on images or DICOM redaction. - You need detection in a language where you can bring your own spaCy or transformer model. - You want a library to embed in a Python application and you'll write the LLM-specific parts yourself. ## When should you choose Shadow? - You want prompts to AI providers masked and responses restored, with no code changes. - Your users or agents rely on streaming and tool calls. - You need consistent fakes across requests and sessions, so the same customer has the same fake name tomorrow. - You want Office uploads masked before they leave. - You prefer a single Rust binary with no Python runtime on every machine. ## Can you use Presidio with Shadow? Yes, and it is a common setup. Shadow can call a local Presidio Analyzer as an additional detector. Presidio's findings are mapped to Shadow kinds (`PERSON` to person, `IBAN_CODE` to iban, and so on), merged with Shadow's own detectors, and then translated, stored and restored by Shadow. If Presidio is down, Shadow fails open to its built-in detectors. ```toml [presidio] enabled = true url = "http://127.0.0.1:5002/analyze" language = "en" min_score = 0.5 ``` See [Detection: Presidio](https://getshadow.dev/concepts/detection/#presidio) for setup. ## Migrating from a Presidio-based LLM pipeline 1. Keep your Presidio Analyzer, including custom recognizers, and point Shadow's `[presidio]` section at it. 2. Remove your own anonymise-before-call and deanonymise-after-call code. 3. Route model calls through Shadow: set the SDK base URL to `http://127.0.0.1:8877/gw//...` ([gateway mode](https://getshadow.dev/integrate/gateway-mode/)) or set `HTTPS_PROXY` ([proxy mode](https://getshadow.dev/integrate/proxy-mode/)). 4. Add your organisation names and codenames as [dictionary terms](https://getshadow.dev/concepts/detection/#dictionary-terms). 5. Run `shadow scan` on representative prompts to compare what is caught before you switch traffic over. ## How do they compare on accuracy? Published Presidio results vary a lot with the dataset and label mapping, so a single number is misleading. Shadow's [benchmark](https://getshadow.dev/benchmarks/) measures leak rate, over-masking, round-trip exactness and latency, with Presidio as a baseline and as a Shadow detector, and publishes its code and mappings. On the held-out set, Presidio alone leaked 20.74% of sensitive values, Shadow's built-in rules 3.08%, and Shadow with Presidio as an extra detector 1.35%. ## Sources - [Presidio repository (data-privacy-stack/presidio)](https://github.com/data-privacy-stack/presidio) - [Presidio Anonymizer operators](https://presidio.dataprivacystack.org/anonymizer/) - [Presidio installation and container images](https://presidio.dataprivacystack.org/installation/) - [Shadow detection: Presidio](https://getshadow.dev/concepts/detection/#presidio) --- *Last reviewed: 2026-10-31. Presidio is a trademark of its owner; this comparison is based on public documentation as of the review date.* ## Frequently asked questions ### Is Shadow an alternative to Microsoft Presidio? Partly. Presidio is a PII detection and anonymisation library; Shadow is a privacy layer that sits in front of AI providers, replaces values with reversible realistic fakes and restores them in responses. Shadow can use Presidio as one of its detectors, so many teams use both. ### Can Presidio restore the original values in an LLM response? Only with the encrypt operator, which produces ciphertext that the decrypt operator reverses. The default replace operator produces placeholders such as that cannot be mapped back. Presidio has no built-in logic for restoring values in streamed LLM responses or tool calls. ### Does Presidio work with streaming LLM responses? Presidio processes text you hand it. It has no built-in handling for server-sent event streams, so you would need to buffer or reassemble the stream yourself. Shadow restores SSE and NDJSON streams on the fly. ### Is Presidio still maintained by Microsoft? As of October 2026 the repository redirects from microsoft/presidio to data-privacy-stack/presidio, and documentation lives at presidio.dataprivacystack.org. It is still MIT-licensed and actively released. Container images moved to ghcr.io/data-privacy-stack. ### Which is more accurate, Presidio or Shadow? Accuracy depends on the dataset and how entity labels are mapped, and published Presidio numbers vary widely. Shadow can run Presidio as an extra detector. On Shadow's held-out benchmark, Presidio alone leaked 20.74% of sensitive values, Shadow's built-in rules 3.08%, and Shadow with Presidio as an extra detector 1.35%. See the Shadow benchmarks page. ### Is Presidio free? Yes. Presidio is open source under the MIT licence. Shadow is open source under Apache-2.0. Both are free to self-host. --- # Shadow vs Private AI — open-source Private AI alternative for LLM prompts Source: https://getshadow.dev/compare/private-ai/ > Shadow vs Private AI for masking PII in LLM prompts. Compare multilingual detection, re-identification, streaming, deployment and price. **Private AI is a commercial PII detection and de-identification engine, delivered mainly as a self-hosted container, with unusually broad language coverage. Shadow is an open-source (Apache-2.0) privacy layer for AI traffic that replaces sensitive values with realistic, reversible fakes and restores them in responses, streams and tool calls.** If multilingual detection quality is your main requirement and you have budget, Private AI is a strong option. If you want a free, local layer that sits in front of OpenAI, Anthropic or Gemini without code changes, Shadow fits. :::note[TL;DR] Private AI focuses on detection quality across many languages and file types, with placeholders, masks or synthetic values and a re-identify API. Shadow focuses on the traffic path: an endpoint proxy and gateway that translates values into persona-consistent fakes and restores them, including in streamed output and tool calls. Private AI is quote-priced; Shadow is free. ::: ## At a glance | | Shadow | Private AI | |---|---|---| | **Open source / licence** | Yes, Apache-2.0 | No, proprietary | | **Deployment** | Endpoint HTTPS proxy, gateway (`/gw//`), Rust library | Container API (self-hosted) or vendor cloud; PrivateGPT Headless for LLM workflows | | **Protection method** | Reversible realistic fakes from a persona; typed tokens for reports; optional redaction | Placeholders (`NAME_1`), masks or synthetic values | | **Restores responses** | Yes, automatically | Yes, re-identify API | | **Streaming** | Yes, SSE and NDJSON with split-fake reassembly | Not documented in the docs we reviewed | | **Tool calls** | Yes, arguments restored locally; intent-aware | Not documented | | **Files** | DOCX, XLSX, PPTX masked in place; PDF to text; CSV, JSON, HTML | Many file types (vendor claim) | | **Self-hosted** | Yes, single binary, no account | Yes, container (licence required) | | **Price model** | Free | Quote; limited free tier reported by a third party | ## What Private AI does well - **Multilingual detection.** Private AI states it detects more than 50 entity types across 52 languages (vendor claim). That is far broader than most open-source detectors, which are often English-first. - **Self-hosted container.** The engine can run inside your own infrastructure, which matters for regulated teams that cannot send text to a third-party API. - **Choice of output.** You can replace entities with numbered placeholders such as `NAME_1`, masks, or synthetic values, and re-identify later. - **An LLM workflow out of the box.** PrivateGPT Headless wraps the de-identify, call model, re-identify sequence, so you don't have to build the round trip yourself. - **File coverage.** Private AI documents support for many file types (vendor claim), useful for document pipelines beyond chat prompts. ## Where Shadow is different ### Is Shadow a free alternative to Private AI? Yes. Shadow is Apache-2.0, runs as a single Rust binary on macOS, Linux and Windows, and needs no account, licence key or telemetry. Private AI is licensed commercially. If budget or procurement is the blocker, Shadow lets you start today and audit the code. ### Realistic fakes from a persistent persona Shadow's default treatment is a format-preserving fake drawn from a per-machine [persona](https://getshadow.dev/concepts/personas/): `jane@acme.com` becomes `hui.zhao@zhilan.cn`, cards stay Luhn-valid, IBANs pass mod-97. Because the persona and [vault](https://getshadow.dev/concepts/vault/) persist, the same customer gets the same fake tomorrow, which keeps long conversations and agents consistent. Numbered placeholders work well for analytics, but they read as placeholders to the model. ### Streams and tool calls Shadow restores fakes in [server-sent event streams](https://getshadow.dev/concepts/how-it-works/#streaming-hold-back), even when a fake is split across events, and in [tool-call arguments](https://getshadow.dev/concepts/tool-calls/) before your tools run. We did not find public documentation of equivalent streaming or tool-call handling in Private AI. ### Intent-aware policy Shadow classifies each request as act, report, code or converse and picks a treatment per value: fakes for conversation, typed tokens such as `[PERSON_0007]` for bulk reports, and an explicit, logged keep for the recipient of a message or action and what a provider-hosted tool's action needs. See [intent-aware policy](https://getshadow.dev/concepts/intent-policy/). ### Interception instead of integration Private AI is an API you call. Shadow is a proxy: set `HTTPS_PROXY` or a base URL and every tool on the machine that calls a [supported AI API](https://getshadow.dev/reference/providers/) is covered, including tools you didn't write. Web chat in a browser is not. See [proxy mode](https://getshadow.dev/integrate/proxy-mode/) and [gateway mode](https://getshadow.dev/integrate/gateway-mode/). ## When should you choose Private AI? - You need strong detection in many languages and don't want to assemble models yourself. - You process many file types in batch, beyond prompts. - You want a supported commercial product with a vendor accountable for accuracy. - You already build your own LLM calls and only need a de-identify and re-identify API. ## When should you choose Shadow? - You want masking in front of AI providers with no code changes, for every tool on a machine that calls their APIs. - You depend on streaming and agents with tool calls. - You want realistic, consistent fakes so model answers stay natural. - You want Office uploads (DOCX, XLSX, PPTX) masked in place. - You need an open-source, free, auditable component with no external account. ## Using them together Shadow's detection is pluggable: it can call a local HTTP detector through its [NER sidecar contract](https://getshadow.dev/concepts/detection/#local-ner-sidecar) or a Presidio Analyzer. A team that has licensed Private AI for its language coverage could put a small adapter in front of the Private AI container that returns spans in Shadow's format, and let Shadow handle fakes, the vault, streaming and restoration. This is not a built-in integration; treat it as a pattern. ## Migrating from Private AI for LLM traffic 1. List the entity types and languages you rely on, and test them with `shadow scan` on representative prompts. 2. Enable the optional open-weight NER sidecar (OpenAI Privacy Filter plus GLiNER) for free-text names, and add your organisation names and codenames as [dictionary terms](https://getshadow.dev/concepts/detection/#dictionary-terms). 3. Remove your de-identify and re-identify calls around the model call. 4. Point the SDK base URL at `http://127.0.0.1:8877/gw//...` or set `HTTPS_PROXY`. 5. If a language is poorly covered, keep Private AI as a detector behind an adapter rather than dropping it. ## How do they compare on accuracy? Private AI publishes comparisons against Presidio, Google DLP, AWS Comprehend, spaCy and Nightfall; these are vendor-run and, per our research, used 2021 versions of the other tools. We have not been able to test Private AI independently without a licence. Shadow's [benchmark](https://getshadow.dev/benchmarks/) publishes its methodology, datasets and label mappings so results can be reproduced. ## Sources - [Private AI](https://www.private-ai.com/) - [Private AI overview and vendor comparison](https://www.private-ai.com/overview/) - [PrivateGPT Headless documentation](https://docs.private-ai.com/pgpt-headless) - [Shadow detection](https://getshadow.dev/concepts/detection/) --- *Last reviewed: 2026-10-31. Private AI is a trademark of its owner; this comparison is based on public documentation as of the review date.* ## Frequently asked questions ### Is Shadow an alternative to Private AI? For masking LLM traffic, yes. Both de-identify sensitive values before a model sees them and re-identify the answer. Private AI is a commercial detection and de-identification container with broad language support; Shadow is a free, Apache-2.0 privacy layer that runs as a local proxy, gateway or Rust library. ### Does Private AI support more languages than Shadow? Very likely. Private AI states it detects 50+ entity types in 52 languages (vendor claim). Shadow's built-in detectors are pattern- and dictionary-based, and its optional NER sidecar is strongest on English, with multilingual names from GLiNER. Multilingual recall is being measured on the Shadow benchmarks page. ### How much does Private AI cost? Private AI sells licences by quote. A third-party source mentions a limited free tier of about 75 API calls per day; we could not confirm that on official pages. Shadow is free and open source. ### Can Private AI restore values in a streamed LLM response? Private AI supports re-identification, and PrivateGPT Headless wraps de-identify, LLM call and re-identify. Public docs we reviewed do not describe streaming (SSE) re-identification or tool-call handling. Shadow restores SSE and NDJSON streams and tool-call arguments on the fly. ### Can I use Private AI and Shadow together? Shadow accepts external detectors over HTTP through its NER sidecar contract and Presidio integration. Wiring Private AI's detector in would need a small adapter; it is not a built-in integration. --- # Shadow vs Prompt Security — open-source Prompt Security alternative for LLM data Source: https://getshadow.dev/compare/prompt-security/ > Shadow vs Prompt Security (SentinelOne). Prompt Security filters or blocks AI prompts; Shadow masks them with reversible fakes and restores answers. **Prompt Security, acquired by SentinelOne in 2025, offers a browser extension and a gateway that give organisations visibility into AI use and filter or block risky prompts. Shadow is an open-source privacy layer that translates sensitive values in AI API requests into realistic, reversible fakes and restores them in the response.** Prompt Security is a broad AI security platform; Shadow does one job, keeping data out of provider requests while tools keep working. :::note[TL;DR] Prompt Security covers AI security broadly: shadow-AI discovery, prompt filtering and blocking, and protection against AI-specific threats, now within SentinelOne. Its redaction behaviour is not documented in detail publicly. Shadow masks values with consistent, format-preserving fakes and restores them in streams, tool calls and files, locally and for free. ::: ## At a glance | | Shadow | Prompt Security (SentinelOne) | |---|---|---| | **Open source / licence** | Yes, Apache-2.0 | No, commercial | | **Deployment** | Endpoint HTTPS proxy, gateway (`/gw//`), Rust library | Browser extension, gateway, SaaS console | | **Protection method** | Reversible realistic fakes from a persona; typed tokens for reports; optional redaction | Filter or block; redaction not verified | | **Restores responses** | Yes, automatically | Not documented | | **Streaming** | Yes, SSE and NDJSON with split-fake reassembly | Not documented | | **Tool calls** | Yes, arguments restored locally; intent-aware for provider-hosted tools | Not documented | | **Files** | DOCX, XLSX, PPTX masked in place; PDF to text; CSV, JSON, HTML | Not documented | | **Self-hosted** | Yes, single binary, no account | Not documented | | **Price model** | Free | Quote | ## What Prompt Security does well - **Broad AI security scope.** Beyond data leakage, it addresses risks such as prompt injection and unsafe AI use, which Shadow does not attempt. - **Shadow-AI discovery.** The extension gives security teams visibility into which AI tools employees use. - **Two enforcement points.** A browser extension for employee use and a gateway for applications cover both people and apps. - **Central policy and reporting.** Policies are managed in one console, with visibility for security teams. - **SentinelOne integration.** As part of SentinelOne, it can sit alongside endpoint and XDR tooling that many security teams already run. ## Where Shadow is different ### Filter and block, or translate? Blocking or filtering a prompt prevents a leak but also stops or degrades the work. Shadow sends the request with every sensitive value it detects translated into a realistic fake from a persistent [persona](https://getshadow.dev/concepts/personas/): `Acme GmbH` becomes `Zhilan Technology Co., Ltd.`, emails and phone numbers keep their shape, card numbers stay Luhn-valid. The model answers normally and Shadow restores the real values. ### Streams and tool calls Shadow restores fakes in [SSE and NDJSON streams](https://getshadow.dev/concepts/how-it-works/#streaming-hold-back), even when split across events, and in [tool-call arguments](https://getshadow.dev/concepts/tool-calls/) before local tools run. Public Prompt Security documentation does not describe restoration. ### Intent-aware policy Shadow picks a treatment per value based on what the request is for: fakes in conversation, typed tokens for bulk reports, kept recipients and provider-hosted action targets. See [intent-aware policy](https://getshadow.dev/concepts/intent-policy/). ### Files Shadow masks DOCX, XLSX and PPTX uploads in place and turns PDFs into masked text. See [file masking](https://getshadow.dev/concepts/file-masking/). ### Open source and local No account, no telemetry, auditable code, and no plaintext sent to a vendor cloud. ## When should you choose Prompt Security? - You want one platform for AI security: discovery, data protection and AI threat protection. - You already use SentinelOne and want AI controls in the same ecosystem. - You need central visibility over employee AI use. - Blocking or filtering fits your risk appetite. ## When should you choose Shadow? - Your priority is keeping sensitive values out of model API requests while answers stay useful. - You rely on streaming, agents and tool calls. - You want Office files masked before upload. - You need an open-source, self-hosted control with no licence cost. ## Using Prompt Security and Shadow together 1. Use Prompt Security for AI discovery, employee browser use and AI threat screening. 2. Install Shadow on developer machines, CI runners and services that call model APIs, in [proxy mode](https://getshadow.dev/integrate/proxy-mode/) or [gateway mode](https://getshadow.dev/integrate/gateway-mode/). 3. If both sit in the API path, put Shadow closest to the application so that the gateway only sees masked content. Check that any prompt-injection scanning still works on masked text. ## How do they compare on accuracy? Prompt Security does not publish PII detection results on public datasets that we could find. Shadow publishes its methodology on the [benchmarks page](https://getshadow.dev/benchmarks/). ## Sources - [SentinelOne acquires Prompt Security (SiliconANGLE)](https://siliconangle.com/2025/08/05/sentinelone-acquires-ai-security-startup-prompt-security/) - [Prompt Security](https://www.prompt.security/) - [Shadow: how it works](https://getshadow.dev/concepts/how-it-works/) --- *Last reviewed: 2026-10-31. Prompt Security and SentinelOne are trademarks of their owners; this comparison is based on public documentation as of the review date.* ## Frequently asked questions ### Is Shadow a free alternative to Prompt Security? For AI API data protection, partly. Shadow is free and open source and masks prompts with reversible fakes on the machines where it runs. Prompt Security, now part of SentinelOne, is a commercial platform with a browser extension and gateway that also addresses prompt injection, shadow AI and other AI security risks that Shadow does not cover. ### Did SentinelOne acquire Prompt Security? Yes. SentinelOne announced the acquisition of Prompt Security in August 2025. ### Does Prompt Security redact sensitive data? Its public material describes filtering and blocking prompts that contain sensitive data. We could not verify detailed documentation of redaction or of restoring values in responses, so check with the vendor. Shadow replaces values with realistic fakes and restores them. ### How much does Prompt Security cost? Pricing is by quote through SentinelOne. Shadow is free under Apache-2.0. ### Can Prompt Security and Shadow run together? Yes. Prompt Security can govern employee AI use and screen for AI threats while Shadow masks the content of API, agent and developer traffic on the endpoint. --- # Shadow vs Protecto — open-source Protecto alternative for LLM data privacy Source: https://getshadow.dev/compare/protecto/ > Shadow vs Protecto (GPTGuard) for masking PII in AI prompts. Compare semantic tokens vs realistic fakes, unmasking, streaming and deployment. **Protecto is a commercial AI data privacy platform that replaces sensitive values with consistent "semantic" tokens and unmasks them under policy, as SaaS or in your own cloud. Shadow is an open-source (Apache-2.0) privacy layer that replaces values with realistic, reversible fakes on the endpoint and restores them in responses, streams and tool calls.** Choose Protecto when you want a governed, enterprise-wide tokenisation service. Choose Shadow when you want a free, local layer in front of AI providers. :::note[TL;DR] Both tools pseudonymise reversibly, which keeps AI useful without exposing originals. Protecto emphasises cross-system token consistency, policy-controlled unmasking and database FPE. Shadow emphasises the traffic path: realistic persona-based fakes, automatic streaming and tool-call restoration, intent policy and Office file masking, all local and free. ::: ## At a glance | | Shadow | Protecto | |---|---|---| | **Open source / licence** | Yes, Apache-2.0 | No, proprietary | | **Deployment** | Endpoint HTTPS proxy, gateway (`/gw//`), Rust library | SaaS or VPC deployment; GPTGuard secure chat | | **Protection method** | Reversible realistic fakes from a persona; typed tokens for reports; optional redaction | Reversible semantic tokens; format-preserving encryption for databases | | **Restores responses** | Yes, automatically | Yes, via unmask API under policy | | **Streaming** | Yes, SSE and NDJSON with split-fake reassembly | Not documented in the docs we reviewed | | **Tool calls** | Yes, arguments restored locally; intent-aware | Not documented | | **Files** | DOCX, XLSX, PPTX masked in place; PDF to text; CSV, JSON, HTML | Not documented in the docs we reviewed | | **Self-hosted** | Yes, single binary, no account | VPC option | | **Price model** | Free | Quote | ## What Protecto does well - **Consistency across systems.** Protecto's tokens are designed to be the same for the same value across databases, pipelines and AI applications, which helps analytics and RAG over tokenised data. - **Policy-governed unmasking.** Authorised users and services can unmask tokens; others see only tokens. That is a good fit for organisations with many consumers of the same data. - **Database protection too.** Format-preserving encryption for databases extends the same approach beyond prompts. - **Published methodology.** Protecto has published a benchmark white paper comparing its detection with Presidio and AWS Comprehend (vendor-run). - **Packaged product.** GPTGuard gives teams a secure chat experience without building one. ## Where Shadow is different ### Is Shadow a free alternative to Protecto? Yes. Shadow is Apache-2.0, runs as a single binary with no account and sends no telemetry. You can deploy it on a laptop in minutes or as a sidecar in Kubernetes. ### Realistic fakes, not tokens, by default Tokens are clear and safe but read as tokens to a model. Shadow's default is a realistic, format-preserving fake from a persistent [persona](https://getshadow.dev/concepts/personas/): names that fit a region, emails at a coherent fake domain, Luhn-valid cards, mod-97-valid IBANs. For bulk data, Shadow's [intent policy](https://getshadow.dev/concepts/intent-policy/) switches to typed tokens such as `[PERSON_0007]`, so you get both styles where each works best. ### Restoration is automatic With Shadow you don't call an unmask API. Fakes are restored in JSON responses, in [streams](https://getshadow.dev/concepts/how-it-works/#streaming-hold-back) even when split across events, and in [tool-call arguments](https://getshadow.dev/concepts/tool-calls/) before your tools run. ### Endpoint proxy Shadow intercepts AI traffic as an HTTPS proxy for every tool on a machine, decrypting only [known AI API hosts](https://getshadow.dev/reference/providers/) and tunnelling everything else untouched. See [proxy mode](https://getshadow.dev/integrate/proxy-mode/). That covers desktop and CLI tools that can't be rewritten to call an API. ### Office files Uploaded DOCX, XLSX and PPTX files are masked in place and remain valid documents. See [file masking](https://getshadow.dev/concepts/file-masking/). ### What Shadow lacks Shadow has no central policy server, role-based unmasking or organisation-wide token service. Each machine or container has its own persona and vault, which can be shared by copying files. ## When should you choose Protecto? - You want a single tokenisation service across databases, data pipelines and AI apps. - You need role-based, audited unmasking for many teams. - You want a vendor-supported product with a managed chat interface. ## When should you choose Shadow? - You want masking on the endpoint, before anything leaves the machine, with no third-party service. - You use streaming and agents and want restoration to just work. - You want realistic fakes that keep answers natural. - You need a free, open-source tool you can audit. ## Using them together If your data is already tokenised by Protecto upstream, Shadow can still run on developer machines and services to catch values that appear outside tokenised flows: pasted logs, config files with secrets, internal hostnames and file paths. The two layers don't conflict. ## Migrating LLM traffic from Protecto 1. Identify the LLM calls that mask and unmask through Protecto. 2. Run Shadow as a [gateway](https://getshadow.dev/integrate/gateway-mode/) and point those calls at `http://127.0.0.1:8877/gw//...`. 3. Remove the explicit mask and unmask calls. 4. Add organisation names and codenames as [dictionary terms](https://getshadow.dev/concepts/detection/#dictionary-terms); enable the optional NER sidecar or Presidio for free-text names. 5. Validate with `shadow scan` before switching production traffic. ## How do they compare on accuracy? Protecto's white paper compares it with Presidio and Comprehend; it is vendor-run, and we have not reproduced it. Shadow's [benchmark](https://getshadow.dev/benchmarks/) publishes code, datasets and label mappings so results are reproducible. ## Sources - [Protecto](https://protecto.ai/) - [How Protecto works (architecture)](https://protecto.ai/tech-behind-protecto/) - [Protecto benchmark white paper (vendor-run, PDF)](https://protecto.ai/wp-content/uploads/2024/07/6646f1564c513545cbf9d2f9_Quantitative-Benchmark-Study-PII-Identification-1.pdf) - [Shadow intent-aware policy](https://getshadow.dev/concepts/intent-policy/) --- *Last reviewed: 2026-10-31. Protecto and GPTGuard are trademarks of their owner; this comparison is based on public documentation as of the review date.* ## Frequently asked questions ### Is Shadow an alternative to Protecto? For masking AI traffic, yes. Both replace sensitive values reversibly before a model sees them. Protecto uses consistent semantic tokens and an unmask API, as SaaS or in your VPC; Shadow uses realistic fakes from a local persona and restores them automatically as a proxy, gateway or Rust library. ### What are Protecto's semantic tokens? Protecto replaces sensitive values with tokens that stay consistent across systems, so the same customer gets the same token everywhere, and authorised users can unmask them under policy. It also offers format-preserving encryption for databases. ### How much does Protecto cost? Protecto is priced by quote; public pages we reviewed do not list prices. Shadow is free and open source under Apache-2.0. ### Can Protecto run on-premises? Protecto offers SaaS and deployment in your own cloud (VPC). Shadow runs on any macOS, Linux or Windows host, server or container, with no hosted component. ### Does Protecto restore values in streamed LLM responses? Protecto offers an unmask API. Public docs we reviewed do not describe streaming (SSE) restoration or tool-call handling. Shadow restores both automatically. --- # Shadow vs Rehydra — open-source local PII proxies for LLMs compared Source: https://getshadow.dev/compare/rehydra/ > Shadow vs Rehydra, two open-source local proxies that mask PII before LLM calls and restore it. Compare fakes vs placeholders, streaming and files. **Rehydra and Shadow are the two open-source projects closest to each other in this space: both mask PII locally before a request reaches an LLM provider, and both restore the original values in responses, streams and tool calls.** Rehydra is a TypeScript SDK and local proxy under MIT that replaces values with XML-style placeholders. Shadow is a Rust binary and library under Apache-2.0 that replaces values with realistic, format-preserving fakes from a persistent persona, adds an intent policy and masks Office files. Pick by language ecosystem and by whether you want placeholders or realistic fakes. :::note[TL;DR] Same model, different choices. Rehydra: TypeScript, runs in Node, Bun and browsers, regex plus ONNX NER, placeholders like ``, AES-256-GCM session map, sentence-buffered streaming. Shadow: Rust, runs as a proxy, gateway or crate, realistic checksum-valid fakes, persistent encrypted vault, character-level stream hold-back, intent policy, DOCX/XLSX/PPTX masking. ::: ## At a glance | | Shadow | Rehydra | |---|---|---| | **Open source / licence** | Yes, Apache-2.0 | Yes, MIT | | **Deployment** | Endpoint HTTPS proxy, gateway (`/gw//`), Rust library | TypeScript SDK (npm) and local proxy; Node, Bun, browser | | **Protection method** | Reversible realistic fakes from a persona; typed tokens for reports; optional redaction | Reversible XML-style placeholders | | **Restores responses** | Yes, automatically | Yes | | **Streaming** | Yes, SSE and NDJSON, holds back at most the longest fake minus one byte | Yes, sentence-buffered with NER overlap | | **Tool calls** | Yes, arguments restored locally; intent-aware for provider-hosted tools | Yes (`onToolCall` argument rehydration) | | **Files** | DOCX, XLSX, PPTX masked in place; PDF to text; CSV, JSON, HTML | `.env` scrubbing | | **Self-hosted** | Yes, single binary, no account | Yes | | **Price model** | Free | Free | ## What Rehydra does well - **TypeScript-native.** If your application is in Node or Bun, Rehydra is an npm package you import. It also runs in the browser, which Shadow does not. - **Local proxy for coding agents.** Rehydra documents a proxy for Claude Code and OpenAI and Anthropic clients. - **Regex plus ONNX NER.** Detection combines patterns with an ONNX NER model in-process, without a separate Python service. - **Encrypted session map.** The placeholder mapping is protected with AES-256-GCM. - **Tool calls handled.** Arguments are rehydrated through an `onToolCall` hook, so agents work. - **Clear placeholders.** XML-style tags make it obvious to the model, and to anyone reading logs, which values were masked. ## Where Shadow is different ### Realistic fakes or placeholders? Placeholders are explicit but read as placeholders, which can make some answers awkward (drafting an email, filling a table, writing code that parses an address). Shadow sends realistic values instead: `jane@acme.com` becomes `hui.zhao@zhilan.cn`, card numbers stay Luhn-valid, IBANs pass mod-97, paths keep working. For bulk data, Shadow's [intent policy](https://getshadow.dev/concepts/intent-policy/) switches to typed tokens like `[PERSON_0007]`, which are similar in spirit to Rehydra's placeholders. ### Persistent persona and vault Shadow's mapping persists in an [encrypted vault](https://getshadow.dev/concepts/vault/) (ChaCha20-Poly1305), and fakes come from a per-machine [persona](https://getshadow.dev/concepts/personas/). The same customer gets the same fake across sessions and days, and several processes on a machine share one mapping. ### Streaming granularity Shadow holds back at most the length of the longest fake minus one byte per stream channel, so tokens keep flowing with a delay of a few characters. See [streaming hold-back](https://getshadow.dev/concepts/how-it-works/#streaming-hold-back). Rehydra buffers by sentence so that NER can see context. Both approaches are valid trade-offs. ### Provider-hosted tools Shadow's intent policy distinguishes tools your code runs from tools the provider runs (hosted MCP, server-side tools) and decides, per configuration, whether the action target is faked or kept and logged. By default the recipient of a message or action is kept, so the model writes to the right person. See [tool calls](https://getshadow.dev/concepts/tool-calls/). ### Files Shadow masks DOCX, XLSX and PPTX uploads in place, extracts PDF text, and handles base64 and multipart payloads. See [file masking](https://getshadow.dev/concepts/file-masking/). ### Whole-machine coverage Shadow's [HTTPS proxy](https://getshadow.dev/integrate/proxy-mode/) with a local CA decrypts only [known AI API hosts](https://getshadow.dev/reference/providers/) and tunnels everything else, so any tool that honours proxy settings is covered when it calls one of those APIs. ## When should you choose Rehydra? - Your app is TypeScript and you want masking in-process via npm. - You need masking in a browser. - You prefer explicit placeholders the model can't mistake for real data. ## When should you choose Shadow? - You want realistic fakes that keep answers natural. - You want one install to cover every tool on a machine that calls a supported AI API. - You need Office file masking or an intent policy. - You want a single native binary, or a Rust crate. ## Using them together Usually you would pick one per path to avoid double masking. A reasonable split: Rehydra inside a browser or TypeScript app, Shadow on developer machines and servers for everything else. If both are in the same path, Shadow will see Rehydra's placeholders as ordinary text and leave them alone unless a detector matches. ## Migrating from Rehydra 1. Install Shadow ([quick start](https://getshadow.dev/getting-started/quick-start/)). 2. Replace the Rehydra proxy URL in your client with Shadow's gateway (`http://127.0.0.1:8877/gw//...`), or use proxy mode. 3. Move custom terms into Shadow's [dictionary](https://getshadow.dev/concepts/detection/#dictionary-terms). 4. Enable the optional NER sidecar (OpenAI Privacy Filter plus GLiNER) if you relied on Rehydra's NER. 5. Compare with `shadow scan` on representative prompts. ## How do they compare on accuracy? Rehydra does not publish benchmarks at the time of review and is not in Shadow's [benchmark](https://getshadow.dev/benchmarks/). There, Shadow's built-in rules leaked 3.08% of sensitive values and round-tripped 100% of documents exactly. ## Sources - [Rehydra SDK on GitHub](https://github.com/rehydra-ai/rehydra-sdk) - [rehydra on npm](https://www.npmjs.com/package/rehydra) - [Shadow streaming hold-back](https://getshadow.dev/concepts/how-it-works/#streaming-hold-back) --- *Last reviewed: 2026-10-31. Rehydra is a trademark of its owner; this comparison is based on public documentation as of the review date.* ## Frequently asked questions ### What is the difference between Shadow and Rehydra? Both are open-source tools that mask PII locally before an LLM call and restore it afterwards, including in streams and tool calls. Rehydra is a TypeScript SDK and proxy (MIT) that uses XML-style placeholders; Shadow is a Rust binary and library (Apache-2.0) that uses realistic, format-preserving fakes from a persistent persona, plus intent policy and Office file masking. ### Is Rehydra free? Yes. Rehydra is MIT-licensed and published on npm. Shadow is Apache-2.0. Both are free. ### Which one runs in the browser? Rehydra runs on Node, Bun and in the browser. Shadow runs on macOS, Linux and Windows hosts, servers and containers, not inside a browser page. A browser can send traffic through it as the system proxy, but only calls to supported AI API hosts are masked, not web chat such as chatgpt.com or claude.ai. ### Do both work with Claude Code? Yes. Rehydra documents a local proxy for Claude Code and OpenAI and Anthropic clients. Shadow covers Claude Code through proxy mode or by setting the Anthropic base URL to its gateway. ### Which one is more accurate? Rehydra does not publish detection benchmarks at the time of review, and it is not in Shadow's benchmark. Shadow publishes its methodology and results: on the held-out set its built-in rules leaked 3.08% of sensitive values, 0.08% with the open-weight NER sidecar. --- # Shadow vs Skyflow — open-source Skyflow LLM Privacy Vault alternative Source: https://getshadow.dev/compare/skyflow/ > Shadow vs Skyflow LLM Privacy Vault. Compare vault tokens vs realistic fakes, re-identification, files, streaming, deployment and pricing. **Skyflow is a commercial data privacy vault: sensitive values are stored in Skyflow, replaced with tokens, and re-identified under fine-grained policy. Its LLM Privacy Vault and Detect APIs apply this to AI prompts and files. Shadow is an open-source (Apache-2.0) privacy layer that replaces values with realistic, reversible fakes on the endpoint and restores them in responses, streams and tool calls.** Choose Skyflow when you want a central, governed vault shared across many systems. Choose Shadow when you want a free, local layer in front of AI providers with no external service. :::note[TL;DR] Skyflow centralises sensitive data in a managed vault and governs re-identification, with strong file de-identification (images, PDF, audio, Office). Shadow keeps the mapping in a local encrypted vault and focuses on the live AI traffic path: persona-consistent fakes, streaming and tool-call restoration, and an endpoint proxy. Skyflow is quote-priced SaaS; Shadow is free and self-hosted. ::: ## At a glance | | Shadow | Skyflow | |---|---|---| | **Open source / licence** | Yes, Apache-2.0 | No, proprietary (SDKs are public) | | **Deployment** | Endpoint HTTPS proxy, gateway (`/gw//`), Rust library | Vault SaaS; LLM Privacy Vault and Detect APIs; Kong plugin | | **Protection method** | Reversible realistic fakes from a persona; typed tokens for reports; optional redaction | Vault tokens, polymorphic encryption | | **Restores responses** | Yes, automatically | Yes, policy-governed re-identification | | **Streaming** | Yes, SSE and NDJSON with split-fake reassembly | Not documented in the docs we reviewed | | **Tool calls** | Yes, arguments restored locally; intent-aware | Not documented | | **Files** | DOCX, XLSX, PPTX masked in place; PDF to text; CSV, JSON, HTML | Images, PDF, audio, Office documents (Detect API) | | **Self-hosted** | Yes, single binary, no account | No, SaaS | | **Price model** | Free | Quote | ## What Skyflow does well - **Governed re-identification.** Skyflow's core idea is that sensitive data lives in one vault, and policies decide who can see what, in clear, masked or tokenised form. That is a strong model for organisations where many services and people need different views of the same data. - **One vault across systems.** The same customer record can be tokenised consistently across your databases, analytics and LLM workflows, not only in prompts. - **File de-identification beyond text.** Skyflow Detect handles images, PDFs, audio and Office documents. Shadow does not do OCR or audio. - **Gateway integration.** A [Kong plugin](https://developer.konghq.com/plugins/skyflow-ai-data-control/) applies Skyflow's vault-backed de-identification and re-identification at an AI gateway. - **Enterprise product.** Vendor support, a managed service and the governance features that procurement and audit teams look for. ## Where Shadow is different ### Is Shadow a free alternative to Skyflow? Yes. Shadow is Apache-2.0, free, and runs without an account. There is no external vault to call, so prompts are masked on the machine before the request leaves it, and the mapping is never stored with a third party. ### Realistic fakes instead of tokens Vault tokens are safe and reversible, but to a model they look like tokens. Shadow's default is a realistic, format-preserving fake from a persistent [persona](https://getshadow.dev/concepts/personas/): a valid email at a coherent fake company, a Luhn-valid card, a mod-97-valid IBAN. Models write naturally about realistic data. When opaque IDs are better, for example bulk reports, Shadow's [intent policy](https://getshadow.dev/concepts/intent-policy/) switches to typed tokens such as `[PERSON_0007]`. ### Streams and agents Shadow restores fakes in [server-sent event streams](https://getshadow.dev/concepts/how-it-works/#streaming-hold-back) as they arrive, including fakes split across events, and in [tool-call arguments](https://getshadow.dev/concepts/tool-calls/) before your code runs the tool. Public Skyflow docs we reviewed do not describe streaming re-identification or tool-call handling in detail. ### Endpoint coverage without code changes Skyflow is integrated through APIs, SDKs or a gateway plugin. Shadow can also intercept traffic as an HTTPS proxy, so desktop tools, IDE assistants and CLIs that you don't control are covered when they call a [supported AI API](https://getshadow.dev/reference/providers/). See [proxy mode](https://getshadow.dev/integrate/proxy-mode/). ### Local vault Shadow's [vault](https://getshadow.dev/concepts/vault/) is a ChaCha20-Poly1305 encrypted file in Shadow's data directory. That is simpler, but it also means Shadow does not offer Skyflow's central, role-based governance across an organisation. ## When should you choose Skyflow? - You want a central privacy vault for customer data across applications, not only AI traffic. - You need role-based, audited re-identification for many teams. - You need de-identification of images, audio or scanned documents. - You prefer a managed service with vendor support. ## When should you choose Shadow? - You want AI prompts masked on the endpoint, with no third-party service in the path. - You rely on streaming responses and agent tool calls. - You want realistic fakes that keep model answers useful. - You need a free, open-source component you can audit and run anywhere. ## Using them together They solve adjacent problems. A team might keep Skyflow as the system of record for customer PII in its applications, and run Shadow on developer laptops and CI runners so code assistants and ad-hoc scripts send fakes instead of the values Shadow detects. If the data reaching a prompt is already Skyflow-tokenised, Shadow will treat the tokens as ordinary text and mask anything else it detects. ## Migrating LLM traffic from Skyflow 1. Identify which LLM calls go through Skyflow's de-identify and re-identify APIs. 2. Run Shadow as a [gateway](https://getshadow.dev/integrate/gateway-mode/) sidecar and point those calls at `http://127.0.0.1:8877/gw//...`. 3. Add your organisation names, customers and codenames as [dictionary terms](https://getshadow.dev/concepts/detection/#dictionary-terms), and enable the optional NER sidecar for free-text names. 4. Test with `shadow scan` on representative prompts and attachments. 5. Keep Skyflow for data at rest and non-AI systems if you use it there. ## How do they compare on accuracy? Skyflow does not publish detection benchmarks that we found, and we could not test it without a commercial account. Shadow's [benchmark](https://getshadow.dev/benchmarks/) publishes methodology, datasets and code so you can run it against your own tools. ## Sources - [Skyflow](https://www.skyflow.com/) - [Skyflow Detect API sample (Node SDK)](https://raw.githubusercontent.com/skyflowapi/skyflow-node-sdk/HEAD/samples/detect-api/get-detect-run.ts) - [Kong Skyflow AI Data Control plugin](https://developer.konghq.com/plugins/skyflow-ai-data-control/) - [Shadow vault](https://getshadow.dev/concepts/vault/) --- *Last reviewed: 2026-10-31. Skyflow is a trademark of its owner; this comparison is based on public documentation as of the review date.* ## Frequently asked questions ### Is Shadow an alternative to Skyflow's LLM Privacy Vault? For protecting LLM traffic, yes. Both replace sensitive values before the model sees them and restore them afterwards. Skyflow is a managed data privacy vault with governed re-identification across your systems; Shadow is a free, Apache-2.0 layer that runs locally as a proxy, gateway or Rust library, with its encrypted vault on the machine. ### Is Skyflow open source? No. Skyflow is a commercial SaaS platform with open-source SDKs. Shadow is fully open source under Apache-2.0. ### How much does Skyflow cost? Skyflow pricing is by quote; public pages we reviewed do not list prices. Shadow is free. ### Can Skyflow de-identify files? Yes. Skyflow Detect de-identifies files including images, PDFs, audio and Office documents through its SaaS API. Shadow masks DOCX, XLSX and PPTX in place and converts PDFs to masked text, but does not do OCR or audio. ### Where is the data stored with Skyflow versus Shadow? Skyflow stores sensitive values in its vault service and governs who can re-identify them. Shadow keeps the real-to-fake mapping in a ChaCha20-Poly1305 encrypted file on the machine running it, with no hosted component. --- # Shadow vs Strac — open-source Strac alternative for AI data protection Source: https://getshadow.dev/compare/strac/ > Shadow vs Strac DLP for ChatGPT and AI apps. Compare browser-extension DLP with an open-source proxy that masks prompts with reversible fakes. **Strac is a commercial SaaS DLP platform: it discovers and remediates sensitive data in SaaS apps and, through a browser extension, can alert, block, redact or pseudonymise what employees send to ChatGPT and other AI tools. Shadow is an open-source (Apache-2.0) privacy layer for AI traffic that replaces sensitive values in API requests with realistic, reversible fakes and restores them in responses, streams and tool calls.** Strac fits company-wide data governance across SaaS and browser use. Shadow fits developers, agents and services calling model APIs. :::note[TL;DR] Strac is broad DLP: SaaS data stores plus a browser extension for AI apps, with alerting and redaction managed centrally. Shadow is narrow and deep: it masks AI API traffic on the endpoint, reversibly, with streaming and tool-call restoration, Office file masking and an intent policy. Strac is quote-priced SaaS; Shadow is free and self-hosted. ::: ## At a glance | | Shadow | Strac | |---|---|---| | **Open source / licence** | Yes, Apache-2.0 | No, proprietary | | **Deployment** | Endpoint HTTPS proxy, gateway (`/gw//`), Rust library | SaaS platform plus browser extension | | **Protection method** | Reversible realistic fakes from a persona; typed tokens for reports; optional redaction | Alert, block, redact, pseudonymise | | **Restores responses** | Yes, automatically | Not documented | | **Streaming** | Yes, SSE and NDJSON with split-fake reassembly | Not documented | | **Tool calls** | Yes, arguments restored locally; intent-aware | No | | **Files** | DOCX, XLSX, PPTX masked in place; PDF to text; CSV, JSON, HTML | Scans files in connected SaaS stores | | **Self-hosted** | Yes, single binary, no account | No, SaaS | | **Price model** | Free | Quote | ## What Strac does well - **Coverage of web AI apps.** A browser extension sees what employees type and paste into ChatGPT and similar web apps, which a network-level API proxy does not target. - **More than AI.** Strac connects to SaaS data stores for discovery and remediation, so one product can cover both data at rest and data flowing into AI tools. - **Range of actions.** Alert, block, redact or pseudonymise, chosen by policy. - **Central management.** Policies, alerts and reporting are managed from one console, which suits security teams overseeing many employees. ## Where Shadow is different ### Is Shadow a free alternative to Strac? For AI API traffic, yes. Shadow is Apache-2.0, needs no account and sends no telemetry. It does not replace Strac's SaaS discovery or browser-extension coverage. ### Translate instead of block Blocking a prompt stops the work, and people route around it. Plain redaction loses meaning. Shadow replaces `jane@acme.com` with `hui.zhao@zhilan.cn`, drawn from a persistent [persona](https://getshadow.dev/concepts/personas/), keeps formats valid (Luhn, mod-97), and turns the fakes back into real values in the answer. Users don't have to change how they work. ### Built for APIs, agents and streams Shadow restores [server-sent event streams](https://getshadow.dev/concepts/how-it-works/#streaming-hold-back), including fakes split across events, and [tool-call arguments](https://getshadow.dev/concepts/tool-calls/) before your tools run. That matters for coding assistants, agents and backend services, which browser extensions don't see. ### Intent-aware policy Each request is classified as act, report, code or converse, and each value gets a fake, a typed token, a logged keep or a redaction. See [intent-aware policy](https://getshadow.dev/concepts/intent-policy/). ### Office files in uploads DOCX, XLSX and PPTX attached to API requests are masked in place and remain valid documents. See [file masking](https://getshadow.dev/concepts/file-masking/). ### What Shadow lacks No central console, alerting, SIEM export, user coaching or SaaS data discovery. Configuration is a file per machine or container. ## When should you choose Strac? - You need to govern employees' use of AI web apps across a company. - You also want discovery and remediation in SaaS apps like Slack or Google Drive. - You want central policy, alerts and reporting for a security team. ## When should you choose Shadow? - You want API and agent traffic masked without breaking answers. - You rely on streaming and tool calls. - You want a free, open-source, local tool with no third-party service in the data path. ## Using them together They cover different paths. Strac on managed browsers covers employees using AI web apps; Shadow on developer machines, CI runners and services covers SDKs, CLIs, IDE assistants and agents calling model APIs directly. Together, the web path and the API path are both covered. ## How do they compare on accuracy? Strac does not publish detection benchmarks that we found, and we could not test it without an account. Shadow's [benchmark](https://getshadow.dev/benchmarks/) publishes methodology and code. ## Sources - [Strac](https://www.strac.io/) - [Strac ChatGPT DLP](https://strac.io/integration/chatgpt-dlp) - [Shadow proxy mode](https://getshadow.dev/integrate/proxy-mode/) --- *Last reviewed: 2026-10-31. Strac is a trademark of its owner; this comparison is based on public documentation as of the review date.* ## Frequently asked questions ### Is Shadow an alternative to Strac? Partly. Strac is a SaaS DLP platform with a browser extension that can alert, block, redact or pseudonymise sensitive data in ChatGPT and SaaS apps. Shadow is an open-source privacy layer for AI API traffic that masks prompts with reversible realistic fakes and restores responses. Strac covers web apps and SaaS stores; Shadow covers API, agent and tool traffic on the machines where it runs. ### Does Strac pseudonymise ChatGPT prompts reversibly? Strac lists pseudonymisation among its actions. We could not verify from public docs whether live prompts are pseudonymised and restored in responses, so treat that as unverified. Shadow restores values in responses, streams and tool calls by default. ### How much does Strac cost? Strac is priced by quote; public pages we reviewed do not list prices. Shadow is free and open source under Apache-2.0. ### Does Shadow cover ChatGPT in the browser? No. Shadow intercepts AI API hosts such as api.openai.com in proxy mode. ChatGPT in the browser talks to chatgpt.com, which is not on that list, so what people type there is not masked. Shadow is designed for API traffic, not the consumer web chat UIs that browser-extension DLP tools target. ### Can Strac scan SaaS apps like Slack or Google Drive? Yes, Strac connects to SaaS data stores for discovery and remediation. Shadow does not scan data at rest; it only masks AI requests in flight. --- # Shadow vs Tonic Textual — open-source Tonic Textual alternative for live LLM traffic Source: https://getshadow.dev/compare/tonic-textual/ > Shadow vs Tonic Textual. Tonic redacts and synthesises data for datasets; Shadow masks live AI traffic with reversible fakes. Compare features and pricing. **Tonic Textual is a commercial platform for redacting and synthesising sensitive data in unstructured text and files, built mainly for preparing datasets for training, RAG and analytics. Shadow is an open-source (Apache-2.0) privacy layer that masks live AI traffic with realistic, reversible fakes and restores them in responses, streams and tool calls.** If you are de-identifying a document corpus, Tonic Textual is built for that. If you are protecting prompts on their way to OpenAI, Anthropic or Gemini, Shadow is built for that. :::note[TL;DR] Tonic Textual and Shadow both replace sensitive values with realistic synthetic ones rather than `[REDACTED]`. Tonic applies this to datasets and files in batch, with strong published detection results and file-format fidelity. Shadow applies it to live requests on the endpoint, reversibly, with streaming and tool-call restoration. They are different jobs and often complementary. ::: ## At a glance | | Shadow | Tonic Textual | |---|---|---| | **Open source / licence** | Yes, Apache-2.0 | No, proprietary | | **Deployment** | Endpoint HTTPS proxy, gateway (`/gw//`), Rust library | SaaS or self-hosted (Docker, Helm); SDK and API | | **Protection method** | Reversible realistic fakes from a persona; typed tokens for reports; optional redaction | Redaction or consistent synthesis | | **Restores responses** | Yes, automatically | Not as a proxy | | **Streaming** | Yes, SSE and NDJSON with split-fake reassembly | No | | **Tool calls** | Yes, arguments restored locally; intent-aware | No | | **Files** | DOCX, XLSX, PPTX masked in place; PDF to text; CSV, JSON, HTML | Files redacted in their original format; PDF and images | | **Self-hosted** | Yes, single binary, no account | Yes (Docker, Helm) | | **Price model** | Free | Pay-as-you-go cloud or quote | ## What Tonic Textual does well - **Detection quality with published numbers.** Tonic's [benchmark](https://www.tonic.ai/ai-model-benchmarks/textual-benchmark) (vendor-run, October 2025, legal, medical and call-transcript data) reports Textual ahead of AWS Comprehend and Presidio on the same data. - **Consistent synthesis.** Textual can replace values with realistic synthetic ones and keep them consistent across documents, so a de-identified dataset still reads naturally. - **Measuring replacement quality.** Tonic released [PrivacyBench](https://www.tonic.ai/ai-model-benchmarks/privacy-bench), a public benchmark (CC-BY-4.0) that scores not only detection but replacement quality and cross-document consistency. That is a useful contribution to the whole field. - **Files in original format.** Documents are redacted in place, including PDFs and images, which matters for dataset pipelines. - **Flexible deployment.** Cloud or self-hosted with Docker or Helm. ## Where Shadow is different ### Is Shadow a free alternative to Tonic Textual? For live AI traffic, yes. Shadow is Apache-2.0, needs no account, and runs as one binary on macOS, Linux or Windows. It is not a dataset de-identification platform. ### Reversible by design Tonic's synthesis is designed to produce a de-identified copy. Shadow's fakes are designed to come back: every real-to-fake pair is stored in an [encrypted local vault](https://getshadow.dev/concepts/vault/), and the model's answer is translated back to real values before you see it. ### Live traffic, streams and agents Shadow intercepts requests as an [HTTPS proxy](https://getshadow.dev/integrate/proxy-mode/) or [gateway](https://getshadow.dev/integrate/gateway-mode/), restores [streamed responses](https://getshadow.dev/concepts/how-it-works/#streaming-hold-back) as they arrive and restores [tool-call arguments](https://getshadow.dev/concepts/tool-calls/) before your tools run. Tonic Textual is called on content, not placed in the request path. ### Persistent persona Shadow's fakes come from one persistent [persona](https://getshadow.dev/concepts/personas/) per machine, so the same customer has the same fake across days and sessions, and fakes are coherent with each other (names, domains, phone dial codes from the same region). ### Intent-aware policy Shadow classifies requests as act, report, code or converse and chooses fakes, typed tokens, a logged keep or redaction per value. See [intent-aware policy](https://getshadow.dev/concepts/intent-policy/). ### Where Tonic is ahead Tonic handles images and scanned PDFs; Shadow has no OCR and converts PDFs to masked text. Tonic also publishes detection results; Shadow's are on its [benchmark page](https://getshadow.dev/benchmarks/). ## When should you choose Tonic Textual? - You need de-identified datasets for model training, fine-tuning, RAG indexes or analytics. - You need files, PDFs and images redacted in their original format. - You want a vendor-supported platform with published accuracy figures. ## When should you choose Shadow? - You want prompts to AI providers masked and responses restored, in real time. - You use streaming, tool calls and agents. - You need a free, open-source, local component. ## Using them together A natural split: use Tonic Textual to de-identify the corpus you index for RAG or use for fine-tuning, and run Shadow on the request path so that whatever users paste in at query time, and whatever the retriever pulls in, is also masked before it reaches the provider. ## How do they compare on accuracy? Tonic's numbers are vendor-run on Tonic-selected data, and Tonic Textual is not in Shadow's [benchmark](https://getshadow.dev/benchmarks/). On that benchmark's held-out set, Shadow's built-in rules leaked 3.08% of sensitive values (0.08% with the open-weight NER sidecar) and round-tripped 100% of documents exactly. ## Sources - [Tonic Textual](https://www.tonic.ai/) - [Tonic Textual benchmark (vendor-run)](https://www.tonic.ai/ai-model-benchmarks/textual-benchmark) - [Tonic PrivacyBench](https://www.tonic.ai/ai-model-benchmarks/privacy-bench) - [PrivacyBench scoring code](https://github.com/TonicAI/privacy_bench_metrics) - [Shadow personas](https://getshadow.dev/concepts/personas/) --- *Last reviewed: 2026-10-31. Tonic, Tonic Textual and PrivacyBench are trademarks of their owner; this comparison is based on public documentation as of the review date.* ## Frequently asked questions ### Is Shadow an alternative to Tonic Textual? For live AI traffic, yes; for building de-identified datasets, usually not. Tonic Textual redacts or synthesises sensitive data in documents and datasets, often for training and RAG. Shadow sits in front of AI providers and translates live requests into realistic fakes, then restores the response. ### Does Tonic Textual restore original values? Tonic Textual's focus is one-way de-identification and consistent synthesis for datasets. It is not documented as a request-and-response proxy that restores values in LLM answers. Shadow restores values automatically, including in streams and tool calls. ### How much does Tonic Textual cost? Tonic Textual offers pay-as-you-go cloud usage and quoted enterprise plans, including self-hosting. Shadow is free and open source under Apache-2.0. ### How accurate is Tonic Textual? Tonic publishes a vendor-run benchmark (October 2025) on legal, medical and call-transcript data in which Textual scores highest. Tonic Textual is not in Shadow's benchmark; Shadow publishes its own methodology and results on the benchmarks page. ### Can Tonic Textual run on-premises? Yes. Tonic Textual is available as SaaS or self-hosted with Docker or Helm. Shadow is self-hosted only, as a single binary or container. --- # Shadow vs Wald.ai — open-source, self-hosted Wald.ai alternative Source: https://getshadow.dev/compare/wald-ai/ > Shadow vs Wald.ai for safe use of ChatGPT and Claude. Compare contextual substitution, restoration, deployment and pricing with a free local proxy. **Wald.ai is a commercial secure-AI service: it replaces sensitive values with context-aware substitutes before prompts reach ChatGPT, Claude or other models, keeps the substitutions consistent through a conversation, and restores them in the answer. Shadow does the same job as open-source (Apache-2.0) software you run yourself, as an endpoint proxy, gateway or Rust library, and adds streaming and tool-call restoration, an intent policy and Office file masking.** Wald.ai suits teams that want a hosted chat product. Shadow suits teams that want the mapping and the traffic to stay on their own machines. :::note[TL;DR] Wald.ai and Shadow share the idea that substitution beats blocking. Wald.ai delivers it as a hosted chat and API, priced per user. Shadow delivers it as free, self-hosted software that sits in front of AI tools and SDKs that call provider APIs, with realistic persona-based fakes and restoration in streams and tool calls. ::: ## At a glance | | Shadow | Wald.ai | |---|---|---| | **Open source / licence** | Yes, Apache-2.0 | No, proprietary | | **Deployment** | Endpoint HTTPS proxy, gateway (`/gw//`), Rust library | SaaS secure chat and API | | **Protection method** | Reversible realistic fakes from a persona; typed tokens for reports; optional redaction | Contextual substitution with encrypted obfuscation maps | | **Restores responses** | Yes, automatically | Yes | | **Streaming** | Yes, SSE and NDJSON with split-fake reassembly | Not documented | | **Tool calls** | Yes, arguments restored locally; intent-aware | Not documented | | **Files** | DOCX, XLSX, PPTX masked in place; PDF to text; CSV, JSON, HTML | Not documented in the docs we reviewed | | **Self-hosted** | Yes, single binary, no account | No, SaaS | | **Price model** | Free | Per user (consumer plan about $19.99/month) | ## What Wald.ai does well - **Substitution, not blocking.** Wald.ai replaces values in context rather than refusing the prompt, so users keep getting useful answers. - **Conversation consistency.** It describes encrypted obfuscation maps that keep substitutions consistent across a conversation. - **Ready-made product.** A secure chat interface means non-technical users can start immediately, without configuring proxies or base URLs. - **Contextual detection.** Wald.ai positions its redaction as context-aware rather than purely pattern-based. ## Where Shadow is different ### Is Shadow a free, self-hosted alternative to Wald.ai? Yes. Shadow is Apache-2.0, runs on macOS, Linux, Windows, servers and containers, and has no hosted component. The real-to-fake mapping lives in an [encrypted vault](https://getshadow.dev/concepts/vault/) on your machine. With a hosted service, prompts pass through the vendor before reaching the model; with Shadow, they are masked before they leave the machine. ### Works with the tools you already use Wald.ai is a destination: users chat in Wald's interface or call its API. Shadow is in the path: set `HTTPS_PROXY` or a base URL and existing tools, IDE assistants, CLIs and SDKs that call a [supported AI API](https://getshadow.dev/reference/providers/) keep working, now masked. Web chat such as chatgpt.com or claude.ai in a browser is not covered. See [proxy mode](https://getshadow.dev/integrate/proxy-mode/) and [gateway mode](https://getshadow.dev/integrate/gateway-mode/). ### Persistent persona Shadow's fakes come from a persistent [persona](https://getshadow.dev/concepts/personas/), so consistency extends across conversations and days, not just one chat. Fakes keep their format: valid emails, Luhn-valid cards, mod-97 IBANs. ### Streams, tools and intent Shadow restores [streamed responses](https://getshadow.dev/concepts/how-it-works/#streaming-hold-back) and [tool-call arguments](https://getshadow.dev/concepts/tool-calls/), and its [intent policy](https://getshadow.dev/concepts/intent-policy/) uses typed tokens for reports and handles provider-hosted tools explicitly. ### Files DOCX, XLSX and PPTX uploads are masked in place. See [file masking](https://getshadow.dev/concepts/file-masking/). ### What Shadow lacks No chat UI, no hosted service, no admin console. Someone has to install and configure it. ## When should you choose Wald.ai? - You want a ready-made secure chat for business users with no installation. - You prefer a managed service and per-user billing. - Your use is primarily English chat. ## When should you choose Shadow? - You want data masked before it leaves your machines, with no third party in the path. - You want to keep using existing tools, SDKs and agents. - You use streaming, tool calls or Office uploads. - You need open-source code you can audit, at no cost. ## Migrating from Wald.ai 1. Install Shadow on the machines or as a sidecar for the services that used Wald.ai's API ([quick start](https://getshadow.dev/getting-started/quick-start/)). 2. Point SDKs at `http://127.0.0.1:8877/gw//...`, or set `HTTPS_PROXY` for whole-machine coverage. 3. Add your organisation names, customers and codenames as [dictionary terms](https://getshadow.dev/concepts/detection/#dictionary-terms). 4. Test with `shadow scan` on typical prompts. 5. Give chat users an API-based AI client of your choice that goes through Shadow; the chatgpt.com and claude.ai websites are not masked. ## How do they compare on accuracy? Wald.ai does not publish detection benchmarks that we found. Shadow's [benchmark](https://getshadow.dev/benchmarks/) publishes methodology and code. ## Sources - [Wald.ai data security for AI](https://wald.ai/datasecurity-for-ai) - [Dark Reading: Wald launches data loss protection for AI platforms](https://darkreading.com/data-privacy/wald-launches-data-loss-protection-for-ai-platforms) - [Shadow security model](https://getshadow.dev/reference/security/) --- *Last reviewed: 2026-10-31. Wald.ai is a trademark of its owner; this comparison is based on public documentation as of the review date.* ## Frequently asked questions ### Is Shadow an alternative to Wald.ai? Yes, conceptually they are close. Both substitute sensitive values before a model sees them and restore them in the answer. Wald.ai is a cloud-hosted secure chat and API service; Shadow is a free, open-source layer that runs on your own machines as a proxy, gateway or Rust library. ### How much does Wald.ai cost? Wald.ai lists a consumer plan at about $19.99 per user per month; business pricing may differ. Check Wald.ai for current prices. Shadow is free and open source under Apache-2.0. ### Is Wald.ai self-hosted? Wald.ai is offered as SaaS. Public material we reviewed does not describe a self-hosted option. Shadow is self-hosted only, with no hosted component and no telemetry. ### Which languages does Wald.ai support? Wald.ai's API documentation states English only, with a 128K-token limit. Shadow's pattern detectors are language-independent for structured values, and its optional NER sidecar adds multilingual names via GLiNER; multilingual recall is measured on the benchmarks page. ### Does Wald.ai work with agents and tool calls? Public docs we reviewed do not describe tool-call or streaming restoration. Shadow restores both. --- # Shadow vs Zscaler — open-source Zscaler GenAI DLP alternative for AI APIs Source: https://getshadow.dev/compare/zscaler/ > Shadow vs Zscaler GenAI protection. Zscaler blocks, isolates and logs AI app use; Shadow masks API prompts with reversible fakes and restores them. **Zscaler's GenAI protection, part of the Zscaler Zero Trust Exchange, lets security teams allow, block, isolate and log employee use of AI apps, with DLP inspection of what is sent. Shadow is an open-source privacy layer that translates sensitive values in AI API requests into realistic, reversible fakes and restores them in the response.** Zscaler governs who uses which AI apps across a fleet; Shadow keeps the content of API, agent and file traffic private while the tools keep working. :::note[TL;DR] Zscaler is a fleet-wide control point for AI use: visibility, block or allow, browser isolation and logging. Shadow masks prompts on the endpoint with format-preserving fakes and restores them in streams, tool calls and files. They solve different problems and fit together. ::: ## At a glance | | Shadow | Zscaler GenAI protection | |---|---|---| | **Open source / licence** | Yes, Apache-2.0 | No, commercial | | **Deployment** | Endpoint HTTPS proxy, gateway (`/gw//`), Rust library | SASE inline (Zero Trust Exchange, Client Connector) | | **Protection method** | Reversible realistic fakes from a persona; typed tokens for reports; optional redaction | Allow, block, isolate, log; DLP inspection | | **Restores responses** | Yes, automatically | Not documented | | **Streaming** | Yes, SSE and NDJSON with split-fake reassembly | Not applicable to block or isolate decisions; not documented for transformation | | **Tool calls** | Yes, arguments restored locally; intent-aware for provider-hosted tools | Not documented | | **Files** | DOCX, XLSX, PPTX masked in place; PDF to text; CSV, JSON, HTML | Upload inspection and blocking | | **Self-hosted** | Yes, single binary, no account | No, cloud service | | **Price model** | Free | Quote | ## What Zscaler does well - **Scale and coverage.** Zscaler inspects traffic from managed devices wherever they are, so AI use on laptops at home, in offices and on the road falls under one policy. - **Shadow-AI discovery.** Security teams can see which AI apps are used and by whom, and decide which to sanction. - **Browser isolation.** Instead of a hard block, risky AI apps can be opened in an isolated browser where, depending on policy, pasting or uploading is restricted. That keeps the tool usable for low-risk work. - **Unified DLP.** The same dictionaries and engines used for web and SaaS DLP apply to AI prompts. - **Logging and SOC integration.** Prompts and decisions can be logged centrally and streamed to SIEM tools, which supports investigations and audit evidence. - **Part of a wider zero-trust platform.** Organisations already using Zscaler for internet and private-app access add AI controls without new agents. ## Where Shadow is different ### Does Zscaler keep AI tools working when data is sensitive? Zscaler's documented actions for AI apps are allow, block, isolate and log. Blocking protects data but stops the work, and people find workarounds. Shadow lets the request through with every sensitive value it detects translated: `jane@acme.com` becomes `hui.zhao@zhilan.cn`, a card number stays Luhn-valid, and a codename becomes another plausible codename from a persistent [persona](https://getshadow.dev/concepts/personas/). The model answers, and Shadow restores the real values before you see them. ### Streaming and tool calls Shadow restores fakes in [SSE and NDJSON streams](https://getshadow.dev/concepts/how-it-works/#streaming-hold-back), even when a fake is split across events, and in [tool-call arguments](https://getshadow.dev/concepts/tool-calls/) so agents act on real targets. Public Zscaler documentation does not describe value transformation and restoration for these flows. ### Intent-aware handling Shadow treats an agent action, a report over customer rows, a code question and a chat differently. See [intent-aware policy](https://getshadow.dev/concepts/intent-policy/). ### Files are masked, not only blocked Shadow masks DOCX, XLSX and PPTX uploads in place and turns PDFs into masked text. See [file masking](https://getshadow.dev/concepts/file-masking/). ### Open source and local No account, no telemetry, no plaintext sent to a security cloud. Shadow runs on the endpoint, in a container or inside a Rust service. ## When should you choose Zscaler? - You need company-wide visibility and control over AI app use on managed devices. - You already run Zscaler for internet access and want AI policy in the same console. - Browser isolation or block-and-coach fits your risk appetite. - You need central logs and reporting for your SOC and auditors. ## When should you choose Shadow? - The traffic you care about is API calls from developer tools, agents, CI and services. - People must keep using AI with sensitive data, with answers restored to real values. - You need streaming and tool-call support. - You want an open-source control you can audit and run without a vendor cloud. ## Using Zscaler and Shadow together 1. Keep Zscaler as the fleet control for browser and SaaS AI use. 2. Install Shadow on machines and services that call model APIs ([proxy mode](https://getshadow.dev/integrate/proxy-mode/) for whole machines, [gateway mode](https://getshadow.dev/integrate/gateway-mode/) for SDKs, or the [Docker sidecar](https://getshadow.dev/integrate/docker-kubernetes/)). 3. Avoid double TLS interception: exclude the AI API hosts Shadow handles from SSL inspection on those machines, or send Shadow's outbound traffic through Zscaler with `upstream_proxy` so Zscaler inspects already-masked requests. 4. Keep allow-listing decisions in Zscaler; let Shadow handle the content of allowed requests. ## How do they compare on accuracy? Zscaler does not publish PII detection results on public datasets, and an independent comparison would need a tenant. Shadow publishes its methodology and code on the [benchmarks page](https://getshadow.dev/benchmarks/). ## Sources - [Zscaler: Make generative AI tools like ChatGPT safe and secure](https://www.zscaler.com/blogs/product-insights/make-generative-ai-tools-chatgpt-safe-and-secure-zscaler) - [Zscaler product site](https://www.zscaler.com/) - [Shadow: how it works](https://getshadow.dev/concepts/how-it-works/) - [Shadow: security model](https://getshadow.dev/reference/security/) --- *Last reviewed: 2026-10-31. Zscaler is a trademark of its owner; this comparison is based on public documentation as of the review date.* ## Frequently asked questions ### Is Shadow a free alternative to Zscaler for AI? Only for one slice of the problem. Shadow is a free, open-source layer that masks sensitive values in AI API traffic on the machines where it runs. Zscaler is a commercial zero-trust platform that governs AI app use across a whole fleet, alongside web, SaaS and private-app access. Shadow does not replace that. ### Can Zscaler redact sensitive data in ChatGPT prompts? Zscaler's public material on generative AI describes allowing, blocking, isolating (browser isolation) and logging AI apps, plus DLP inspection of prompts. Reversible pseudonymisation of prompt values is not documented. ### Does Zscaler restore values in the AI response? Not that public documentation describes. Shadow replaces values with realistic fakes and restores the real values in responses, SSE streams and tool calls locally. ### How much does Zscaler cost? Zscaler sells by quote and bundle; list prices are not published. Shadow is free under Apache-2.0. ### Can Zscaler and Shadow be used together? Yes. Zscaler can govern browser and SaaS AI use on managed devices while Shadow masks API, agent and developer traffic. Exclude the AI API hosts Shadow intercepts from Zscaler SSL inspection on those machines, or chain Shadow through Zscaler with upstream_proxy. --- # AI privacy use cases: mask PII and secrets in LLM prompts with Shadow Source: https://getshadow.dev/use-cases/ > Step-by-step guides: stop PII leaks to ChatGPT, redact prompts for OpenAI and Claude, protect secrets from coding assistants, agents, RAG and documents. These guides answer the questions people actually search for when they want to use LLMs without leaking data: how to stop customer data reaching ChatGPT, how to redact PII before the OpenAI API, how to keep API keys out of coding assistants. Each one starts with a direct answer, then gives the exact commands and configuration. All of them use the same tool. Shadow is an open-source privacy layer that replaces sensitive values with realistic, reversible fakes before a request leaves your machine or server, and restores them in the answer. ## Stop data leaking to AI tools | Guide | Answers | |---|---| | [How to stop employees pasting customer data into ChatGPT](https://getshadow.dev/use-cases/stop-pii-leaks-to-chatgpt/) | Policy, blocking or translation; what works and what people route around | | [LLM data loss prevention (DLP) without blocking](https://getshadow.dev/use-cases/llm-data-loss-prevention-dlp/) | How Shadow differs from and complements DLP products | | [Shadow AI governance](https://getshadow.dev/use-cases/shadow-ai-governance/) | Managing unsanctioned AI tools by giving staff a safe approved path | | [Privacy for AI in customer support](https://getshadow.dev/use-cases/privacy-for-ai-in-customer-support/) | Tickets, macros, support agents and ticket analytics | ## Mask prompts for a specific provider or tool | Guide | Answers | |---|---| | [Redact PII before sending prompts to the OpenAI API](https://getshadow.dev/use-cases/redact-pii-before-sending-to-openai-api/) | One base-URL change for the OpenAI SDKs, Azure OpenAI and compatible APIs | | [Anonymise prompts before sending them to Claude](https://getshadow.dev/use-cases/anonymize-prompts-for-claude/) | The Anthropic SDK, Claude Code, Bedrock and Vertex | | [Keep API keys and internal hosts out of AI coding assistants](https://getshadow.dev/use-cases/protect-secrets-from-ai-coding-assistants/) | Claude Code, Codex CLI, Aider, Continue, Cline; what is and is not covered | | [Self-hosted AI gateway with PII masking](https://getshadow.dev/use-cases/self-hosted-ai-gateway-with-pii-masking/) | Gateway routes, custom upstreams and sidecars for internal apps | ## Build AI applications that handle personal data | Guide | Answers | |---|---| | [Mask PII in LLM agents without breaking tool calls](https://getshadow.dev/use-cases/mask-pii-in-llm-agents-and-tool-calls/) | Restoring tool-call arguments locally; provider-hosted tools | | [Protect customer data in RAG pipelines](https://getshadow.dev/use-cases/protect-customer-data-in-rag-pipelines/) | Where retrieval leaks, and where to put the masking | | [Redact PDFs, Word and Excel files before uploading them to AI](https://getshadow.dev/use-cases/redact-documents-before-uploading-to-ai/) | DOCX, XLSX and PPTX in place; PDF as text; images | | [Reversible pseudonymisation for LLMs](https://getshadow.dev/use-cases/reversible-pseudonymization-for-llms/) | Surrogates, tokens and redaction; the vault; restoring answers | ## Support regulatory requirements | Guide | Answers | |---|---| | [Use LLMs in line with GDPR](https://getshadow.dev/use-cases/gdpr-compliant-llm-usage/) | Data minimisation, pseudonymisation, data protection by design | | [Use LLMs with patient data more safely under HIPAA](https://getshadow.dev/use-cases/hipaa-safe-llm-usage/) | Minimum necessary, Safe Harbor identifiers, what Shadow does not cover | For sector-specific guidance, see [Shadow by industry](https://getshadow.dev/industries/). ## The building blocks every guide uses | Need | Shadow feature | Reference | |---|---|---| | Cover AI API tools on a laptop | HTTPS proxy with a local CA; only AI hosts are decrypted | [Proxy mode](https://getshadow.dev/integrate/proxy-mode/) | | Cover an SDK or service | `http://127.0.0.1:8877/gw//` base URL | [Gateway mode](https://getshadow.dev/integrate/gateway-mode/) | | Run next to an app in containers | Sidecar on `localhost` | [Docker and Kubernetes](https://getshadow.dev/integrate/docker-kubernetes/) | | Protect your own vocabulary | `organization`, `terms`, `internal_domains`, `shadow terms add` | [Detection](https://getshadow.dev/concepts/detection/) | | Different handling per request | `act`, `report`, `code`, `converse` intents | [Intent-aware policy](https://getshadow.dev/concepts/intent-policy/) | | See what would be masked | `shadow scan ` | [CLI](https://getshadow.dev/reference/cli/#shadow-scan) | ## Try it before you read further ```sh shadow init --org "Acme GmbH" echo "Summarise: jane.doe@acme.example at Acme GmbH disputes a charge on card 4111 1111 1111 1111" | shadow mask ``` The output contains a fake email, the persona's company name and a different card number that still passes the Luhn check. Pipe it into `shadow unmask` to get the original back. Nothing is sent anywhere. The full walkthrough is in the [quick start](https://getshadow.dev/getting-started/quick-start/), and the [benchmarks](https://getshadow.dev/benchmarks/) show how detection performs on public datasets. ## Frequently asked questions ### What problem does Shadow solve? It keeps the sensitive values it detects in prompts, files and tool results from reaching AI providers, without breaking the tools. Names, emails, IDs, card numbers, hostnames and secrets are replaced with realistic fakes before the request leaves and restored in the answer. When the goal is to write to or act on someone, that recipient is passed through as written so the model addresses the right person; recipients and the values a provider-hosted action needs are the only real values sent, and each one is logged. ### How is this different from redacting PII with placeholders? Placeholders such as destroy meaning, so answers get worse and actions cannot reach the real target. Shadow uses format-preserving fakes from one consistent persona and restores them in the response, including streams and tool calls. It uses typed tokens only where they work better, such as bulk reports. ### Do I need to change my code? Usually not. Point the SDK's base URL at Shadow's gateway, or set HTTPS_PROXY so the tool goes through Shadow's proxy. Rust applications can embed the library instead. ### Is Shadow free? Yes. It is open source under Apache-2.0, runs on your own machines and servers, and has no account, cloud service or telemetry. ### Where should I start? Run the five-minute quick start, then pick the guide below that matches your task. If you are rolling Shadow out to a team, read the company-size guides as well. --- # How to stop employees pasting customer data into ChatGPT Source: https://getshadow.dev/use-cases/stop-pii-leaks-to-chatgpt/ > Stop customer names, emails and IDs leaking into ChatGPT and other LLMs without banning AI: give staff an approved client that masks data through Shadow. You can't reliably stop people pasting customer data into a chatbot by asking them not to, and blocking AI sites mostly pushes the habit somewhere you can't see. The approach that holds up is to give staff an approved, API-backed AI client and route it through Shadow, which replaces customer names, emails, phone numbers, IDs and secrets with realistic fakes before the request leaves the machine and restores them in the answer. Pair that with a short policy and a block or warning on the consumer sites you don't sanction. ## Why do employees paste customer data into ChatGPT? Because it works. Drafting a reply to an angry customer, summarising a ticket thread or cleaning up a spreadsheet is faster with a model, and the fastest way to give the model context is to paste the whole thing. A typical paste looks like this (all data fictional): ```text Reply to this complaint politely. Customer: Jane Doe , account 4111 1111 1111 1111, phone (415) 555-0134, order #A-20931 shipped to 221 Baker Street. She says our agent Tom Reyes promised a refund. ``` That single prompt sends a name, an email, a card number, a phone number, an address and an employee's name to a third party. Multiply by every support agent, account manager and analyst. ## What are the options? | Approach | What happens | Typical outcome | |---|---|---| | Policy only ("don't paste customer data") | Nothing technical changes | People paste anyway, because the tool is useful | | Block AI sites | Requests fail | Use moves to phones and personal accounts; no visibility | | Blocking DLP on prompts | Requests with PII are refused | The tool breaks for exactly the tasks people need it for | | Placeholder redaction (``) | Request goes through with placeholders | Model can't tell people apart or write to the right address | | **Translate with Shadow** | Values replaced with consistent, realistic fakes and restored in the answer | The tool keeps working; the provider sees fakes for every detected value except the person a message is addressed to | Shadow is the last row. It does not replace a policy or your blocking controls for unsanctioned sites; it makes the sanctioned path good enough that people use it. See [LLM data loss prevention](https://getshadow.dev/use-cases/llm-data-loss-prevention-dlp/) for how this fits with an existing DLP stack. :::caution[What Shadow covers, and what it does not] Shadow intercepts the **provider APIs**: `api.openai.com`, Azure OpenAI, `api.anthropic.com`, Gemini, Vertex AI, Bedrock runtime, OpenRouter, Mistral and [others](https://getshadow.dev/reference/providers/). It does **not** intercept the consumer web apps `chatgpt.com` or `claude.ai`, which use their own undocumented protocols. You can add hosts with `[proxy] extra_hosts`, but that is untested and unsupported for consumer web apps. The dependable setup is an approved client that calls the API. ::: ## How to set it up ### 1. Pick an approved, API-backed client Choose something that calls a provider API with your organisation's key, not a consumer web app: - an internal chat UI your team already runs (Open WebUI, LibreChat or a home-grown one) pointed at OpenAI, Azure OpenAI or Anthropic; - a bring-your-own-key desktop chat client that lets you set the API base URL or honours the system proxy; - task-specific internal tools, for example a "draft reply" button in your helpdesk. For a shared internal chat UI, run Shadow next to the server and point the UI at the [gateway](https://getshadow.dev/integrate/gateway-mode/). For desktop clients, install Shadow on each machine (step 2). ### 2. Install Shadow on each machine ```sh # macOS / Linux curl -fsSL https://raw.githubusercontent.com/Tiny-Bubble-Company/shadow/main/packaging/install.sh | sh shadow init --org "Acme Ltd" # persona, vault key, local CA; protects "Acme Ltd" and "Acme" shadow install # dry run: prints every step shadow install --apply # background service, CA trust, env file ``` On Windows use `install.ps1` (see [Installation](https://getshadow.dev/getting-started/installation/)). Then either set the client's base URL to `http://127.0.0.1:8877/gw/openai/v1`, or let it use the proxy through `HTTPS_PROXY=http://127.0.0.1:8877` or the [PAC file](https://getshadow.dev/integrate/proxy-mode/#system-wide-with-a-pac-file). ### 3. Teach Shadow your vocabulary Detectors find structured values (emails, phones, cards, IBANs, national IDs, IPs, hostnames, secrets) and common names. They cannot know your customers, products or codenames: ```toml organization = ["Acme Ltd"] terms = ["org:Globex", "org:Initech", "person:Tom Reyes", "Project Falcon"] internal_domains = ["acme.example", "acme.internal"] [policy.always] secret = "redact" # detected keys and passwords never leave, even as fakes [files] images = "block" # screenshots are not OCR'd, so stop them leaving unmasked ``` Put this in `shadow.toml` in the data directory, or add terms at runtime with `shadow terms add "org:Globex"`. ### 4. Check what gets caught Save a representative, approved sample prompt to a file and scan it. Nothing is sent: ```sh shadow scan sample-ticket.txt shadow scan sample-ticket.txt --reveal # full values instead of previews ``` Anything missing? Add it as a term, or enable the [open-weight NER sidecar or Presidio](https://getshadow.dev/concepts/detection/) for better recall on free-text names. ### 5. Roll out, then restrict the unsanctioned path Tell people which client to use and why. Then use the controls you already have (secure web gateway, DNS filtering, browser policy) to warn on or block the consumer AI sites you don't sanction. Because the approved client is just as useful, the block is far less likely to be routed around. ## What the provider sees The complaint above, through Shadow on one machine (your persona differs): ```text Reply to this complaint politely. Customer: Hui Zhao , account 4837 2191 9471 3480, phone +86 28 6512 0417, order #A-20931 shipped to 88 Tianfu Road. She says our agent Lei Li promised a refund. ``` The card number still passes Luhn and the email is still an email, so the model writes a normal reply. Wherever it uses the full fakes (Hui Zhao, 88 Tianfu Road, Lei Li), the answer arrives with Jane Doe, her address and Tom Reyes restored. A greeting with the first name alone ("Dear Hui") is restored too; other fragments, such as the last four digits of the fake card, stay fake. Order `#A-20931` is not a detected format; add such references as terms if they matter. Here the reply is addressed to "this complaint", so nobody in the prompt is the recipient. If the request names the person it writes to ("Reply to Jane Doe at jane.doe@acme.example about her refund"), Shadow passes that recipient through as written and logs it, so the model greets the right person; the card, phone, address and Tom Reyes are still masked. Set `[policy.act] targets = "surrogate"` to fake the recipient too. ## Rolling out to a team | Team size | Suggested pattern | |---|---| | One person or a small team | Endpoint install, BYOK client, shared `shadow.toml` in a repo | | Dozens to hundreds | Push `shadow.toml` and run `shadow install --apply` through MDM or configuration management | | Internal chat UI for everyone | One Shadow next to the chat server in [gateway mode](https://getshadow.dev/integrate/gateway-mode/) or as a [sidecar](https://getshadow.dev/integrate/docker-kubernetes/) | There is no central admin console or fleet management yet, and an optional local audit log is a [roadmap](https://getshadow.dev/project/roadmap/) item. See [Shadow by company size](https://getshadow.dev/company-size/) for rollout checklists. ## Limits to be honest about - **Consumer web apps** such as `chatgpt.com` are not covered (see above). - **Detection is best effort.** Unusual names and internal ID formats can be missed. Test with `shadow scan`. - **No OCR.** Screenshots and scanned PDFs pass through unless you block them. - **Dates are not masked**, and **context can identify**: a unique incident described in detail can point to a customer even with every name replaced. ## Related - [Shadow AI governance](https://getshadow.dev/use-cases/shadow-ai-governance/): unsanctioned AI tools and what to do about them - [Privacy for AI in customer support](https://getshadow.dev/use-cases/privacy-for-ai-in-customer-support/) - [Redact documents before uploading to AI](https://getshadow.dev/use-cases/redact-documents-before-uploading-to-ai/) - [Intent-aware policy](https://getshadow.dev/concepts/intent-policy/), [Detection](https://getshadow.dev/concepts/detection/), [Security model](https://getshadow.dev/reference/security/) - How Shadow compares with DLP and redaction tools: [compare](https://getshadow.dev/compare/). Detection results: [benchmarks](https://getshadow.dev/benchmarks/) ## Frequently asked questions ### Can Shadow mask what employees type into the chatgpt.com website? Not as a built-in, supported integration. Shadow intercepts the provider APIs such as api.openai.com, Azure OpenAI and api.anthropic.com, not consumer web apps such as chatgpt.com or claude.ai. The reliable pattern is an approved API-backed client that runs through Shadow. ### Why not just block ChatGPT? Blocking rarely holds. People switch to a phone, a personal laptop or another AI site, and you lose all visibility. Shadow keeps an approved tool useful while the customer data in it is replaced with fakes, so there is less reason to route around it. ### Will the answers be worse if customer data is masked? Usually not noticeably. Shadow uses realistic, format-preserving fakes from one coherent persona, so the model still sees a name, an email and a company, and the real values are restored in the answer. ### Does Shadow tell me who pasted what? No. Shadow is not a monitoring tool. It logs, per request, the intent and how many values of each kind were masked, never the values themselves. A local audit log of decisions is on the roadmap. ### Does Shadow cost anything? No. Shadow is open source under Apache-2.0, self-hosted, with no account and no telemetry. You pay only for the model API you already use. --- # How to redact PII before sending prompts to the OpenAI API Source: https://getshadow.dev/use-cases/redact-pii-before-sending-to-openai-api/ > Mask names, emails, cards and secrets in OpenAI API requests with one base URL change, and get real values back in responses, streams and tool calls. Run Shadow next to your code and change one thing: the OpenAI base URL. Shadow detects names, emails, phone numbers, addresses, card numbers, IBANs, national IDs, IPs, internal hostnames and secrets in every request body, replaces them with realistic fakes, and restores the real values in the response, including streams and tool calls. It runs locally or as a sidecar, so nothing goes to a third-party redaction service. ## Why redact PII before calling the OpenAI API? Even with zero data retention or an enterprise agreement, prompts are processed and may be logged for abuse monitoring by a third party. Data protection rules (GDPR data minimisation, HIPAA minimum necessary, contractual limits on customer data) push you to send only what the task needs. For most prompts the model does not need the real email address to write an email. Plain redaction (``, `***`) breaks answers: the model can't tell two customers apart or address a reply. Shadow uses **format-preserving fakes** instead, so the prompt still reads naturally: | You send | OpenAI sees | |---|---| | `jane.doe@acme.example` | `hui.zhao@zhilan.cn` | | `Acme Ltd` | `Zhilan Technology Co., Ltd.` | | `4111 1111 1111 1111` | `4837 2191 9471 3480` (valid Luhn) | | `DE89 3704 0044 0532 0130 00` | another German IBAN (valid mod-97) | | `sk-proj-…` | a fake key of the same shape, or `[REDACTED_SECRET]` | The mapping stays in an [encrypted local vault](https://getshadow.dev/concepts/vault/). ## How to set it up ### 1. Install and initialise ```sh curl -fsSL https://raw.githubusercontent.com/Tiny-Bubble-Company/shadow/main/packaging/install.sh | sh shadow init --org "Acme Ltd" ``` ### 2. Try masking offline ```sh echo "Refund jane.doe@acme.example, card 4111 1111 1111 1111" | shadow mask echo "Refund jane.doe@acme.example, card 4111 1111 1111 1111" | shadow mask | shadow unmask ``` The first command prints the masked text; the second gets the original back. Nothing is sent. ### 3. Run Shadow ```sh shadow run # foreground on 127.0.0.1:8877 ``` For a server, use the systemd unit or `shadow install --apply`; for containers, a [sidecar](https://getshadow.dev/integrate/docker-kubernetes/). ### 4. Point the SDK at the gateway Python: ```python from openai import OpenAI client = OpenAI(base_url="http://127.0.0.1:8877/gw/openai/v1") # key from OPENAI_API_KEY reply = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "Summarise this refund ticket from jane.doe@acme.example"}], ) print(reply.choices[0].message.content) # mentions jane.doe@acme.example; OpenAI never saw it ``` Node.js: ```ts import OpenAI from "openai"; const client = new OpenAI({ baseURL: "http://127.0.0.1:8877/gw/openai/v1" }); ``` Or without touching code, since the official SDKs read it from the environment: ```sh export OPENAI_BASE_URL=http://127.0.0.1:8877/gw/openai/v1 ``` The same works for LangChain, LlamaIndex, the Vercel AI SDK and anything else that accepts an OpenAI base URL. Tools that can't set one can use [proxy mode](https://getshadow.dev/integrate/proxy-mode/) instead. ### 5. Azure OpenAI Add a route for your resource: ```toml [proxy.routes] azure = "https://my-resource.openai.azure.com" ``` Requests to `http://127.0.0.1:8877/gw/azure/openai/deployments/gpt-4o/chat/completions?api-version=2024-10-21` then go to Azure, masked. ### 6. Tune what gets masked ```toml organization = ["Acme Ltd"] terms = ["org:Globex", "Project Falcon", "person:Tom Reyes"] internal_domains = ["acme.example", "acme.internal"] allow_terms = ["Jane Austen"] # never masked [policy.always] secret = "redact" # irreversible for secrets [policy.report] person = "token" # bulk data in reports becomes [PERSON_0001] email = "token" default = "surrogate" ``` Check coverage on a sample with `shadow scan sample.json --reveal`. For better recall on free-text names, enable the [open-weight NER sidecar or Presidio](https://getshadow.dev/concepts/detection/). ## What about streaming, tools and files? | Feature | Handling | |---|---| | Streaming (`stream: true`) | Restored on the fly; a fake split across events is reassembled. Hold-back is at most the length of the longest fake | | Chat Completions `tool_calls`, Responses function calls | Arguments restored before your code sees them. See [agents and tool calls](https://getshadow.dev/use-cases/mask-pii-in-llm-agents-and-tool-calls/) | | `POST /v1/files`, `input_file` base64 | Text, CSV, JSON, DOCX, XLSX, PPTX masked; PDF text extracted and sent masked. See [file masking](https://getshadow.dev/concepts/file-masking/) | | `user`, `safety_identifier` fields | Replaced with persona values | | Embeddings | Inputs are masked like any JSON string; see [RAG pipelines](https://getshadow.dev/use-cases/protect-customer-data-in-rag-pipelines/) before relying on it | | Realtime API (WebSocket) | Not processed | ## Example: before and after Request your code sends (fictional data): ```json {"model": "gpt-4o-mini", "messages": [{"role": "user", "content": "Summarise: Jane Doe (jane.doe@acme.example, +1 415 555 0134) disputes invoice 7741 for Acme Ltd."}]} ``` What OpenAI receives: ```json {"model": "gpt-4o-mini", "messages": [{"role": "user", "content": "Summarise: Hui Zhao (hui.zhao@zhilan.cn, +86 28 6512 0417) disputes invoice 7741 for Zhilan Technology Co., Ltd."}]} ``` What your code gets back: a summary about Jane Doe and Acme Ltd. With `shadow run` in the foreground, the log shows the intent and how many values were masked, never the values. ## Limits - Detection is best effort. Internal reference formats (invoice, ticket, account numbers) are not detected by the built-ins; add known values as terms, or write a custom detector if you [embed Shadow in Rust](https://getshadow.dev/integrate/rust/#custom-detectors). - Dates are not masked. Images are not OCR'd. - Masking hides content, not your account: the API key still identifies you to OpenAI. ## Related - [Anonymise prompts for Claude](https://getshadow.dev/use-cases/anonymize-prompts-for-claude/) - [Reversible pseudonymisation for LLMs](https://getshadow.dev/use-cases/reversible-pseudonymization-for-llms/) - [Self-hosted AI gateway with PII masking](https://getshadow.dev/use-cases/self-hosted-ai-gateway-with-pii-masking/) - [Gateway mode](https://getshadow.dev/integrate/gateway-mode/), [Configuration](https://getshadow.dev/reference/configuration/), [Supported providers](https://getshadow.dev/reference/providers/) - [Benchmarks](https://getshadow.dev/benchmarks/) and [how Shadow compares](https://getshadow.dev/compare/) ## Frequently asked questions ### Do I need to change my code to mask PII for the OpenAI API? Usually only the base URL. Point the OpenAI SDK at http://127.0.0.1:8877/gw/openai/v1, or set OPENAI_BASE_URL, and Shadow masks the sensitive values it detects in every request and restores them in every response. Your API key passes through unchanged. ### Is this redaction or pseudonymisation? By default it is reversible pseudonymisation: values become realistic fakes that are restored in the response. You can opt in to irreversible redaction per kind, for example secrets, with [policy.always] secret = "redact". ### Does it work with streaming and the Responses API? Yes. Chat Completions and Responses API streams are restored on the fly, including fakes split across server-sent events and streamed tool-call arguments. ### Does it work with Azure OpenAI? Yes. Add a custom gateway route for your Azure resource under [proxy.routes], or use proxy mode, where *.openai.azure.com is intercepted by default. ### How much latency does masking add? Built-in masking is local, in-process string work: about 7.5 ms per document on the benchmark set, small next to model latency. Optional detectors such as Presidio or the NER sidecar add a local HTTP call each. --- # How to anonymise prompts before sending them to Claude Source: https://getshadow.dev/use-cases/anonymize-prompts-for-claude/ > Mask personal data, customer names and secrets in Anthropic Claude API requests locally, and restore them in streamed answers and tool use. Put Shadow between your code and `api.anthropic.com` by setting `ANTHROPIC_BASE_URL=http://127.0.0.1:8877/gw/anthropic`. Shadow replaces names, emails, phone numbers, customer and company names, IDs, internal hostnames and secrets with realistic fakes before the request leaves, then restores them in Claude's answer, including streamed text, thinking and `tool_use` input. Strictly speaking this is reversible pseudonymisation rather than anonymisation, which is what keeps the answers useful. ## Why anonymise prompts for Claude? Claude is often handed long, rich context: entire ticket threads, contracts, repositories, meeting transcripts. That is where personal data and confidential names pile up. Anthropic's commercial terms and retention settings govern what happens to it, but the simplest protection is not to send it. Placeholder redaction (`[NAME]`) undermines long-context work: Claude can't keep five people apart across a 40-page document if they are all `[NAME]`. Shadow gives each real value a distinct, consistent fake from one [persona](https://getshadow.dev/concepts/personas/), so Claude reasons about a coherent fake world and you read the answer about the real one. ## Which Claude workflows benefit most? | Workflow | What Shadow replaces | |---|---| | Long-document analysis (contracts, transcripts, ticket exports) | People, companies, emails, phones, addresses, IBANs, national IDs; DOCX, XLSX and PPTX attachments masked in place | | Claude Code and other coding agents | API keys, internal hostnames, IPs, home-directory usernames, git identity | | Agents with `tool_use` | Values in prompts and tool results; real values restored in tool input | | Batch summarisation and reporting | Bulk personal data becomes typed tokens such as `[PERSON_0001]` | | Prompts naming customers and codenames | Your own `terms` and `organization` entries | ## How to set it up ### 1. Install and create a persona ```sh curl -fsSL https://raw.githubusercontent.com/Tiny-Bubble-Company/shadow/main/packaging/install.sh | sh shadow init --org "Acme Ltd" shadow identity # the fake identity Claude will see ``` ### 2. Start Shadow ```sh shadow run # or: shadow install --apply for a background service ``` ### 3. Point the Anthropic SDK at the gateway Python: ```python import anthropic client = anthropic.Anthropic(base_url="http://127.0.0.1:8877/gw/anthropic") msg = client.messages.create( model="claude-sonnet-4-5", max_tokens=512, messages=[{"role": "user", "content": "Write a short update to Jane Doe at Globex about Project Falcon"}], ) print(msg.content[0].text) # mentions Jane Doe, Globex and Project Falcon ``` Shadow restores whole fakes, and a fake person's first or last name on its own, so a greeting by first name comes back right. Other fragments, such as the domain of a fake email, stay fake, so ask for full values when the text goes to a real person. TypeScript: ```ts import Anthropic from "@anthropic-ai/sdk"; const client = new Anthropic({ baseURL: "http://127.0.0.1:8877/gw/anthropic" }); ``` Or for any tool built on the official SDKs: ```sh export ANTHROPIC_BASE_URL=http://127.0.0.1:8877/gw/anthropic ``` The base URL stops at the route: the SDK appends `/v1/messages` itself. ### 4. Claude Code and other CLIs Claude Code calls `api.anthropic.com`, so either of these works: ```sh # Gateway: no certificate needed ANTHROPIC_BASE_URL=http://127.0.0.1:8877/gw/anthropic claude # Proxy: covers everything the process sends to known AI hosts source "$HOME/Library/Application Support/shadow/env.sh" # written by shadow install --apply claude ``` The proxy route needs the local CA trusted; `env.sh` sets `NODE_EXTRA_CA_CERTS` for Node-based tools. For coding work, also read [protect secrets from AI coding assistants](https://getshadow.dev/use-cases/protect-secrets-from-ai-coding-assistants/). ### 5. Teach Shadow your names ```toml organization = ["Acme Ltd"] terms = ["org:Globex", "Project Falcon", "person:Jane Doe"] internal_domains = ["acme.example"] ``` Or `shadow terms add "org:Globex" "Project Falcon"`. Check a sample with `shadow scan sample.txt`. ## Claude on Bedrock and Vertex AI | Platform | Status | |---|---| | Anthropic API (`api.anthropic.com`) | Gateway route `anthropic` and proxy mode | | Google Vertex AI (`aiplatform.googleapis.com`, `*-aiplatform.googleapis.com`) | Intercepted in proxy mode; add a custom route for gateway mode | | AWS Bedrock runtime with an API key (bearer) | Masked normally; add a `bedrock` route under `[proxy.routes]` | | AWS Bedrock with SigV4 (IAM) signing | **Forwarded unmasked with a warning.** Modifying a signed body breaks the signature | ```toml [proxy.routes] bedrock = "https://bedrock-runtime.eu-central-1.amazonaws.com" ``` ## Example: before and after What you send (fictional data): ```text Summarise this escalation for the account team. Jane Doe (jane.doe@globex.example) says the Project Falcon rollout at Globex broke SSO on idp.acme.internal. ``` What Claude sees with this machine's persona (yours will differ): ```text Summarise this escalation for the account team. Ying Chen (ying.chen@lanxin.cn) says the Velora Quilent rollout at Lanxin Trading broke SSO on xinghe-srv-11. ``` The summary you receive names Jane Doe, Globex, Project Falcon and `idp.acme.internal` again. Streamed responses are restored as they arrive, even when a fake is split across two events. ## Tool use and agents When Claude answers with a `tool_use` block, its `input` contains the fakes it saw. Shadow restores the real values before your code runs the tool, including when the input JSON is streamed in pieces. Tools hosted by the provider are handled by [policy](https://getshadow.dev/concepts/intent-policy/): by default the action's target is kept so the action works, and the keep is logged. Details in [mask PII in agents and tool calls](https://getshadow.dev/use-cases/mask-pii-in-llm-agents-and-tool-calls/). ## Limits - `claude.ai` and the Claude desktop and mobile apps are not intercepted. - Images in prompts are not OCR'd; set `[files] images = "block"` if screenshots may contain personal data. PDFs in `document` blocks are sent as masked text by default. - Dates are not masked, and detection is best effort. Unique context can still identify a person or company. ## Related - [Redact PII before sending to the OpenAI API](https://getshadow.dev/use-cases/redact-pii-before-sending-to-openai-api/) - [GDPR-compliant LLM usage](https://getshadow.dev/use-cases/gdpr-compliant-llm-usage/) - [Gateway mode](https://getshadow.dev/integrate/gateway-mode/), [Proxy mode](https://getshadow.dev/integrate/proxy-mode/), [Supported providers](https://getshadow.dev/reference/providers/) - [Benchmarks](https://getshadow.dev/benchmarks/) and [how Shadow compares](https://getshadow.dev/compare/) ## Frequently asked questions ### How do I route the Anthropic SDK through Shadow? Set the base URL to http://127.0.0.1:8877/gw/anthropic, either in code or with ANTHROPIC_BASE_URL. The SDK appends /v1/messages itself. Your x-api-key header passes through unchanged. ### Can I use Shadow with Claude Code? Yes. Claude Code talks to api.anthropic.com, so set ANTHROPIC_BASE_URL to the Shadow gateway or run it with HTTPS_PROXY pointing at Shadow and the local CA trusted through NODE_EXTRA_CA_CERTS. ### Does it work with Claude on Amazon Bedrock or Google Vertex AI? Vertex AI hosts are intercepted in proxy mode. Bedrock works with API-key (bearer) auth; requests signed with AWS SigV4 are forwarded unmasked with a warning, because changing the body would break the signature. ### Can I use Shadow with the claude.ai website or desktop app? Not as a supported integration. Shadow intercepts api.anthropic.com, not claude.ai. Use an API-backed client or Claude Code through Shadow instead. ### Is anonymised text still personal data? For you, yes. Shadow pseudonymises reversibly and you hold the mapping, so under GDPR the data remains personal data in your hands. It reduces what Anthropic receives; it does not take the data out of scope for you. --- # How to keep API keys and internal hosts out of AI coding assistants Source: https://getshadow.dev/use-cases/protect-secrets-from-ai-coding-assistants/ > Stop Claude Code, Aider, Cline and other AI coding tools sending API keys, internal hostnames, IPs and usernames to the model. Set up Shadow in minutes. Coding assistants read your working tree, your terminal output and whatever you paste, which means `.env` values, cloud keys, internal hostnames, IP addresses and your home-directory username go to the model provider. Run Shadow locally and point the assistant at it with `ANTHROPIC_BASE_URL`, `OPENAI_BASE_URL` or `HTTPS_PROXY`; Shadow replaces those values with fakes (or redacts secrets outright) and restores them in the reply. It works with any assistant that calls a supported provider API with your own key. ## What leaks from a developer's machine? | Source | Example of what reaches the provider | |---|---| | Config files the agent opens | `STRIPE_SECRET_KEY=sk_live_…`, `AWS_SECRET_ACCESS_KEY=…`, `password = "…"` | | Stack traces and logs | `ConnectionError: db01.prod.acme.internal:5432`, `10.20.4.17`, customer emails | | File paths | `/Users/jdoe/src/billing/...` (your username) | | Git metadata | `user.name`, `user.email` | | Pasted snippets | JWTs, bearer tokens, private keys | | Request metadata | SDK OS/arch headers, `metadata.user_id` | Most of these are things you would never type into a chat on purpose. They leak because agents read files for context. ## Which assistants are covered? | Assistant | How it connects | Covered by Shadow | |---|---|---| | Claude Code | `api.anthropic.com` | Yes: `ANTHROPIC_BASE_URL` or `HTTPS_PROXY` | | CLIs and scripts using an OpenAI API key | `api.openai.com` | Yes: `OPENAI_BASE_URL` or `HTTPS_PROXY` | | Aider, Continue, Cline and similar with your own key | The provider you configure | Yes, through a base URL setting or the proxy | | VS Code / JetBrains extensions calling provider APIs | Provider API | Yes, through the IDE proxy setting and the trusted CA | | GitHub Copilot | GitHub's own backend | Not built in | | Cursor (hosted models) | Cursor's own backend | Not built in | Shadow intercepts the [provider API hosts](https://getshadow.dev/reference/providers/). Tools that route through their vendor's backend are not on that list. You can add other hosts with `[proxy] extra_hosts`, and Shadow masks string values in any JSON body it intercepts, but that is untested for these products; check the per-request log if you try it. ## How to set it up ### 1. Install and initialise ```sh curl -fsSL https://raw.githubusercontent.com/Tiny-Bubble-Company/shadow/main/packaging/install.sh | sh shadow init --org "Acme Ltd" ``` Shadow discovers your OS user, hostname and git identity at startup (`local_facts`) and maps them to the persona, so they are masked without configuration. ### 2. Configure for code Edit `shadow.toml` in the data directory (`~/Library/Application Support/shadow` on macOS, `~/.local/share/shadow` on Linux): ```toml organization = ["Acme Ltd"] internal_domains = ["acme.internal", "acme.example", "corp.acme.example"] terms = ["hostname:build01", "hostname:db01", "Project Falcon"] [policy.always] secret = "redact" # keys, tokens and private keys leave as [REDACTED_SECRET] ``` With redaction on, the model can't echo a key back, so you won't get it restored in an answer either. That is usually what you want for code. ### 3. Scan before you trust it Run a scan on the files your assistant is likely to read. Nothing is changed or sent: ```sh shadow scan .env shadow scan config/production.toml --reveal ``` If a value is missing from the output, Shadow will not mask it. Typical gaps are passwords inside connection URLs (`postgres://user:pw@host`) and unusual in-house token formats. Move those to variables the agent does not need to read, or add the host to `internal_domains`. ### 4. Point the assistant at Shadow Gateway mode, no certificate needed: ```sh shadow run & export ANTHROPIC_BASE_URL=http://127.0.0.1:8877/gw/anthropic export OPENAI_BASE_URL=http://127.0.0.1:8877/gw/openai/v1 claude ``` Proxy mode, which covers every tool in the shell that calls a supported provider API, including ones without a base URL setting: ```sh shadow install --apply # background service, CA trust, env.sh source "$HOME/Library/Application Support/shadow/env.sh" ``` `env.sh` sets `HTTPS_PROXY`, `NO_PROXY`, and the trust variables Node and Python tools need (`NODE_EXTRA_CA_CERTS`, `REQUESTS_CA_BUNDLE`, `SSL_CERT_FILE`, `CURL_CA_BUNDLE`). Add the `source` line to `~/.zshrc` or `~/.bashrc`. For IDE extensions, set the IDE's HTTP proxy to `http://127.0.0.1:8877`. ### 5. Watch the log With `shadow run` in the foreground you see one line per request: the intent (usually `code`) and how many values were masked by kind. Values are never logged. ## Example: debugging a failing service What the assistant sends (fictional data): ```text Why does this fail? File "/Users/jdoe/src/billing/db.py", line 12 OperationalError: could not connect to db01.prod.acme.internal (10.20.4.17) config: STRIPE_SECRET_KEY=sk_live_51Hx... owner=jane.doe@acme.example ``` What the provider sees (persona values differ per machine): ```text Why does this fail? File "/Users/yingchen/src/billing/db.py", line 12 OperationalError: could not connect to lanxin-db-02.zhilan.internal (10.88.31.204) config: STRIPE_SECRET_KEY=[REDACTED_SECRET] owner=ying.chen@xinghe.cn ``` The model's answer refers to `db01.prod.acme.internal` and your real path when it reaches you. Because `Acme Ltd` is configured as the organisation, the bare name `Acme` is protected wherever it appears, including in repository and package names. ## Code intent: why answers stay good Shadow classifies requests that contain source code, diffs, stack traces or configuration as `code`. Identifiers, function names and structure pass through untouched; only detected values are replaced. A fake hostname is still a hostname, a fake IP is still a valid IP, and a surrogate key keeps the shape of the original, so generated code and configs remain coherent. See [intent-aware policy](https://getshadow.dev/concepts/intent-policy/). ## Limits - Copilot and Cursor's hosted backends are not covered (see the table). - Detection is best effort, and proprietary code is not "masked": Shadow hides values, not your algorithms. If source code itself is the secret, decide which repositories an assistant may read at all. - Apps that pin certificates reject the local CA in proxy mode. Use a base URL where possible. ## Related - [Anonymise prompts for Claude](https://getshadow.dev/use-cases/anonymize-prompts-for-claude/) - [Mask PII in LLM agents and tool calls](https://getshadow.dev/use-cases/mask-pii-in-llm-agents-and-tool-calls/) - [SaaS and technology](https://getshadow.dev/industries/saas-technology/), [solo developers and freelancers](https://getshadow.dev/company-size/solo-developers-freelancers/) - [Proxy mode](https://getshadow.dev/integrate/proxy-mode/), [Detection](https://getshadow.dev/concepts/detection/), [Security model](https://getshadow.dev/reference/security/) - [Benchmarks](https://getshadow.dev/benchmarks/) and [how Shadow compares](https://getshadow.dev/compare/) ## Frequently asked questions ### Which coding assistants does Shadow work with? Any tool that calls a supported provider API with your key and honours a base URL or HTTPS_PROXY: Claude Code, OpenAI-API-based CLIs, Aider, Continue and Cline with your own keys, and your own scripts. GitHub Copilot and Cursor's hosted models send traffic to their vendors' own backends, which are not built-in intercepted hosts. ### Are secrets redacted or replaced with fakes? By default a secret becomes a fake of the same shape, so code and configs stay coherent and the real key is restored locally if the model quotes it back. For coding work we recommend [policy.always] secret = "redact", which sends [REDACTED_SECRET] and never the key in any form. ### Does masking make code suggestions worse? Rarely. Requests with code are classified as code intent, identifiers stay readable, and only sensitive values such as keys, hostnames, IPs, emails and home-directory usernames are replaced. ### Will Shadow catch a password in a database URL? Not reliably. The secret detector finds known key formats, JWTs, PEM private keys, bearer tokens and password= style assignments, but not a password embedded in a connection URL such as postgres://user:pw@host. Keep such URLs in environment variables, or add the hostname to internal_domains so at least the host is masked. ### Can I bypass Shadow for a single command? Yes. Unset the proxy for that command with env -u HTTPS_PROXY, or drop the base URL variable. Shadow never falls back to a direct connection: if it is down, proxied tools fail to connect. Hosts that are not on the intercepted list, and anything in NO_PROXY, go direct by design. --- # How to use ChatGPT, Claude and other LLMs in line with GDPR Source: https://getshadow.dev/use-cases/gdpr-compliant-llm-usage/ > Use LLMs with less personal data: Shadow pseudonymises prompts locally (GDPR Art. 4(5), 25, 32) before they reach OpenAI, Anthropic or Gemini. Using LLMs under GDPR comes down to sending the provider as little personal data as the task needs, and protecting what you do send. Shadow helps with the first part technically: it pseudonymises names, emails, phone numbers, addresses, national IDs, IBANs and other identifiers in every prompt on your own infrastructure, sends realistic fakes to the provider, and restores the real values in the answer. You still need the organisational side (lawful basis, DPA, records, DPIA); Shadow does not make anything "GDPR compliant" on its own. ## What does GDPR require when you use an LLM? When staff or systems send personal data to an AI provider, you are disclosing it to a processor (or, depending on the terms, another controller), often outside the EEA. The obligations that matter most in practice: | GDPR provision | What it asks | How Shadow helps | |---|---|---| | Art. 5(1)(c) data minimisation | Only data adequate, relevant and limited to what is necessary | Most prompts don't need real names or addresses to be answered. Shadow replaces them before sending | | Art. 4(5) pseudonymisation | Data that can't be attributed to a person without separately kept additional information | The real↔fake mapping is the additional information, held in an [encrypted local vault](https://getshadow.dev/concepts/vault/), never sent | | Art. 25 data protection by design and by default | Technical measures built into processing | Masking happens automatically on the request path, not by user discipline | | Art. 32 security of processing | Appropriate measures, explicitly including pseudonymisation | Pseudonymisation plus encryption at rest (ChaCha20-Poly1305) on infrastructure you control | | Art. 35 DPIA | Assess high-risk processing | Shadow is a mitigation you can document; the DPIA itself is yours | | Art. 44+ transfers | A valid transfer mechanism for data leaving the EEA | Less personal data is transferred. A transfer mechanism is still required | Recital 26 matters here: pseudonymised data remains personal data for whoever can re-identify it, and you can. The [EDPB Guidelines 01/2025 on pseudonymisation](https://www.edpb.europa.eu/) describe pseudonymisation as a risk-reduction measure, not an exemption. :::caution[Not legal advice] This page describes technical capabilities. Whether your processing complies with GDPR depends on facts only you and your counsel or DPO can assess. ::: ## Why pseudonymise rather than redact? Redaction (`[NAME]`, `***`) minimises well but breaks many tasks: a model can't draft a reply to `[NAME]` at `[EMAIL]` or keep three customers apart. Shadow's surrogates are **format-preserving and consistent**: the same person always gets the same fake, emails stay emails, IBANs still pass mod-97. The answer comes back about your real data. For bulk processing, such as reports over customer lists, Shadow switches to typed tokens (`[PERSON_0001]`) so the model treats people as opaque IDs. For values that must never leave in any form, you can opt in to irreversible redaction per kind. ## How to set it up ### 1. Map where personal data meets an LLM List the tools and flows: chat clients, coding assistants, support tools, internal apps, agents, RAG pipelines. Note which provider each one calls. Shadow covers the provider APIs (OpenAI, Azure OpenAI, Anthropic, Gemini, Vertex AI, Mistral and [others](https://getshadow.dev/reference/providers/)), not consumer web apps such as `chatgpt.com`. ### 2. Install Shadow where the data originates ```sh # Laptops and workstations curl -fsSL https://raw.githubusercontent.com/Tiny-Bubble-Company/shadow/main/packaging/install.sh | sh shadow init --org "Acme GmbH" shadow install --apply ``` For services, run it as a [gateway](https://getshadow.dev/integrate/gateway-mode/) or [Kubernetes sidecar](https://getshadow.dev/integrate/docker-kubernetes/) next to the app. Shadow runs in the same place as the data, so it introduces no new processor. ### 3. Configure for personal data ```toml organization = ["Acme GmbH"] terms = ["org:Globex AG", "person:Jana Novak"] internal_domains = ["acme.example"] [persona] region = "JP" # where the fake identities "live"; pick one that suits your work [presidio] enabled = true # wider recall: many national ID formats (ES_NIF, IT_FISCAL_CODE, PL_PESEL, ...) url = "http://127.0.0.1:5002/analyze" language = "en" # other languages need a Presidio image with that language's NLP model [policy.report] person = "token" email = "token" phone = "token" address = "token" national_id = "token" default = "surrogate" [policy.always] secret = "redact" [files] images = "block" # images are not OCR'd ``` Built in, Shadow detects emails, phones, names, English and German street addresses, IBANs, cards, IPs, MACs, hostnames, secrets, and national IDs including US SSN and ITIN, UK NINO and NHS number, Aadhaar, Indian PAN and Australian TFN and ABN. [Presidio](https://getshadow.dev/concepts/detection/#presidio) and the open-weight NER sidecar add multilingual names and more ID formats. ### 4. Test on representative data ```sh shadow scan sample-ticket-de.txt shadow scan export.csv --json ``` Keep the results as evidence for your DPIA: what is detected, what is not, and which mitigations cover the gaps. ### 5. Decide on message recipients and provider-hosted tools When someone asks the model to write to or act on a person ("Draft a reply to Jana Novak at Globex"), Shadow by default passes that **recipient** through as written, so the model addresses the right person, and logs each keep. Other people and data in the request stay pseudonymised. If an agent uses tools that run in the provider's cloud, Shadow also sends the real target of that action (and logs it) so the action works. If data minimisation matters more than the model seeing who it writes to, set: ```toml [policy.act] targets = "surrogate" # recipients become stand-ins, restored in the answer remote_tool_targets = "surrogate" ``` ### 6. Document it Record Shadow in your records of processing and DPIA as a pseudonymisation measure: where it runs, which detectors and terms are configured, the policy, where the vault key lives and who can read it, and the known limits below. ## Example: a customer email in German What the support agent sends (fictional data): ```text Antworte höflich: Frau Jana Novak (jana.novak@globex.example, +49 30 1234567, Hauptstraße 5, IBAN DE89 3704 0044 0532 0130 00) fordert eine Rückerstattung. ``` What the provider sees (persona values differ per machine): ```text Antworte höflich: Frau Yui Sato (yui.sato@hoshino.jp, +81 3 5412 7780, Sakura-dori 12, IBAN DE44 5001 0517 5407 3249 31) fordert eine Rückerstattung. ``` The reply arrives with Jana Novak and her contact details restored wherever the model used the full fake values. A salutation with the surname alone ("Sehr geehrte Frau Sato") is restored too, because fake first and last names are restored on their own; other fragments, such as the domain of a fake email, stay fake. ## Limits to record in your DPIA - **Dates are not masked**, including dates of birth. - **Detection is best effort**, especially for names in languages and formats the detectors have not seen. Measure it: see [benchmarks](https://getshadow.dev/benchmarks/). - **Context can identify.** A unique event described in detail can identify a person with every name replaced. - **Not processed:** images (no OCR), audio, WebSocket and Realtime APIs, SigV4-signed Bedrock requests. - **Access to the vault** re-identifies everything. Protect the data directory and `vault.key`. ## Related - [Reversible pseudonymisation for LLMs](https://getshadow.dev/use-cases/reversible-pseudonymization-for-llms/) - [HIPAA-safe LLM usage](https://getshadow.dev/use-cases/hipaa-safe-llm-usage/) - [Regulated enterprise](https://getshadow.dev/company-size/regulated-enterprise/), [HR and recruiting](https://getshadow.dev/industries/hr-recruiting/) - [Security model](https://getshadow.dev/reference/security/), [Vault](https://getshadow.dev/concepts/vault/), [Intent-aware policy](https://getshadow.dev/concepts/intent-policy/) - [How Shadow compares](https://getshadow.dev/compare/) ## Frequently asked questions ### Does Shadow make our use of ChatGPT or the OpenAI API GDPR compliant? No single tool does. Shadow is a technical measure that supports data minimisation (Art. 5(1)(c)), data protection by design (Art. 25) and security of processing (Art. 32) by pseudonymising personal data before it reaches the provider. You still need a lawful basis, a DPA with the provider, records of processing and, where required, a DPIA. ### Is pseudonymised data still personal data under GDPR? For you, yes. Shadow keeps the key (the vault) on your side, and Recital 26 treats pseudonymised data as personal data for anyone who can reasonably re-identify it. Whether masked prompts are personal data in the provider's hands depends on the circumstances; do not assume they are not. ### Do we still need a data processing agreement with OpenAI or Anthropic? Yes, if personal data may reach them. Detection is best effort, free text can contain identifiers Shadow does not recognise, and context can identify people. Shadow reduces what is sent; it does not remove the need for a DPA. ### Does Shadow help with international data transfers? It reduces the personal data that is transferred, which can be relevant to your transfer risk assessment. It does not replace a transfer mechanism such as the EU-US Data Privacy Framework or standard contractual clauses. ### Does Shadow itself process personal data in the cloud? No. Shadow runs on your machines or in your pods, has no account, no telemetry and no hosted service. The real-to-fake mapping stays in an encrypted vault under your control. --- # How to use LLMs with patient data more safely under HIPAA Source: https://getshadow.dev/use-cases/hipaa-safe-llm-usage/ > Keep PHI out of OpenAI, Azure OpenAI and Claude prompts: Shadow masks patient identifiers locally, supports minimum necessary, and restores answers. To use LLMs with patient data more safely, send the provider only what the task needs. Shadow does that technically: it runs on your own machines or pods, replaces patient and clinician names, emails, phone numbers, addresses, SSNs and other identifiers with realistic fakes before a request reaches OpenAI, Azure OpenAI, Anthropic or Gemini, and restores them in the answer. It supports the HIPAA minimum necessary standard; it does not de-identify data on its own, and it does not replace a business associate agreement (BAA). ## What does HIPAA mean for LLM prompts? | HIPAA concept | What it means for AI use | Where Shadow fits | |---|---|---| | Minimum necessary (45 CFR 164.502(b)) | Use and disclose only the PHI needed for the purpose | Most writing and summarising tasks need no real identifiers. Shadow removes the detected ones automatically | | Business associates | A vendor that receives PHI on your behalf needs a BAA | Still required if PHI may reach the vendor. Shadow reduces exposure, it does not change the relationship | | Security Rule safeguards | Technical safeguards proportionate to risk | A control on a specific data flow, on infrastructure you run, with no telemetry | | De-identification (45 CFR 164.514) | Safe Harbor (remove 18 identifiers) or expert determination | Shadow covers several identifier types but not all. It is not a Safe Harbor method | :::caution[Not legal advice] This page describes technical capabilities, not legal conclusions. Using Shadow does not make an organisation HIPAA compliant. Involve your privacy officer, security officer and counsel. ::: ## Which identifiers does Shadow mask? | Safe Harbor identifier | Shadow today | |---|---| | Names | Built-in name detector; `person:` terms; NER sidecar or Presidio for better recall | | Geographic subdivisions smaller than a state | English and German street addresses built in; Presidio and the NER sidecar add locations. City names alone are often not masked | | **Dates (except year)** | **Not masked** | | Phone numbers, emails | Built in | | Fax numbers | Detected when formatted like phone numbers | | SSNs | Built in (US SSN and ITIN; also UK NHS number, NINO) | | **Medical record numbers, health plan beneficiary numbers, account numbers** | **Not detected by built-ins.** Presidio covers `US_MBI` and some account formats; the NER sidecar finds some account-style numbers; add known values as terms | | Certificate or licence numbers | Presidio `MEDICAL_LICENSE`, `US_DRIVER_LICENSE` | | Vehicle and device identifiers, serial numbers | Not detected unless added as terms | | URLs, IP addresses | Internal hostnames and IPs built in; public URLs left alone | | Biometric identifiers, full-face photos | Not handled; images are not OCR'd or analysed | | Any other unique code | Terms, or a custom detector if you [embed Shadow in Rust](https://getshadow.dev/integrate/rust/#custom-detectors) | There is no custom regex setting in `shadow.toml`. Be explicit about these gaps in your risk analysis. ## How to set it up ### 1. Choose the AI service and sign the BAA Pick a provider API offered under a BAA (for example an enterprise or cloud-platform offering). Shadow then sits in front of that API. It covers provider APIs such as `api.openai.com`, `*.openai.azure.com`, `api.anthropic.com`, Gemini and Vertex AI, not consumer web apps such as `chatgpt.com`. ### 2. Install Shadow where PHI originates ```sh curl -fsSL https://raw.githubusercontent.com/Tiny-Bubble-Company/shadow/main/packaging/install.sh | sh shadow init --org "St Mary's Health Partners" shadow install --apply # workstation: background service, CA trust, proxy env ``` For an internal scribe or summarisation service, run Shadow as a [gateway](https://getshadow.dev/integrate/gateway-mode/) or [sidecar](https://getshadow.dev/integrate/docker-kubernetes/) and set the app's base URL, for example a custom `azure` route. ### 3. Configure for PHI ```toml organization = ["St Mary's Health Partners"] terms = ["org:Northside Imaging", "person:Dr Amara Okafor", "term:Ward 7B"] internal_domains = ["stmarys.example", "ehr.stmarys.internal"] [presidio] enabled = true url = "http://127.0.0.1:5002/analyze" entities = ["PERSON", "PHONE_NUMBER", "EMAIL_ADDRESS", "US_SSN", "US_MBI", "MEDICAL_LICENSE", "US_DRIVER_LICENSE", "LOCATION"] [policy.act] targets = "keep" # default: a patient you write to is sent as written; "surrogate" hides them [policy.report] person = "token" email = "token" phone = "token" address = "token" national_id = "token" default = "surrogate" [policy.always] secret = "redact" [files] pdf = "text" images = "block" [proxy.routes] azure = "https://my-resource.openai.azure.com" ``` Run Presidio locally: `docker run -d --name presidio -p 127.0.0.1:5002:3000 mcr.microsoft.com/presidio-analyzer`. For multilingual names, add the open-weight NER sidecar (`python serve_hf.py --backend ensemble --host 127.0.0.1 --port 8878` in `ml/`, then `[model_detector] enabled = true`). ### 4. Handle dates and record numbers in the workflow Because dates and MRNs are not masked, design workflows around them: strip date-of-birth and MRN columns from exports, use relative times ("day 3 of admission") in prompts, and add known identifiers as terms where practical. ### 5. Test, then keep testing ```sh shadow scan synthetic-discharge-letter.txt --reveal ``` Use synthetic or already-approved test documents. Re-run scans when templates or EHR exports change. ## Example: drafting a patient letter What the clinician sends (fictional data): ```text Draft a follow-up letter to Maria Lopez, 12 Elm Road, Springfield, phone (217) 555-0142, about her MRI results with Dr Amara Okafor. ``` What the provider sees (persona values differ): ```text Draft a follow-up letter to Maria Lopez, 12 Elm Road, Springfield, phone (217) 555-0142, about her MRI results with Dr Tao Zhu. ``` The goal is to write to Maria Lopez, so she is the recipient: her name, address and phone pass through as written, each keep is logged, and the model greets the right patient. Dr Amara Okafor comes after "about", so she is masked and restored in the draft ("Dr Zhu" on its own is restored too, because fake first and last names are restored on their own). This means the recipient's identifiers reach the provider, under your BAA, whenever you ask the model to write to a patient. If that is not acceptable, set `[policy.act] targets = "surrogate"`: the provider then writes to a stand-in patient and Shadow restores Maria's name and address in the draft. The trade-off is that the model may personalise the letter for the wrong person, so review it before sending. Leave identifiers the letter does not need, such as an SSN, out of the prompt altogether. ## Example: a report over patient rows Ask for "a breakdown of readmissions by ward" and attach a CSV of 800 rows. Shadow classifies the request as `report`, turns names, emails, phones and addresses into typed tokens (`[PERSON_0001]`), and restores them in the answer. The model counts and groups without inventing facts about fake patients. ## Rollout notes - Fleet rollout uses your MDM or configuration management; there is no central admin console yet. - Logs record intent and counts per treatment, never values. An optional local audit log is on the [roadmap](https://getshadow.dev/project/roadmap/). - Protect the data directory: anyone who can read `vault.key` can re-identify masked text. Use full-disk encryption. ## Related - [Healthcare](https://getshadow.dev/industries/healthcare/) and [pharma and life sciences](https://getshadow.dev/industries/pharma-life-sciences/) - [Redact documents before uploading to AI](https://getshadow.dev/use-cases/redact-documents-before-uploading-to-ai/) - [GDPR-compliant LLM usage](https://getshadow.dev/use-cases/gdpr-compliant-llm-usage/) - [Detection](https://getshadow.dev/concepts/detection/), [File masking](https://getshadow.dev/concepts/file-masking/), [Security model](https://getshadow.dev/reference/security/) - [Benchmarks](https://getshadow.dev/benchmarks/) and [how Shadow compares](https://getshadow.dev/compare/) ## Frequently asked questions ### Is there a HIPAA-compliant way to use ChatGPT? Compliance comes from your whole programme, not one tool. The common pattern is an API-based service under a business associate agreement, used through an approved client, with technical safeguards such as Shadow keeping identifiers out of prompts. Consumer ChatGPT without a BAA is generally not suitable for PHI. ### Does masking with Shadow de-identify PHI under the Safe Harbor method? No. Safe Harbor requires removing all 18 identifier types. Shadow does not mask dates and does not detect medical record numbers or health plan IDs by default, so treat masked text as PHI with reduced risk, not as de-identified data. ### Do we still need a BAA with the AI provider? Yes, whenever PHI may reach that provider. Shadow reduces what is sent, but detection is best effort and dates, unknown IDs or context can still identify patients. ### Can Shadow mask PHI in clinical documents and spreadsheets? It masks the text of DOCX, XLSX and PPTX files in place and sends PDF text masked. Scanned documents and images are not OCR'd, so block images if they may contain PHI. ### Where are the real patient identifiers stored? In an encrypted vault (ChaCha20-Poly1305) on the machine or pod running Shadow. Nothing is sent to a Shadow service; there isn't one. --- # How to mask PII in LLM agents without breaking tool calls Source: https://getshadow.dev/use-cases/mask-pii-in-llm-agents-and-tool-calls/ > Keep personal data away from the model while agents still email, query and update the real records: recipients pass through, everything else is restored locally. Route the agent's model calls through Shadow and keep tool execution in your own code. The person you ask the agent to contact or act on passes through as written, so the model writes to the right person; each keep is logged. Everything else the model sees in prompts, history and tool results is a realistic fake; when it calls a tool, Shadow translates the arguments back to real values before your tool runs, and masks the tool's result again on the way to the model. If the recipient must stay hidden too, set `targets = "surrogate"`. Provider-hosted tools are covered [below](#provider-hosted-tools-keep-or-surrogate). ## Why is PII in agents harder than in chat? In chat, a model only needs to *write about* a person. An agent needs to *act on* one: send the email, update the CRM record, look up the order. Masking that ignores this breaks the agent: | Approach | What goes wrong | |---|---| | No masking | Tool results (database rows, CRM records, inboxes) pour personal data into every turn | | Placeholder redaction | The model calls `send_email(to="")`. The action fails | | One-way fakes | The model calls `send_email(to="hui.zhao@zhilan.cn")`. The action goes to a stranger | | **Shadow** | The recipient is passed through, so the model calls `send_email(to="jane.doe@acme.example")` and writes "Dear Jane"; other people and values in the body are fakes, restored locally before your code sends it. With `targets = "surrogate"` the recipient is a fake too, restored the same way | ## How the round trip works ```text agent ──(prompt + tool schemas)────────▶ Shadow ──(masked)──────────────▶ model agent ◀──(tool call, real arguments)── Shadow ◀──(tool call, fake args)── model agent runs the tool with real values agent ──(tool result)──────────────────▶ Shadow ──(result masked)────────▶ model ``` 1. Prompts, history and earlier tool results are masked. Protocol keys such as `type`, `id` and `tool_call_id` are never touched. 2. The model writes arguments with what it saw: the real recipient, fakes for everything else. 3. Shadow restores the fakes in the arguments before your code sees them, also when they are streamed in fragments. 4. Your tool's result contains real values; Shadow masks them with the same fakes, because the [vault](https://getshadow.dev/concepts/vault/) maps values consistently. Supported shapes: OpenAI Chat Completions `tool_calls`, OpenAI Responses function calls, Anthropic `tool_use`, and Gemini `functionCall`. Full detail in [tool calls and agents](https://getshadow.dev/concepts/tool-calls/). ## How to set it up ### 1. Run Shadow next to the agent ```sh shadow init --org "Acme Ltd" shadow run ``` In production, run it as a [sidecar](https://getshadow.dev/integrate/docker-kubernetes/) in the agent's pod, listening on loopback. ### 2. Point the agent's model client at Shadow ```sh export OPENAI_BASE_URL=http://127.0.0.1:8877/gw/openai/v1 export ANTHROPIC_BASE_URL=http://127.0.0.1:8877/gw/anthropic ``` Most frameworks (LangChain, LlamaIndex, the OpenAI Agents SDK, the Vercel AI SDK) pick these up or accept a base URL parameter. Python example with plain function calling: ```python from openai import OpenAI import json client = OpenAI(base_url="http://127.0.0.1:8877/gw/openai/v1") tools = [{"type": "function", "function": { "name": "send_email", "parameters": {"type": "object", "properties": { "to": {"type": "string"}, "body": {"type": "string"}}, "required": ["to", "body"]}}}] resp = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "Email jane.doe@acme.example that her renewal is confirmed"}], tools=tools, ) call = resp.choices[0].message.tool_calls[0] args = json.loads(call.function.arguments) print(args["to"]) # jane.doe@acme.example: the recipient, passed through and logged ``` ### 3. Set the agent policy ```toml [policy] intent = true # classify act / report / code / converse [policy.act] targets = "keep" # the recipient: pass through, logged ("surrogate" hides it) remote_tool_targets = "keep" # provider-hosted tools: send the real target, logged [policy.report] person = "token" email = "token" phone = "token" address = "token" default = "surrogate" [policy.always] secret = "redact" ``` ### 4. Prefer local tool execution Only tools that **your code** executes can receive restored values. For actions on personal data, especially with `targets = "surrogate"`, run the tool yourself (your own MCP client and server, your function-calling loop) rather than a provider-hosted connector. ### 5. Watch the decisions The log shows each request's intent and counts per treatment. Every `keep` is logged with the value's kind and reason, so you can see exactly what left in clear text. ## Provider-hosted tools: keep or surrogate? | Setting | Provider sees | Hosted action | |---|---|---| | `remote_tool_targets = "keep"` (default) | The real target of that one action; everything else masked | Works | | `remote_tool_targets = "surrogate"` | A fake target | Runs against the fake (the email goes nowhere useful) | Choose `surrogate` when exposure matters more than the hosted action succeeding. ## Example: CRM lookup and follow-up User instruction (fictional data): *"Email Jane Doe at Globex a status update on her open tickets. Tom Reed handled the last one."* - Model sees: *"Email Jane Doe at Globex a status update on her open tickets. Wei Chen handled the last one."* Jane Doe and Globex are the recipient and pass through (logged); Tom Reed is not, so he is a fake. - Model calls `search_tickets(customer="Globex", contact="Jane Doe")`; your code runs it as is. - Your tool returns three tickets with other people's names and emails; the model sees them masked. - Model calls `send_email(to=..., body="Dear Jane, ... Wei Chen handled ...")`; Shadow restores "Tom Reed" in the body before your code sends it to Jane. Detectors can't know your customers, so add them as terms (`shadow terms add "org:Globex"`). Here Globex passes through because it is part of the recipient; in a request that only talks about Globex, the term makes sure it is masked. ## In-process with Rust If your agent is written in Rust, [embed `shadow-core`](https://getshadow.dev/integrate/rust/) and use one session per model call: ```rust let session = shadow.session_for(&request_json); // classifies intent from instruction and tools session.mask_json(&mut request_json); let mut response = send(&request_json)?; session.unmask_json(&mut response); // tool-call arguments come back real let summary = session.summary(); // surrogates, tokens, kept, redacted ``` For streams, `session.stream_unmasker()` restores text and arguments chunk by chunk. ## Practical tips - Keep tool **descriptions** free of personal data. - Shadow restores whole fakes, and a fake person's first or last name on its own ("Dear Hui," becomes "Dear Jane," with `targets = "surrogate"`). Other fragments, such as the domain of a fake email or the last four digits of a fake card, stay fake, so ask for full values when the text goes to a real person. - When a tool must quote an exact value back (an order ID, an IBAN), surrogates still round-trip. Test with `shadow mask` and `shadow unmask`. - Keep the vault persistent so fakes stay stable across long-running agent sessions. - Not supported: Realtime and WebSocket agents (voice), and SigV4-signed Bedrock requests. ## Related - [Protect customer data in RAG pipelines](https://getshadow.dev/use-cases/protect-customer-data-in-rag-pipelines/) - [Privacy for AI in customer support](https://getshadow.dev/use-cases/privacy-for-ai-in-customer-support/) - [Intent-aware policy](https://getshadow.dev/concepts/intent-policy/), [Tool calls and agents](https://getshadow.dev/concepts/tool-calls/), [Integrating any endpoint](https://getshadow.dev/integrate/any-endpoint/) - [Benchmarks](https://getshadow.dev/benchmarks/) and [how Shadow compares](https://getshadow.dev/compare/) ## Frequently asked questions ### If the model only sees fake data, how does the agent act on the real customer? The person the agent is asked to contact or act on is the recipient, and Shadow passes the recipient through as written (and logs it), so the model addresses the right person. Everything else is masked; the model writes tool calls with the fakes it saw, and Shadow restores the real values in the tool-call arguments on your machine before your code runs the tool. Set [policy.act] targets = "surrogate" to fake the recipient too and restore it locally. ### What about tools the provider runs, such as hosted MCP servers or web search? Shadow can't restore values inside the provider's cloud. By default it sends the real target of that action and logs the keep, while everything else stays masked. Set [policy.act] remote_tool_targets = "surrogate" if you would rather the action fail than reveal the target. ### Does this work with streamed tool calls? Yes. Tool-call arguments streamed in pieces are reassembled and restored, for OpenAI Chat Completions and Responses, Anthropic tool_use and Gemini functionCall. ### Does it work with LangChain, LlamaIndex or the OpenAI Agents SDK? Yes, if the framework lets you set the provider base URL or honours HTTPS_PROXY. Shadow works at the HTTP level, so no framework plugin is needed. ### How should agents handle large tool results such as query exports? Requests with many personal values or tabular data are classified as report, and people, emails, phones and addresses become typed tokens such as [PERSON_0001]. The model carries them through as opaque IDs and they are restored in the answer. --- # Reversible pseudonymisation for LLMs: mask data and restore it in the answer Source: https://getshadow.dev/use-cases/reversible-pseudonymization-for-llms/ > How to pseudonymise prompts for OpenAI, Anthropic or Gemini with realistic, reversible fakes, keep the key local, and get real values back in responses. To pseudonymise LLM prompts reversibly, replace each sensitive value with a substitute before the request leaves, keep the mapping somewhere the provider cannot reach, and translate the substitutes back in the response. Shadow does all three on your machine: it swaps names, emails, IDs and secrets for format-preserving fakes from a persistent persona, stores the pairs in an encrypted local vault, and restores real values in answers, streams and tool calls. ## What does reversible pseudonymisation mean for LLM prompts? GDPR Article 4(5) defines pseudonymisation as processing personal data so that it can no longer be attributed to a person *without additional information*, provided that information is kept separately and protected. Shadow maps onto that definition directly: | GDPR element | In Shadow | |---|---| | Personal data replaced | Detected values become fakes or typed tokens | | Additional information | The real↔fake mapping in `vault.bin` | | Kept separately | The vault and its key stay on the machine or pod running Shadow. The provider receives fakes for every detected value, never the mapping | | Technical measures | ChaCha20-Poly1305 encryption, a 32-byte key in `vault.key` with mode `0600`, atomic writes | The "reversible" part is what makes it practical. Plain redaction (``) is safe but destroys meaning, and the answer refers to placeholders you then have to resolve by hand. With reversible pseudonymisation the answer comes back about your real customer. ## How does Shadow pseudonymise a prompt? ```text you ──"Summarise this thread: jane.doe@acme.example asks about the Acme GmbH renewal"────────────────▶ Shadow Shadow ──"Summarise this thread: xiu.wang@lingxi.cn asks about the Lingxi Technology Co., Ltd. renewal"──▶ provider provider ──"xiu.wang@lingxi.cn wants renewal terms…"──────────────────────────────────────────▶ Shadow Shadow ──"jane.doe@acme.example wants renewal terms…"───────────────────────────────────────▶ you ``` Three properties make the round trip reliable: - **Format-preserving.** A fake email is an email, a fake card passes Luhn, a fake IBAN passes mod-97, a fake UK NINO looks like a NINO. Code, validation logic and the model's reasoning all keep working. - **Consistent.** The same real value always gets the same fake, and no two real values share one, so restoration is exact. Because fakes come from one [persona](https://getshadow.dev/concepts/personas/), they are also consistent with each other: your company maps to the persona's company, and emails at your domain map to the persona's domain. - **Persistent.** The vault survives restarts, so a customer gets the same fake tomorrow. Conversation history and long-running agents stay coherent. ## Surrogates, tokens or redaction: which treatment? Shadow's [intent-aware policy](https://getshadow.dev/concepts/intent-policy/) picks a treatment per value. Three of them are pseudonymisation choices: | Treatment | Provider sees | Reversible | Best for | |---|---|---|---| | `surrogate` (default) | `hui.zhao@zhilan.cn` | Yes | Conversation, analysis, code: the model writes naturally | | `token` | `[PERSON_0001]`, `[EMAIL_0002]` | Yes | Reports over many rows: the model treats values as opaque IDs | | `redact` | `[REDACTED_SECRET]` | **No** | Values that must never leave and never need to come back | The fourth treatment, `keep`, sends the real value. It is used for the recipient when you ask the model to write to or act on someone (faking the recipient would make the model address the wrong person) and for what an action in the provider's cloud needs; every keep is logged. Set `[policy.act] targets = "surrogate"` to pseudonymise recipients too, restored in the answer. See [intent-aware policy](https://getshadow.dev/concepts/intent-policy/). ## How to set up reversible pseudonymisation ### 1. Install and create the persona ```sh curl -fsSL https://raw.githubusercontent.com/Tiny-Bubble-Company/shadow/main/packaging/install.sh | sh shadow init --org "Acme GmbH" shadow identity # the fake identity providers will see ``` ### 2. Try it offline ```sh echo "Ask Jane Doe (jane.doe@acme.example, +1 415 555 0100) about Acme GmbH" | shadow mask > masked.txt cat masked.txt shadow unmask < masked.txt # the original text comes back ``` Nothing is sent anywhere by these commands. They use the same vault as the proxy. ### 3. Add the names detectors cannot know ```sh shadow terms add "org:Globex" "person:Jane Doe" "Project Falcon" ``` or in `shadow.toml`: ```toml organization = ["Acme GmbH"] terms = ["org:Globex", "person:Jane Doe", "Project Falcon"] internal_domains = ["acme.example"] ``` ### 4. Choose treatments ```toml [policy.report] person = "token" email = "token" phone = "token" address = "token" default = "surrogate" [policy.always] secret = "redact" # opt-in; irreversible ``` ### 5. Route traffic through Shadow ```sh shadow run export OPENAI_BASE_URL=http://127.0.0.1:8877/gw/openai/v1 export ANTHROPIC_BASE_URL=http://127.0.0.1:8877/gw/anthropic ``` Or cover every tool on the machine that calls a [supported provider](https://getshadow.dev/reference/providers/) with [proxy mode](https://getshadow.dev/integrate/proxy-mode/) (`shadow install --apply`). If you build in Rust, [embed `shadow-core`](https://getshadow.dev/integrate/rust/) and call `mask` / `unmask` directly. ### 6. Inspect and manage the mapping ```sh shadow vault stats # counts by kind shadow vault export --i-understand # real↔fake pairs in clear text shadow vault forget "jane.doe@acme.example" # drop one mapping ``` After `forget`, the value gets a new fake next time it appears, and older responses that mention the old fake are no longer restored. That is useful for honouring an erasure request against your local mapping. It does not delete anything the provider already holds, which for a detected value is only the fake. ## Limits to know :::caution[Pseudonymisation is not anonymity] - **Detection is best effort.** A value that is not detected is not pseudonymised. Add terms, enable the [NER sidecar or Presidio](https://getshadow.dev/concepts/detection/), and test with `shadow scan`. - **Dates and internal record numbers** (MRNs, claim or matter numbers) are not detected by the built-in detectors. There is no custom regex setting in `shadow.toml`. - **Context can identify.** A unique event or a rare combination of facts can point to a person or company even with every name replaced. - **Whole values, plus name parts.** Restoration matches complete fakes, and a fake person's first or last name on its own. Other fragments, such as the last four digits of a fake card or the domain of a fake email, are not translated back. - **Coincidental fakes.** If the model happens to write text equal to one of your fakes, it is translated back too. This is rare and usually harmless. - **The vault is the key.** Anyone who can read the data directory as your user can reverse everything. Use full-disk encryption and treat `vault.key` like a credential. ::: ## Regulations and frameworks Pseudonymisation is named in GDPR Article 25 (data protection by design) and Article 32 (security of processing), and the EDPB's Guidelines 01/2025 on pseudonymisation describe it as a way to reduce risk while keeping data useful. Shadow gives you a technical implementation of it for AI traffic: the substitution happens before transfer, and the additional information never leaves your infrastructure. Pseudonymised data is still personal data for whoever holds the key (Recital 26), so your other obligations remain. :::caution[Not legal advice] Shadow is a technical control. It does not make processing lawful or compliant on its own. Confirm your legal position with your DPO or counsel. ::: ## Related - [The vault](https://getshadow.dev/concepts/vault/), [personas](https://getshadow.dev/concepts/personas/), [intent-aware policy](https://getshadow.dev/concepts/intent-policy/) - [Help meet GDPR when using LLMs](https://getshadow.dev/use-cases/gdpr-compliant-llm-usage/) and [anonymise prompts for Claude](https://getshadow.dev/use-cases/anonymize-prompts-for-claude/) - [Masking PII in agents and tool calls](https://getshadow.dev/use-cases/mask-pii-in-llm-agents-and-tool-calls/) - How Shadow compares with placeholder-based tools: [compare](https://getshadow.dev/compare/). Detection numbers: [benchmarks](https://getshadow.dev/benchmarks/) ## Frequently asked questions ### What is the difference between pseudonymisation and anonymisation for LLM prompts? Anonymised data cannot be linked back to a person by anyone. Pseudonymised data can be, but only with additional information kept separately. Shadow pseudonymises: the provider sees fakes, and the mapping back to real values stays in an encrypted vault on your machine. ### How does Shadow restore the real values in the model's answer? Every fake is recorded in the vault next to the real value. When the response comes back, including streamed SSE events and tool-call arguments, Shadow replaces each fake with its real value before your app sees it. ### Can the AI provider reverse Shadow's fakes? No. Fakes are generated randomly from the persona, not derived from the real value, so there is nothing to decrypt or brute-force. The only link is the vault, which never leaves your machine. ### Is pseudonymised data still personal data under GDPR? For you, yes: you hold the key, so Recital 26 treats it as personal data. Pseudonymisation is still a recognised safeguard under Articles 25 and 32. Whether masked text counts as personal data for the recipient is a legal question; ask your DPO. ### When should I use tokens or redaction instead of realistic fakes? Use typed tokens such as [PERSON_0001] for bulk data in reports, so the model does not invent facts about fake people. Use redaction for values that must never leave in any form and never need to come back, such as secrets. --- # How to redact PDFs, Word and Excel files before uploading them to AI Source: https://getshadow.dev/use-cases/redact-documents-before-uploading-to-ai/ > Mask names, emails and IDs in DOCX, XLSX, PPTX, CSV and PDF uploads to OpenAI, Anthropic or Gemini automatically, with Office files kept valid. To redact documents before uploading them to an AI model, mask the file's text in transit rather than editing copies by hand. Shadow does this for uploads to OpenAI, Anthropic and Gemini APIs: DOCX, XLSX and PPTX are masked in place and stay valid documents, text formats are masked line by line, and PDF text is extracted, masked and sent as text. The answer comes back with real values restored. ## Why are documents riskier than prompts? A prompt is a sentence or two. An attached contract, export or slide deck carries pages of names, addresses, account numbers and internal codenames, and nobody reads every cell before pressing upload. Manual redaction is slow, inconsistent between people, and usually destroys the structure the model needed. ## Which upload paths does Shadow mask? | Transport | Examples | |---|---| | `multipart/form-data` uploads | OpenAI `POST /v1/files`, `POST /v1/uploads` parts | | Base64 files inside JSON | OpenAI `input_file.file_data`, Anthropic `document` blocks with a `base64` source, Gemini `inline_data` | | `data:` URIs | `data:text/csv;base64,...` anywhere in a JSON string | The format is recognised from the declared MIME type and the file name. ## Which formats are supported? | Format | Treatment | Result | |---|---|---| | Plain text, Markdown | Masked as text | Same file, masked | | CSV | Masked cell by cell | Same columns and rows | | JSON | String values masked | Valid JSON | | HTML, XML | Text masked | Markup intact | | DOCX | Text runs masked in place | A valid Word document | | XLSX | Shared strings masked in place | A valid workbook; numbers and formulas untouched | | PPTX | Slide text runs masked in place | A valid presentation | | PDF | Text extracted, masked, sent as text (`pdf = "text"`) | The model receives masked text, not the PDF | | Images | Passed through (`images = "pass"`) or refused (`"block"`) | No OCR | | Anything else | Passed through | | Files use the same detectors, persona, vault and [policy](https://getshadow.dev/concepts/intent-policy/) as prompts. A customer named in the prompt and in the spreadsheet gets the same fake in both, and a customer list attached to a "build a report" request gets typed tokens such as `[PERSON_0001]`. ## How to mask documents before they reach the model ### 1. Install Shadow and start it ```sh curl -fsSL https://raw.githubusercontent.com/Tiny-Bubble-Company/shadow/main/packaging/install.sh | sh shadow init --org "Acme GmbH" shadow run ``` ### 2. Set file handling in `shadow.toml` ```toml [files] enabled = true # default pdf = "text" # text | block | pass images = "block" # no OCR: refuse screenshots and scans instead of sending them unmasked max_bytes = 52428800 # 50 MiB; larger files are not masked block_oversize = true # refuse files above max_bytes instead of forwarding them unmasked ``` If your users need the model to reason about PDF layout (forms, charts), `pdf = "text"` will give worse answers. Prefer `pdf = "block"` for those workflows and send the source DOCX or XLSX instead, which is masked in place. ### 3. Teach Shadow your vocabulary Customer names, matter or project codenames and colleagues are not something a regex can know: ```sh shadow terms add "org:Globex" "org:Initech" "Project Falcon" "person:Jane Doe" ``` For better recall on free-text names in long documents, enable the open-weight [NER sidecar](https://getshadow.dev/concepts/detection/#local-ner-sidecar) or [Presidio](https://getshadow.dev/concepts/detection/#presidio). ### 4. Test on a representative document `shadow scan` reads text files. Export the document's text (for example *Save as plain text* in Word, or CSV from Excel) and scan it: ```sh shadow scan contract.txt # redacted previews shadow scan contract.txt --reveal # full values, on your screen only shadow scan customers.csv --json # kind, line, column, detector, score, value ``` Anything missing from the output will reach the provider as is. Add terms until the scan covers what matters. ### 5. Upload through Shadow Point the SDK at the gateway, or use [proxy mode](https://getshadow.dev/integrate/proxy-mode/) for tools you don't control: ```python from openai import OpenAI client = OpenAI(base_url="http://127.0.0.1:8877/gw/openai/v1") with open("q3-renewals.xlsx", "rb") as f: file = client.files.create(file=f, purpose="user_data") # masked in transit ``` ## Example: a contract review Clause in the DOCX the lawyer attaches (fictional): ```text This Agreement is made between Acme GmbH and Globex Ltd. Notices to Jane Doe, jane.doe@acme.example, Hauptstraße 5, 10115 Berlin. Payments to DE89 3704 0044 0532 0130 00. ``` With `organization = ["Acme GmbH"]` and the term `org:Globex Ltd`, the provider receives this inside the still-valid DOCX (output from `shadow mask` on one machine; your persona's fakes will differ): ```text This Agreement is made between Lingxi Technology Co., Ltd. and Talari Technology Co., Ltd. Notices to Yong Sun, xiu.wang@lingxi.cn, 53 Binjiang Avenue, 10115 Berlin. Payments to DE46 0256 5725 3509 8212 53. ``` The street address takes the persona's style while the postcode and city stay, and the IBAN is a different German IBAN that still passes mod-97. Without the `org:Globex Ltd` term, Globex would have been sent as is: add counterparties before you rely on it. The model's markup of risky clauses comes back about Acme GmbH, Globex Ltd and Jane Doe, so the lawyer works on the real contract. Shadow restores whole fakes, and a fake person's first or last name on its own; if the model shortens a company ("Talari" for Talari Technology Co., Ltd.), that short form stays fake, so ask it to quote party names in full. ## Limitations :::caution[Read before relying on file masking] - **No OCR.** Text inside images, screenshots and scanned PDFs is not detected. Block images if they may carry sensitive data. - **PDF layout is lost** in `text` mode. - **Embedded objects** in Office files (cached chart values, embedded workbooks, images) are not masked. - **Split formatting.** A name that is half bold spans two text runs and may not be recognised. - **Uploads by reference.** A file uploaded earlier and referenced by `file_id` is not re-scanned. - **Archives** (`.zip`, `.tar`) other than Office formats pass through. - **Consumer web apps.** Uploads to chatgpt.com or claude.ai in a browser are not intercepted. Shadow covers API traffic. ::: ## Related - [File masking](https://getshadow.dev/concepts/file-masking/) and [detection](https://getshadow.dev/concepts/detection/) - [Protect customer data in RAG pipelines](https://getshadow.dev/use-cases/protect-customer-data-in-rag-pipelines/) - Industries with document-heavy work: [legal](https://getshadow.dev/industries/legal/), [healthcare](https://getshadow.dev/industries/healthcare/), [financial services](https://getshadow.dev/industries/financial-services/) - [Benchmarks](https://getshadow.dev/benchmarks/) and [how Shadow compares](https://getshadow.dev/compare/) ## Frequently asked questions ### Can Shadow redact a PDF before it is sent to ChatGPT or Claude? Through the API, yes for text. With the default pdf = "text", Shadow extracts the PDF's text, masks it and sends it as a text document. Layout and images are lost, and scanned PDFs are not OCR'd. Consumer web apps such as chatgpt.com are not intercepted. ### Will my Word or Excel file still open after masking? Yes. DOCX, XLSX and PPTX are edited inside their ZIP container, so styles, tables and layout survive. In XLSX only shared strings are changed; numbers and formulas are untouched. ### Does Shadow mask text in images or screenshots? No. Shadow does not run OCR yet. Images pass through unchanged by default; set images = "block" to refuse requests that carry them. Local OCR is on the roadmap. ### What happens to a file uploaded earlier without Shadow? It is not re-scanned. If a request references a file by an earlier upload id, the provider already holds the original. Upload files through Shadow from the start. ### How do I check what Shadow will find in a document? Export the document's text and run shadow scan on it. It reports each finding's kind, position and detector without changing anything or touching the vault. --- # Self-hosted AI gateway with PII masking: put Shadow in front of any LLM API Source: https://getshadow.dev/use-cases/self-hosted-ai-gateway-with-pii-masking/ > Run a self-hosted, open-source gateway that masks PII and secrets before requests reach OpenAI, Anthropic, Azure OpenAI, Bedrock or vLLM, and restores answers. To self-host a gateway that masks PII before it reaches an LLM provider, run Shadow next to your application and change the app's base URL to `http://127.0.0.1:8877/gw//...`. Shadow masks names, emails, IDs, hostnames and secrets in each request with reversible fakes, forwards it over TLS, and restores real values in the response, including streams and tool calls. It is a single Apache-2.0 Rust binary with no account and no telemetry. ## How does the Shadow gateway work? ```text http://127.0.0.1:8877/gw// → / ``` `/gw/openai/v1/chat/completions` goes to `https://api.openai.com/v1/chat/completions`. Query strings are kept, and your API key travels in the usual header and is passed through. On the way out, Shadow classifies the request's intent, detects sensitive values, replaces them with fakes from the machine's [persona](https://getshadow.dev/concepts/personas/) and scrubs identifying fields such as OpenAI `user` and Anthropic `metadata.user_id`. On the way back, it restores every fake. Built-in routes: `openai`, `anthropic`, `openrouter`, `gemini` (or `google`), `mistral`, `groq`, `together`, `deepseek`, `xai`, `cohere`, `perplexity`, `fireworks`, `cerebras`. See [gateway mode](https://getshadow.dev/integrate/gateway-mode/) for SDK examples. ## How to set up a self-hosted PII-masking gateway ### 1. Install and initialise ```sh curl -fsSL https://raw.githubusercontent.com/Tiny-Bubble-Company/shadow/main/packaging/install.sh | sh shadow init --org "Acme GmbH" ``` On a server, use the hardened systemd unit described in [installation](https://getshadow.dev/getting-started/installation/#run-as-a-background-service). In containers, build the image (a published image is on the [roadmap](https://getshadow.dev/project/roadmap/)): ```sh docker build -f packaging/Dockerfile -t shadow:local . ``` ### 2. Add routes for your own endpoints Azure OpenAI, Bedrock and self-hosted servers have per-account URLs: ```toml [proxy.routes] azure = "https://my-resource.openai.azure.com" bedrock = "https://bedrock-runtime.eu-central-1.amazonaws.com" vllm = "https://vllm.internal.example.com" ``` `shadow hosts` lists the routes in effect. An Azure call then looks like: ```text http://127.0.0.1:8877/gw/azure/openai/deployments/gpt-4o/chat/completions?api-version=2024-10-21 ``` Any JSON request shape is masked: Shadow masks every string value except protocol keys such as `model`, `role` and `type`, so internal APIs work without a plugin. ### 3. Point the application at Shadow ```sh export OPENAI_BASE_URL=http://127.0.0.1:8877/gw/openai/v1 export ANTHROPIC_BASE_URL=http://127.0.0.1:8877/gw/anthropic ``` Most frameworks (LangChain, LlamaIndex, Vercel AI SDK, the OpenAI and Anthropic SDKs) accept a base URL. No code change beyond that. ### 4. Run it as a sidecar One Shadow per application pod, on loopback: ```yaml containers: - name: app image: my-app:latest env: - { name: OPENAI_BASE_URL, value: "http://127.0.0.1:8877/gw/openai/v1" } - name: shadow image: registry.example.com/shadow:0.1.0 args: ["run", "--listen", "127.0.0.1:8877"] volumeMounts: - { name: shadow-data, mountPath: /data } readinessProbe: exec: { command: ["shadow", "status", "--quiet"] } ``` Mount a persistent volume at `/data` so fakes stay stable across restarts, and add a `NetworkPolicy` so only the sidecar can reach provider hosts. Full example: [Docker and Kubernetes](https://getshadow.dev/integrate/docker-kubernetes/). ### 5. Tune the policy and verify ```toml [policy.always] secret = "redact" # opt-in; secrets are surrogates by default [files] images = "block" # no OCR yet ``` ```sh shadow status # proxy health, persona, vault, CA fingerprint curl -s http://127.0.0.1:8877/healthz ``` With `shadow run` in the foreground, each request logs its intent and how many values got each treatment, never the values. :::caution[Keep the gateway on loopback] Anyone who can reach the gateway can talk to providers through your persona and have responses unmasked with your vault. Shadow warns when it listens on a non-loopback address. In containers, keep it inside the pod or publish the port only to `127.0.0.1`. ::: ## Shadow versus a full AI gateway Shadow is deliberately narrow. Compared with general-purpose gateways: | | Typical AI gateway (LiteLLM, Kong AI Gateway, Portkey) | Shadow | |---|---|---| | Routing, fallback, load balancing | Yes | Fixed routes only | | Virtual keys, auth, rate limits, budgets, cost tracking | Yes | No | | PII handling | Guardrail add-ons that mostly block or redact with placeholders; restoring is limited or plan-dependent | Realistic, format-preserving, reversible fakes from a persona | | Streaming restoration | Varies; some buffer or have known gaps | SSE and NDJSON, including split fakes | | Tool-call arguments | Usually not restored | Restored locally before your tools run | | Where it runs | Central service | Next to each app: loopback, sidecar or in-process | They combine well. Put Shadow next to the app and route it to your existing gateway, so the central gateway keeps doing keys, quotas and observability but sees prompts with detected values already masked: ```toml [proxy.routes] litellm = "https://litellm.internal.example.com" ``` ```text app ──▶ Shadow (sidecar) ──/gw/litellm/v1/...──▶ LiteLLM ──▶ OpenAI, Anthropic, Bedrock ``` A side benefit: gateway logs, traces and caches now hold fakes instead of customer data. ## Limitations - **AWS SigV4.** IAM-signed Bedrock requests cannot be modified without breaking the signature, so they are forwarded unmasked with a warning. Use Bedrock API keys through Shadow. - **WebSocket, Realtime and gRPC** are not parsed. - **Detection is best effort.** Add terms, enable the [NER sidecar or Presidio](https://getshadow.dev/concepts/detection/), and check with `shadow scan`. - **Several replicas, one identity.** Share a `ReadWriteMany` volume or provision the same `persona.json`, `vault.key` and `vault.bin` from a secret. A shared team identity with key distribution is a roadmap item. ## Related - [Gateway mode](https://getshadow.dev/integrate/gateway-mode/), [integrating any endpoint](https://getshadow.dev/integrate/any-endpoint/), [supported providers](https://getshadow.dev/reference/providers/) - [Redact PII before sending to the OpenAI API](https://getshadow.dev/use-cases/redact-pii-before-sending-to-openai-api/) - [LLM data loss prevention](https://getshadow.dev/use-cases/llm-data-loss-prevention-dlp/) and [SaaS and technology companies](https://getshadow.dev/industries/saas-technology/) - [How Shadow compares](https://getshadow.dev/compare/) and [benchmarks](https://getshadow.dev/benchmarks/) ## Frequently asked questions ### Is Shadow a replacement for LiteLLM, Kong or Portkey? No. Shadow is a privacy layer, not a full AI gateway: it has no virtual keys, user authentication, rate limits, budgets or model fallback. It does one thing those gateways mostly do as a block-or-redact add-on, reversible masking, and it can sit in front of them. ### Does the gateway need a TLS certificate? No. In gateway mode your app talks plain HTTP to Shadow on loopback or inside the pod, and Shadow connects to the provider over TLS. Only proxy mode needs the local CA. ### Can I route Azure OpenAI, AWS Bedrock or a self-hosted model through Shadow? Yes. Add a custom route under [proxy.routes] pointing at your Azure resource, Bedrock runtime endpoint or vLLM/TGI server. Bedrock requests signed with SigV4 cannot be modified and are forwarded unmasked with a warning; Bedrock API keys work. ### Can I expose the Shadow gateway to the whole network? It is not designed for that. Anyone who can reach the port can use your persona and get responses unmasked with your vault. Keep it on loopback or inside the pod as a sidecar, one instance per app. ### Does Shadow handle streaming through the gateway? Yes. SSE and NDJSON streams are restored on the fly, including fakes split across events and streamed tool-call arguments. --- # How to protect customer data in RAG pipelines sent to LLMs Source: https://getshadow.dev/use-cases/protect-customer-data-in-rag-pipelines/ > Keep customer names, emails and IDs in retrieved chunks away from OpenAI or Anthropic. Mask RAG context at generation time with Shadow and restore the answer. To protect customer data in a RAG pipeline, keep the document store and retrieval inside your infrastructure and mask the retrieved chunks at the moment they are sent to the model. Shadow does that masking in transit: names, emails, IBANs, hostnames and secrets in the prompt and its context become reversible fakes, and the model's answer, including streamed text and citations, comes back with real values. ## Where does customer data leak in a RAG pipeline? | Stage | What leaves | Typical destination | |---|---|---| | Ingestion: chunk embedding | Every chunk of every document | Hosted embedding API (OpenAI, Voyage AI, Cohere) | | Query embedding | The user's question | Same embedding API | | Generation | The question plus the top *k* retrieved chunks, often with metadata | Chat model API | | Observability | Prompts and completions | Tracing tools, gateway logs, caches | Retrieved chunks are the dangerous part. They are selected *because* they are relevant to a specific customer, so they concentrate exactly the data you would least like to send: the support history, contract or account notes for that person. ## Recommended architecture ```text your infrastructure provider ┌──────────────────────────────────────────────────────┐ │ documents ──▶ embed ──▶ vector store (real text) │ │ question ──▶ retrieve top-k ──▶ build prompt │ │ │ │ │ Shadow (mask) ─────────┼──▶ chat model │ answer ◀── Shadow (restore) ◀──────────────────────┼─── response └──────────────────────────────────────────────────────┘ ``` 1. **Store real data locally.** Your vector store holds real text, so retrieval, filtering and access control work on real values. 2. **Embed where you can control it.** Use a local embedding model, or a hosted one covered by your agreements. 3. **Mask at generation.** Route the chat call through Shadow, so the question and the retrieved context are masked together with consistent fakes. 4. **Restore on the way back.** Answers and citations refer to real customers on your side only. ## Step by step ### 1. Run Shadow next to the RAG service ```sh shadow init --org "Acme GmbH" shadow run ``` In Kubernetes, run it as a [sidecar](https://getshadow.dev/integrate/docker-kubernetes/) with a persistent `/data` volume. ### 2. Point the generation client at the gateway ```python from langchain_openai import ChatOpenAI llm = ChatOpenAI(model="gpt-4o-mini", base_url="http://127.0.0.1:8877/gw/openai/v1") ``` ```python from llama_index.llms.anthropic import Anthropic llm = Anthropic(model="claude-sonnet-4-5", base_url="http://127.0.0.1:8877/gw/anthropic") ``` Or set `OPENAI_BASE_URL` / `ANTHROPIC_BASE_URL` for the whole process. ### 3. Add the vocabulary in your corpus Customer company names, product codenames and internal domains are what retrieval surfaces most: ```toml organization = ["Acme GmbH"] terms = ["org:Globex Ltd", "org:Initech", "Project Falcon"] internal_domains = ["acme.example", "kb.acme.internal"] ``` For corpora full of free-text names, enable the open-weight [NER sidecar](https://getshadow.dev/concepts/detection/#local-ner-sidecar) (OpenAI Privacy Filter and GLiNER) or [Presidio](https://getshadow.dev/concepts/detection/#presidio). ### 4. Use tokens for bulk context When a request carries many rows (a CRM export, many tickets) or asks for a report, Shadow's [intent policy](https://getshadow.dev/concepts/intent-policy/) classifies it as `report` and uses typed tokens for people and contact details: ```toml [policy.report] person = "token" email = "token" phone = "token" address = "token" default = "surrogate" ``` ### 5. Check what the chunks contain Export a sample of chunks to a text file and scan it: ```sh shadow scan sample-chunks.txt shadow scan sample-chunks.txt --json > findings.json ``` ## Example User question and retrieved context, as built by the app (fictional): ```text Q: Does our policy cover water damage for Tom Becker at 12 Elm Road? Context: [chunk 1] Tom Becker, policy holder, tom.becker@mail.example ``` What the model receives (output from `shadow mask` on one machine; yours will differ): ```text Q: Does our policy cover water damage for Qiang Xu at 211 Jianguo Road? Context: [chunk 1] Qiang Xu, policy holder, jing.zhu@bryula.cn ``` The person, address and email are consistent between the question and the chunk, so the model can connect them. The answer comes back about Tom Becker at 12 Elm Road: each full fake is restored, and so is a fake first or last name on its own. "Mr Xu" is the exception: name parts under three characters are not restored, nor are other fragments such as part of the address, so ask the model to use full names and addresses in answers. ## What about embeddings? Shadow intercepts embedding hosts such as `api.openai.com` and `api.voyageai.com`, and embedding requests are JSON, so text sent to them through Shadow is masked too. If you embed through Shadow: - the index stores embeddings of **fakes**, and query embeddings go through the same vault, so a customer's name maps to the same fake at indexing and at query time; - the index is then tied to that vault: losing or resetting `vault.bin` and `vault.key` changes future fakes, and stored vectors stop matching new queries on those names; - semantic similarity on names and companies is now similarity between fakes, which can shift ranking. That is a reasonable trade if the embedding provider must never see real text. Otherwise, embed locally and keep Shadow on the generation path. ## Limitations - Shadow does not filter what is retrieved: access control on documents is still your retriever's job. - Scanned PDFs and images in the corpus are not OCR'd. - Internal record numbers (claim, matter, account references) are not detected by the built-in detectors; there is no custom regex setting in `shadow.toml`. - Context can identify: a chunk describing a unique incident can point to a customer without any name. ## Related - [File masking](https://getshadow.dev/concepts/file-masking/) and [redacting documents before upload](https://getshadow.dev/use-cases/redact-documents-before-uploading-to-ai/) - [Self-hosted AI gateway with PII masking](https://getshadow.dev/use-cases/self-hosted-ai-gateway-with-pii-masking/) - [Privacy for AI in customer support](https://getshadow.dev/use-cases/privacy-for-ai-in-customer-support/) - [SaaS and technology](https://getshadow.dev/industries/saas-technology/), [insurance](https://getshadow.dev/industries/insurance/) - [Benchmarks](https://getshadow.dev/benchmarks/) and [how Shadow compares](https://getshadow.dev/compare/) ## Frequently asked questions ### Where does customer data leave a RAG pipeline? At two points: when chunks are sent to a hosted embedding API, and when retrieved chunks are pasted into the generation prompt. If your vector store and retriever run in your own infrastructure, the generation call is usually the larger exposure, and it is where Shadow fits most naturally. ### Should I embed documents through Shadow? Only if sending real text to the embedding provider is unacceptable. Shadow masks JSON embedding requests to intercepted hosts such as OpenAI and Voyage AI, so the index then holds embeddings of fakes. That works consistently with a persistent vault, but it ties the index to that vault and can shift retrieval quality. Embedding locally with an open model is often simpler. ### Will masking hurt answer quality in RAG? Usually not noticeably for content questions, because fakes keep the same shape and are consistent across chunks. For bulk context, the report intent swaps people, emails and phones for typed tokens so the model carries them through without inventing details. ### Does Shadow work with LangChain and LlamaIndex? Yes. Both accept a base URL for their OpenAI or Anthropic clients. Point it at the Shadow gateway, or run the pipeline with HTTPS_PROXY set in proxy mode. ### Are citations and source IDs preserved? Document IDs and URLs on public domains are left alone. Internal hostnames, emails and names inside chunk metadata are masked like any other string and restored in the answer, so citations still point to the real source on your side. --- # LLM data loss prevention: DLP for ChatGPT, Claude and AI APIs without blocking Source: https://getshadow.dev/use-cases/llm-data-loss-prevention-dlp/ > Open-source DLP for LLM traffic: Shadow masks PII, customer data and secrets in AI API requests with reversible fakes instead of blocking them. Data loss prevention for LLMs means keeping personal data, customer records and secrets out of what you send to AI providers. Shadow does it by translation rather than blocking: it runs on endpoints, servers or as a sidecar, replaces sensitive values in AI API requests with realistic, reversible fakes, and restores them in the response. The tool keeps working, so people have no reason to route around it. ## Why does blocking-style DLP fail for AI? DLP was built for files and email: find a sensitive pattern, stop the transfer. Applied to AI assistants, that approach has three problems: - **It breaks the task.** A blocked prompt gets no answer. People retry with the data slightly altered, switch to a personal account, or use their phone. - **Placeholder redaction degrades answers.** `` and `` give the model nothing to work with. It cannot tell two people apart or write to the right address, and you have to map placeholders back by hand. - **Streaming and agents need restoration.** Assistants stream answers and call tools. A DLP layer that only inspects outbound traffic cannot give the agent back the real values it needs to act. Shadow keeps the request flowing with data that has the same shape as the original: | Real value | What the provider sees | |---|---| | `jane.doe@acme.example` | `xiu.wang@lingxi.cn` | | `Acme GmbH` | `Lingxi Technology Co., Ltd.` | | `4111 1111 1111 1111` | `4265 3052 9922 6576` (valid Luhn) | | `DE89 3704 0044 0532 0130 00` | `DE46 0256 5725 3509 8212 53` (valid mod-97) | | `build01.acme.internal`, `10.20.4.17` | `velond.draix.internal`, `10.227.176.110` | | `sk-proj-…` | a fake key of the same shape, or `[REDACTED_SECRET]` if you opt in | Fakes come from one machine's [persona](https://getshadow.dev/concepts/personas/); yours will differ. ## Where Shadow fits next to existing DLP | | Browser, SASE and SaaS DLP (Netskope, Zscaler, Purview and similar) | Shadow | |---|---|---| | Covers | Web chat UIs, SaaS apps, email, the network edge | AI API traffic from SDKs, agents, IDE assistants, internal apps | | Main action | Block, warn, coach; some can redact | Translate with reversible fakes; block only images or PDFs if configured | | Response handling | Usually none | Restores answers, streams and tool-call arguments | | Runs | Vendor cloud, browser extension, endpoint agent | Single binary on the endpoint, server or pod | | Management | Central console, policies, reporting | Local `shadow.toml` per machine; no console yet | | Cost | Commercial | Free, Apache-2.0 | A practical split: let your existing DLP or CASB govern consumer web apps such as chatgpt.com, which Shadow does not intercept, and use Shadow for the API-based tools and applications your teams are encouraged to use. ## How to roll out Shadow as LLM DLP ### 1. Install on the machines that call AI APIs ```sh curl -fsSL https://raw.githubusercontent.com/Tiny-Bubble-Company/shadow/main/packaging/install.sh | sh shadow init --org "Acme GmbH" shadow install # dry run: prints every change shadow install --apply # background service, CA trust, env file ``` Proxy mode decrypts only [known AI hosts](https://getshadow.dev/reference/providers/). Add your internal LLM gateway with `extra_hosts`. ### 2. Write the policy ```toml organization = ["Acme GmbH"] terms = ["Project Falcon", "org:Globex Ltd"] internal_domains = ["acme.example", "acme.internal"] [policy.act] targets = "surrogate" # optional: fake message recipients too (default "keep" sends them, logged) remote_tool_targets = "surrogate" # never send action targets to provider-hosted tools in clear [policy.always] secret = "redact" # detected secrets never leave in any form [files] pdf = "text" images = "block" # no OCR yet block_oversize = true # refuse files Shadow cannot mask ``` ### 3. Improve recall where it matters Built-in detectors cover emails, phones, names, addresses, IBANs, cards, national IDs (US SSN and ITIN, UK NINO and NHS number, Aadhaar, Indian PAN, Australian TFN and ABN), IPs, MACs, hostnames, secrets and home-directory usernames. For free-text names, enable the open-weight [NER sidecar](https://getshadow.dev/concepts/detection/#local-ner-sidecar) or [Presidio](https://getshadow.dev/concepts/detection/#presidio). ### 4. Test with real-looking content ```sh shadow scan sample-ticket.txt shadow scan sample-ticket.txt --json ``` ### 5. Distribute with your own tooling There is no central admin console or fleet management yet. Use MDM or configuration management to place `shadow.toml` in the data directory, run `shadow install --apply`, and use `shadow status --quiet` as a health check. ### 6. Monitor With `shadow run` in the foreground or the service logs, each request shows its intent and counts per treatment (surrogates, tokens, kept, redacted). Every `keep` is logged with kind and reason. Values are never logged. On each machine, the local [activity dashboard](https://getshadow.dev/concepts/activity-dashboard/) (`shadow dashboard`) shows the same counts by data type, provider and app. ## What Shadow does not do - **No discovery or inventory** of which AI tools people use. - **No web chat coverage.** chatgpt.com, claude.ai and similar consumer apps are not intercepted; vendor backends such as GitHub Copilot and Cursor's hosted service are not built in either. - **No OCR**, WebSocket/Realtime or gRPC processing. - **No central reporting.** A local, append-only audit log of per-request decisions is on the [roadmap](https://getshadow.dev/project/roadmap/). Blocking-only DLP features are explicitly not planned. - **Not perfect detection.** Values that are not detected are sent as is. See [benchmarks](https://getshadow.dev/benchmarks/). ## Related - [Shadow AI governance](https://getshadow.dev/use-cases/shadow-ai-governance/) and [stopping PII leaks to ChatGPT](https://getshadow.dev/use-cases/stop-pii-leaks-to-chatgpt/) - [Protect secrets from AI coding assistants](https://getshadow.dev/use-cases/protect-secrets-from-ai-coding-assistants/) - [Enterprise](https://getshadow.dev/company-size/enterprise/) and [regulated enterprise](https://getshadow.dev/company-size/regulated-enterprise/) rollouts - [Security model](https://getshadow.dev/reference/security/) and [how Shadow compares](https://getshadow.dev/compare/) ## Frequently asked questions ### How is Shadow different from a traditional DLP tool? Traditional DLP mostly blocks, warns or redacts, which breaks the request or the answer. Shadow translates: sensitive values are replaced with realistic fakes, the request still works, and the answer is restored. Blocking is used only where translation cannot work, such as images or PDFs if you choose. ### Does Shadow replace Netskope, Zscaler or Microsoft Purview? No. Those products cover browsers, SaaS apps and the network edge, with central consoles and reporting. Shadow covers AI API traffic from endpoints, servers and containers, and transforms rather than blocks. Many teams will want both. ### Does Shadow have a central DLP console or incident reporting? Not today. Each instance logs per-request intent and counts by treatment, never values. An optional local audit log is a roadmap item; central fleet management is not available. ### Can Shadow stop secrets from being sent to AI tools? Yes, with opt-in redaction. By default secrets become fakes of the same shape. Set [policy.always] secret = "redact" to send [REDACTED_SECRET] instead, which cannot be reversed. ### Does Shadow inspect non-AI traffic? No. In proxy mode it decrypts only known AI hosts and tunnels everything else byte for byte, so banking, email and package downloads are never decrypted. --- # Shadow AI governance: how to manage unsanctioned AI tools without banning AI Source: https://getshadow.dev/use-cases/shadow-ai-governance/ > Shadow AI is employees using unapproved AI tools. Learn how to govern it, and how the open-source Shadow privacy layer makes a sanctioned path safe to use. Shadow AI is the use of AI tools that your organisation has not approved: personal ChatGPT accounts, unvetted browser extensions, AI features switched on inside SaaS apps. You govern it by finding out what is used, offering an approved alternative that is genuinely useful, and making that alternative safe for real data. Shadow, the open-source privacy layer documented here, helps with the third part: it masks sensitive data in AI API traffic from sanctioned tools, so saying yes to AI does not mean sending detected customer data to a provider in clear. :::note[Two different things called "shadow"] **Shadow AI** (lower case, the phenomenon) is unsanctioned AI use. **Shadow** (the product) is a self-hosted, Apache-2.0 privacy layer that replaces sensitive values in AI requests with reversible fakes. The product does not detect or block shadow AI. It makes an approved path safe enough that people stop needing an unapproved one. ::: ## Why does shadow AI happen? People use AI because it saves them hours. They use *unapproved* AI when the approved route is missing, slow to get, or so locked down that it is useless for their actual work. A DLP rule that blocks any prompt containing a customer name pushes that work to a personal phone. The data at risk is ordinary and constant: customer emails pasted for a reply, a spreadsheet uploaded for analysis, a stack trace with an internal hostname and an API key, a contract for summarising. ## A practical governance model | Step | What it involves | Where Shadow fits | |---|---|---| | 1. Discover | Find which AI services are used: proxy and DNS logs, CASB or SASE reports, SSO app catalogues, expense reports | Not a Shadow feature | | 2. Decide | Pick approved providers and clients; sign agreements; write an acceptable-use policy | Shadow widens what is safe to approve | | 3. Provide | Give people approved tools that work on real tasks | Shadow masks data in those tools' API traffic | | 4. Protect | Technical controls on what leaves | Reversible masking, secret redaction, file masking | | 5. Monitor and review | Track usage and decisions; revisit the policy | Per-request counts in logs today; audit log on the [roadmap](https://getshadow.dev/project/roadmap/) | ## How to give teams a safe, sanctioned AI path with Shadow ### 1. Choose API-based tools for the approved list Shadow intercepts provider APIs (OpenAI, Anthropic, Azure OpenAI, Gemini and Vertex AI, Bedrock runtime, OpenRouter, Mistral and others; see [providers](https://getshadow.dev/reference/providers/)). Good candidates for the approved list: - coding assistants that call the provider with your key: Claude Code, Codex CLI, Aider, Continue, Cline; - desktop chat clients that use your own API key; - internal assistants and agents your teams build on the OpenAI or Anthropic SDKs. Consumer web apps such as chatgpt.com and claude.ai, and vendor backends such as GitHub Copilot or Cursor's hosted service, are not built in. Govern those with your existing web controls. ### 2. Install Shadow on endpoints ```sh curl -fsSL https://raw.githubusercontent.com/Tiny-Bubble-Company/shadow/main/packaging/install.sh | sh shadow init --org "Acme GmbH" shadow install --apply # background service, CA trust, proxy env file ``` Run `shadow install` without `--apply` first to see every change it will make. ### 3. Set an organisation-wide `shadow.toml` ```toml organization = ["Acme GmbH"] terms = ["Project Falcon", "org:Globex Ltd", "org:Initech"] internal_domains = ["acme.example", "acme.internal"] [policy.always] secret = "redact" [files] images = "block" ``` Distribute it with MDM or configuration management. There is no central console yet, so treat the file like any other managed configuration. ### 4. Cover internal apps with the gateway ```sh export OPENAI_BASE_URL=http://127.0.0.1:8877/gw/openai/v1 export ANTHROPIC_BASE_URL=http://127.0.0.1:8877/gw/anthropic ``` For services in Kubernetes, use the [sidecar pattern](https://getshadow.dev/integrate/docker-kubernetes/). ### 5. Write it into the policy Make the acceptable-use policy concrete: which tools are approved, that they must run with Shadow, what still must not be pasted (for example scanned documents, since there is no OCR), and how to request a new tool. Tell people what Shadow does; it works better when they know the approved tool is safe to use with real data. ### 6. Check it is working ```sh shadow status shadow scan sample-prompt.txt ``` The service log shows each request's intent and treatment counts, and the local [activity dashboard](https://getshadow.dev/concepts/activity-dashboard/) (`shadow dashboard`) shows them by data type, provider and app. ## What Shadow does not cover - **Discovery.** It cannot tell you which AI tools are in use. - **Personal devices and accounts.** It protects the machines it is installed on. - **Web chat UIs and pinned apps.** Browser chat and certificate-pinning apps are outside its scope. - **Phones and Chromebooks.** Shadow runs on macOS, Linux, Windows, servers and containers. - **User-level monitoring.** No per-user reporting; logs never contain values. ## Regulations and frameworks ISO/IEC 42001 (AI management systems) expects organisations to know how AI is used and to manage the risk, and the EU AI Act adds AI literacy and deployer expectations on top (check the current text: the 2026 Digital Omnibus amended several provisions and timelines). GDPR Articles 25 and 32 expect data protection by design and appropriate security. Shadow supports these as one technical control on sanctioned AI data flows; it is not a governance programme by itself. :::caution[Not legal advice] This page describes technical capabilities. It does not make your AI use compliant with any law or standard. Involve your security, privacy and legal teams. ::: ## Related - [LLM data loss prevention](https://getshadow.dev/use-cases/llm-data-loss-prevention-dlp/) and [stopping PII leaks to ChatGPT](https://getshadow.dev/use-cases/stop-pii-leaks-to-chatgpt/) - Rollout by size: [small business](https://getshadow.dev/company-size/small-business/), [mid-market](https://getshadow.dev/company-size/mid-market/), [enterprise](https://getshadow.dev/company-size/enterprise/) - [Proxy mode](https://getshadow.dev/integrate/proxy-mode/), [security model](https://getshadow.dev/reference/security/), [how Shadow compares](https://getshadow.dev/compare/) ## Frequently asked questions ### What is shadow AI? Shadow AI is the use of AI tools that IT and security have not approved, such as personal ChatGPT accounts, browser extensions or AI features in SaaS apps. The main risk is company and customer data reaching providers outside any contract or review. ### Is the Shadow privacy layer a shadow AI discovery tool? No. Shadow, the open-source product these docs describe, does not discover or inventory AI tools. It masks sensitive data in AI API traffic from the tools you sanction. For discovery, use network logs, a CASB or SASE product, or your identity provider's app catalogue. ### Should we just block ChatGPT and other AI tools? Outright bans tend to push usage onto personal devices and accounts, where you have no visibility at all. Most guidance favours offering an approved tool that is safe enough to use for real work, plus a clear policy and monitoring. ### How does Shadow reduce shadow AI? It removes the main reason people go around policy: the approved tool was either blocked or too restricted to be useful. With Shadow, teams can use API-based assistants and agents on real work while the names, customer data and secrets Shadow detects leave as reversible fakes (or redacted, for secrets, if you opt in). ### Can Shadow show which employees sent sensitive data to AI? No. Shadow logs per-request intent and counts by treatment, never values, and has no central console. An optional local audit log of decisions is on the roadmap. It is a protection layer, not a monitoring tool. --- # Privacy for AI in customer support: mask customer data in tickets and agents Source: https://getshadow.dev/use-cases/privacy-for-ai-in-customer-support/ > Use LLMs for ticket replies, summaries and support agents without sending customer names, emails, orders or card numbers to the provider. Restored in answers. To use AI in customer support without exposing customers, mask their personal data before each request reaches the model and restore it in the reply. Shadow does that for ticket summaries, reply drafts, support agents and ticket analytics built on provider APIs: names, emails, phones, addresses, order references you register, and card numbers become reversible fakes, and streamed replies come back with the real details. When the goal is to reply to or act on a customer, that customer is the recipient and passes through as written (logged), so the reply greets the right person and tool calls such as refunds reach them. ## What customer data reaches the model in support workflows? | Workflow | What the prompt contains | |---|---| | Drafting a reply | The whole thread: name, email, phone, address, order history, sometimes card digits | | Summarising a ticket for hand-off | Customer details plus internal notes and colleague names | | Support agent with tools | Customer lookup results, refund or shipping actions, outbound emails | | Ticket analytics and QA | Hundreds of tickets at once, exported as CSV or JSON | | Knowledge-base answers (RAG) | Retrieved past tickets that mention other customers | Support data is high-volume and personal by nature, and it usually flows through your own integration code, which is exactly where Shadow fits. ## How Shadow protects support data | Data | Handling | |---|---| | Names, emails, phones, street addresses | Built-in detectors; realistic fakes from the [persona](https://getshadow.dev/concepts/personas/) | | Card numbers, IBANs | Luhn- and mod-97-checked; replaced with valid-looking fakes | | National IDs (US SSN, UK NINO, Aadhaar and others) | Built-in detectors | | Your company, partner and product names | `organization` and `terms` | | Agent and colleague names | Name detector, plus `person:` terms for unusual names | | Ticket exports in reports | Typed tokens via the `report` intent | | Attached CSV, DOCX, XLSX, PDF text | [File masking](https://getshadow.dev/concepts/file-masking/) | | The customer you reply to or act on | Passed through as the recipient and logged; `targets = "surrogate"` fakes it and restores it locally ([intent policy](https://getshadow.dev/concepts/intent-policy/)) | Order numbers, ticket IDs and loyalty IDs are not personal data on their own and are not detected by the built-in detectors. If they are sensitive for you, there is no custom regex setting in `shadow.toml`; use the NER sidecar for account-style numbers, or a custom detector if you embed Shadow in Rust. ## How to set it up ### 1. Run Shadow next to your support integration ```sh shadow init --org "Acme GmbH" shadow run ``` In production, run it as a [sidecar](https://getshadow.dev/integrate/docker-kubernetes/) of the service that calls the model, with a persistent `/data` volume so a customer keeps the same fake across a long thread. ### 2. Point the model client at the gateway ```python from openai import OpenAI client = OpenAI(base_url="http://127.0.0.1:8877/gw/openai/v1") stream = client.chat.completions.create( model="gpt-4o-mini", stream=True, messages=[ {"role": "system", "content": "You are a support agent for Acme GmbH. Be concise."}, {"role": "user", "content": ticket_thread}, ], ) for chunk in stream: print(chunk.choices[0].delta.content or "", end="") # real names, restored on the fly ``` ### 3. Configure the policy ```toml organization = ["Acme GmbH"] terms = ["org:Globex Ltd", "Acme Plus"] # partner and plan names internal_domains = ["acme.example", "support.acme.internal"] [policy.act] targets = "keep" # the customer you write to passes through, logged ("surrogate" hides them) remote_tool_targets = "surrogate" # never send real targets to provider-hosted tools [policy.report] person = "token" email = "token" phone = "token" address = "token" default = "surrogate" [policy.always] credit_card = "token" secret = "redact" ``` ### 4. Run tools in your own code Keep actions such as `issue_refund`, `send_email` and `lookup_order` as local tools your service executes. Other values in their arguments (and the recipient, with `targets = "surrogate"`) are restored on your side before they run. Provider-hosted tools cannot have values restored inside the provider's cloud. ### 5. Test on real-looking tickets ```sh shadow scan sample-ticket.txt shadow scan export.csv --json ``` ## Example: a duplicate-charge ticket What the support tool sends (fictional): ```text Draft an apology to Jane Doe (jane.doe@acme.example) and refund ticket #88213. It was charged twice on card ending 1111; our agent Tom Reyes confirmed it. The order shipped to 221 Baker Street, London. ``` What the provider sees (persona values differ per machine): ```text Draft an apology to Jane Doe (jane.doe@acme.example) and refund ticket #88213. It was charged twice on card ending 1111; our agent Lei Li confirmed it. The order shipped to 192 Keyuan Road, London. ``` Jane Doe and her email are the recipient, so they pass through as written and each keep is logged: the model greets her by name and calls `issue_refund(customer_email="jane.doe@acme.example", ...)` with her real address. Tom Reyes and the delivery address are not the recipient, so the provider sees fakes, and Shadow restores them in the draft reply. With `targets = "surrogate"`, Jane would be a fake as well and Shadow would restore her email in the tool arguments and her name in the draft. Note that the last four card digits and the ticket number are sent as is: they are not full identifiers, and the built-in detectors do not treat them as such. ## Example: weekly ticket themes An analyst sends 900 tickets as CSV and asks for the top complaint themes. Shadow classifies it as `report`: customers become `[PERSON_0001]`, `[EMAIL_0001]` and so on. The model clusters and counts without inventing details about fake people, and the report is restored locally. ## Limitations - **Vendor-hosted AI features** in help-desk products run in the vendor's cloud and are not intercepted. - **Voice and Realtime APIs** (WebSocket) are not processed, so AI phone agents built on them are not covered. - **Screenshots** customers attach are not OCR'd. Set `[files] images = "block"` if they reach the model. - **Context can identify.** A unique complaint about a named event can point to a customer even with every name replaced. ## Regulations and frameworks Masking customer data before it reaches an AI provider supports GDPR data minimisation (Art. 5(1)(c)), pseudonymisation by design (Art. 25, 32), and keeping card data out of AI systems helps limit PCI DSS scope. It is a technical control, not a compliance programme. :::caution[Not legal advice] Shadow does not make your support operation compliant with GDPR, PCI DSS or any other framework. Confirm your obligations with your privacy and legal teams. ::: ## Related - [Masking PII in LLM agents and tool calls](https://getshadow.dev/use-cases/mask-pii-in-llm-agents-and-tool-calls/) - [Protect customer data in RAG pipelines](https://getshadow.dev/use-cases/protect-customer-data-in-rag-pipelines/) - [E-commerce and retail](https://getshadow.dev/industries/ecommerce-retail/), [SaaS and technology](https://getshadow.dev/industries/saas-technology/), [telecommunications](https://getshadow.dev/industries/telecommunications/) - [Intent-aware policy](https://getshadow.dev/concepts/intent-policy/), [benchmarks](https://getshadow.dev/benchmarks/), [how Shadow compares](https://getshadow.dev/compare/) ## Frequently asked questions ### Can support agents use AI to draft replies without exposing customer data? Yes, if the drafting tool calls the model through Shadow. The customer you reply to is the recipient: by default their name and email pass through as written, so the model greets the right person, and each keep is logged. Colleagues, other customers, card numbers and other details are replaced with realistic fakes and restored in the draft. Set [policy.act] targets = "surrogate" to fake the recipient too. ### Does Shadow work with Zendesk, Intercom or Freshdesk AI features? Not with features that run inside the help-desk vendor's cloud, because that traffic never passes through your machines. It works with AI features you build yourself on the OpenAI, Anthropic or other supported APIs, for example a help-desk app or webhook service that calls the model through the Shadow gateway. ### Will an AI support agent still be able to issue refunds or send emails? Yes, if the tools run in your code. The customer the agent acts on is passed through, so the model calls the tool with their real email; any other masked values in the arguments are restored on your side before the tool runs. ### Can we analyse thousands of tickets with an LLM safely? Yes. Requests that summarise or analyse many records are classified as report, so people, emails, phones and addresses become typed tokens such as [PERSON_0001]. The model groups and counts them, and the report is restored with real values locally. ### Are payment card numbers in tickets protected? Card numbers are detected with a Luhn check and replaced with a different valid-looking number, or with a token in reports. You can also send them as [CREDIT_CARD_0001] in every request with [policy.always] credit_card = "token". --- # Secure ChatGPT and LLM use by industry: protect regulated data with Shadow Source: https://getshadow.dev/industries/ > How healthcare, finance, legal, public sector and 14 more industries use ChatGPT, Claude and LLM APIs without sending personal or confidential data. Every industry has its own version of the same problem: people get real value from ChatGPT, Claude and LLM APIs, and the prompts that get the best answers are full of data that should not leave the organisation. Shadow is an open-source privacy layer that sits between your tools and the AI provider. It replaces names, emails, customer records, IDs, internal hostnames and secrets with realistic, reversible fakes before a request leaves, and restores the real values in the answer. The pages below explain, sector by sector, what leaks, how Shadow handles it, which regulations it helps with, and how to deploy it. ## Choose your industry ### Health and life sciences | Industry | Main concern | Key frameworks | |---|---|---| | [Healthcare](https://getshadow.dev/industries/healthcare/) | Patient data (PHI) in clinical notes, letters, coding and EHR work | HIPAA, GDPR, NHS guidance | | [Pharma and life sciences](https://getshadow.dev/industries/pharma-life-sciences/) | Clinical trial data, compound codenames, pharmacovigilance narratives | GDPR, ICH E6(R3), Part 11, Annex 11 | ### Financial services | Industry | Main concern | Key frameworks | |---|---|---| | [Banking and financial services](https://getshadow.dev/industries/financial-services/) | Customer records, IBANs, card numbers, KYC files | PCI DSS, GLBA, DORA, GDPR | | [Insurance](https://getshadow.dev/industries/insurance/) | Policyholder and claims data, adjuster notes | NAIC AI bulletin, NYDFS Part 500, EU AI Act | | [Real estate](https://getshadow.dev/industries/real-estate/) | Tenant, buyer and mortgage data, leases | GLBA, Fair Housing, state privacy laws | ### Legal, professional and public services | Industry | Main concern | Key frameworks | |---|---|---| | [Legal](https://getshadow.dev/industries/legal/) | Client confidentiality, privilege, matter names | ABA Opinion 512, Model Rule 1.6, GDPR | | [Consulting and professional services](https://getshadow.dev/industries/consulting-professional-services/) | Client names, engagement codenames, deliverables | NDAs, GDPR, ISO 27001 | | [Government and public sector](https://getshadow.dev/industries/government-public-sector/) | Citizen data, case files, data residency | FedRAMP context, CJIS, GDPR | | [Education](https://getshadow.dev/industries/education/) | Student records, IEPs, grades | FERPA, COPPA, GDPR | | [Nonprofit](https://getshadow.dev/industries/nonprofit/) | Donor and beneficiary data on a small budget | GDPR, HIPAA (health charities) | | [HR and recruiting](https://getshadow.dev/industries/hr-recruiting/) | Candidate CVs, employee records | GDPR, EU AI Act, NYC Local Law 144 | ### Technology, commerce and media | Industry | Main concern | Key frameworks | |---|---|---| | [SaaS and technology](https://getshadow.dev/industries/saas-technology/) | Customer data in support and engineering, product AI features | SOC 2, GDPR, customer DPAs | | [E-commerce and retail](https://getshadow.dev/industries/ecommerce-retail/) | Order data, addresses, payment details | PCI DSS, GDPR, CCPA | | [Telecommunications](https://getshadow.dev/industries/telecommunications/) | Subscriber data, network configs | CPNI rules, ePrivacy, NIS2 | | [Media and entertainment](https://getshadow.dev/industries/media-entertainment/) | Unreleased titles, talent names, scripts | TPN, NDAs, GDPR | ### Industrial and infrastructure | Industry | Main concern | Key frameworks | |---|---|---| | [Manufacturing](https://getshadow.dev/industries/manufacturing/) | Trade secrets, supplier names, part codenames | Trade secret law, ITAR/EAR context, ISO 27001 | | [Energy and utilities](https://getshadow.dev/industries/energy-utilities/) | OT hostnames and IPs, customer billing data | NERC CIP-011, NIS2 | | [Automotive](https://getshadow.dev/industries/automotive/) | Connected-car and driver data, supplier IP | UNECE R155/R156, ISO/SAE 21434, GDPR | ## What is the same in every industry The specifics change from sector to sector. The mechanism does not. | Step | What happens | |---|---| | Detect | Built-in detectors find emails, phones, names, addresses, IBANs, cards, national IDs, IPs, hostnames, secrets and usernames. Your dictionary adds customers, matters, codenames and colleagues. Optional [Presidio](https://getshadow.dev/concepts/detection/#presidio) or a local NER model raise recall | | Decide | The [intent-aware policy](https://getshadow.dev/concepts/intent-policy/) picks a treatment per value: realistic fake, typed token, kept or redacted | | Translate | Values become fakes from one coherent [persona](https://getshadow.dev/concepts/personas/), recorded in an encrypted local [vault](https://getshadow.dev/concepts/vault/) | | Restore | The answer, including streamed text and [tool-call arguments](https://getshadow.dev/concepts/tool-calls/), comes back with your real values | Uploaded files go through the same pipeline: DOCX, XLSX and PPTX are masked in place, and PDF text is extracted and masked. See [File masking](https://getshadow.dev/concepts/file-masking/). ## What no industry should assume :::caution[Know the limits before you rely on it] - **Detection is best effort.** Unusual formats and internal IDs (MRNs, claim, policy or matter numbers) are not detected by the built-in detectors. Add terms, enable Presidio or the NER sidecar, and test with `shadow scan`. - **Dates are not masked.** This matters for HIPAA Safe Harbor and for any re-identification analysis. - **No OCR.** Text in images and scanned PDFs is not masked. Block images where they may contain personal data. - **Context is not masked.** A unique situation can identify a person or company even with every name replaced. - **Consumer web apps are not covered.** Shadow protects API traffic. chatgpt.com and claude.ai are not built-in hosts. - **No central console yet.** Fleet rollout uses your existing device management. See [Shadow by company size](https://getshadow.dev/company-size/). ::: Read the full [security model](https://getshadow.dev/reference/security/) and the [benchmarks](https://getshadow.dev/benchmarks/) before you make claims about Shadow in a risk assessment. ## Where to go next - **Try it in five minutes:** [Quick start](https://getshadow.dev/getting-started/quick-start/) - **Pick a deployment:** [Integration modes](https://getshadow.dev/integrate/overview/) - **Find your task:** [Use cases](https://getshadow.dev/use-cases/) - **Plan a rollout:** [Shadow by company size](https://getshadow.dev/company-size/) - **See the alternatives:** [How Shadow compares](https://getshadow.dev/compare/) ## Frequently asked questions ### Which industries is Shadow built for? Any organisation whose prompts contain personal data, customer data, secrets or confidential names. It is most useful in regulated sectors such as healthcare, finance, insurance, legal and the public sector, where sending that data to an AI provider is restricted. ### Does Shadow make us compliant with HIPAA, GDPR or PCI DSS? No tool does that on its own. Shadow is a technical control that reduces what reaches AI providers, which supports requirements such as data minimisation, pseudonymisation and minimum necessary. Compliance depends on your whole programme and your legal advice. ### What does Shadow cost for a regulated organisation? Nothing to license. Shadow is open source under Apache-2.0, self-hosted, with no account and no telemetry. Your costs are the time to roll it out and any optional detector infrastructure, such as a Presidio container. ### Does the AI provider still receive our data? It receives a masked version: realistic, format-preserving fakes instead of names, emails, IDs, card numbers, hostnames and secrets. The only real values sent on purpose are the recipient when you ask the model to write to or act on someone, and those a provider-hosted action (such as a hosted MCP tool sending an email) needs to reach the right person; each one is logged and both can be turned off. Detection is best effort and context is not masked, so keep the provider under your normal vendor and contract controls. ### Which AI tools does Shadow cover? Anything that calls a model API through Shadow's proxy or gateway: SDKs, internal apps, agents, and coding assistants that use your own API key, for OpenAI, Anthropic, Azure OpenAI, Gemini, Bedrock, OpenRouter, Mistral and more. Consumer web apps such as chatgpt.com are not built-in hosts. --- # Secure ChatGPT and LLM use in healthcare: protect PHI with Shadow Source: https://getshadow.dev/industries/healthcare/ > Keep patient names, contact details and identifiers out of OpenAI, Anthropic and Gemini prompts. Shadow masks PHI locally and restores it in the answer. Shadow is for hospitals, clinics, health tech vendors and digital health teams that want to use large language models without sending protected health information (PHI) to the model provider. It runs on your own laptops, servers or pods, replaces patient names, emails, phone numbers, addresses and national IDs with realistic fakes before a request leaves, and restores the real values in the response. It is a technical safeguard that reduces what reaches the provider; it does not replace your HIPAA programme or a business associate agreement. ## What data leaks into AI tools in healthcare Clinical and administrative staff use LLMs for the same reason everyone else does: they save time on writing. The text they paste is rarely clean. | Workflow | What typically ends up in the prompt | |---|---| | Summarising a discharge letter or referral | Patient name, date of birth, address, NHS or Medicare number, GP name, ward | | Drafting a reply to a patient complaint | Patient and family names, email, phone number, clinician names | | Ambient scribe or note-cleanup tools | Full consultation transcript: symptoms, medications, family history, names | | Coding and billing support | Encounter details, insurer member IDs, claim references | | Analytics on exported reports | Spreadsheets with hundreds of patient rows | | Engineering on EHR integrations | HL7 or FHIR samples with real identifiers, internal hostnames, API keys | Each of these sends PHI to a third party, often outside any agreement your compliance team has reviewed. ## How Shadow protects it Shadow sits between the tool and the provider. For every outbound request it detects sensitive values, swaps them for format-preserving fakes from a persistent [persona](https://getshadow.dev/concepts/personas/), keeps the mapping in an [encrypted local vault](https://getshadow.dev/concepts/vault/), and translates the answer back. | PHI element | How Shadow handles it | |---|---| | Patient, family and clinician names | Built-in name detector; add known names with `person:` terms; the optional NER sidecar or Presidio improves recall on free text | | Emails, phone numbers, street addresses | Built-in detectors, replaced with persona-consistent fakes | | SSN, UK NHS number, Medicare and similar IDs | US SSN and UK NHS number (checksum-validated) are built in; enable [Presidio](https://getshadow.dev/concepts/detection/#presidio) for `AU_MEDICARE`, `US_MBI`, `MEDICAL_LICENSE` and more | | Hospital, practice and partner names | `organization` and `org:` terms | | Internal hostnames, IPs, API keys | `hostname`, `ip` and `secret` detectors, plus `internal_domains` | | Bulk patient lists in reports | The `report` intent swaps people, emails, phones and addresses for typed tokens such as `[PERSON_0004]` | | Uploaded DOCX, XLSX, PPTX, PDF text | [File masking](https://getshadow.dev/concepts/file-masking/) | | The patient or family member a letter or reply is addressed to | Passed through as the recipient so the model writes to the right person, each keep logged; health details and other people in the request stay masked. Set `[policy.act] targets = "surrogate"` to fake the recipient too | A starting configuration for a health system: ```toml organization = ["St Mary's Health Partners"] terms = ["org:Northside Imaging", "term:Ward 7B", "person:Dr Amara Okafor"] internal_domains = ["stmarys.example", "ehr.stmarys.internal"] [presidio] enabled = true url = "http://127.0.0.1:5002/analyze" entities = [] # all Presidio recognizers, including AU_MEDICARE, US_MBI and MEDICAL_LICENSE [policy.report] person = "token" email = "token" phone = "token" address = "token" national_id = "token" default = "surrogate" [policy.always] secret = "redact" [files] pdf = "text" images = "block" # no OCR yet: stop screenshots and scans from leaving unmasked ``` :::caution[Known gaps for PHI] - **Dates are not masked.** Dates of birth, admission and discharge dates pass through. Remove or generalise them before sending, or keep them out of the workflow. - **Medical record numbers are not detected by the built-in detectors.** There is no custom regex setting in `shadow.toml`. Add known values as terms, enable the open-weight NER sidecar (which finds account-style numbers), or write a custom detector in Rust if you embed Shadow. - **No OCR.** Scanned PDFs and images are not masked. - **Context can identify.** A rare condition in a small town can identify a patient even with every name replaced. ::: Run `shadow scan` on representative, already approved test documents to see what is found before relying on it. ## Regulations and frameworks | Requirement | How Shadow helps | |---|---| | HIPAA minimum necessary (45 CFR 164.502(b)) | Limits the identifiers that reach an AI vendor to what the task needs: usually none | | HIPAA Security Rule safeguards | A technical control on a known data flow, running on infrastructure you control, with no telemetry | | HIPAA de-identification (45 CFR 164.514) | Removes many of the 18 Safe Harbor identifiers, but not dates or unrecognised record numbers. Not a de-identification method on its own; an expert determination would need to assess it | | GDPR Art. 9 health data, Art. 25 and 32 | Pseudonymisation by design before transfer. Pseudonymised data is still personal data for you, because you hold the key (Recital 26) | | ISO/IEC 27001, ISO/IEC 42001 | Evidence of a technical control for AI data flows in your ISMS or AI management system | Where PHI may still reach a vendor (missed detections, dates, free text), keep a business associate agreement with that vendor. Shadow reduces exposure; it does not change who is a business associate. :::caution[Not legal advice] This page describes technical capabilities, not legal conclusions. Using Shadow does not make an organisation HIPAA or GDPR compliant. Work with your privacy officer and counsel. ::: ## Example workflows ### Summarising a discharge letter What the clinician sends (all data fictional): ```text Summarise for the GP: Maria Lopez (NHS 943 476 5919), 12 Elm Road, Leeds, phone +44 113 496 0123, admitted under Dr Amara Okafor for community-acquired pneumonia. ``` What the provider sees with this machine's persona (your fakes will differ): ```text Summarise for the GP: Ying Chen (NHS 485 771 2046), 88 Tianfu Road, Leeds, phone +86 28 6512 0417, admitted under Dr Tao Zhu for community-acquired pneumonia. ``` The summary comes back with Maria Lopez, Dr Amara Okafor and the real NHS number restored. If the model shortens the fake to "Dr Zhu", Shadow restores that too, because fake surnames are restored on their own. The clinical content is unchanged, which is why the answer stays useful. ### Patient-experience report over a spreadsheet An analyst uploads an XLSX of 600 complaint records and asks for an analysis of the records by department. The wording reads as a report over records, so Shadow classifies it as `report` and names, emails and phones in the workbook become `[PERSON_0001]`, `[EMAIL_0001]` and so on. The model groups and counts them without inventing details, and the final report lists real patients only on the analyst's screen. ### EHR integration debugging A developer pastes a FHIR bundle and a stack trace into Claude Code. Patient names, the `ehr.stmarys.internal` hostname, the developer's home path and the API key in the config are all replaced. With `secret = "redact"`, the key leaves as `[REDACTED_SECRET]`. ## Deployment patterns | Pattern | When to use it | |---|---| | **Endpoint install** on clinician and analyst machines: `shadow install --apply` | Staff using API-backed chat clients, scripts or coding assistants. Covers any tool that honours `HTTPS_PROXY` | | **Gateway** for internal apps: base URL `http://127.0.0.1:8877/gw/openai/v1` or a custom `azure` route | Your own scribe, triage or summarisation services | | **Sidecar** in Kubernetes | Product features in a health app; one Shadow per pod, egress locked to the sidecar. See [Docker and Kubernetes](https://getshadow.dev/integrate/docker-kubernetes/) | Shadow has no central admin console yet, so fleet rollout uses your existing tooling (MDM, configuration management) to place `shadow.toml` and run `shadow install --apply`. An optional local audit log is on the [roadmap](https://getshadow.dev/project/roadmap/). Today, each request logs its intent and counts per treatment, never values. ## Related - [HIPAA-safe LLM usage](https://getshadow.dev/use-cases/hipaa-safe-llm-usage/) and [redacting documents before upload](https://getshadow.dev/use-cases/redact-documents-before-uploading-to-ai/) - [Pharma and life sciences](https://getshadow.dev/industries/pharma-life-sciences/) and [insurance](https://getshadow.dev/industries/insurance/) - [Detection](https://getshadow.dev/concepts/detection/), [intent-aware policy](https://getshadow.dev/concepts/intent-policy/), [security model](https://getshadow.dev/reference/security/) - Detection quality on public datasets: [benchmarks](https://getshadow.dev/benchmarks/). How Shadow differs from other tools: [compare](https://getshadow.dev/compare/) ## Frequently asked questions ### Does Shadow make ChatGPT or the OpenAI API HIPAA compliant? No tool makes you compliant on its own. Shadow is a technical safeguard that keeps detected identifiers out of AI requests, which supports the minimum necessary standard. You still need a risk analysis, policies and, where PHI may reach a vendor, a business associate agreement. ### Is text masked by Shadow de-identified under HIPAA Safe Harbor? Not by itself. Safe Harbor requires removing all 18 identifier types, including dates and record numbers. Shadow does not mask dates, and it does not detect medical record numbers unless you add them or a detector that finds them, so treat masked text as reduced-risk PHI rather than de-identified data. ### Can clinicians use Shadow with the ChatGPT website? Not as a built-in integration. Shadow intercepts provider APIs such as api.openai.com and Azure OpenAI, not consumer web apps like chatgpt.com. Give clinicians an approved API-backed client or internal tool and route it through Shadow. ### Where does patient data go when Shadow masks it? The real values stay in an encrypted vault on the machine or pod running Shadow. The provider receives realistic fakes, and the answer is translated back locally before anyone sees it. ### Can Shadow mask patient data in uploaded PDFs and Word documents? Yes for text. DOCX, XLSX and PPTX are masked in place and PDF text is extracted and sent masked. Scanned PDFs and images are not OCR'd, so block images if they may contain PHI. --- # Secure LLM use in banking and financial services: mask customer and card data with Shadow Source: https://getshadow.dev/industries/financial-services/ > Keep IBANs, card numbers, customer names and account data out of AI prompts. Shadow masks them locally with checksum-valid fakes and restores the answer. Shadow is for banks, payment companies, asset managers, fintechs and credit providers that want to use LLMs on customer-facing and back-office work without handing customer data to the model provider. It runs on your endpoints or next to your services, replaces names, emails, IBANs, card numbers, national IDs and internal hostnames with realistic, checksum-valid fakes, and restores the real values in the answer. It is a control on one data flow, not a compliance programme. ## What data leaks into AI tools in financial services | Workflow | What ends up in the prompt | |---|---| | Drafting replies to complaints and disputes | Customer name, email, phone, account and card numbers, transaction details | | KYC and onboarding reviews | Passport and ID numbers, addresses, dates of birth, employer names | | Credit memos and relationship notes | Client company names, counterparties, deal codenames, exposures | | Fraud and AML investigation summaries | Account numbers, IBANs, IPs, device identifiers, linked parties | | Analytics on exported customer lists | Hundreds of rows of names, emails and balances | | Engineering on payment systems | Sample payloads with PANs, internal hostnames, API keys, connection strings | The data is often regulated several times over: by banking secrecy, by GLBA or GDPR, and, for cards, by PCI DSS. ## How Shadow protects it | Data | Treatment | |---|---| | Card numbers | Detected with a **Luhn** check, replaced by a fake that also passes Luhn | | IBANs | Detected with the **mod-97** check, replaced by a valid IBAN of the same country | | Customer names, emails, phones, addresses | Built-in detectors and persona-consistent fakes; known clients as `org:` and `person:` terms | | SSNs and national IDs | Built in for US SSN and ITIN, UK NINO, Aadhaar and Indian PAN; [Presidio](https://getshadow.dev/concepts/detection/#presidio) adds `US_BANK_NUMBER`, `US_ITIN`, `US_PASSPORT` and many others | | Account numbers in free text | Not detected by built-ins. The open-weight NER sidecar labels account numbers, or add known values as terms | | Deal codenames, counterparties | Dictionary `terms` | | Bulk customer exports | The `report` intent turns people, emails, phones and addresses into typed tokens | | Secrets in code and configs | Faked by default; redact with `[policy.always] secret = "redact"` | ```toml organization = ["Northwind Bank plc"] terms = ["Project Harbour", "org:Globex Capital", "org:Initech Holdings"] internal_domains = ["northwind.example", "corp.northwind.internal"] [model_detector] enabled = true # open-weight NER sidecar on loopback url = "http://127.0.0.1:8878/detect" [policy.report] person = "token" email = "token" phone = "token" address = "token" national_id = "token" default = "surrogate" [policy.always] secret = "redact" credit_card = "token" # never send even a fake PAN-shaped value, if you prefer [files] images = "block" ``` Start the sidecar with `python serve_hf.py --backend ensemble --host 127.0.0.1 --port 8878` from the `ml/` directory after `pip install -r requirements-hf.txt`. It combines OpenAI Privacy Filter and GLiNER (both Apache-2.0) and runs locally. :::caution[Check these before rollout] - **AWS Bedrock with SigV4** is forwarded unmasked with a warning, because changing the body breaks the signature. Bedrock API keys work normally. - **Dates of birth and transaction dates** are not masked. - **Images and scanned statements** are not OCR'd. Block images if they may contain account data. - **Detection is best effort.** Run `shadow scan` on representative, approved test files, and see [benchmarks](https://getshadow.dev/benchmarks/). ::: ## Regulations and frameworks | Framework | How Shadow helps | |---|---| | PCI DSS v4.0.1 | Keeps PAN out of what reaches the AI provider, supporting scope reduction and Requirement 3's limits on stored and transmitted PAN. The endpoint running Shadow still sees PAN and stays in scope | | GLBA Safeguards Rule (16 CFR 314) | A technical safeguard on a data flow to a service provider, under your control | | DORA (applies since 17 January 2025) | Reduces what a third-party AI provider receives; useful in ICT third-party risk treatment. No telemetry, no hosted component | | GDPR Art. 5(1)(c), 25, 32 | Data minimisation and pseudonymisation by design before transfer. Pseudonymised data remains personal data for you (Recital 26) | | EU AI Act | Credit scoring of natural persons is an Annex III high-risk use, with obligations now due from 2 December 2027. Shadow is not a conformity tool, but it limits personal data in prompts during development and use | | SOC 2, ISO/IEC 27001, ISO/IEC 42001 | Evidence of a technical control for AI data flows | :::caution[Not legal advice] This page describes technical capabilities. Shadow does not make you PCI DSS, GLBA, DORA or GDPR compliant, and scope decisions belong to your assessors and counsel. ::: ## Example workflows ### Dispute reply Sent (fictional data): ```text Draft a reply to Jane Doe (jane.doe@acme.example) about the disputed charge on her card ending 1111. The refund goes to DE89 3704 0044 0532 0130 00. ``` Seen by the provider: ```text Draft a reply to Jane Doe (jane.doe@acme.example) about the disputed charge on her card ending 1111. The refund goes to DE44 5001 0517 5407 3249 31. ``` Jane Doe and her email are the recipient, so they pass through as written (each keep is logged) and the model addresses the reply to her. The IBAN comes after "about", so the provider sees a fake one, and the draft comes back with her real IBAN restored, ready to send. To hide the customer's name and email from the provider as well, set `[policy.act] targets = "surrogate"`. The last four digits of a card are not a card number, so they pass through as written. A full card number would be replaced with a Luhn-valid fake (or a token with `credit_card = "token"`), but a customer reply rarely needs one, and Shadow does not restore fragments of a fake card: if the model quoted just the last four digits of one, they would stay fake. ### Churn analysis over a customer export An analyst pastes a CSV of 800 customers and asks for churn drivers by segment. As a `report`, every customer becomes `[PERSON_0001]` and `[EMAIL_0001]`; product names and balances stay readable. The model ranks segments without making up facts about fake people, and the result is restored locally. ### Payment-service debugging An engineer pastes a failing ISO 20022 payload and a config into Claude Code. IBANs and names become valid fakes, `payments.corp.northwind.internal` becomes a persona hostname, and the database password is redacted. ## Deployment patterns | Pattern | Use | |---|---| | **Endpoint** (`shadow install --apply`) | Analysts, operations and engineers using API-backed assistants and scripts | | **Gateway** with a custom `azure` route | Internal copilots on Azure OpenAI: `[proxy.routes] azure = "https://.openai.azure.com"` | | **Sidecar** with a `NetworkPolicy` | Customer-facing AI features; only the sidecar may reach provider hosts | | **Rust library** | Payment or risk services written in Rust that call models in-process | Use `upstream_proxy` if AI egress must go through a corporate proxy. There is no central admin console or SIEM export yet; per-request logs (intent and counts, never values) go to stderr and can be collected by your existing agents. A local audit log is on the [roadmap](https://getshadow.dev/project/roadmap/). ## Related - [Insurance](https://getshadow.dev/industries/insurance/) and [e-commerce and retail](https://getshadow.dev/industries/ecommerce-retail/) - [Redact PII before sending to the OpenAI API](https://getshadow.dev/use-cases/redact-pii-before-sending-to-openai-api/), [LLM data loss prevention](https://getshadow.dev/use-cases/llm-data-loss-prevention-dlp/) - [Regulated enterprise](https://getshadow.dev/company-size/regulated-enterprise/) - [Intent-aware policy](https://getshadow.dev/concepts/intent-policy/), [providers](https://getshadow.dev/reference/providers/), [compare](https://getshadow.dev/compare/) ## Frequently asked questions ### Can bank staff use ChatGPT or Claude without exposing customer data? They can reduce exposure substantially by routing API-based AI tools through Shadow, which replaces customer names, emails, IBANs, card numbers and national IDs with fakes before the request leaves. Consumer web apps such as chatgpt.com are not covered, so give staff an approved API-backed client. ### Does Shadow take an LLM workflow out of PCI DSS scope? It can help keep primary account numbers out of the data sent to an AI provider, because card numbers are detected with a Luhn check and replaced. Scope is decided by your QSA and depends on every system that handles PAN, including the machine running Shadow. ### Will the model still work with fake IBANs and card numbers? Yes. Fake IBANs pass the mod-97 check and fake card numbers pass Luhn, so validation logic, formatting questions and code that processes them keep working. The real values are restored in the answer. ### Does Shadow work with AWS Bedrock? With Bedrock API keys (bearer tokens), yes. Requests signed with AWS SigV4 cannot be modified without breaking the signature, so Shadow forwards them unmasked with a warning. Use API-key auth or another route for masked traffic. ### How does Shadow help with DORA? DORA asks financial entities to manage ICT third-party risk. Shadow is self-hosted software with no telemetry, and it limits what a third-party AI provider receives, which can form part of your risk treatment for AI services. It does not replace contract, register or exit-strategy obligations. --- # Secure generative AI in insurance: protect policyholder and claims data with Shadow Source: https://getshadow.dev/industries/insurance/ > Use LLMs on claims notes, adjuster reports and policyholder letters without sending personal data to the provider. Shadow masks it locally and restores it. Shadow is for insurers, brokers, MGAs, third-party administrators and claims teams that want LLM help with claims notes, correspondence and analysis without sending policyholder data to the AI provider. It runs on your machines or beside your services, replaces names, contact details, addresses, national IDs, bank details and internal names with realistic fakes, and restores them in the answer. Health information in claims gets the same treatment, with the gaps listed below. ## What data leaks into AI tools in insurance | Workflow | What ends up in the prompt | |---|---| | Summarising a claim file | Claimant and witness names, addresses, phone numbers, injury and medical details, policy and claim numbers | | Drafting declinature or settlement letters | Policyholder name, address, bank details for payment, broker names | | Subrogation and fraud review | Third parties, vehicle registrations, IPs, linked claims | | Underwriting submissions | Commercial client names, locations, loss histories, employee schedules | | Portfolio and loss analytics | Spreadsheets of policyholders with contact details and claim amounts | | Building claims automation | Sample FNOL payloads, internal hostnames, API keys | ## How Shadow protects it | Data | Treatment | |---|---| | Claimant, witness, adjuster names | Built-in name detector, `person:` terms, optional NER sidecar or Presidio for free text | | Emails, phones, street addresses | Built-in detectors, persona-consistent fakes | | SSN, NINO and other national IDs | Built in for US SSN and ITIN, UK NINO and NHS number, Aadhaar; [Presidio](https://getshadow.dev/concepts/detection/#presidio) adds driver licence, passport and many national formats | | IBANs and card numbers for payouts | Checksum-validated detection and checksum-valid fakes | | Policy and claim numbers | **Not detected by built-ins.** Use terms for known values, the NER sidecar, or a custom Rust detector | | Brokers, reinsurers, commercial clients | `org:` terms | | Bordereaux and loss runs | The `report` intent swaps people and contact details for typed tokens | | Claim attachments | DOCX, XLSX, PPTX masked in place; PDF text extracted and masked; images blocked if you choose | ```toml organization = ["Harbourline Mutual Insurance Ltd"] terms = ["org:Globex Re", "org:Brightside Brokers", "term:Operation Tidewater"] internal_domains = ["harbourline.example", "claims.harbourline.internal"] [model_detector] enabled = true # open-weight NER: names, addresses, account-style numbers [presidio] enabled = true entities = [] # all recognizers, incl. US_DRIVER_LICENSE, UK_NINO, US_SSN [policy.report] person = "token" email = "token" phone = "token" address = "token" national_id = "token" default = "surrogate" [files] pdf = "text" images = "block" ``` :::note[What Shadow does not change] Shadow masks identifiers, not facts. Injury descriptions, diagnoses and loss narratives reach the model as written, and dates (date of loss, date of birth) are not masked. A distinctive event in a small community can still identify someone. Decide which claim types are suitable for AI assistance at all. ::: ## Regulations and frameworks | Framework | How Shadow helps | |---|---| | NAIC Model Bulletin on the Use of AI Systems by Insurers (2023) | A documented technical control over data sent to third-party AI systems, which the bulletin expects insurers to govern | | NYDFS Part 500 | Supports limiting nonpublic information sent to third-party service providers; runs on infrastructure you control | | GLBA and state privacy laws | Reduces nonpublic personal information disclosed to an AI vendor | | GDPR Art. 5(1)(c), 9, 25, 32 | Minimisation and pseudonymisation of personal and health data before transfer. Still personal data for you (Recital 26) | | EU AI Act Annex III | Life and health insurance risk assessment and pricing for individuals is high-risk, with obligations from 2 December 2027. Shadow supports data minimisation, not conformity assessment | | HIPAA (US health plans) | Supports minimum necessary when plans use AI on member data. See [healthcare](https://getshadow.dev/industries/healthcare/) | :::caution[Not legal advice] This page describes what the software does. It is not a compliance determination. Confirm obligations with your compliance team and counsel. ::: ## Example workflows ### Claim summary for a supervisor Sent (fictional): ```text Summarise claim notes: insured Tom Becker, 41 Harbour Street, Bristol, +44 117 496 0555, tom.becker@acme.example. Third party Priya Patel admits fault. Pay to GB82 WEST 1234 5698 7654 32. ``` Seen by the provider (your persona will differ): ```text Summarise claim notes: insured Lei Li, 17 Jinjiang Street, Bristol, +86 28 6513 2290, lei.li@zhilan.cn. Third party Yong Xu admits fault. Pay to GB33 BUKB 2020 1555 5555 55. ``` The summary is restored with Tom Becker, Priya Patel and the real account. The liability reasoning is unaffected, because it does not depend on who the people are. ### Loss-run analysis An underwriter attaches an XLSX of 1,200 claims and asks for a breakdown of the claim records by peril, with frequency and severity. The request reads as a report, so Shadow masks the workbook's shared strings in place: claimant names become typed tokens, amounts and formulas are untouched. The model's tables come back with real names wherever it quotes them. ### Letter drafting with an agent A claims assistant built on the OpenAI SDK offers a local `send_letter` tool. When the adjuster names the policyholder ("Send the settlement letter to Tom Becker, 9 High Street, Leeds"), that recipient passes through as written and is logged, so the model writes the tool call to the real address. Other people and details in the claim are masked and restored in the tool-call arguments before your code sends the letter. With `[policy.act] targets = "surrogate"`, the recipient is a fake too and Shadow restores the real address the same way. See [tool calls and agents](https://getshadow.dev/concepts/tool-calls/). ## Deployment patterns | Pattern | Use | |---|---| | **Endpoint** with `shadow install --apply` | Adjusters, underwriters and analysts using API-backed assistants | | **Gateway** | Claims platforms and internal copilots: point the SDK base URL at `/gw/openai/v1`, `/gw/anthropic` or a custom route | | **Sidecar** | Customer-facing chat and FNOL automation in Kubernetes, with egress limited to the sidecar | Shadow has no central console. Roll out `shadow.toml` with your configuration management or MDM, and keep a shared persona across a team by copying `persona.json`, `vault.bin` and `vault.key` (treat the key as a secret). Shared team identities are a [roadmap](https://getshadow.dev/project/roadmap/) item. ## Related - [Financial services](https://getshadow.dev/industries/financial-services/) and [healthcare](https://getshadow.dev/industries/healthcare/) - [Privacy for AI in customer support](https://getshadow.dev/use-cases/privacy-for-ai-in-customer-support/), [redact documents before uploading to AI](https://getshadow.dev/use-cases/redact-documents-before-uploading-to-ai/) - [File masking](https://getshadow.dev/concepts/file-masking/), [intent-aware policy](https://getshadow.dev/concepts/intent-policy/), [benchmarks](https://getshadow.dev/benchmarks/), [compare](https://getshadow.dev/compare/) ## Frequently asked questions ### Can claims adjusters use ChatGPT on claim files safely? Adjusters can reduce what leaves by using an API-backed assistant routed through Shadow, which replaces claimant names, contact details, IDs and addresses with fakes and restores them in the answer. Consumer web apps such as chatgpt.com are not covered, and claim and policy numbers need extra configuration to be detected. ### Does Shadow detect policy and claim numbers? Not with its built-in detectors, and shadow.toml has no custom regex setting. Add known numbers as terms, enable the open-weight NER sidecar, which recognises account-style numbers, or implement a custom detector when embedding Shadow in Rust. ### What does the NAIC Model Bulletin on AI expect from insurers? The 2023 bulletin, adopted by many states, expects insurers to run a written AI programme with governance, risk management and controls over third-party AI systems and data. Shadow is one possible technical control for data sent to model providers; it does not cover fairness testing or model governance. ### Is insurance pricing with AI high-risk under the EU AI Act? AI used for risk assessment and pricing of natural persons in life and health insurance is listed as high-risk in Annex III. Under the 2026 Digital Omnibus those obligations apply from 2 December 2027. Shadow helps with data minimisation but is not a conformity tool. ### Can Shadow mask medical reports attached to a claim? Text-based DOCX, XLSX, PPTX and PDF attachments are masked; PDF text is extracted and sent masked. Scanned reports and photos are images, which Shadow does not OCR, so block images for these workflows. --- # Secure ChatGPT and LLM use for law firms: protect client confidentiality with Shadow Source: https://getshadow.dev/industries/legal/ > Keep client names, matter codenames and counterparties out of AI prompts and uploaded contracts. Shadow masks them locally and restores the answer. Shadow is for law firms, in-house legal teams and legal service providers that want to use LLMs for drafting, review and research without sending client identities to the AI provider. It runs on your machines or servers, replaces client and party names, contact details, matter codenames and internal hostnames with consistent fakes, masks uploaded Word documents in place, and restores everything in the answer. It narrows what a third party learns; it does not make disclosure risk disappear. ## What data leaks into AI tools in legal work | Task | What ends up in the prompt | |---|---| | Summarising or redlining a contract | Party names, signatories, addresses, deal values, bank details | | Drafting a letter before action | Client and opponent names, contact details, factual narrative | | Preparing a chronology from emails | Every name, email address and phone number in the thread | | Due diligence on a data room export | Employee schedules, customer lists, IP registers | | Time narratives and billing | Matter names, client names, codenames for deals | | Litigation support scripts and tools | Internal hostnames, document IDs, API keys | Even when a firm has an enterprise AI agreement, fee earners often use whatever tool is at hand. Identifiers are the part of that text a firm most often has a duty to protect. ## How Shadow protects it | Data | Treatment | |---|---| | Client, opponent and counterparty organisations | `organization` for the firm, `org:` terms for clients and counterparties | | Individuals: clients, witnesses, signatories | Built-in name detector plus `person:` terms; optional NER sidecar or Presidio for recall on free text | | Matter names and deal codenames | Dictionary `terms`, replaced with consistent invented names | | Emails, phones, addresses, IBANs | Built-in detectors with format-preserving fakes | | Contracts and memos | DOCX masked in place (styles and tables survive), PDF text extracted and masked | | Bulk schedules in due diligence | The `report` intent: typed tokens for people and contact details | ```toml organization = ["Hartley & Quinn LLP"] terms = [ "org:Globex Holdings", "org:Initech Ltd", # clients and counterparties "Project Lighthouse", "term:HQ-2026-0457", # deal codename and a matter number "person:Jane Doe", ] internal_domains = ["hartleyquinn.example", "dms.hq.internal"] [policy.always] secret = "redact" [files] pdf = "text" images = "block" ``` Add new matters as they open: ```sh shadow terms add "org:Umbrella Pharma" "Project Northgate" "term:HQ-2026-0512" shadow terms list ``` :::caution[Masking is not anonymity] Shadow replaces identifiers. The facts, the legal issue and the structure of a deal still reach the provider. A unique transaction described in detail can be recognisable with every name changed. Matter numbers that are not listed as terms are not detected, and dates are not masked. ::: ## Regulations and professional rules | Rule or framework | How Shadow helps | |---|---| | ABA Model Rule 1.6 and Formal Opinion 512 (2024) | Supports "reasonable efforts" to prevent unauthorised disclosure when using generative AI by keeping client identities out of prompts. The opinion's duties of competence, consent assessment, supervision and fee reasonableness still apply | | Model Rules 5.1 and 5.3 (supervision) | A firm-configured control that applies to every API-based tool on a machine that calls a supported provider API, not just the ones lawyers remember to sanitise | | GDPR Art. 5(1)(c), 25, 32 | Minimisation and pseudonymisation of personal data in matters before transfer to a processor | | Outside counsel guidelines and client security questionnaires | A concrete, inspectable answer to "what client data reaches your AI vendors": self-hosted, Apache-2.0, no telemetry | | ISO/IEC 27001 | Evidence of a technical control on AI data flows | :::caution[Not legal advice] This page describes technical behaviour. It is not an ethics opinion. Check your jurisdiction's rules and bar guidance, and client engagement terms. ::: ## Example workflows ### Contract review An associate uploads `Globex-Initech-SPA.docx` to an internal review tool built on the Anthropic API. Shadow masks the document's text runs inside the DOCX: Globex Holdings and Initech Ltd become persona companies, signatories and notice addresses become fakes, and the escrow IBAN becomes a valid fake IBAN. The review comes back with the real party names. ### Letter before action Sent (fictional): ```text Draft a letter before action from Hartley & Quinn LLP for Globex Holdings to Initech Ltd, attn. Jane Doe (jane.doe@initech.example), re unpaid invoices on Project Lighthouse. ``` Seen by the provider (persona varies): ```text Draft a letter before action from Zhilan Technology Co., Ltd. for Globex Holdings to Initech Ltd, attn. Jane Doe (jane.doe@initech.example), re unpaid invoices on Velora Quilent. ``` The letter is addressed to Initech Ltd and Jane Doe, so the recipient phrase (everything after "for" up to "re") passes through as written and each keep is logged: the model writes to the real opponent and does not research a made-up company. Your firm's name and the matter codename are not the recipient, so the provider sees fakes and the draft comes back with Hartley & Quinn LLP and Project Lighthouse restored. If party identities must not reach the provider, set `[policy.act] targets = "surrogate"`; the model then writes to stand-ins that Shadow restores in the draft. ### Chronology from an email export A paralegal pastes 40 emails and asks for a dated chronology. Names and addresses are replaced consistently across all 40, so the model can still follow who wrote to whom. Dates pass through, which is what a chronology needs, and is also why the text is not anonymous. ## Deployment patterns | Pattern | Use | |---|---| | **Endpoint** (`shadow install --apply`) | Fee earners and knowledge lawyers using API-backed assistants, coding tools for legal engineering, or scripts | | **Gateway** | Firm-built drafting and review tools: set the SDK base URL to `/gw/anthropic`, `/gw/openai/v1` or an Azure route | | **Sidecar** | Client-facing portals and document automation in containers | Consumer web apps such as chatgpt.com and hosted legal AI platforms are not covered by Shadow's built-in host list. Pair Shadow with a policy that directs staff to an approved API-backed client. A firm-wide terms list can be distributed with `shadow.toml`; there is no central console yet (see [roadmap](https://getshadow.dev/project/roadmap/)). ## Related - [Consulting and professional services](https://getshadow.dev/industries/consulting-professional-services/) - [Redact documents before uploading to AI](https://getshadow.dev/use-cases/redact-documents-before-uploading-to-ai/), [shadow AI governance](https://getshadow.dev/use-cases/shadow-ai-governance/) - [Detection and dictionary terms](https://getshadow.dev/concepts/detection/#dictionary-terms), [file masking](https://getshadow.dev/concepts/file-masking/), [security model](https://getshadow.dev/reference/security/), [compare](https://getshadow.dev/compare/) ## Frequently asked questions ### Can lawyers put client information into ChatGPT? ABA Formal Opinion 512 says lawyers must assess the risk of disclosing client information to generative AI tools and, for self-learning tools, generally need informed client consent. Shadow lowers the risk by replacing client names, parties, contacts and matter codenames before the request leaves, but it does not remove the need for that judgement. ### Does masking with Shadow preserve attorney-client privilege? Privilege is a legal question that depends on jurisdiction and facts, and no tool settles it. Shadow reduces what a third party receives: names and identifiers are replaced, but the substance of the facts and legal questions still reaches the provider. ### Can Shadow mask a contract I upload to an AI tool? Yes, when the upload goes through an API that Shadow intercepts. DOCX files are masked in place and remain valid Word documents; PDF text is extracted and sent masked. Scanned PDFs are not OCR'd. ### How do I protect matter numbers and codenames? Add them as dictionary terms in shadow.toml or with shadow terms add. Terms are matched case-insensitively on word boundaries and replaced with consistent fakes. Unlisted matter numbers are not detected by the built-in detectors. ### Does Shadow work with legal AI products like Harvey or CoCounsel? Only if the product calls a model API from your machine through a host Shadow intercepts, which hosted legal AI products generally do not. Shadow is aimed at API-based tools: SDK apps, internal assistants, agents and bring-your-own-key clients. --- # Secure generative AI in government and the public sector: mask citizen data with Shadow Source: https://getshadow.dev/industries/government-public-sector/ > Self-hosted, no-telemetry masking of citizen and case data before it reaches an AI provider. Runs inside your boundary; restores answers locally. Shadow is for government departments, agencies, local authorities and public bodies that want to use LLMs on casework, correspondence and policy work without sending citizen identities to a commercial AI provider. It is open-source software that runs inside your own boundary, on endpoints, servers or Kubernetes, with no hosted component and no telemetry. It replaces names, contact details, national IDs and internal hostnames with realistic fakes before a request leaves and restores them locally in the answer. ## What data leaks into AI tools in the public sector | Workflow | What ends up in the prompt | |---|---| | Summarising case files and benefit claims | Citizen names, addresses, national insurance or social security numbers, family members | | Drafting replies to correspondence and FOI requests | Requester names, emails, phone numbers, officials' names | | Policy analysis on consultation responses | Thousands of responses with names and email addresses | | Inspection and enforcement reports | Business names, owners, site addresses | | Digital service development | Sample records, internal hostnames, IP ranges, API keys | | Procurement | Supplier names, contract values, evaluation notes | ## How Shadow protects it | Data | Treatment | |---|---| | Citizen names, emails, phones, addresses | Built-in detectors with persona-consistent fakes | | SSN, ITIN, UK NINO and NHS number, Aadhaar, PAN, Australian TFN and ABN | Built in; [Presidio](https://getshadow.dev/concepts/detection/#presidio) adds passports, driver licences and many national IDs | | Case and reference numbers | Not detected by built-ins. Known values as terms; the NER sidecar for account-style numbers; a custom Rust detector when embedding | | Department, programme and system names | `organization` and dictionary `terms` | | Internal hostnames and IPs | `internal_domains`, plus internal TLDs such as `.internal`, `.corp`, `.local` | | Bulk consultation data | The `report` intent: typed tokens for people and contact details | | Request metadata | OpenAI `user`, Anthropic `metadata.user_id`, org and OS headers replaced or stripped | ```toml organization = ["Department of Civic Services"] terms = ["Programme Meridale", "term:Case Hub", "org:Northshire Council"] internal_domains = ["civic.gov.example", "corp.civic.internal"] [persona] region = "AU" # pick a region that keeps answers relevant; see Personas [policy.always] secret = "redact" national_id = "token" [files] pdf = "text" images = "block" [proxy] upstream_proxy = "http://egress.corp.civic.internal:3128" # route AI egress via your gateway [proxy.routes] llm = "https://llm.civic.gov.example" # a self-hosted or sovereign endpoint ``` The persona's region affects answers about holidays, phone formats and legal context. Pin a region that suits your work, as described in [Personas](https://getshadow.dev/concepts/personas/). ### Data residency, in practice | Stays on your infrastructure | Still reaches the provider | |---|---| | Real identifiers, the vault and its key, the persona, logs | The masked prompt and its non-identifying content | | Detection, including the optional NER and Presidio sidecars on loopback | Your API key (identifying the account) and your egress IP, unless you use a relay | | Restored answers | Any value a detector missed, and values a provider-hosted action needs (kept real by default and logged) | For the strongest position, combine Shadow with a provider region or sovereign endpoint that meets your residency rules, or a self-hosted model added through `[proxy.routes]` or `extra_hosts`. ## Regulations and frameworks | Framework | How Shadow helps | |---|---| | FedRAMP | Not applicable to Shadow itself, which is software you operate, not a cloud service. It runs inside your authorised boundary and reduces what reaches an authorised (or unauthorised) AI service | | CJIS Security Policy | Reduces criminal justice identifiers in prompts; it does not make an unapproved service acceptable for CJI | | GDPR / UK GDPR Art. 5(1)(c), 25, 32, and Chapter V transfers | Minimisation and pseudonymisation before data reaches a processor, which also reduces what is transferred abroad. Pseudonymised data is still personal data for you (Recital 26) | | EU AI Act | Public-sector uses such as eligibility for essential public benefits are Annex III high-risk, with obligations now due from 2 December 2027. Shadow supports data minimisation only | | NIS2, ISO/IEC 27001 | A local technical control with a documented [security model](https://getshadow.dev/reference/security/), memory-safe implementation and open source for review | :::caution[Not legal advice] This page describes technical capabilities, not an authorisation decision. Your accreditation, security and data protection officers decide what is permitted. ::: ## Example workflows ### Correspondence reply Sent (fictional): ```text Draft a reply to Sam Okoro, 7 Mill Lane, Northshire, sam.okoro@mail.example, NINO AB 12 34 56 C, about the delayed housing benefit decision. ``` Seen by the provider (persona varies): ```text Draft a reply to Sam Okoro, 7 Mill Lane, Northshire, sam.okoro@mail.example, NINO [NATIONAL_ID_0001], about the delayed housing benefit decision. ``` Sam Okoro is the recipient, so his name, address and email pass through as written and each keep is logged; the model writes to the right citizen. National IDs are never kept: the NINO is a token because the configuration above sets `national_id = "token"` (without it, a fake of the same format), and it is restored in the draft before an officer reviews it. If citizens' contact details must not reach the provider at all, set `[policy.act] targets = "surrogate"`: the model then writes to a stand-in and Shadow restores Sam's details in the draft. ### Consultation analysis A policy team pastes a CSV of 5,000 responses and asks for an analysis of themes and counts. With that many names and emails in tabular form, Shadow classifies it as `report`, so respondent names and emails become typed tokens, the free text is masked by the detectors, and the analysis comes back with real names where quotes are attributed. ### Service team debugging A developer on a benefits platform uses Claude Code through Shadow. Internal hostnames under `corp.civic.internal`, home-directory usernames and secrets in configs are replaced before the code reaches the provider. ## Deployment patterns | Pattern | Use | |---|---| | **Endpoint** on managed devices | Policy and casework staff using approved API-backed clients; distribute `shadow.toml` and run `shadow install --apply` with your device management | | **Gateway** on a hardened VM | Shared internal assistants: systemd unit with a dedicated user, gateway on loopback, app on the same host | | **Sidecar** in Kubernetes | Citizen-facing services; `NetworkPolicy` so only the sidecar can reach the model endpoint | | **Restricted network** | Build from source (`cargo build --release -p shadow-cli`) on an approved build host; no runtime downloads | Signed release binaries and a published container image are on the [roadmap](https://getshadow.dev/project/roadmap/); today the install script falls back to building with `cargo`, and the container image is built with `docker build -f packaging/Dockerfile`. There is no central admin console or SIEM export yet. ## Related - [Regulated enterprise](https://getshadow.dev/company-size/regulated-enterprise/), [education](https://getshadow.dev/industries/education/) - [Self-hosted AI gateway with PII masking](https://getshadow.dev/use-cases/self-hosted-ai-gateway-with-pii-masking/), [GDPR-compliant LLM usage](https://getshadow.dev/use-cases/gdpr-compliant-llm-usage/) - [Integrating any endpoint](https://getshadow.dev/integrate/any-endpoint/), [proxy mode](https://getshadow.dev/integrate/proxy-mode/), [benchmarks](https://getshadow.dev/benchmarks/), [compare](https://getshadow.dev/compare/) ## Frequently asked questions ### Is Shadow FedRAMP authorised? FedRAMP authorises cloud services, and Shadow is not one. It is open-source software you run on your own endpoints, servers or clusters, with no hosted component or telemetry. The AI provider you send masked requests to is a cloud service and still needs whatever authorisation your agency requires. ### Does Shadow solve data residency for AI? Partly. Detection, masking and the vault all stay on your infrastructure, so detected identifiers leave only as fakes or tokens, with two logged exceptions by default: the recipient when someone asks the model to write to or act on a person (set [policy.act] targets = "surrogate" to fake it), and the contact details a provider-hosted action needs, such as a hosted MCP tool sending an email (set remote_tool_targets = "surrogate"). The masked text still goes to the provider's region, so residency requirements for the remaining content, and for metadata, still apply to the provider. ### Can Shadow run in an air-gapped or restricted network? Shadow itself needs no internet access: it builds from source, has no licence server and sends nothing except the masked requests you route through it. It is useful in restricted networks that allow egress only to an approved model endpoint, which you can add with a custom gateway route or extra_hosts. ### Can caseworkers use ChatGPT with Shadow? Not the consumer chatgpt.com web app, which is not a built-in intercepted host. Shadow covers API-based tools: internal assistants, SDK apps, agents and approved clients that call Azure OpenAI, OpenAI, Anthropic, Gemini or a self-hosted endpoint. ### Is masked data safe to send for criminal justice information? CJIS Security Policy governs criminal justice information and the systems that handle it. Shadow can reduce identifiers in prompts, but it does not determine whether a workflow is permitted. Treat that as a decision for your CJIS security officer. --- # Secure ChatGPT and AI use in education: protect student data under FERPA with Shadow Source: https://getshadow.dev/industries/education/ > Let staff and edtech apps use LLMs without sending student names, IDs and records to the provider. Shadow masks them locally and restores the answer. Shadow is for schools, districts, universities and edtech companies that want staff and products to use LLMs without sending student identities to the model provider. It runs on staff machines, servers or alongside your application, replaces student and parent names, emails, phone numbers, addresses and known identifiers with realistic fakes, and restores the real values in the answer. It supports your FERPA and data protection obligations; it does not replace them. ## What data leaks into AI tools in education | Workflow | What ends up in the prompt | |---|---| | Writing feedback on essays | Student names, sometimes personal stories in the essay itself | | Drafting emails to parents | Student and parent names, emails, phone numbers | | Summarising IEPs, 504 plans or support plans | Disability information, diagnoses, names, dates of birth, staff names | | Admissions and financial aid review | Applicant names, addresses, national IDs, family income details | | Analysing grade exports | Spreadsheets of names, student IDs and grades | | Building edtech features | Real student records in test fixtures, LMS hostnames, API keys | ## How Shadow protects it | Data | Treatment | |---|---| | Student, parent and staff names | Built-in name detector, `person:` terms for known names, optional NER sidecar for better recall on free text | | Emails, phones, home addresses | Built-in detectors, persona-consistent fakes | | SSNs and national IDs in admissions or aid files | Built-in national ID detector; [Presidio](https://getshadow.dev/concepts/detection/#presidio) for more formats | | Student ID numbers | Not detected by built-ins. Use terms for known IDs, the NER sidecar, or a custom detector in Rust | | School and district names | `organization` and `org:` terms | | Grade exports and rosters | The `report` intent: typed tokens such as `[PERSON_0012]` for names and contact details | | Uploaded documents | DOCX, XLSX and PPTX masked in place; PDF text masked; images blocked if you choose | ```toml organization = ["Riverside Unified School District"] terms = ["org:Lincoln Middle School", "org:Riverside High School", "term:Bridge Program"] internal_domains = ["riverside-usd.example", "sis.riverside.internal"] [model_detector] enabled = true # open-weight NER on loopback: better recall on names in free text [policy.report] person = "token" email = "token" phone = "token" address = "token" default = "surrogate" [files] pdf = "text" images = "block" # handwritten work and scans are images; no OCR yet ``` :::caution[Limits to plan for] Dates of birth are not masked. Student IDs need configuration. Disability, health and behavioural details in IEPs are content, not identifiers, so they reach the model as written. In a small class, context alone can identify a student. ::: ## Regulations and frameworks | Framework | How Shadow helps | |---|---| | FERPA (US) | Reduces personally identifiable information from education records disclosed to an AI vendor. Whether a vendor is a "school official" under your agreement is a separate question | | COPPA (US, under-13s) | Helps an operator minimise children's personal information sent to AI providers; notice and consent duties remain | | State student privacy laws (for example California SOPIPA) | Supports contractual limits on how student data is shared with subprocessors | | GDPR / UK GDPR, including Art. 8 for children | Minimisation and pseudonymisation by design (Art. 5(1)(c), 25, 32). Pseudonymised data remains personal data for you (Recital 26) | | EU AI Act | AI used for admission, evaluation of learning outcomes, or proctoring is Annex III high-risk, with obligations due from 2 December 2027. Shadow supports data minimisation, not conformity | :::caution[Not legal advice] This page explains what the software does. It is not a FERPA or GDPR determination. Check with your privacy office and counsel. ::: ## Example workflows ### Email to a parent Sent (fictional): ```text Write a supportive email to Ana Silva (ana.silva@mail.example, +1 415 555 0147) about her son Leo Silva's missed assignments at Lincoln Middle School. ``` Seen by the provider (persona varies): ```text Write a supportive email to Ana Silva (ana.silva@mail.example, +1 415 555 0147) about her son Ren Tanaka's missed assignments at Hoshino Gakuen. ``` Ana Silva is the recipient, so her name, email and phone pass through as written and each keep is logged; the model writes to the right parent. The student and the school come after "about", so the provider sees fakes, and the draft comes back about Leo Silva at Lincoln Middle School ("Ren" on its own is restored too, because fake first and last names are restored on their own). Set `[policy.act] targets = "surrogate"` if parent contact details must not reach the provider. ### Grade analysis A head of department attaches an XLSX gradebook and asks "Analyse these records: which students are at risk?". The wording reads as a report over records, so student names become typed tokens inside the workbook; grades and formulas are untouched. The model lists `[PERSON_0007]` and `[PERSON_0019]`, which the teacher sees as the real names. ### Edtech feedback feature A writing-feedback product sends essays to Anthropic through a Shadow sidecar. Names and emails in the essay are masked, the feedback streams back, and any fake split across stream events is reassembled and restored before it reaches the student's screen. ## Deployment patterns | Pattern | Use | |---|---| | **Endpoint** on staff laptops (`shadow install --apply`) | Teachers and administrators using approved API-backed assistants on macOS or Windows | | **Gateway** on a district or campus server | A shared internal assistant or LMS integration; keep Shadow on loopback next to the app | | **Sidecar** in Kubernetes | Edtech products with AI features, with egress limited to the sidecar | Shadow does not run on phones, tablets or Chromebooks, and it does not cover consumer web chat sites. The practical model for schools is an approved, API-backed assistant routed through Shadow. Distribute `shadow.toml` with your device management; there is no central console yet (see the [roadmap](https://getshadow.dev/project/roadmap/)). ## Related - [Nonprofit](https://getshadow.dev/industries/nonprofit/) and [government and public sector](https://getshadow.dev/industries/government-public-sector/) - [Stop employees pasting personal data into ChatGPT](https://getshadow.dev/use-cases/stop-pii-leaks-to-chatgpt/), [shadow AI governance](https://getshadow.dev/use-cases/shadow-ai-governance/) - [Gateway mode](https://getshadow.dev/integrate/gateway-mode/), [intent-aware policy](https://getshadow.dev/concepts/intent-policy/), [benchmarks](https://getshadow.dev/benchmarks/), [compare](https://getshadow.dev/compare/) ## Frequently asked questions ### Is it FERPA compliant for teachers to use ChatGPT with student work? FERPA limits disclosure of personally identifiable information from education records. Shadow reduces what reaches an AI provider by replacing student names, emails, IDs and contact details with fakes, but it does not decide whether a disclosure is permitted. Your institution's FERPA policy and vendor agreements still govern. ### Does Shadow detect student ID numbers? Not with its built-in detectors, because student ID formats vary by institution and shadow.toml has no custom regex setting. Add known IDs as terms, enable the open-weight NER sidecar for account-style numbers, or embed Shadow in Rust with a custom detector. ### Can Shadow protect students using AI tools directly? Shadow runs on managed laptops and servers, not phones or Chromebooks, and it covers API-based tools rather than consumer web apps like chatgpt.com. It suits staff devices and the institution's own AI services more than student-owned devices. ### Can an LMS or edtech product use Shadow? Yes. Point the product's model client at Shadow's gateway (for example http://127.0.0.1:8877/gw/openai/v1) or run Shadow as a sidecar. Student data is masked before it leaves and restored in responses, including streamed ones. ### Does Shadow help with COPPA? It can reduce children's personal information sent to an AI provider by an edtech operator. COPPA obligations such as notice, parental consent and retention limits still apply to the operator. --- # Secure generative AI in pharma and life sciences: protect clinical and R&D data with Shadow Source: https://getshadow.dev/industries/pharma-life-sciences/ > Mask investigator, patient and partner names, compound codenames and internal systems before AI requests leave. Self-hosted, reversible, no telemetry. Shadow is for pharmaceutical, biotech, CRO and medical device teams that want to use LLMs on clinical, regulatory and research documents without handing confidential identifiers to the model provider. It runs on your machines or beside your services, replaces patient, investigator and partner names, contact details, compound and programme codenames and internal hostnames with consistent fakes, and restores them in the answer. Clinical facts are not masked, so plan which workflows are suitable. ## What data leaks into AI tools in pharma and life sciences | Workflow | What ends up in the prompt | |---|---| | Drafting clinical study reports and narratives | Subject IDs, ages, dates, adverse events, investigator and site names | | Pharmacovigilance case intake | Reporter and patient names, phone numbers, addresses, free-text narratives | | Medical information replies | Healthcare professional names, emails, institutions | | Competitive and portfolio analysis | Compound codenames, licensing partners, programme names | | Regulatory submissions | Draft module text, internal document IDs, unreleased results | | Lab and data engineering | ELN exports, LIMS hostnames, service account keys | Two kinds of risk mix here: personal data of patients and HCPs, and trade secrets about pipeline and partners. ## How Shadow protects it | Data | Treatment | |---|---| | Patient, reporter, investigator names | Built-in name detector, `person:` terms, optional NER sidecar or Presidio | | Emails, phones, addresses | Built-in detectors, persona-consistent fakes | | Compound codes, programme names, partners | Dictionary `terms` and `org:` terms | | Sites and institutions | `org:` terms for known sites | | Trial subject IDs, document IDs | **Not detected by built-ins.** Terms for known values, the NER sidecar for account-style numbers, or a custom Rust detector | | Internal hostnames, keys | `internal_domains`, `hostname` and `secret` detectors | | Listings and line listings in XLSX | Masked in place; with the `report` intent, people become typed tokens | ```toml organization = ["Helix Therapeutics AG"] terms = [ "HLX-4417", "HLX-5520", # compound codes "Project Aurora", # programme codename "org:Globex Biologics", # licensing partner "org:St Mary's Research Hospital", # trial site ] internal_domains = ["helix.example", "lims.helix.internal"] [model_detector] enabled = true [policy.always] secret = "redact" [files] pdf = "text" images = "block" ``` Add new codenames as programmes start: ```sh shadow terms add "HLX-6031" "Project Borealis" ``` :::caution[What stays visible] Ages, dates, doses, diagnoses and adverse events are not masked. Rare-disease trials with few subjects can be identifying through context alone. Dates are not detected, so a narrative with an event date, a site and a rare indication is not anonymous even after masking. ::: ## Regulations and frameworks | Framework | How Shadow helps | |---|---| | GDPR Art. 9 (health data), 5(1)(c), 25, 32 | Pseudonymises identifiers before data reaches a processor. Key-coded trial data is already pseudonymised; Shadow keeps additional direct identifiers out of AI requests. Still personal data for you (Recital 26) | | HIPAA (US covered entities and business associates) | Supports minimum necessary when PHI enters AI workflows; see [healthcare](https://getshadow.dev/industries/healthcare/) | | ICH E6(R3) Good Clinical Practice | Supports confidentiality of trial subjects when AI tools are used on trial records | | 21 CFR Part 11 and EU GMP Annex 11 | Shadow is not a validated system. If AI output feeds a GxP record, validate the process and configuration, and keep the human review step | | Trade secret protection | Codenames and partners as terms reduce what an AI provider learns about your pipeline | | ISO/IEC 27001, ISO/IEC 42001 | Evidence of a technical control on AI data flows | :::caution[Not legal advice] This page describes technical capabilities. It is not a regulatory or validation opinion. Involve your quality, privacy and legal functions. ::: ## Example workflows ### Medical information reply Sent (fictional): ```text Draft a reply to Dr Amara Okafor (a.okafor@stmarys.example) at St Mary's Research Hospital asking whether HLX-4417 can be co-administered with warfarin. ``` Seen by the provider (persona varies): ```text Draft a reply to Dr Amara Okafor (a.okafor@stmarys.example) at St Mary's Research Hospital asking whether Velora Quilent can be co-administered with warfarin. ``` Dr Okafor, her email and her hospital are the recipient, so they pass through as written and each keep is logged; the model writes to the right physician. The unreleased compound is not, so the provider sees a codename and the reply comes back about HLX-4417. Warfarin stays as written because it is public. Set `[policy.act] targets = "surrogate"` if the enquirer's identity must stay hidden from the provider. ### Partnering memo A business development lead asks for a summary of term-sheet differences between two DOCX drafts. Partner names, the programme codename and signatories are masked inside both documents consistently, so the comparison still lines up. ### PV case triage tool A triage service built on the OpenAI SDK sends case narratives through the Shadow gateway. Reporter names, phones and addresses are faked; tool calls that file the case into the safety database are restored to real values before your code runs them. See [tool calls](https://getshadow.dev/concepts/tool-calls/). ## Deployment patterns | Pattern | Use | |---|---| | **Endpoint** (`shadow install --apply`) | Medical writers, regulatory and BD staff using approved API-backed assistants | | **Gateway** with a custom route | Internal copilots on Azure OpenAI or a private endpoint: `[proxy.routes] azure = "https://.openai.azure.com"` | | **Sidecar** | PV, medical information and data platforms in Kubernetes | | **Rust library** | Data pipelines written in Rust that call models in-process | Keep a shared terms list for codenames under change control and distribute it with `shadow.toml`. There is no central console yet; an optional local audit log is on the [roadmap](https://getshadow.dev/project/roadmap/). ## Related - [Healthcare](https://getshadow.dev/industries/healthcare/), [manufacturing](https://getshadow.dev/industries/manufacturing/) - [Redact documents before uploading to AI](https://getshadow.dev/use-cases/redact-documents-before-uploading-to-ai/), [protect customer data in RAG pipelines](https://getshadow.dev/use-cases/protect-customer-data-in-rag-pipelines/) - [Detection](https://getshadow.dev/concepts/detection/), [file masking](https://getshadow.dev/concepts/file-masking/), [benchmarks](https://getshadow.dev/benchmarks/), [compare](https://getshadow.dev/compare/) ## Frequently asked questions ### Can we use LLMs on clinical trial documents without exposing subject data? Shadow reduces exposure by replacing names, contact details, investigator names, sites and known codenames before text reaches the provider. Trial subject IDs and dates are not detected by default, so add known IDs as terms or a detector that finds them, and review what remains with shadow scan. ### How do we protect compound and programme codenames? List them as dictionary terms in shadow.toml or with shadow terms add. Each codename gets a consistent invented name, so the model can still reason about it, and the real codename is restored in the answer. ### Is Shadow GxP validated? No. Shadow is open-source software without a vendor validation package. If an AI-assisted process falls under GxP, validate the process, including Shadow's configuration, under your computer system validation approach. ### Can Shadow handle pharmacovigilance case narratives? It masks reporter and patient names, contact details, addresses and national IDs in narratives. Ages, dates, drugs and adverse events are content and are not masked, which keeps the narrative useful but means the text is not anonymised. ### Does Shadow mask data in protocol PDFs and Excel listings? Yes for text. XLSX shared strings, DOCX and PPTX text runs are masked in place, and PDF text is extracted and sent masked. Scanned pages and images are not OCR'd. --- # Secure LLM use for SaaS and technology companies: keep customer data out of AI prompts Source: https://getshadow.dev/industries/saas-technology/ > Mask customer emails, IPs, hostnames and API keys in support, engineering and product AI traffic. Self-hosted proxy, gateway or sidecar; answers restored. Shadow is for SaaS and technology companies whose support, engineering and product teams use LLMs on text that contains customer data. It runs on laptops, servers or as a sidecar, replaces customer names, emails, IPs, internal hostnames, usernames and secrets with realistic, format-preserving fakes, and restores them in the answer, including streamed responses and tool calls. The mapping stays on your infrastructure and the provider sees a coherent but fake world. The exceptions are values detection misses and, for an action the provider runs itself (a hosted MCP server or server-side tool), the contact details it needs, which are kept real by default and logged. ## What data leaks into AI tools at SaaS and tech companies | Team | What ends up in the prompt | |---|---| | Support | Ticket threads with customer names, emails, phone numbers, account details | | Engineering | Stack traces and logs with emails, IPs, tenant hostnames; configs with API keys and database URLs | | SRE and security | Incident timelines, internal hostnames, IP ranges, usernames | | Sales and success | Account names, contacts, renewal notes, usage exports | | Product | AI features that send end-user content to a provider on every request | | Data | Query results and CSV exports pasted for analysis | For B2B companies this is also your customers' customers' data, covered by DPAs that list your AI provider as a subprocessor. ## How Shadow protects it | Data | Treatment | |---|---| | Customer and end-user emails, names, phones | Built-in detectors with persona-consistent fakes | | Customer company names | `org:` terms for key accounts; NER sidecar or Presidio for unknown ones | | IPs, MACs, internal hostnames | `ip`, `mac` and `hostname` detectors; `internal_domains` covers subdomains, e.g. tenant hosts | | Usernames in paths, OS user, git identity | `path` and `local_facts` detectors | | API keys, tokens, private keys, passwords | `secret` detector; faked by default, redacted with `[policy.always] secret = "redact"` | | Product and codenames | Dictionary `terms` | | CSV exports and usage reports | The `report` intent: typed tokens for people and contact details | | Request metadata | OpenAI `user`, Anthropic `metadata.user_id`, org and OS headers scrubbed | ```toml organization = ["Acme Cloud Inc."] terms = ["Project Falcon", "org:Globex", "org:Initech"] internal_domains = ["acme.example", "acme.internal", "tenants.acme.example"] [policy.always] secret = "redact" [policy.act] remote_tool_targets = "keep" # default; set "surrogate" if a failed hosted action is preferable [files] images = "block" # screenshots in tickets are not OCR'd ``` :::note[Which coding assistants are covered] Shadow intercepts provider API hosts such as `api.openai.com` and `api.anthropic.com`. Claude Code, Codex CLI, Aider, Continue and Cline with your own key are covered through `HTTPS_PROXY` or a base URL. GitHub Copilot and Cursor's hosted models talk to their vendors' own backends, which are not on the built-in list. You can add hosts with `extra_hosts`, but those integrations are untested. See [protecting secrets from AI coding assistants](https://getshadow.dev/use-cases/protect-secrets-from-ai-coding-assistants/). ::: ## Regulations and frameworks | Framework | How Shadow helps | |---|---| | SOC 2 (confidentiality, privacy) | A documented, testable control for data sent to AI subprocessors. Evidence: config, per-request logs (intent and counts, never values), `shadow scan` results | | GDPR Art. 28 processors, 5(1)(c), 25, 32 | Data protection by design for your processing and for subprocessor flows; pseudonymisation is still personal data for you (Recital 26) | | Customer DPAs and security questionnaires | A clear answer to "what reaches your AI provider": self-hosted, open source, no telemetry | | ISO/IEC 27001 and ISO/IEC 42001 | A technical control in your ISMS or AI management system | | EU AI Act | Most SaaS AI features are not high-risk; if yours are (for example hiring or credit), see [HR and recruiting](https://getshadow.dev/industries/hr-recruiting/) | :::caution[Not legal advice] This page explains what Shadow does. It is not an audit opinion or legal advice. Shadow does not make you SOC 2, ISO or GDPR compliant. ::: ## Example workflows ### Debugging with a coding assistant Sent from Claude Code (fictional): ```text ERROR auth: login failed for jane.doe@acme.example from 203.0.113.42 at /Users/jdoe/src/acme-api/auth.rs:118 db host=db01.acme.internal password=Pa55w0rd!2026 ``` Seen by the provider (persona varies, `secret = "redact"` on): ```text ERROR auth: login failed for hui.zhao@zhilan.cn from 198.51.100.17 at /Users/huizhao/src/acme-api/auth.rs:118 db host=zhilan-db-03.zhilan.cn password=[REDACTED_SECRET] ``` The fix comes back referring to your real hostname and path. ### Support macro drafting An agent pastes a ticket thread and asks for a reply to the customer. The customer named as the recipient ("Reply to Jane Doe at Globex") passes through as written and is logged, so the model greets the right person; colleagues, other customers, hostnames and keys in the thread become fakes and are restored in the reply before the agent sends it. ### Product AI feature A "summarise this conversation" feature calls Anthropic through a Shadow sidecar. End-user names and emails are masked in each request, streamed responses are restored on the fly, and a fake split across two SSE events is reassembled before it is translated back. ## Deployment patterns | Pattern | Use | |---|---| | **Endpoint** (`shadow install --apply`) | Engineering and support laptops; covers CLIs, scripts and IDE extensions that honour `HTTPS_PROXY` | | **Gateway** | Internal tools: `OPENAI_BASE_URL=http://127.0.0.1:8877/gw/openai/v1`, `ANTHROPIC_BASE_URL=http://127.0.0.1:8877/gw/anthropic` | | **Sidecar** | Product AI features in Kubernetes; `NetworkPolicy` so only the sidecar reaches provider hosts. See [Docker and Kubernetes](https://getshadow.dev/integrate/docker-kubernetes/) | | **CI runners** | `HTTPS_PROXY` on runners that call models for code review or test generation | For several replicas presenting one identity, share the persona and vault through a `ReadWriteMany` volume or provision `persona.json`, `vault.key` and `vault.bin` from a secret. ## Related - [Startups](https://getshadow.dev/company-size/startups/), [mid-market](https://getshadow.dev/company-size/mid-market/) - [Privacy for AI in customer support](https://getshadow.dev/use-cases/privacy-for-ai-in-customer-support/), [mask PII in LLM agents and tool calls](https://getshadow.dev/use-cases/mask-pii-in-llm-agents-and-tool-calls/) - [Proxy mode](https://getshadow.dev/integrate/proxy-mode/), [gateway mode](https://getshadow.dev/integrate/gateway-mode/), [benchmarks](https://getshadow.dev/benchmarks/), [compare](https://getshadow.dev/compare/) ## Frequently asked questions ### How do we stop engineers sending customer data to AI coding assistants? Route API-based assistants such as Claude Code, Codex CLI, Aider, Continue or Cline through Shadow's proxy or gateway. Customer emails, IPs, internal hostnames, usernames in paths and secrets are replaced before code and logs leave the laptop. Assistants that use their vendor's own backend, such as GitHub Copilot, are not built-in hosts. ### Can we add an AI feature to our product without sending customer PII to OpenAI? Yes. Point the feature's model client at a Shadow sidecar or gateway. Customer values are masked in the request, restored in the response, including streams and tool calls, and the mapping stays in your infrastructure. ### Does Shadow help with SOC 2? It can be a documented control for data sent to AI subprocessors and supports confidentiality and privacy criteria. SOC 2 is an attestation of your controls by an auditor; Shadow does not provide a report or certification. ### What should we tell customers whose data might reach an AI provider? Describe the control accurately: identifiers such as names, emails, IPs and secrets are replaced with fakes on your infrastructure before requests reach the provider, and detection is best effort. Keep the AI provider on your subprocessor list, because content still reaches it. ### Will masking break code or logs the model needs to understand? Usually not, because fakes keep the format: an IP stays an IP, an email stays an email and a fake API key has the same shape. The code intent keeps identifiers readable, and the answer is restored with real values. --- # Secure AI for e-commerce and retail: protect customer and payment data with Shadow Source: https://getshadow.dev/industries/ecommerce-retail/ > Use LLMs for customer service, order operations and analytics without sending shopper names, addresses or card numbers to the AI provider. Shadow is for online retailers, marketplaces, D2C brands and store operators that use LLMs for customer service, order operations, marketing and analytics. It runs on your machines or next to your services, replaces shopper names, emails, phone numbers, delivery addresses and card numbers with realistic, format-preserving fakes, and restores them in the answer. Your bots and agents keep working on real orders while the AI provider sees fake customers. ## What data leaks into AI tools in e-commerce and retail | Workflow | What ends up in the prompt | |---|---| | Support bots and agent assist | Customer name, email, phone, delivery address, order history, sometimes a card number typed into chat | | Returns and fraud review | Billing and shipping addresses, IPs, card BINs and last-4, account emails | | Review and survey analysis | Reviewer names and emails, free-text complaints | | Marketing segmentation | CRM exports with contact details and purchase history | | Store and supplier operations | Supplier contacts, contract terms, internal system names | | Building commerce integrations | Sample orders with real data, internal hostnames, payment API keys | ## How Shadow protects it | Data | Treatment | |---|---| | Card numbers | Luhn-checked detection, Luhn-valid fakes; or tokens with `[policy.always] credit_card = "token"` | | Names, emails, phones | Built-in detectors with persona-consistent fakes | | Delivery and billing addresses | Street-address detector (English and German styles); NER sidecar or Presidio for other formats | | IPs | `ip` detector | | Payment and platform API keys | `secret` detector, including Stripe live keys; redact with `secret = "redact"` | | Brands, suppliers, campaign codenames | `organization` and dictionary `terms` | | CRM and order exports | The `report` intent turns people and contact details into typed tokens | | Order numbers | Not detected by default; terms or a custom Rust detector if needed | ```toml organization = ["Northwind Outfitters Ltd"] terms = ["org:Globex Logistics", "Spring Drop 2027"] internal_domains = ["northwind.example", "oms.northwind.internal"] [policy.report] person = "token" email = "token" phone = "token" address = "token" default = "surrogate" [policy.always] secret = "redact" credit_card = "token" [files] images = "block" # photos of receipts or damaged goods are not OCR'd ``` :::note[Coverage of street addresses] The built-in address detector recognises English-style (`221 Baker Street`) and German-style (`Hauptstraße 5`) street addresses. For other formats, or apartment and postcode details, enable the open-weight NER sidecar or Presidio, and check results with `shadow scan`. ::: ## Regulations and frameworks | Framework | How Shadow helps | |---|---| | PCI DSS v4.0.1 | Keeps PAN out of what reaches the AI provider, supporting scope reduction. The Shadow host sees real PAN and is in scope; ideally keep PAN out of chat channels altogether | | GDPR Art. 5(1)(c), 25, 32 | Minimises and pseudonymises customer data before it reaches a processor. Still personal data for you (Recital 26) | | CCPA / CPRA and US state privacy laws | Reduces personal information shared with AI service providers | | ePrivacy and marketing rules | Not addressed by Shadow; consent for marketing use of data is a separate question | | SOC 2, ISO/IEC 27001 | Evidence of a technical control for AI data flows | :::caution[Not legal advice] This page describes technical behaviour. It does not determine your PCI DSS scope or privacy compliance. Ask your QSA and counsel. ::: ## Example workflows ### Delivery complaint Sent (fictional): ```text Draft an apology email to Jane Doe (jane.doe@acme.example, +1 415 555 0132) with next steps. Her order NW-77812 to 221 Baker Street never arrived. She pasted her card number 4111 1111 1111 1111 into the chat. ``` Seen by the provider (persona varies): ```text Draft an apology email to Jane Doe (jane.doe@acme.example, +1 415 555 0132) with next steps. Her order NW-77812 to 48 Huaihai Road never arrived. She pasted her card number [CREDIT_CARD_0001] into the chat. ``` Jane Doe is the recipient, so her name, email and phone pass through as written and each keep is logged; the model writes to the right customer. Her delivery address is not part of the recipient phrase, so it is masked and restored in the draft, ready to send. The card number was never needed for the apology, so the provider only ever saw a token. The order number passes through unless you configure it. ### Refund agent with a local tool A support agent built on the OpenAI SDK offers `issue_refund(email, amount)`. When the instruction names the customer ("Refund jane.doe@acme.example for order NW-77812"), the email is the recipient and passes through, logged, so the model calls the tool with it. When the email only appears in the ticket history or a lookup result, the model sees a fake and calls the tool with it; Shadow restores `jane.doe@acme.example` in the arguments before your code runs the refund. See [tool calls](https://getshadow.dev/concepts/tool-calls/). ### Review mining A merchandiser pastes 3,000 reviews as CSV and asks for the top complaints by product. With that many names and emails in tabular form, Shadow treats the request as a report, so reviewer names and emails become tokens, product names stay readable, and quoted reviews are restored with real names on the merchandiser's screen. ## Deployment patterns | Pattern | Use | |---|---| | **Sidecar** next to the support bot or agent-assist service | Customer-facing AI with egress limited to the sidecar | | **Gateway** on an app server | Order operations and marketing tools calling OpenAI, Anthropic or Gemini via a base URL | | **Endpoint** (`shadow install --apply`) | Analysts and engineers using API-backed assistants on laptops | Voice bots built on WebSocket or Realtime APIs are not processed by Shadow today. Hosted support platforms that call models from their own cloud are outside Shadow's reach; Shadow protects calls your own systems make. ## Related - [Financial services](https://getshadow.dev/industries/financial-services/), [small business](https://getshadow.dev/company-size/small-business/) - [Privacy for AI in customer support](https://getshadow.dev/use-cases/privacy-for-ai-in-customer-support/), [mask PII in LLM agents and tool calls](https://getshadow.dev/use-cases/mask-pii-in-llm-agents-and-tool-calls/) - [Intent-aware policy](https://getshadow.dev/concepts/intent-policy/), [benchmarks](https://getshadow.dev/benchmarks/), [compare](https://getshadow.dev/compare/) ## Frequently asked questions ### Can we use ChatGPT for customer service without exposing customer data? If your support tool or bot calls a model API, route it through Shadow. Customer names, emails, phones, delivery addresses and card numbers are replaced with fakes before the request leaves, and restored in the reply. The consumer chatgpt.com app is not covered. ### Does Shadow keep card numbers out of AI prompts for PCI DSS? Card numbers are detected with a Luhn check and replaced with a fake, or a typed token if you configure it, so the PAN does not reach the provider. The machine running Shadow still handles the real PAN and stays in your PCI scope. ### Do order numbers get masked? Not by default. Order numbers are usually not personal on their own and the built-in detectors do not look for them. If yours are sensitive, add known values as terms or use a custom detector in Rust. ### Can an AI agent still refund or email the right customer? Yes. The customer the instruction names is the recipient and passes through as written, logged, so the model calls the tool with their real email. Any other masked value in the tool call is restored in the arguments before your code executes it. If the tool is hosted by the provider (a hosted MCP server or server-side tool), Shadow cannot restore inside the provider, so by default the customer contact details the action needs are kept real and each keep is logged. ### How do I analyse thousands of customers with an LLM safely? Ask for a report and attach the export. Shadow classifies the request as a report and replaces names, emails, phones and addresses with typed tokens such as [PERSON_0001], so the model treats them as IDs. The answer comes back with real values. --- # Secure LLM use in telecommunications: keep subscriber data and network details out of AI prompts Source: https://getshadow.dev/industries/telecommunications/ > Mask subscriber names, phone numbers, IPs, MACs and internal hostnames before telecom teams send prompts to OpenAI, Anthropic or Gemini. Self-hosted. Shadow is for telecom operators, MVNOs, ISPs and network service providers whose care, network operations and engineering teams use large language models. It runs on your own laptops, servers or pods, replaces subscriber names, phone numbers, emails, IP and MAC addresses, internal hostnames and secrets with realistic fakes before a request reaches the AI provider, and restores the real values in the answer. It is a technical safeguard on AI data flows, not a CPNI compliance programme. ## What data leaks into AI tools in telecommunications Telecoms hold two kinds of sensitive data that both end up in prompts: subscriber data and network data. | Workflow | What typically ends up in the prompt | |---|---| | Customer care: drafting replies, summarising complaints | Subscriber name, MSISDN, email, billing address, account number, plan details | | Retention and billing analysis | Exported spreadsheets of subscribers, usage and churn flags | | NOC and incident troubleshooting | Router and switch configs, syslog lines, IP ranges, MAC addresses, device hostnames | | Provisioning and OSS/BSS engineering | API payloads with subscriber identifiers, internal service hostnames, API keys | | Fraud and abuse investigation | Call patterns, numbers involved, SIM swap histories | | Field service | Customer addresses, site names, access notes | Network data is a security problem as much as a privacy one: an internal hostname scheme, management IP ranges and device credentials are useful to an attacker. ## How Shadow protects it Shadow intercepts AI API traffic, detects sensitive values in every JSON string and uploaded file, swaps them for fakes from a persistent [persona](https://getshadow.dev/concepts/personas/), keeps the mapping in an [encrypted local vault](https://getshadow.dev/concepts/vault/), and translates responses back, including streams and tool calls. | Data | How Shadow handles it | |---|---| | Subscriber names, emails, addresses | Built-in detectors; the open-weight NER sidecar or Presidio improves recall on free text | | Phone numbers (MSISDNs) | `phone` detector for international (`+44 20 7946 0000`) and US formats; replaced with numbers in the persona's region | | IPv4, IPv6, MAC addresses | `ip` and `mac` detectors, replaced with valid-looking addresses | | Device and service hostnames | `hostname` detector plus `internal_domains`, which covers every subdomain | | API keys, tokens, `password=` assignments | `secret` detector; opt in to irreversible redaction | | Account numbers | Not built in; the NER sidecar maps account-style numbers to `national_id` | | Bulk subscriber exports | The `report` intent turns people, emails, phones and addresses into typed tokens such as `[PHONE_0012]` | | Partner and wholesale carrier names | `org:` terms | A starting configuration for a network operator: ```toml organization = ["Northwind Telecom plc"] terms = ["org:Contoso Wholesale", "term:Project Lighthouse", "hostname:core-rtr-lon-01"] internal_domains = ["northwind.example", "noc.northwind.internal", "oss.northwind.internal"] [model_detector] enabled = true # ml/serve_hf.py: account-style numbers, free-text names url = "http://127.0.0.1:8878/detect" [policy.report] person = "token" email = "token" phone = "token" address = "token" default = "surrogate" [policy.always] secret = "redact" [files] images = "block" # no OCR: network diagrams and screenshots are not masked ``` :::caution[Known gaps for telecom data] - **IMSI, IMEI and ICCID are not detected** by the built-in detectors, and there is no custom regex setting in `shadow.toml`. Add known values as terms, rely on the NER sidecar for account-style numbers, or write a custom Rust detector when embedding Shadow. - **Passwords inside connection URLs** (`ssh://admin:pw@host`) are not caught. The `secret` detector finds known key formats, JWTs, PEM keys, bearer tokens and `password=`-style assignments. - **Topology and usage patterns are content.** Masking identifiers does not hide that a cell site failed at 02:14 or that a number made 400 calls. - **No voice.** WebSocket and Realtime APIs are not processed. ::: Run `shadow scan` on a sanitised sample config and a sample ticket export before rollout to see what is found. ## Regulations and frameworks | Requirement | How Shadow helps | |---|---| | US CPNI rules (47 CFR 64.2001 and following) | Reduces subscriber identifiers in AI requests, so fewer records are tied to a person if a vendor is breached. It does not decide whether a use of CPNI is permitted | | GDPR Art. 5(1)(c), 25 and 32 | Data minimisation and pseudonymisation by design before data leaves your infrastructure. Pseudonymised data is still personal data for you, because you hold the key (Recital 26) | | EU ePrivacy rules on traffic and location data | Masks identifiers attached to traffic data; the traffic data itself still needs a lawful basis to be processed by AI at all | | NIS2 (telecom and digital infrastructure) | Supports supply-chain and data-handling measures for AI vendors: internal hostnames, IPs and secrets stay local | | ISO/IEC 27001, ISO/IEC 42001 | A documented technical control for AI data flows in your ISMS or AI management system | :::caution[Not legal advice] This page describes technical capabilities, not legal conclusions. Using Shadow does not make an operator compliant with CPNI, GDPR, ePrivacy or NIS2 obligations. Work with your privacy, security and regulatory teams. ::: ## Example workflows ### Drafting a care reply What the agent sends (all data fictional): ```text Draft an apology email to Priya Shah (+44 7700 900123, priya.shah@mail.example) about the outage on her fibre line last weekend. Mention that engineer Tom Reed restored service at 14 Kingsway, Manchester. ``` What the provider sees with this machine's persona (your fakes will differ): ```text Draft an apology email to Priya Shah (+44 7700 900123, priya.shah@mail.example) about the outage on her fibre line last weekend. Mention that engineer Wei Chen restored service at 88 Tianfu Road, Manchester. ``` Priya Shah is the recipient, so her name, number and email pass through as written and each keep is logged; the model writes to the right subscriber. The engineer and the address are not the recipient, so they are masked and restored in the draft. Set `[policy.act] targets = "surrogate"` if subscriber details must not reach the provider. ### NOC troubleshooting with a config An engineer pastes a BGP neighbour config and log lines into a terminal assistant: ```text router bgp 64512 neighbor 10.20.30.1 remote-as 64513 neighbor 10.20.30.1 description core-rtr-lon-01.noc.northwind.internal snmp-server community api_key=Nw-Prod-7781 RO ``` The provider sees different private IPs, a fake hostname under the persona's domain, and `[REDACTED_SECRET]` for the assignment. The AS numbers and the BGP logic are unchanged, which is what the model needs. The explanation comes back referring to your real neighbour and hostname. ### Churn analysis over an export An analyst pastes a CSV of 1,200 subscribers and asks for churn drivers by plan. Shadow classifies this as `report`, so subscriber names, numbers and emails become `[PERSON_0001]`, `[PHONE_0001]` and so on. Plan names and usage figures are left alone so the analysis still works. The answer is restored locally. ## Deployment patterns | Pattern | When to use it | |---|---| | **Endpoint install** for NOC engineers and analysts: `shadow install --apply` | API-backed chat clients, terminal assistants and scripts that honour `HTTPS_PROXY` | | **Gateway** for internal care and OSS tools: `http://127.0.0.1:8877/gw/openai/v1`, or a custom `azure` route | Agent-assist, ticket summarisation and knowledge tools you build | | **Sidecar** in Kubernetes | Customer-facing chatbots and BSS integrations; egress to providers only from the sidecar. See [Docker and Kubernetes](https://getshadow.dev/integrate/docker-kubernetes/) | Shadow has no central console or fleet management today. Roll it out with your existing configuration management, and use `shadow status --quiet` as a health check. Each request logs its intent and counts per treatment, never values; a local append-only audit log is on the [roadmap](https://getshadow.dev/project/roadmap/). ## Related - [Privacy for AI in customer support](https://getshadow.dev/use-cases/privacy-for-ai-in-customer-support/) and [protect secrets from AI coding assistants](https://getshadow.dev/use-cases/protect-secrets-from-ai-coding-assistants/) - [Energy and utilities](https://getshadow.dev/industries/energy-utilities/) and [SaaS and technology](https://getshadow.dev/industries/saas-technology/) - [Detection](https://getshadow.dev/concepts/detection/), [intent-aware policy](https://getshadow.dev/concepts/intent-policy/), [security model](https://getshadow.dev/reference/security/) - Detection quality: [benchmarks](https://getshadow.dev/benchmarks/). Other approaches: [compare](https://getshadow.dev/compare/) ## Frequently asked questions ### Can telecom staff use LLMs on subscriber records without exposing CPNI? Shadow reduces what reaches the provider: subscriber names, phone numbers, emails, addresses and account-style identifiers it detects are replaced with fakes before the request leaves. Call detail records and usage patterns are content, not identifiers, so they still reach the model. Decide which CPNI workflows are acceptable at all, and use Shadow as a safeguard on the ones you allow. ### Does Shadow detect IMSI, IMEI or ICCID numbers? Not with the built-in detectors. There is no custom regex setting in shadow.toml today. Use the open-weight NER sidecar or Presidio for account-style numbers, add known values as terms, or write a custom detector in Rust if you embed Shadow. ### Is it safe to paste router and firewall configs into an AI assistant through Shadow? Shadow replaces IP addresses, MAC addresses, internal hostnames and recognised secrets with format-preserving fakes, so the config still makes sense to the model. Topology and vendor details are not masked, and passwords embedded in connection URLs are not caught, so review configs before sending and turn on secret redaction. ### Will masking phone numbers break troubleshooting answers? Usually not. Fakes keep the format of the original, so a phone number stays a valid-looking number from the persona's region and an IP stays an IP. If the model's answer depends on the real number plan or country, keep that detail out of the prompt or pin the persona region. ### Does Shadow work for voice AI in the contact centre? Not today. WebSocket and Realtime APIs, which most voice products use, are passed through unprocessed. Shadow covers text APIs, including streaming responses and tool calls. --- # Secure LLM use in manufacturing: protect trade secrets and supplier data in AI prompts Source: https://getshadow.dev/industries/manufacturing/ > Mask programme codenames, part names, supplier and customer names before engineers send prompts to OpenAI, Anthropic or Gemini. Self-hosted, open source. Shadow is for manufacturers whose engineering, quality, procurement and operations teams use large language models on documents that carry intellectual property. It runs on laptops, servers or pods, replaces programme codenames, part and product names, supplier and customer names, people, plant hostnames and secrets with consistent fakes before a request reaches the AI provider, and restores them in the answer. It hides who and what a document is about; it does not hide the engineering in it. ## What data leaks into AI tools in manufacturing | Workflow | What typically ends up in the prompt | |---|---| | Writing or translating work instructions and SOPs | Product and programme names, plant names, process steps, operator names | | Quality: 8D reports, CAPA, non-conformance summaries | Customer names, part numbers, supplier names, defect details, inspector names | | Procurement and supplier negotiation | Supplier names, contract prices, volumes, contact details | | Engineering change and design reviews | Programme codenames, requirement text, test results | | Maintenance and OT troubleshooting | PLC and MES hostnames, IP addresses, alarm logs, service account credentials | | Sales and RFQ responses | Customer names, target prices, delivery schedules | The damage from a leak here is less about privacy law and more about competitive position, customer contracts and NDAs, which often forbid sharing a customer's name or programme with third parties. ## How Shadow protects it Shadow sits between the tool and the provider. Detected values are swapped for fakes from a persistent [persona](https://getshadow.dev/concepts/personas/) and recorded in an [encrypted local vault](https://getshadow.dev/concepts/vault/), so the same codename always maps to the same fake and answers come back with your real names. | Data | How Shadow handles it | |---|---| | Programme and product codenames, part names | Dictionary `terms` (plain or `term:` prefix), consistent fakes | | Suppliers, customers, partners | `org:` terms; organisation names with a legal suffix are recognised as organisations automatically | | Engineers, operators, supplier contacts | Name detector, `person:` terms, the NER sidecar or Presidio for free text | | Emails, phones, addresses | Built-in detectors | | Plant hostnames and OT IP addresses | `hostname`, `ip`, `mac` detectors and `internal_domains` | | Credentials in configs and scripts | `secret` detector; redaction is opt-in | | Supplier price lists and quality exports | XLSX masked in place (shared strings); numbers and formulas untouched | | SOPs and 8D reports | DOCX and PPTX masked in place, so they remain valid documents | ```toml organization = ["Kestrel Precision Components GmbH"] terms = [ "Project Falcon", "Project Osprey", # programme codenames "term:KX-400 Actuator", # product names "org:Globex Automotive", "org:Initech Castings", # customers and suppliers "hostname:mes-plant2-01", ] internal_domains = ["kestrel.example", "plant2.kestrel.internal"] [policy.always] secret = "redact" [files] pdf = "text" images = "block" # drawings, photos and screenshots are not OCR'd ``` Add terms at runtime as new programmes start: ```sh shadow terms add "Project Kestrel" "org:Umbrella Tooling" shadow terms list ``` :::caution[What masking does not hide] - **Technical content.** Tolerances, materials, process parameters, test results and design rationale reach the model as written. If that content is the trade secret, keep it out of external AI. - **Patterned part numbers** such as `KX-400-0193-B` are not detected automatically; there is no custom regex setting. List the important ones as terms, or write a custom detector in Rust when embedding Shadow. - **CAD, drawings and images** are not masked. No OCR. ::: ## Regulations and frameworks | Requirement | How Shadow helps | |---|---| | Trade secret law (for example the EU Trade Secrets Directive, the US DTSA) | Supports "reasonable steps" to keep information secret by limiting identifying details sent to AI vendors. It is one control among many | | Customer NDAs and quality agreements | Keeps customer and programme names out of third-party AI services where NDAs forbid disclosure | | ITAR and EAR | **Not an export-control solution.** Masking names leaves technical data intact, so controlled technical data should not go to external AI at all | | GDPR Art. 5(1)(c), 25 and 32 | Minimises and pseudonymises employee and supplier-contact data before it leaves | | NIS2 (manufacturing of critical products) and IEC 62443 programmes | Keeps OT hostnames, IP ranges and credentials local | | ISO/IEC 27001, ISO/IEC 42001, TISAX | Evidence of a technical control on AI data flows | :::caution[Not legal advice] This page describes technical capabilities, not legal conclusions. Shadow does not make AI use export-compliant or guarantee trade secret protection. Work with your legal, export-control and security teams. ::: ## Example workflows ### Summarising an 8D report What the quality engineer sends (all data fictional): ```text Summarise this 8D for Globex Automotive: KX-400 Actuator housings from Initech Castings failed porosity checks at Plant 2. Containment led by Jonas Weber (jonas.weber@kestrel.example). ``` What the provider sees (your fakes will differ): ```text Summarise this 8D for Zhilan Motors: Velora Quilent housings from Lumeth Casting Co., Ltd. failed porosity checks at Plant 2. Containment led by Ying Chen (ying.chen@xinghe.cn). ``` The summary comes back about Globex Automotive, the KX-400 Actuator and Jonas Weber. The porosity issue is still visible to the provider, because it is the substance of the question. ### Supplier comparison in a spreadsheet A buyer uploads an XLSX with quotes from eight suppliers and asks for a ranked shortlist. Supplier names in the shared strings are replaced; prices and lead times, which are numbers, are untouched. The ranked list comes back with real supplier names. ### PLC script troubleshooting A controls engineer pastes a Python script that polls `mes-plant2-01.plant2.kestrel.internal` at `10.40.2.15` with `password="Plant2!"`. The hostname and IP become fakes, the password assignment becomes `[REDACTED_SECRET]`, and the fix comes back with your real host and IP restored. ## Deployment patterns | Pattern | When to use it | |---|---| | **Endpoint install** for engineers: `shadow install --apply` | API-backed chat clients, coding assistants and scripts on engineering laptops | | **Gateway** for internal tools: `http://127.0.0.1:8877/gw//...` or a custom route to your Azure OpenAI resource | Quality, SOP and procurement assistants you build | | **Sidecar** in Kubernetes | Shared AI services; restrict provider egress to the sidecar. See [Docker and Kubernetes](https://getshadow.dev/integrate/docker-kubernetes/) | | **Server install on a restricted network** | Build from source (`cargo install`) and use `upstream_proxy` to send AI traffic through the approved egress | There is no central admin console yet: distribute `shadow.toml` with your term list through existing configuration management. Shared personas and vaults for groups of machines are on the [roadmap](https://getshadow.dev/project/roadmap/); today you copy `persona.json`, `vault.bin` and `vault.key`. ## Related - [Redact documents before uploading to AI](https://getshadow.dev/use-cases/redact-documents-before-uploading-to-ai/) and [LLM data loss prevention](https://getshadow.dev/use-cases/llm-data-loss-prevention-dlp/) - [Automotive](https://getshadow.dev/industries/automotive/) and [energy and utilities](https://getshadow.dev/industries/energy-utilities/) - [Detection and dictionary terms](https://getshadow.dev/concepts/detection/), [file masking](https://getshadow.dev/concepts/file-masking/), [security model](https://getshadow.dev/reference/security/) - Detection quality: [benchmarks](https://getshadow.dev/benchmarks/). Other approaches: [compare](https://getshadow.dev/compare/) ## Frequently asked questions ### Can Shadow stop trade secrets reaching ChatGPT or the OpenAI API? It replaces the names that make content attributable: programme codenames, part names, suppliers, customers, plants, people and internal hostnames, using terms you define. It does not hide technical content such as tolerances, process parameters or chemistry. If the technical content itself is the secret, it should not go to an external model. ### Is Shadow an ITAR or EAR export-control solution? No. Masking names does not remove technical data, so export-controlled technical data should not be sent to external AI services at all. Shadow can protect identifiers in the AI use you do allow, alongside your export-control process. ### Does Shadow mask CAD files, drawings or PLM data? No. Shadow masks text: prompts, CSV, JSON, DOCX, XLSX, PPTX and PDF text. CAD formats and images are not masked; block images with images = "block" if drawings or screenshots may be uploaded. ### How do we protect our part numbers and project names? Add them as dictionary terms in shadow.toml or with shadow terms add. Each term gets a consistent fake, so the model can still reason about Project Falcon versus Project Kestrel without seeing either name. Patterned part numbers are not detected automatically, because there is no custom regex setting. ### Does it work on air-gapped or restricted plant networks? Shadow itself has no telemetry and needs no account, so it runs anywhere you can install a binary, and it can be built from source. It only helps when the AI provider is reachable; for fully air-gapped sites you would point a custom route at a self-hosted model. --- # Secure LLM use in energy and utilities: keep OT details and customer data out of AI prompts Source: https://getshadow.dev/industries/energy-utilities/ > Mask OT hostnames, IPs, credentials and customer billing data before utility teams use OpenAI, Anthropic or Gemini. Self-hosted, no telemetry, open source. Shadow is for electricity, gas, water and renewable energy companies whose engineering, operations, customer service and IT teams use large language models. It runs on your own machines, replaces OT hostnames, IP and MAC addresses, credentials, site codenames and customer identifiers with fakes before a request reaches the AI provider, and restores them in the answer. For critical infrastructure, the point is simple: the provider should not learn your network layout, your credentials or your customers. ## What data leaks into AI tools in energy and utilities | Workflow | What typically ends up in the prompt | |---|---| | OT and SCADA troubleshooting | RTU and PLC configs, historian queries, alarm logs, device hostnames, IP ranges, service accounts | | IT and security operations | Firewall rules, VPN configs, incident timelines, usernames | | Engineering studies and reports | Substation and plant names, project codenames, contractor names | | Customer service and billing | Customer names, service addresses, phone numbers, IBANs, payment arrangements, vulnerability flags | | Regulatory filings and correspondence | Site names, incident details, staff names | | Field work orders | Addresses, access codes, site contacts | Several of these sit inside regulated categories, such as BES Cyber System Information in North America, and almost all are useful reconnaissance for an attacker. ## How Shadow protects it Shadow decrypts only known AI hosts and tunnels every other connection untouched, so it never inspects SCADA, historian or business traffic. For AI requests, it masks values with fakes from a persistent [persona](https://getshadow.dev/concepts/personas/) and keeps the mapping in an [encrypted local vault](https://getshadow.dev/concepts/vault/). | Data | How Shadow handles it | |---|---| | OT and IT hostnames | `hostname` detector; `internal_domains` covers every subdomain; `.internal`, `.corp`, `.lan`, `.local` always count as internal | | IPv4, IPv6, MAC addresses | `ip` and `mac` detectors, replaced with valid-looking addresses | | API keys, tokens, PEM keys, `password=` assignments | `secret` detector; redact with `[policy.always]` | | Substation, plant and project names | Dictionary `terms` | | Contractors and partners | `org:` terms | | Customer names, emails, phones, addresses | Built-in detectors; `report` intent tokens for bulk exports | | IBANs and card numbers | Checksum-validated detectors with checksum-valid fakes | ```toml organization = ["Riverside Energy Ltd"] terms = [ "term:Hollow Creek Substation", "term:Project Beacon", "org:Contoso Grid Services", "hostname:hmi-hc-01", "hostname:hist-core-02", ] internal_domains = ["riverside.example", "ot.riverside.internal"] [policy.always] secret = "redact" # never send credentials in any form [proxy] upstream_proxy = "http://egress.riverside.internal:3128" [files] images = "block" # no OCR: one-line diagrams and HMI screenshots are not masked ``` :::caution[Known gaps for OT and customer data] - **Passwords inside connection URLs** (`mqtt://ops:pw@host`) are not caught. Use `password=`-style assignments or remove them before sending. - **Device models, protocols, setpoints and logic are content.** They reach the model as written. - **Meter, account and asset IDs** are not detected by the built-in detectors, and there is no custom regex setting. List known identifiers as terms or use the NER sidecar. - **No OCR, no WebSocket or Realtime APIs.** ::: ## Regulations and frameworks | Requirement | How Shadow helps | |---|---| | NERC CIP-011 (information protection for BCSI) | Masks hostnames, IPs and credentials in AI requests and runs inside your environment. It does not identify BCSI for you or replace your information protection programme | | NIS2 (energy, drinking water, wastewater) | Supports supply-chain security and data-handling measures for AI services: infrastructure details stay local | | TSA security directives, national critical infrastructure guidance | Keeps network details out of third-party services; no telemetry or outbound connections of its own | | GDPR Art. 5(1)(c), 25 and 32 | Minimises and pseudonymises customer data before it reaches the provider. Pseudonymised data remains personal data for you (Recital 26) | | ISO/IEC 27001, ISO/IEC 27019, IEC 62443 programmes | A documented technical control for AI data flows | :::caution[Not legal advice] This page describes technical capabilities, not legal or regulatory conclusions. Using Shadow does not make an entity compliant with NERC CIP, NIS2 or other requirements. Work with your compliance and security teams, and decide which data classes may reach external AI at all. ::: ## Example workflows ### Explaining an RTU configuration What the engineer sends (all data fictional): ```text Why does polling fail? RTU hc-rtu-03.ot.riverside.internal at 172.16.40.12, master hmi-hc-01, DNP3 outstation 10, token: api_key=rv-ot-88213 ``` What the provider sees (your fakes will differ): ```text Why does polling fail? RTU xinghe-srv-07.xinghe.cn at 172.16.91.204, master xinghe-srv-11, DNP3 outstation 10, token: [REDACTED_SECRET] ``` The protocol details stay, so the model can still diagnose the problem, and the answer comes back with your real hostnames and IP. ### Customer payment-plan letter A customer service agent asks to draft a letter to Sarah Murphy at 21 Mill Lane, phone 555-0142, IBAN `DE89 3704 0044 0532 0130 00`, about her new payment plan. Sarah is the recipient, so her name, address, phone and IBAN pass through as written and each keep is logged; the model writes to the right customer. Anything after "about", such as a colleague's name or a meter account holder, is masked and restored in the letter. Set `[policy.act] targets = "surrogate"` to send a stand-in customer and a mod-97-valid fake IBAN instead, restored in the letter. ### Outage report over a work-order export An analyst pastes a CSV of 900 work orders and asks for a summary. The rows of personal data make it a `report`, so Shadow turns customer names, phones and addresses into typed tokens; site terms become consistent fakes. The summary by cause and region is restored locally. ## Deployment patterns | Pattern | When to use it | |---|---| | **Endpoint install** on IT and engineering workstations: `shadow install --apply` | API-backed assistants, scripts and chat clients. Keep AI tools off OT-network hosts entirely if policy requires | | **Gateway** for internal tools, with `upstream_proxy` | Customer service and engineering assistants you build, using approved egress | | **Sidecar** in Kubernetes | Shared AI services; a `NetworkPolicy` so only the sidecar reaches providers. See [Docker and Kubernetes](https://getshadow.dev/integrate/docker-kubernetes/) | | **Hardened server install** | Systemd unit with `NoNewPrivileges` and `ProtectSystem=strict`, dedicated user, data directory restricted to it | Shadow has no central admin console. Distribute configuration with your existing tooling. Logs show intent and counts per request, never values; an optional local audit log is on the [roadmap](https://getshadow.dev/project/roadmap/). ## Related - [Protect secrets from AI coding assistants](https://getshadow.dev/use-cases/protect-secrets-from-ai-coding-assistants/) and [self-hosted AI gateway with PII masking](https://getshadow.dev/use-cases/self-hosted-ai-gateway-with-pii-masking/) - [Government and public sector](https://getshadow.dev/industries/government-public-sector/) and [telecommunications](https://getshadow.dev/industries/telecommunications/) - [Proxy mode](https://getshadow.dev/integrate/proxy-mode/), [intent-aware policy](https://getshadow.dev/concepts/intent-policy/), [security model](https://getshadow.dev/reference/security/) - Detection quality: [benchmarks](https://getshadow.dev/benchmarks/). Other approaches: [compare](https://getshadow.dev/compare/) ## Frequently asked questions ### Can utility engineers use AI assistants on SCADA or OT configurations? Shadow replaces internal hostnames, IP and MAC addresses and recognised secrets with fakes before a config leaves the machine, so the provider does not learn your addressing scheme or credentials. Device models, protocols and logic remain visible. Whether OT configurations may go to an external AI service at all is a decision for your security and compliance teams. ### Does Shadow help with NERC CIP-011 BES Cyber System Information? It can reduce the BCSI in an AI request by masking hostnames, IPs and credentials, and it runs entirely on infrastructure you control. It is not a CIP-011 information protection programme and does not decide whether information is BCSI. Treat it as one technical control. ### Should secrets be faked or redacted? For operational technology, redact. Set [policy.always] secret = "redact" and recognised keys, tokens and password assignments leave as [REDACTED_SECRET], which cannot be reversed. The default fake has the same shape as a real secret, which is convenient for code but gives a weaker guarantee. ### Can Shadow send AI traffic through our approved egress? Yes. Set [proxy] upstream_proxy to an http://, socks5:// or socks5h:// relay and intercepted AI traffic leaves through it. Shadow sends nothing else: no telemetry, no account, no update checks. ### Does Shadow mask customer billing data? Yes for detected identifiers: names, emails, phone numbers, street addresses in English and German styles, IBANs and card numbers. Meter and account numbers are not detected by the built-in detectors; use the NER sidecar, terms or a custom Rust detector. --- # Secure LLM use in media and entertainment: keep unreleased titles and talent names out of AI prompts Source: https://getshadow.dev/industries/media-entertainment/ > Mask unreleased titles, codenames, talent and deal terms before writers, producers and marketers use OpenAI, Anthropic or Gemini. Scripts masked in place. Shadow is for studios, broadcasters, publishers, music labels, games companies and agencies whose creative, marketing and business teams use large language models on material that has not been released. It runs on your own laptops and servers, replaces working titles, production codenames, talent and character names, deal parties and contact details with consistent fakes before a request reaches the AI provider, and restores them in the answer. It hides what a project is and who is on it; it does not hide the story. ## What data leaks into AI tools in media and entertainment | Workflow | What typically ends up in the prompt | |---|---| | Script coverage, notes and rewrites | Working title, character names, attached talent, full scenes | | Marketing and release planning | Unannounced titles, release windows, campaign codenames, partner brands | | Talent and business affairs | Talent names, agents, fees, contract terms, personal contact details | | Localisation and subtitles | Dialogue from unreleased episodes, episode titles | | Games and music | Unannounced game titles, track lists, featured artists, label deals | | Audience and subscriber analytics | Subscriber names, emails, viewing histories in exports | The risk is a spoiler or an early announcement, a broken NDA, or a leaked deal, often with direct financial consequences. ## How Shadow protects it Shadow detects sensitive values in every outbound AI request and uploaded file, swaps them for fakes from a persistent [persona](https://getshadow.dev/concepts/personas/), keeps the mapping in an [encrypted local vault](https://getshadow.dev/concepts/vault/), and restores the answer, including streamed responses. | Data | How Shadow handles it | |---|---| | Working titles, production and campaign codenames | Dictionary `terms`, consistent fakes | | Talent, characters, crew | `person:` terms for known names; the name detector, NER sidecar or Presidio for free text | | Studios, distributors, brand partners | `org:` terms | | Agents' and talent emails, phones, addresses | Built-in detectors | | Scripts, treatments, decks, budgets | DOCX, PPTX and XLSX masked in place; PDF text extracted and masked | | Subscriber exports | `report` intent with typed tokens such as `[PERSON_0042]` | | Internal hostnames and API keys | `hostname` and `secret` detectors | ```toml organization = ["Northlight Pictures LLC"] terms = [ "Project Nightjar", # production codename "term:The Long Winter", # working title "person:Ava Brennan", "person:Marcus Hale", # attached talent "person:Captain Ilsa Varga", # character "org:Globex Streaming", # distributor ] internal_domains = ["northlight.example"] [files] pdf = "text" images = "block" # stills, storyboards and screenshots are not masked ``` Add names as they are attached to a project: ```sh shadow terms add "person:Jun Park" "term:Episode 104 - Glasshouse" ``` :::caution[What masking does not hide] - **The story, dialogue and plot** are content. A distinctive premise can identify a project even with every name replaced. - **Images, video and audio** are not masked. No OCR. - **Names not in your term list** depend on detection. Free-text names are found by the name detector and, with better recall, the NER sidecar or Presidio, but detection is best effort. Run `shadow scan` on a sample script before relying on it. ::: ## Regulations and frameworks | Requirement | How Shadow helps | |---|---| | Studio and production content security programmes, TPN | A technical control for AI tools: local, no telemetry, minimal interception, encrypted vault. Not an assessment or a substitute for studio requirements | | NDAs and talent agreements | Keeps talent, title and deal-party names out of third-party AI services | | GDPR and US state privacy laws for subscriber data | Data minimisation and pseudonymisation before data leaves; pseudonymised data remains personal data for you (Recital 26) | | ISO/IEC 27001, SOC 2 | Evidence of a control on AI data flows | :::caution[Not legal advice] This page describes technical capabilities, not legal conclusions. Shadow does not satisfy content security, contractual or privacy obligations on its own. Check production contracts and studio policies before sending unreleased material to any AI service. ::: ## Example workflows ### Script notes on a scene What the writer sends (all data fictional): ```text Give notes on this scene from The Long Winter (Project Nightjar). Ava Brennan plays Captain Ilsa Varga; we want it tighter before Globex Streaming sees the cut. ``` What the provider sees (your fakes will differ): ```text Give notes on this scene from Lumeth Draane (Velora Quilent). Ying Chen plays Captain Hui Yang; we want it tighter before Zhilan Media Co., Ltd. sees the cut. ``` The notes come back referring to Captain Ilsa Varga and The Long Winter. The scene text itself is unchanged. ### Marketing deck review A marketer uploads a PPTX for an unannounced campaign. Title, codename, partner brand and talent names in the slide text runs are replaced, the deck stays a valid presentation, and suggestions come back with the real names. Images on the slides are not masked, so with `images = "block"` any embedded screenshot uploaded separately is refused. ### Subscriber churn analysis An analyst attaches a CSV of subscribers and viewing data and asks "Analyse these subscriber records: which titles drive cancellations?". The request reads as a report, so the `report` intent tokenises subscriber names and emails; title names in your term list get consistent fakes. The analysis is restored locally. ## Deployment patterns | Pattern | When to use it | |---|---| | **Endpoint install** for writers, producers and marketers: `shadow install --apply` | API-backed chat clients and desktop apps that honour the system proxy or PAC file | | **Gateway** for internal tools: `http://127.0.0.1:8877/gw/anthropic` and similar | Coverage, localisation and marketing assistants you build | | **Sidecar** in Kubernetes | Product features such as recommendations or support bots. See [Docker and Kubernetes](https://getshadow.dev/integrate/docker-kubernetes/) | Consumer web chat apps such as chatgpt.com are not built-in intercepted hosts, so give teams an approved API-backed client. There is no central admin console yet; distribute `shadow.toml` and term lists through your existing tooling. Shared vaults for teams working on one production are on the [roadmap](https://getshadow.dev/project/roadmap/); today you copy `persona.json`, `vault.bin` and `vault.key`. ## Related - [Redact documents before uploading to AI](https://getshadow.dev/use-cases/redact-documents-before-uploading-to-ai/) and [shadow AI governance](https://getshadow.dev/use-cases/shadow-ai-governance/) - [Consulting and professional services](https://getshadow.dev/industries/consulting-professional-services/) and [legal](https://getshadow.dev/industries/legal/) - [File masking](https://getshadow.dev/concepts/file-masking/), [detection](https://getshadow.dev/concepts/detection/), [security model](https://getshadow.dev/reference/security/) - Detection quality: [benchmarks](https://getshadow.dev/benchmarks/). Other approaches: [compare](https://getshadow.dev/compare/) ## Frequently asked questions ### Can writers and producers use AI on unreleased scripts without leaking the title? Shadow replaces the titles, production codenames, character and talent names you list as terms, plus detected people, emails and phone numbers, before the request leaves. The story itself is content and still reaches the model. Whether a script may be sent to an external AI service at all is a decision for the production and its contracts. ### Does Shadow mask screeners, stills or video? No. Shadow masks text and text inside DOCX, XLSX, PPTX and PDF uploads. Images and video are not masked and there is no OCR; set images = "block" so stills and screenshots cannot be uploaded through Shadow. ### Does Shadow help with TPN or studio content security requirements? It can be a documented technical control for AI tools: it runs on your machines, has no telemetry, decrypts only known AI hosts and keeps the mapping in an encrypted local vault. It does not give you a TPN assessment result or satisfy a studio's requirements on its own. ### How do we protect a production codename? Add it as a dictionary term, for example shadow terms add "Project Nightjar" "person:Ava Brennan". The term always maps to the same fake, so long conversations and agents stay consistent, and the answer comes back with the real name. ### Will AI feedback on a script get worse if names are replaced? Usually not much. Fakes are realistic names and titles from one coherent persona, so dialogue and structure still read naturally. Feedback that depends on a real-world person or franchise will lose that context. --- # Secure LLM use in HR and recruiting: protect candidate and employee data in AI prompts Source: https://getshadow.dev/industries/hr-recruiting/ > Mask candidate and employee names, contact details and IDs in CVs and HR documents before they reach OpenAI, Anthropic or Gemini. Self-hosted, reversible. Shadow is for HR teams, recruiters, staffing agencies and HR tech vendors that use large language models on CVs, employee records and people-related documents. It runs on your own laptops, servers or pods, replaces candidate and employee names, emails, phone numbers, addresses and recognised national IDs with fakes before a request reaches the AI provider, and restores them in the answer. It reduces personal data sent to AI vendors; it does not make automated hiring decisions fair or lawful by itself. ## What data leaks into AI tools in HR and recruiting | Workflow | What typically ends up in the prompt | |---|---| | CV screening and summarising | Full CVs: names, contact details, addresses, dates of birth, photos, employment history | | Writing job offers and rejection emails | Candidate names, salaries, start dates, personal emails | | Interview notes and scorecards | Candidate and interviewer names, opinions, sometimes health or family details | | Employee relations and investigations | Names, allegations, sick leave, disciplinary history | | Payroll and benefits questions | Salaries, bank details, national insurance or social security numbers | | People analytics | Exports of the workforce with names, demographics and performance ratings | Much of this is sensitive by any standard, and some of it falls into GDPR special categories: health, ethnicity, trade union membership, religion. ## How Shadow protects it Shadow masks values in every AI request and uploaded file with fakes from a persistent [persona](https://getshadow.dev/concepts/personas/), keeps the mapping in an [encrypted local vault](https://getshadow.dev/concepts/vault/), and restores responses, including streams and tool calls. | Data | How Shadow handles it | |---|---| | Candidate, employee and referee names | Name detector, `person:` terms, the NER sidecar or Presidio for multilingual free text | | Emails, phones, street addresses | Built-in detectors (addresses in English and German styles; others need the NER sidecar or Presidio) | | National IDs | US SSN and ITIN, UK National Insurance and NHS numbers, Indian Aadhaar and PAN, Australian TFN built in; more with Presidio | | Bank details | IBAN detector with mod-97-valid fakes | | Employer and client company names | `org:` terms, organisations with a legal suffix detected automatically | | CVs and HR documents | DOCX masked in place; PDF text extracted and masked | | Candidate lists and workforce exports | `report` intent: typed tokens for people and contact details | ```toml organization = ["Acme Staffing Ltd"] terms = ["org:Globex", "org:Initech", "term:Requisition Falcon"] internal_domains = ["acme-staffing.example"] [model_detector] enabled = true # ml/serve_hf.py: multilingual names and addresses url = "http://127.0.0.1:8878/detect" [policy.report] person = "token" email = "token" phone = "token" address = "token" national_id = "token" default = "surrogate" [files] pdf = "text" images = "block" # CV photos and scanned documents are not OCR'd ``` :::caution[What masking does not hide] - **Dates of birth and other dates** are not masked. - **Special-category details** written in prose (a diagnosis, a religion, a union role) are content, not identifiers, and reach the model. - **Employers, schools and job titles** can identify someone, especially senior or unusual roles. Add key employers as terms if needed. - **Photos** are not masked. No OCR. ::: ## Regulations and frameworks | Requirement | How Shadow helps | |---|---| | GDPR Art. 5(1)(c), 25 and 32 | Data minimisation and pseudonymisation by design before candidate and employee data reaches an AI vendor. Pseudonymised data remains personal data for you (Recital 26) | | GDPR Art. 9 special categories | Reduces identifiers attached to sensitive content; it does not remove the sensitive content, so a lawful basis is still needed | | GDPR Art. 22 automated decisions | Not addressed by masking. Keep humans in the decision | | EU AI Act, Annex III employment use cases | High-risk obligations apply from 2 December 2027 under the 2026 Digital Omnibus. Shadow supports data governance and security; it is not a risk management system, conformity assessment or human oversight measure | | NYC Local Law 144 (automated employment decision tools) | Unrelated to masking: a bias audit and notices are still required where the law applies | | ISO/IEC 27001, ISO/IEC 42001, SOC 2 | Evidence of a technical control on AI data flows | :::caution[Not legal advice] This page describes technical capabilities, not legal conclusions. Using Shadow does not make AI-assisted hiring lawful, fair or compliant. Work with your data protection officer, employment counsel and works council where relevant. ::: ## Example workflows ### Summarising a CV What the recruiter sends (all data fictional): ```text Summarise this CV for the hiring manager: Daniel Okoye, daniel.okoye@mail.example, +44 7700 900456, 7 Station Road, Bristol. NI number AB 12 34 56 C. Five years at Globex as a data engineer. ``` What the provider sees (your fakes will differ): ```text Summarise this CV for the hiring manager: Tao Zhu, tao.zhu@xinghe.cn, +86 139 2245 7710, 88 Tianfu Road, Bristol. NI number CE 48 21 07 B. Five years at Zhilan Technology as a data engineer. ``` The summary comes back about Daniel Okoye. His experience, which is what the hiring manager needs, is unchanged. ### Comparing a shortlist A recruiter pastes a CSV of 40 shortlisted candidates with notes and asks for a comparison against the job description. With that many names and contact details in tabular form, Shadow classifies this as `report`: candidate names, emails and phones become `[PERSON_0001]`, `[EMAIL_0001]` and so on. The model compares tokens and the ranking comes back with real names. A human still makes the decision. ### Offer letter from a template An HR partner uploads a DOCX offer template filled in for a candidate. Names, address and bank details are masked inside the Word document, it stays valid, and the edited letter comes back with the real details restored. ### Scheduling interviews with an agent If an assistant emails or schedules candidates itself, they still get the message. The candidate the instruction names ("Invite Daniel Okoye, daniel.okoye@mail.example, to a Tuesday interview") is the recipient, so their name and email pass through as written and each keep is logged; the model writes to the right person. Other candidates, notes and IDs in the request stay masked and are restored in the tool-call arguments before your code sends the invite. Set `[policy.act] targets = "surrogate"` to fake the recipient too. With a provider-hosted tool, Shadow cannot restore inside the provider, so by default the contact details the action needs are kept real and each keep is logged. See [intent-aware policy](https://getshadow.dev/concepts/intent-policy/). ## Deployment patterns | Pattern | When to use it | |---|---| | **Endpoint install** for recruiters and HR partners: `shadow install --apply` | API-backed chat clients and desktop apps on HR laptops | | **Gateway** for an ATS or HR tool integration: `http://127.0.0.1:8877/gw/openai/v1` | Screening, scheduling and letter-writing features you build | | **Sidecar** in Kubernetes | HR tech products calling LLMs for many customers. See [Docker and Kubernetes](https://getshadow.dev/integrate/docker-kubernetes/) | For agencies serving several clients, a separate data directory per client (`SHADOW_HOME` or `--data-dir`) keeps vaults apart. There is no central admin console yet; an optional local audit log is on the [roadmap](https://getshadow.dev/project/roadmap/). ## Related - [GDPR-compliant LLM usage](https://getshadow.dev/use-cases/gdpr-compliant-llm-usage/) and [redact documents before uploading to AI](https://getshadow.dev/use-cases/redact-documents-before-uploading-to-ai/) - [Consulting and professional services](https://getshadow.dev/industries/consulting-professional-services/) and [education](https://getshadow.dev/industries/education/) - [Intent-aware policy](https://getshadow.dev/concepts/intent-policy/), [file masking](https://getshadow.dev/concepts/file-masking/), [detection](https://getshadow.dev/concepts/detection/) - Detection quality: [benchmarks](https://getshadow.dev/benchmarks/). Other approaches: [compare](https://getshadow.dev/compare/) ## Frequently asked questions ### Can recruiters paste CVs into ChatGPT? Sending CVs to a consumer AI service puts candidate personal data with a third party, often without a clear lawful basis or agreement. Shadow masks names, emails, phones, addresses and recognised national IDs in CVs and documents before they reach a provider API. Consumer web apps such as chatgpt.com are not built-in hosts, so give recruiters an approved API-backed client. ### Does masking candidate names make AI screening fair or unbiased? No. Removing names and contact details can reduce some signals a model might react to, but bias can come from schools, employers, gaps, language and many other features that Shadow does not mask. Fairness needs testing, oversight and, where required, a bias audit. ### Is AI-assisted hiring high-risk under the EU AI Act? AI used for recruitment, selection, promotion, termination and task allocation is listed as high-risk in Annex III. Under the 2026 Digital Omnibus, those obligations now apply from 2 December 2027. Shadow is a data protection control; it does not perform conformity assessment or provide human oversight. ### Can Shadow mask CVs uploaded as PDF or Word files? Yes for text. DOCX CVs are masked in place and stay valid documents. PDF text is extracted, masked and sent as text, so layout is lost. Scanned CVs and photos are not OCR'd; block images if they may be uploaded. ### Can we compare a shortlist without the model seeing who the candidates are? Yes. A request that pastes many candidates in at once, such as a table with their names and contact details, is classified as a report, so names, emails, phones and addresses become typed tokens such as [PERSON_0003]. The model ranks and compares tokens, and the answer is restored with real names on your screen. --- # Secure LLM use in consulting and professional services: keep client data out of AI prompts Source: https://getshadow.dev/industries/consulting-professional-services/ > Mask client names, engagement codenames and contacts in prompts, decks and spreadsheets before consultants use OpenAI, Anthropic or Gemini. Self-hosted. Shadow is for management consultancies, accounting and audit firms, advisory, engineering and research firms whose staff use large language models on client work. It runs on consultants' laptops or on firm servers, replaces client names, engagement codenames, people and contact details with consistent fakes before a request reaches the AI provider, and restores them in the answer. Decks, models and reports are masked in place, so they come back as the same kind of file. ## What data leaks into AI tools in professional services | Workflow | What typically ends up in the prompt | |---|---| | Drafting proposals and deliverables | Client name, engagement codename, scope, fees, stakeholder names | | Summarising interviews and workshops | Interviewee names, roles, candid opinions, emails | | Building and checking models | XLSX with client entities, cost centres, employee lists, salaries | | Reworking slide decks | PPTX with client names, logos, org charts, strategy | | Due diligence and M&A | Target company names, deal codenames, financials, management names | | Audit and tax | Client entity names, bank details, tax IDs, transaction listings | Professional services firms hold many clients' confidential information at once, often under NDAs that forbid naming the client to third parties. ## How Shadow protects it Shadow detects sensitive values in every AI request and uploaded file, replaces them with fakes from a persistent [persona](https://getshadow.dev/concepts/personas/), records the mapping in an [encrypted local vault](https://getshadow.dev/concepts/vault/), and restores the answer. | Data | How Shadow handles it | |---|---| | Client and target company names | `org:` terms; names with a legal suffix are recognised as organisations, and the bare name is protected too | | Engagement and deal codenames | Dictionary `terms` | | Stakeholders, interviewees, client staff | Name detector, `person:` terms, NER sidecar or Presidio | | Emails, phones, addresses | Built-in detectors | | IBANs, card numbers, national IDs | Checksum-validated detectors with checksum-valid fakes | | Decks, documents, models | PPTX, DOCX, XLSX masked in place; PDF text masked | | Interview transcripts and listings over many people | `report` intent tokenises people and contact details | A per-client configuration: ```toml # ~/clients/globex/shadow.toml organization = ["Bright Path Advisory LLP"] # your firm terms = [ "org:Globex Corporation", "org:Initech", # client and target "Project Kestrel", # engagement codename "person:Laura Chen", "person:Ravi Menon", # client stakeholders ] internal_domains = ["globex.example", "brightpath.example"] [policy.always] secret = "redact" [files] pdf = "text" images = "block" ``` ### Keeping clients apart Each Shadow data directory has its own persona, vault and key. Give each sensitive client its own directory so mappings never mix: ```sh SHADOW_HOME=~/clients/globex shadow init --org "Bright Path Advisory LLP" SHADOW_HOME=~/clients/globex shadow run --listen 127.0.0.1:8877 SHADOW_HOME=~/clients/umbrella shadow init --org "Bright Path Advisory LLP" SHADOW_HOME=~/clients/umbrella shadow run --listen 127.0.0.1:8879 ``` Point each client workspace's base URL at its own port. For most firms one persona per machine with all client names as terms is enough; separate directories are for engagements with stricter walls. :::caution[What masking does not hide] - **Substance.** Strategy, numbers and findings reach the model as written. A distinctive situation can identify a client. - **Logos, images and embedded charts** in decks are not masked. Charts' cached values and embedded workbooks inside Office files are not masked either. - **Client-specific IDs** (cost centres, entity codes) are not detected unless listed as terms. ::: ## Regulations and frameworks | Requirement | How Shadow helps | |---|---| | NDAs and engagement letters | Reduces disclosure of client identity and personal data to AI vendors. Does not make a prohibited disclosure permitted | | Professional confidentiality rules (for example accountancy ethics codes) | A safeguard that supports confidentiality when AI use is permitted | | GDPR Art. 5(1)(c), 25, 28 and 32 | Data minimisation and pseudonymisation when acting as a processor for client data. Pseudonymised data remains personal data for whoever holds the key (Recital 26) | | EU AI Act | Supports data governance; most consulting use is not high-risk, but client deliverables built on AI may be | | ISO/IEC 27001, ISO/IEC 42001, SOC 2 | Evidence of a technical control on AI data flows for client security questionnaires | :::caution[Not legal advice] This page describes technical capabilities, not legal or professional conclusions. Shadow does not satisfy NDAs, professional standards or data protection law on its own. Check engagement terms and your firm's AI policy. ::: ## Example workflows ### Reworking a steering committee deck A consultant uploads a PPTX and asks for a tighter storyline. What one slide says (all data fictional): ```text Project Kestrel: Globex Corporation acquisition of Initech. Sponsor: Laura Chen (CFO). ``` What the provider sees (your fakes will differ): ```text Velora Quilent: Zhilan Technology Co., Ltd. acquisition of Lumeth Systems. Sponsor: Ying Chen (CFO). ``` The suggestions and any rewritten slide text come back with the real names, and the deck is still a valid presentation. ### Synthesising stakeholder interviews A consultant pastes notes from 25 interviews and asks for themes. With many names and emails in one request, Shadow classifies it as `report` and uses typed tokens, so the model attributes quotes to `[PERSON_0007]` rather than inventing biographies. The synthesis comes back with real interviewee names. ### Checking a cost model An analyst uploads an XLSX of 2,000 rows by cost centre and supplier. Supplier and client names in shared strings are replaced; numbers and formulas are untouched. The model explains anomalies and the answer is restored. ## Deployment patterns | Pattern | When to use it | |---|---| | **Endpoint install** on consultant laptops: `shadow install --apply` | API-backed chat clients, assistants and scripts; terms per client in `shadow.toml` | | **Per-client data directories** | Engagements with strict information barriers | | **Gateway** for firm knowledge tools: `http://127.0.0.1:8877/gw//...` | Proposal, research and document assistants you build | | **Sidecar** in Kubernetes | Firm-wide AI services. See [Docker and Kubernetes](https://getshadow.dev/integrate/docker-kubernetes/) | There is no central admin console or shared team vault yet; both team identities and an optional audit log are on the [roadmap](https://getshadow.dev/project/roadmap/). Consumer web chat apps such as chatgpt.com are not built-in hosts, so provide an approved API-backed client. ## Related - [Redact documents before uploading to AI](https://getshadow.dev/use-cases/redact-documents-before-uploading-to-ai/) and [stop PII leaks to ChatGPT](https://getshadow.dev/use-cases/stop-pii-leaks-to-chatgpt/) - [Legal](https://getshadow.dev/industries/legal/) and [financial services](https://getshadow.dev/industries/financial-services/) - [File masking](https://getshadow.dev/concepts/file-masking/), [the vault](https://getshadow.dev/concepts/vault/), [personas](https://getshadow.dev/concepts/personas/) - Detection quality: [benchmarks](https://getshadow.dev/benchmarks/). Other approaches: [compare](https://getshadow.dev/compare/) ## Frequently asked questions ### Can consultants use ChatGPT or Claude on client work without breaching NDAs? That depends on the NDA and the engagement terms, which often restrict disclosure of a client's identity and information to third parties. Shadow replaces client names, engagement codenames, people and contact details with fakes before requests reach the provider API, which reduces what is disclosed. The substance of the work still reaches the model, so check the engagement terms first. ### Can Shadow mask client decks and models, not just prompts? Yes. PPTX, DOCX and XLSX uploads are masked in place and stay valid files: slide text runs, document text and spreadsheet shared strings are replaced, while numbers and formulas are untouched. PDF text is extracted and sent masked. Images and embedded charts are not masked. ### How do we keep one client's data separate from another's? Run a separate Shadow data directory per client with SHADOW_HOME or --data-dir. Each directory has its own persona, vault and key, so mappings never mix. Each running instance needs its own listen address. ### Will the client name appear in the AI's answer? Yes, on your screen. The provider sees a consistent fake company name, and Shadow restores the real name in the response before it reaches you, including in streamed answers. ### Is Shadow enough for client data protection in an AI policy? No single control is. Shadow is a technical safeguard for detected identifiers. Firms still need an AI policy, approved tools, client consent where required, and review of outputs. --- # Secure LLM use in automotive: protect connected-car, driver and supplier data in AI prompts Source: https://getshadow.dev/industries/automotive/ > Mask driver names, contact details, internal hosts, secrets and programme codenames before automotive teams use OpenAI, Anthropic or Gemini. Self-hosted. Shadow is for vehicle manufacturers, suppliers, mobility and fleet companies whose engineering, after-sales, connected-services and business teams use large language models. It runs on laptops, build servers or pods, replaces driver and customer names, contact details, internal hostnames, secrets, supplier names and programme codenames with consistent fakes before a request reaches the AI provider, and restores them in the answer. Some automotive identifiers, notably VINs and location traces, are not masked, and this page says how to handle them. ## What data leaks into AI tools in automotive | Workflow | What typically ends up in the prompt | |---|---| | Connected-services support and analytics | Driver names, emails, phone numbers, VINs, trip logs, GPS coordinates | | After-sales and warranty claims | Customer and dealer names, addresses, VINs, fault descriptions | | Software engineering for ECUs and cloud backends | Source code, build logs, internal hostnames, signing keys, API tokens | | Supplier management and sourcing | Supplier names, quotes, volumes, programme codenames, contacts | | Homologation and requirements work | Programme codenames, requirement text, test reports | | Fleet and mobility operations | Driver records, licence numbers, booking histories | Two risks overlap: personal data from drivers and customers, and intellectual property from OEMs and suppliers, often covered by strict confidentiality terms. ## How Shadow protects it Shadow masks values in AI requests and uploaded files with fakes from a persistent [persona](https://getshadow.dev/concepts/personas/), keeps the mapping in an [encrypted local vault](https://getshadow.dev/concepts/vault/), and restores responses, including streams and tool-call arguments. | Data | How Shadow handles it | |---|---| | Driver, customer and dealer names | Name detector, `person:` terms, NER sidecar or Presidio | | Emails, phones, street addresses | Built-in detectors (addresses in English and German styles) | | Payment details | Card numbers (Luhn) and IBANs (mod-97) with checksum-valid fakes | | Programme and model codenames | Dictionary `terms` | | Suppliers, dealers, partners | `org:` terms | | Backend and build hostnames, IPs, MACs | `hostname`, `ip`, `mac` detectors and `internal_domains` | | Signing keys, tokens, API keys | `secret` detector: PEM private keys, JWTs, bearer tokens and known key formats; redaction opt-in | | Usernames in paths, git identity | `path` and `local_facts` detectors | | Warranty exports | `report` intent with typed tokens | ```toml organization = ["Nordwerk Fahrzeugbau AG"] terms = [ "Project Kestrel", "term:NW-E7 Platform", # programme codenames "org:Globex Electronics", "org:Initech Seating", # suppliers ] internal_domains = ["nordwerk.example", "ota.nordwerk.internal", "build.nordwerk.internal"] [policy.always] secret = "redact" # signing keys and tokens never leave in any form [policy.report] person = "token" email = "token" phone = "token" address = "token" default = "surrogate" [files] images = "block" ``` :::caution[Known gaps for automotive data] - **VINs, licence plates and fleet IDs** are not detected by the built-in detectors. There is no custom regex setting; list known values as terms or write a custom Rust detector when embedding Shadow. - **GPS coordinates and timestamps** are not masked. Dates are never masked. - **Code and technical content** reach the model as written. Masking hostnames and keys does not hide algorithms or calibration data. - **Passwords inside connection URLs** are not caught; **images and CAD** are not masked. ::: ## Regulations and frameworks | Requirement | How Shadow helps | |---|---| | UNECE R155 (cyber security management system) and R156 (software updates) | A documented control for one data flow: development and operational data sent to AI services. Not a CSMS or SUMS | | ISO/SAE 21434 | Supports protection of development assets such as keys, internal hosts and codenames in AI-assisted work | | GDPR Art. 5(1)(c), 25 and 32, and EDPB guidance on connected vehicles | Data minimisation and pseudonymisation for driver data before it reaches an AI vendor. Pseudonymised data is still personal data for you (Recital 26) | | EU Data Act (connected product data) | Does not address data access or sharing obligations; only limits what reaches AI vendors | | Supplier NDAs, TISAX | Keeps supplier and programme names out of third-party AI services; evidence of a technical control | | ISO/IEC 27001, ISO/IEC 42001 | Evidence of a control on AI data flows | :::caution[Not legal advice] This page describes technical capabilities, not legal or homologation conclusions. Shadow does not make vehicles or processes compliant with UNECE regulations, ISO/SAE 21434 or GDPR. Work with your cybersecurity, legal and privacy teams. ::: ## Example workflows ### Warranty claim summary What the after-sales analyst sends (all data fictional): ```text Summarise claim for Lena Fischer (lena.fischer@mail.example, +49 30 1234567), Hauptstraße 5, Berlin. Dealer: Initech Seating. Fault: seat heater on NW-E7 Platform. ``` What the provider sees (your fakes will differ): ```text Summarise claim for Ying Chen (ying.chen@xinghe.cn, +86 28 6512 0417), Renmin Road 21, Berlin. Dealer: Lumeth Interiors Co., Ltd. Fault: seat heater on Velora Quilent. ``` The summary comes back about Lena Fischer and the NW-E7 Platform. If the claim text contained a VIN, it would have been sent unmasked unless you added it as a term. ### Debugging an OTA backend An engineer pastes a stack trace from `ota-api-03.ota.nordwerk.internal` and a config with a PEM private key into Claude Code. The hostname becomes a fake, the key becomes `[REDACTED_SECRET]`, and `/Users/lfischer/...` becomes the persona's username. The suggested fix comes back with real hostnames restored. ### Supplier quote comparison A buyer uploads an XLSX of quotes from six suppliers. Supplier names in shared strings are replaced, prices and volumes are left as numbers, and the comparison comes back with real names. ## Deployment patterns | Pattern | When to use it | |---|---| | **Endpoint install** on engineering laptops: `shadow install --apply` | API-backed coding assistants, chat clients and scripts | | **CI runners and build servers** | Systemd unit with gateway URLs or `HTTPS_PROXY`, so AI steps in pipelines are masked | | **Gateway** for internal tools: custom route to your Azure OpenAI or Bedrock (API-key auth) resource | Warranty, service and requirements assistants. SigV4-signed Bedrock requests are forwarded unmasked, so use API keys | | **Sidecar** in Kubernetes | Connected-services backends calling LLMs. See [Docker and Kubernetes](https://getshadow.dev/integrate/docker-kubernetes/) | GitHub Copilot and Cursor's hosted backends are not built-in intercepted hosts. There is no central admin console yet; see the [roadmap](https://getshadow.dev/project/roadmap/). ## Related - [Protect secrets from AI coding assistants](https://getshadow.dev/use-cases/protect-secrets-from-ai-coding-assistants/) and [LLM data loss prevention](https://getshadow.dev/use-cases/llm-data-loss-prevention-dlp/) - [Manufacturing](https://getshadow.dev/industries/manufacturing/) and [telecommunications](https://getshadow.dev/industries/telecommunications/) - [Detection](https://getshadow.dev/concepts/detection/), [Docker and Kubernetes](https://getshadow.dev/integrate/docker-kubernetes/), [security model](https://getshadow.dev/reference/security/) - Detection quality: [benchmarks](https://getshadow.dev/benchmarks/). Other approaches: [compare](https://getshadow.dev/compare/) ## Frequently asked questions ### Does Shadow mask VINs and vehicle identifiers? Not with the built-in detectors, and there is no custom regex setting in shadow.toml. List known VINs or fleet IDs as terms, use the NER sidecar or Presidio for some identifier types, or write a custom detector in Rust if you embed Shadow. Treat VINs in prompts as unmasked unless you have verified otherwise with shadow scan. ### Can connected-car location data be sent to an LLM through Shadow? Shadow masks street addresses in English and German styles, and the NER sidecar or Presidio finds more locations. GPS coordinates and timestamped trip data are not masked. Location traces are highly identifying, so aggregate or remove them before using an external model. ### Can engineers use AI coding assistants on ECU or backend code? Yes for API-based assistants such as Claude Code, Codex CLI, Aider, Continue or Cline. Shadow masks internal hostnames, IPs, usernames in paths and recognised secrets in code and logs. The code itself still reaches the provider; whether that is acceptable for safety-critical or supplier-owned code is a policy decision. ### Does Shadow help with UNECE R155 or ISO/SAE 21434? It can be a documented control in your cybersecurity management system for one risk: sensitive data and infrastructure details sent to AI services. It does not cover vehicle security engineering or type approval. ### Can we protect supplier and programme names in AI prompts? Yes. Add vehicle programme codenames as terms and suppliers as org: terms. Each gets a consistent fake, so supplier comparisons and programme reviews still make sense to the model, and the answer comes back with real names. --- # Secure ChatGPT and LLM use for nonprofits: protect donor and beneficiary data for free Source: https://getshadow.dev/industries/nonprofit/ > Free, open-source way for charities and NGOs to mask donor, volunteer and beneficiary data before staff use OpenAI, Anthropic or Gemini. No account needed. Shadow is for charities, NGOs, foundations, community groups and social enterprises whose staff and volunteers use large language models to write appeals, summarise case notes and analyse supporter data. It is free, open source and runs on your own computers. It replaces donor, volunteer and beneficiary names, emails, phone numbers, addresses and bank details with realistic fakes before a request reaches the AI provider, and puts the real values back in the answer. It is a safeguard that fits a small budget, not a replacement for a data protection policy. ## What data leaks into AI tools at nonprofits | Workflow | What typically ends up in the prompt | |---|---| | Fundraising appeals and thank-you letters | Donor names, addresses, gift amounts, giving history | | Donor and supporter analysis | CRM exports with names, emails, postcodes, gift aid or tax status | | Case notes and referrals | Beneficiary names, family details, health, immigration or housing situations | | Grant applications and reports | Partner organisations, beneficiary stories, staff names | | Volunteer coordination | Volunteer names, phone numbers, availability, background-check status | | Finance | Bank details, IBANs, payment card numbers from donation records | Nonprofits often hold data on people in vulnerable situations, under tight budgets, with staff using whatever AI tool is free. ## How Shadow protects it Shadow sits between the AI tool and the provider. It masks values with fakes from a persistent [persona](https://getshadow.dev/concepts/personas/), keeps the mapping in an [encrypted vault](https://getshadow.dev/concepts/vault/) on your computer, and restores the response. | Data | How Shadow handles it | |---|---| | Donor, volunteer and beneficiary names | Name detector, `person:` terms, optional NER sidecar for better recall | | Emails, phones, street addresses | Built-in detectors (addresses in English and German styles) | | Bank details and card numbers | IBAN (mod-97) and card (Luhn) detectors with valid-looking fakes | | National IDs | US SSN and ITIN, UK National Insurance and NHS numbers, Indian Aadhaar and PAN, Australian TFN and ABN | | Partner organisations and programme names | `org:` and plain terms | | CRM exports, spreadsheets, letters | CSV, XLSX and DOCX masked; donor lists get typed tokens in reports | A simple configuration for a charity: ```toml organization = ["Riverside Community Trust"] terms = ["org:Northside Food Bank", "term:Warm Homes Appeal"] internal_domains = ["riversidetrust.example"] [policy.report] person = "token" email = "token" phone = "token" address = "token" default = "surrogate" [files] images = "block" # photos and scans are not masked ``` ### Setting it up on one laptop ```sh # macOS / Linux curl -fsSL https://raw.githubusercontent.com/Tiny-Bubble-Company/shadow/main/packaging/install.sh | sh shadow init --org "Riverside Community Trust" shadow install # shows what it will change shadow install --apply # runs in the background and trusts the local certificate ``` Then point your AI tool at Shadow, either with its base URL setting (`http://127.0.0.1:8877/gw/openai/v1`) or the proxy setting. The [quick start](https://getshadow.dev/getting-started/quick-start/) walks through it. Release binaries are on the [roadmap](https://getshadow.dev/project/roadmap/); until they exist for your platform, the installer builds from source, which needs Rust and takes a few minutes. :::caution[What masking does not hide] - **Situations.** A case note describing a rare circumstance in a small community can identify someone without any name. - **Dates** are not masked, and **images and scanned forms** are not masked (no OCR). - **The ChatGPT website** is not covered. Shadow protects tools that call the provider APIs. ::: ## Regulations and frameworks | Requirement | How Shadow helps | |---|---| | GDPR / UK GDPR Art. 5(1)(c), 25 and 32 | Data minimisation and pseudonymisation before supporter or beneficiary data reaches an AI vendor. Pseudonymised data is still personal data for you (Recital 26) | | GDPR Art. 9 special categories (health, religion, ethnicity) | Removes identifiers attached to sensitive content; the content itself still reaches the model, so a lawful basis is needed | | HIPAA, for health nonprofits that are covered entities or business associates | Supports the minimum necessary standard; does not de-identify data (dates are not masked) and does not replace a BAA | | PCI DSS, for donation card data | Card numbers are replaced with Luhn-valid fakes, keeping real numbers out of AI requests. It does not change the scope of your payment systems | | Fundraising regulator codes and donor promises | Helps honour commitments not to share supporter data with third parties beyond what is necessary | :::caution[Not legal advice] This page describes technical capabilities, not legal conclusions. Using Shadow does not make a charity compliant with data protection, health privacy or fundraising rules. Ask your data protection lead or a pro bono adviser. ::: ## Example workflows ### Thank-you letter for a major donor What the fundraiser sends (all data fictional): ```text Write a warm thank-you letter to Margaret Ellis, 4 Church Lane, York, margaret.ellis@mail.example, for her gift to the Warm Homes Appeal. Mention Northside Food Bank, our partner. ``` What the provider sees (your fakes will differ): ```text Write a warm thank-you letter to Margaret Ellis, 4 Church Lane, York, margaret.ellis@mail.example, for her gift to the Lumeth Draane. Mention Zhilan Food Co., Ltd., our partner. ``` Margaret Ellis is the recipient, so her name, address and email pass through as written and each keep is logged; the model thanks the right donor. The appeal and the partner are not the recipient, so they are masked and the letter comes back about the Warm Homes Appeal and Northside Food Bank. Set `[policy.act] targets = "surrogate"` if donor details must not reach the provider. ### Segmenting a donor export A CRM export of 3,000 supporters is pasted in as CSV with the question "Which segments lapsed this year?". Shadow classifies it as a report, so names, emails, phones and addresses become `[PERSON_0001]`, `[EMAIL_0001]` and so on. Gift amounts and dates are left alone so the analysis works. The answer is restored on your screen. ### Grant report from case summaries A programme manager pastes five anonymised-by-hand case summaries. Shadow still catches the names and phone numbers someone forgot to remove. It is a second line of defence, not the first. ## Deployment patterns | Pattern | When to use it | |---|---| | **One laptop**: `shadow install --apply` | A small team or a single fundraiser or analyst | | **A few laptops** with the same `shadow.toml` | Small charities; copy the config file and run the installer on each | | **Server or container** with the gateway | A CRM integration or internal tool you build. See [Docker and Kubernetes](https://getshadow.dev/integrate/docker-kubernetes/) | There is no central console to manage many machines yet. See [small businesses](https://getshadow.dev/company-size/small-business/) for a practical rollout checklist. ## Related - [Stop PII leaks to ChatGPT](https://getshadow.dev/use-cases/stop-pii-leaks-to-chatgpt/) and [GDPR-compliant LLM usage](https://getshadow.dev/use-cases/gdpr-compliant-llm-usage/) - [Healthcare](https://getshadow.dev/industries/healthcare/) and [education](https://getshadow.dev/industries/education/) - [Quick start](https://getshadow.dev/getting-started/quick-start/), [intent-aware policy](https://getshadow.dev/concepts/intent-policy/), [security model](https://getshadow.dev/reference/security/) - Detection quality: [benchmarks](https://getshadow.dev/benchmarks/). Other approaches: [compare](https://getshadow.dev/compare/) ## Frequently asked questions ### Is Shadow free for nonprofits? Yes. Shadow is open source under Apache-2.0 and free for everyone. There is no account, no licence key, no hosted service and no telemetry. Your only costs are the AI provider you already use and the time to set it up. ### Can charity staff paste donor lists into ChatGPT safely? Not into the consumer chatgpt.com website through Shadow, because that is not a built-in intercepted host. With an approved API-backed client, Shadow turns donor names, emails, phones and addresses in bulk lists into typed tokens such as [PERSON_0001] before the request leaves, and restores them in the answer. ### We support vulnerable people. Is masking enough? No. Masking replaces names and contact details, but case notes describe situations that can identify someone, and Shadow does not mask dates or images. For safeguarding cases, keep the most sensitive material out of external AI and use Shadow for the work you do allow. ### Do we need technical staff to run it? Someone comfortable installing software and editing a short text file can set it up on a laptop in under an hour. Running it for a whole organisation without a central console takes more care; see the small business guide. ### Does Shadow help with GDPR for charities? It supports data minimisation and pseudonymisation when staff use AI, which are GDPR principles and safeguards. It does not provide a lawful basis, privacy notices or a data protection impact assessment. --- # Secure ChatGPT and LLM use in real estate: protect tenant and buyer data in AI prompts Source: https://getshadow.dev/industries/real-estate/ > Mask tenant, buyer and landlord names, contact details, bank data and IDs in leases and listings before agents use OpenAI, Anthropic or Gemini. Shadow is for estate agents, brokers, property managers, landlords, mortgage brokers and proptech companies whose teams use large language models on listings, leases, applications and correspondence. It runs on your own computers or servers, replaces tenant, buyer, seller and landlord names, emails, phone numbers, addresses, bank details and recognised national IDs with realistic fakes before a request reaches the AI provider, and restores them in the answer. It reduces the personal data you share with AI vendors; it does not review AI decisions for fairness. ## What data leaks into AI tools in real estate | Workflow | What typically ends up in the prompt | |---|---| | Writing listings and marketing copy | Property address, seller name, sometimes access codes and occupancy details | | Tenant and buyer correspondence | Names, emails, phone numbers, viewing times, complaints | | Lease drafting and review | Party names, addresses, rent, deposit, bank details, guarantor details | | Rental applications and screening | Income, employer, references, national IDs, credit details | | Mortgage and financing | SSNs, bank statements, account numbers, loan details | | Maintenance and portfolio analysis | Tenant lists, unit addresses, arrears, contractor names | Rental and mortgage files are dense with personal and financial data, and agents often use free AI tools on their phones and laptops between viewings. ## How Shadow protects it Shadow masks values in every AI request and uploaded file with fakes from a persistent [persona](https://getshadow.dev/concepts/personas/), records the mapping in an [encrypted local vault](https://getshadow.dev/concepts/vault/), and restores the response. | Data | How Shadow handles it | |---|---| | Tenant, buyer, seller, landlord, guarantor names | Name detector, `person:` terms, NER sidecar or Presidio | | Emails and phone numbers | Built-in detectors | | Street addresses | English and German styles built in; NER sidecar or Presidio for other formats | | Bank details and cards | IBAN (mod-97) and card (Luhn) detectors with valid-looking fakes | | National IDs | US SSN and ITIN, UK National Insurance number, Indian Aadhaar and PAN, Australian TFN and ABN; more with Presidio | | Agency, developer and contractor names | `organization` and `org:` terms | | Leases, applications, rent rolls | DOCX and XLSX masked in place; PDF text masked | | Tenant lists and arrears reports | `report` intent with typed tokens | ```toml organization = ["Harbour Lettings Ltd"] terms = ["org:Initech Property Services", "term:Riverside Quarter"] internal_domains = ["harbourlettings.example"] [presidio] enabled = true url = "http://127.0.0.1:5002/analyze" entities = ["PERSON", "LOCATION", "US_SSN", "US_BANK_NUMBER", "US_DRIVER_LICENSE", "UK_NINO"] [policy.report] person = "token" email = "token" phone = "token" address = "token" national_id = "token" default = "surrogate" [files] pdf = "text" images = "block" # ID scans, payslips as photos and floor plans are not OCR'd ``` :::caution[Known gaps for property data] - **Addresses in other formats** (outside English and German styles) need the NER sidecar or Presidio, and detection is best effort. - **Dates** (dates of birth, move-in dates) are not masked. - **Account and loan numbers** without a recognised format are not detected by the built-in detectors; there is no custom regex setting. Presidio's `US_BANK_NUMBER` and the NER sidecar's account-number detection help. - **Images and scanned documents** are not masked. Many rental applications include ID photos and scanned payslips. ::: ## Regulations and frameworks | Requirement | How Shadow helps | |---|---| | GLBA Safeguards Rule (16 CFR 314), for mortgage brokers and lenders | A technical safeguard that keeps detected customer information out of AI vendor requests. It does not replace your written information security programme or service provider oversight | | GDPR / UK GDPR Art. 5(1)(c), 25 and 32 | Data minimisation and pseudonymisation for tenant and buyer data. Pseudonymised data is still personal data for you (Recital 26) | | US state privacy laws (for example CCPA/CPRA) | Reduces personal information shared with AI service providers | | Fair Housing Act and equivalent equality laws | **Not addressed by masking.** AI-written ads and AI-assisted screening still need human review for discriminatory content or outcomes | | AML and KYC rules for estate agents | Masks identifiers in AI-assisted KYC work; it does not perform checks or keep records | :::caution[Not legal advice] This page describes technical capabilities, not legal conclusions. Using Shadow does not make a business compliant with GLBA, GDPR, fair housing or AML rules. Work with your compliance adviser. ::: ## Example workflows ### Drafting an arrears letter What the property manager sends (all data fictional): ```text Draft a polite arrears reminder to Tom Barker, Flat 3, 221 Baker Street, London, tom.barker@mail.example, +44 20 7946 0018. Two months owed; pay to IBAN GB33 BUKB 2020 1555 5555 55. ``` What the provider sees (your fakes will differ): ```text Draft a polite arrears reminder to Tom Barker, Flat 3, 221 Baker Street, London, tom.barker@mail.example, +44 20 7946 0018. Two months owed; pay to IBAN GB71 WEST 1234 5698 7654 32. ``` Tom Barker is the recipient, so his name, address, email and phone pass through as written and each keep is logged; the model writes to the right tenant. The payment IBAN is not part of the recipient phrase, so the provider sees a fake that still passes mod-97, and the letter comes back with the real IBAN. Set `[policy.act] targets = "surrogate"` if tenant details must not reach the provider. ### Reviewing a lease An agent uploads a DOCX lease and asks for a plain-English summary of the break clause and the tenant's obligations. Names of landlord, tenant and guarantor, addresses and bank details are replaced inside the Word document. The summary comes back with the real parties. ### Portfolio arrears analysis A finance manager uploads an XLSX rent roll of 450 units and asks "Analyse these records: which buildings have rising arrears?". The request reads as a report, so the `report` intent turns tenant names, emails and phones into typed tokens; amounts stay as numbers. The analysis is restored locally. ## Deployment patterns | Pattern | When to use it | |---|---| | **Endpoint install** on office laptops: `shadow install --apply` | Agents and property managers using API-backed chat clients and desktop apps | | **Gateway** for a CRM or property management integration: `http://127.0.0.1:8877/gw/openai/v1` | Letter generation, enquiry triage and listing tools you build | | **Sidecar** in Kubernetes | Proptech products calling LLMs for many customers. See [Docker and Kubernetes](https://getshadow.dev/integrate/docker-kubernetes/) | Shadow does not run on phones, so mobile AI apps are not covered; steer sensitive work to laptops with Shadow installed. There is no central admin console yet; see [small businesses](https://getshadow.dev/company-size/small-business/) for a rollout checklist. ## Related - [Redact documents before uploading to AI](https://getshadow.dev/use-cases/redact-documents-before-uploading-to-ai/) and [privacy for AI in customer support](https://getshadow.dev/use-cases/privacy-for-ai-in-customer-support/) - [Financial services](https://getshadow.dev/industries/financial-services/) and [insurance](https://getshadow.dev/industries/insurance/) - [File masking](https://getshadow.dev/concepts/file-masking/), [detection](https://getshadow.dev/concepts/detection/), [intent-aware policy](https://getshadow.dev/concepts/intent-policy/) - Detection quality: [benchmarks](https://getshadow.dev/benchmarks/). Other approaches: [compare](https://getshadow.dev/compare/) ## Frequently asked questions ### Can property managers use ChatGPT on tenant data? Not through the consumer chatgpt.com website with Shadow, because that is not a built-in intercepted host. With an API-backed client or an internal tool routed through Shadow, tenant names, emails, phone numbers, addresses, bank details and recognised IDs are replaced with fakes before the request leaves, and restored in the answer. ### Does Shadow mask property addresses? It detects street addresses written in English and German styles, and the optional NER sidecar or Presidio finds more locations. Addresses in other formats may not be detected. In listings the property address is often the point of the prompt, so decide per workflow whether it should be masked. ### Can AI help with leases and contracts without exposing the parties? Yes. DOCX leases are masked in place, so party names, contact details and bank details are replaced inside the Word file and the document stays valid. PDF text is extracted, masked and sent as text. Scanned leases are not OCR'd. ### Does Shadow make AI tenant screening compliant with fair housing rules? No. Masking names does not remove bias that can come from addresses, income sources, language or history, and Shadow does not evaluate model decisions. AI-assisted screening and advertising still need human review and legal guidance. ### Does it help mortgage brokers with GLBA? It supports the Safeguards Rule by keeping detected customer identifiers, such as names, SSNs, IBANs and (with Presidio or the NER sidecar) account numbers, out of requests to AI vendors. It does not replace an information security programme or vendor oversight. --- # Rolling out AI privacy by company size: Shadow from one laptop to a fleet Source: https://getshadow.dev/company-size/ > How solo developers, startups, small businesses, mid-market firms and enterprises deploy Shadow to keep customer data and secrets out of AI tools. Shadow is the same binary for a freelancer and for a bank. What changes with size is how you install it, how you keep its configuration consistent, and what evidence you need to show that it works. These guides cover each stage, with a recommended deployment, a rollout checklist and an honest list of what is not there yet. ## Choose your size | Guide | Typical setup | Main concern | |---|---|---| | [Solo developers and freelancers](https://getshadow.dev/company-size/solo-developers-freelancers/) | One laptop, coding assistants, client work | Client confidentiality, your own secrets | | [Startups](https://getshadow.dev/company-size/startups/) | A handful of laptops, one product with AI features | Speed, security questionnaires, SOC 2 readiness | | [Small businesses](https://getshadow.dev/company-size/small-business/) | Up to ~100 people, little or no IT team | Simple setup, customer data in everyday AI use | | [Mid-market](https://getshadow.dev/company-size/mid-market/) | Hundreds of devices, MDM, internal apps | Consistent configuration, pilot and rollout | | [Enterprise](https://getshadow.dev/company-size/enterprise/) | Thousands of devices, platform teams, existing DLP | Coverage gaps, scale, fit with the security stack | | [Regulated enterprise](https://getshadow.dev/company-size/regulated-enterprise/) | Banks, insurers, health systems, public bodies | Evidence, change control, risk assessments | ## What changes with size | | Solo / startup | Small business | Mid-market | Enterprise | |---|---|---|---|---| | **Install** | Install script, `shadow install --apply` | Same, done by whoever runs IT | MDM script per user | MDM plus your own signed build | | **Configuration** | Local `shadow.toml` | One shared file copied to each laptop | Managed `shadow.toml` pushed into each data directory | Managed file, versioned and reviewed | | **Servers and apps** | Gateway in `.env` | Rarely needed | systemd service or sidecar | Sidecar per workload, `NetworkPolicy` egress | | **Detection** | Built-ins plus terms | Built-ins plus terms | Plus Presidio or the NER sidecar where needed | Tuned per workload, tested with `shadow scan` | | **Evidence** | None needed | Written policy | Config in version control, rollout logs | Risk assessment, change records, test results | ## What every size gets - **No licence and no account.** Apache-2.0, nothing to buy, nothing to sign in to. - **No telemetry.** Shadow sends only the masked requests you were already sending, to the providers you already use. - **Local mapping.** The real-to-fake mapping lives in an encrypted [vault](https://getshadow.dev/concepts/vault/) on each machine or volume. - **Three integration modes.** [Proxy](https://getshadow.dev/integrate/proxy-mode/), [gateway](https://getshadow.dev/integrate/gateway-mode/) and the [Rust library](https://getshadow.dev/integrate/rust/), mixable on one machine. ## What is not there yet, at any size :::note[Current gaps, from the roadmap] - **No central admin console or fleet management.** Configuration is a file; distribute it with the tools you already use. - **No audit log yet.** Today the proxy logs the intent and counts per request, never values, and each machine's local [activity dashboard](https://getshadow.dev/concepts/activity-dashboard/) shows counts by data type. An optional local, append-only audit log is a [roadmap](https://getshadow.dev/project/roadmap/) "Next" item. - **No shared team identity yet.** Each machine has its own persona and vault. Team identities with key distribution are a "Later" item; today you can copy `persona.json`, `vault.bin` and `vault.key`. - **Release binaries and a published container image are in progress.** The install scripts fall back to building with `cargo` where no release exists, and the Docker image is built from `packaging/Dockerfile`. - **API traffic only.** Consumer web apps such as chatgpt.com are not built-in hosts. ::: ## Related - [Shadow by industry](https://getshadow.dev/industries/) for sector-specific risks and regulations - [Use cases](https://getshadow.dev/use-cases/) for step-by-step guides - [How Shadow compares](https://getshadow.dev/compare/) with DLP products, gateways and privacy vaults ## Frequently asked questions ### How long does it take to set up Shadow? About five minutes on one machine: install, run shadow init, then point a tool at the gateway or run shadow install --apply for proxy mode. A team rollout takes longer because you decide on shared terms, policy and how to distribute the configuration. ### Is there a central admin console? No. Shadow is configured with a shadow.toml file on each machine or container. Teams distribute that file with their existing device or configuration management. A local audit log and shared team identities are on the roadmap. ### Does Shadow need SSO or user accounts? No. There are no accounts, logins or licences. That keeps setup simple, but it also means Shadow has no per-user identity or central reporting of its own. ### What does Shadow cost at scale? The software is free under Apache-2.0 at any size. The costs are rollout time, support for your own users, and optional detector infrastructure such as Presidio containers. ### Can Shadow run in Kubernetes? Yes, as a sidecar container next to each application, reached on localhost in gateway mode. You build the image from the repository's Dockerfile; a published image is on the roadmap. --- # AI privacy for solo developers and freelancers: keep client data and keys out of LLMs Source: https://getshadow.dev/company-size/solo-developers-freelancers/ > Use Claude Code, Aider and LLM APIs on client work without sending client names, customer data or API keys. Free, local, five-minute setup. If you work alone, Shadow is a five-minute install that keeps your clients' names, their customers' data and your API keys out of the prompts your AI tools send. It runs on your laptop, costs nothing, needs no account, and restores the real values in every answer so your assistant keeps working. The main decision is which tools to route through it. ## What matters when you are on your own | Concern | Why it matters to you | What Shadow does | |---|---|---| | Client confidentiality | NDAs and contracts often restrict sharing client material | Client names, people and codenames become consistent fakes | | Customer data in code and logs | Debugging a client's app means pasting real emails and IPs | Built-in detectors replace them in prompts and tool results | | Your own secrets | `.env` files and configs end up in context | Keys are replaced or, if you opt in, redacted | | Machine identity | Paths like `/Users/jdoe/` and your git email go everywhere | Your username, hostname and git identity are replaced | | Time and money | You have no IT team and no budget for DLP | Single binary, free, no service to run | ## Recommended setup One laptop, proxy mode for coding assistants and terminal tools, gateway URLs for your own scripts. ### 1. Install and create your persona ```sh curl -fsSL https://raw.githubusercontent.com/Tiny-Bubble-Company/shadow/main/packaging/install.sh | sh shadow init --org "Rivera Code Studio" ``` `--org` protects your own business name. The install script uses a release binary where one exists and otherwise builds with `cargo`. ### 2. Add your clients and their vocabulary ```sh shadow terms add "org:Globex Corporation" "org:Initech" "Project Falcon" "hostname:build01" ``` Or put them in `shadow.toml` in the data directory: ```toml organization = ["Rivera Code Studio"] terms = ["org:Globex Corporation", "org:Initech", "Project Falcon"] internal_domains = ["globex.example", "initech.internal"] [policy.always] secret = "redact" # API keys never leave, even as fakes ``` ### 3. Cover your terminal and IDE ```sh shadow install # read the plan shadow install --apply # background service, trust the local CA, write env.sh ``` Add `source "/env.sh"` to your shell profile. Claude Code, Aider and other API-based tools started from that shell now go through Shadow. For the Node-based CLIs, `env.sh` already sets `NODE_EXTRA_CA_CERTS`. ### 4. Point your own scripts at the gateway ```sh export OPENAI_BASE_URL=http://127.0.0.1:8877/gw/openai/v1 export ANTHROPIC_BASE_URL=http://127.0.0.1:8877/gw/anthropic ``` ### 5. Check it works ```sh shadow status shadow scan client-notes.md # what would be masked, without sending anything shadow vault stats # how many values are protected, by kind ``` With `shadow run` in the foreground you also see one log line per request with the intent and the number of values masked. ## Example: debugging a client's app You paste a stack trace into Claude Code: ```text Before: ConnectionError for jane.doe@globex.example at db01.globex.internal (10.20.4.17), OPENAI_API_KEY=sk-proj-AbC123... loaded from /Users/sam/clients/globex/.env Sent: ConnectionError for hui.zhao@lumeth.cn at db-kx4.lumeth.cn (10.84.211.9), OPENAI_API_KEY=[REDACTED_SECRET] loaded from /Users/yingchen/clients/lumeth/.env ``` The assistant's fix comes back with the real hostname and path. The fakes shown are illustrative; your persona generates its own. ## Keeping clients apart For most freelancers one persona with all clients as terms is enough. If a client requires a stricter wall, give it its own data directory, persona and vault: ```sh SHADOW_HOME=~/clients/globex shadow init --org "Rivera Code Studio" SHADOW_HOME=~/clients/globex shadow run --listen 127.0.0.1:8879 ``` Point that client's workspace at port 8879. The [consulting guide](https://getshadow.dev/industries/consulting-professional-services/) shows the full pattern. ## Rollout checklist - [ ] Read each client contract for AI or third-party restrictions - [ ] `shadow init --org` with your business name - [ ] Add client names, codenames and internal domains as terms - [ ] Opt in to `secret = "redact"` - [ ] `shadow install --apply` and source `env.sh` - [ ] Run `shadow scan` on a real file from each client - [ ] Back up `persona.json`, `vault.bin` and `vault.key` with your encrypted backups ## Honest limits :::caution[What Shadow will not do for you] - **Copilot and Cursor's hosted models** use their vendors' backends, not built-in hosts. Use an assistant that takes your own API key if you want it covered. - **Code itself is not masked.** Shadow replaces identifiers, not logic. Proprietary algorithms still reach the model as written. - **Passwords inside connection URLs** (`postgres://user:pw@host`) are not detected as secrets. Keep them in `password=`-style variables or out of context. - **Images and screenshots** are not OCR'd. ::: ## Related - [Protect secrets from AI coding assistants](https://getshadow.dev/use-cases/protect-secrets-from-ai-coding-assistants/) - [Anonymise prompts for Claude](https://getshadow.dev/use-cases/anonymize-prompts-for-claude/) - [Proxy mode](https://getshadow.dev/integrate/proxy-mode/) and [gateway mode](https://getshadow.dev/integrate/gateway-mode/) - [Startups](https://getshadow.dev/company-size/startups/), when you hire your first engineer - [Benchmarks](https://getshadow.dev/benchmarks/) and [comparisons](https://getshadow.dev/compare/) ## Frequently asked questions ### Can I use AI coding assistants on client code under an NDA? Check the NDA first: some forbid sharing client material with any third party, masked or not. Where AI use is allowed, Shadow reduces what the provider sees by replacing client names, customer data, hostnames, usernames and secrets with fakes, and restoring them in the answer. ### Which coding assistants does Shadow cover? Tools that call a model API with your own key: Claude Code, Codex CLI and other OpenAI-based CLIs, Aider, Continue and Cline, plus your own scripts and SDKs. GitHub Copilot and Cursor's hosted models go to their vendors' own backends, which are not built-in hosts. ### How do I keep different clients' data apart? Give each client its own data directory with SHADOW_HOME. Each directory has its own persona, vault and key. For most freelancers one persona with every client name added as a term is enough. ### Does Shadow slow down my assistant? Masking is local, in-process string work: about 7.5 ms per document on the benchmark set, which is small next to model latency. Streams are held back by at most the length of the longest fake. ### What does it cost? Nothing. Shadow is Apache-2.0 open source with no account, no subscription and no telemetry. --- # LLM data privacy for startups: protect customer data in AI tools and features Source: https://getshadow.dev/company-size/startups/ > Keep customer PII and secrets out of OpenAI and Anthropic from day one: engineer laptops, product AI features and security questionnaires. Free and self-hosted. For a startup, Shadow is a cheap way to keep the customer data it detects out of AI providers from the start: a free binary on each engineer's laptop and a sidecar next to any product feature that calls a model. It takes minutes per machine, needs no vendor contract, and gives you a concrete answer when an enterprise prospect asks what customer data reaches OpenAI or Anthropic. ## What matters at this stage | Priority | What it means for AI privacy | |---|---| | Speed | Nothing that slows engineers or needs a procurement cycle | | Product AI features | Customer content goes to a provider on every request, in production | | First enterprise deals | Security questionnaires ask about AI subprocessors and data minimisation | | SOC 2 readiness | You need controls you can describe and evidence you can show | | Small team | Whatever you deploy has to run itself | ## Recommended deployment | Where | Pattern | Why | |---|---|---| | Engineer laptops | `shadow install --apply`, `env.sh` in the shell profile | Covers Claude Code, Codex CLI, Aider and scripts in one step | | Product backend | Sidecar or systemd service, gateway base URL | No code change; restores streams and tool calls | | CI jobs that call models | `shadow run` in the job, gateway URLs | Test data and logs are masked too | | Shared config | `shadow.toml` in your infra repo | Same terms and policy everywhere | ### Laptops ```sh curl -fsSL https://raw.githubusercontent.com/Tiny-Bubble-Company/shadow/main/packaging/install.sh | sh shadow init --org "Acme Labs Inc." shadow install --apply ``` Keep a shared `shadow.toml` in your infra repo and copy it into each engineer's data directory (`~/Library/Application Support/shadow/` on macOS, `~/.local/share/shadow/` on Linux): ```sh cp ~/src/infra/shadow/shadow.toml "$HOME/Library/Application Support/shadow/shadow.toml" ``` :::caution[Don't point SHADOW_CONFIG at a repo] When `--config` or `SHADOW_CONFIG` names a file without a `data_dir` key, Shadow uses that file's folder as its data directory. Pointing it into a git checkout would put the vault key there. Copy the file into the data directory instead, or set an absolute `data_dir` in it. ::: ```toml organization = ["Acme Labs Inc."] terms = ["org:Globex", "org:Initech", "Project Falcon"] internal_domains = ["acme.example", "acme.internal"] [policy.always] secret = "redact" ``` ### The product feature ```yaml services: api: image: acme-api:latest environment: OPENAI_BASE_URL: http://shadow:8877/gw/openai/v1 ANTHROPIC_BASE_URL: http://shadow:8877/gw/anthropic shadow: image: shadow:local # docker build -f packaging/Dockerfile -t shadow:local . volumes: - shadow-data:/data - ./shadow.toml:/data/shadow.toml:ro volumes: shadow-data: ``` Keep the volume: it holds the persona and vault, so the same customer gets the same fake across requests. See [Docker and Kubernetes](https://getshadow.dev/integrate/docker-kubernetes/) for the Kubernetes sidecar and replica options. If your feature uses tools (sending emails, updating records), read [Mask PII in agents and tool calls](https://getshadow.dev/use-cases/mask-pii-in-llm-agents-and-tool-calls/) first: tools your code runs get real values back; tools the provider runs need a policy decision. ## What to tell customers and auditors Describe the control precisely, because overclaiming shows up in diligence: > "Requests from our application to AI providers pass through an open-source privacy layer on our infrastructure. Detected personal data, customer identifiers, internal hostnames and secrets are replaced with format-preserving pseudonyms before leaving, and restored in responses. The mapping is encrypted and never leaves our infrastructure. Detection is best effort and the provider remains a subprocessor." Keep the AI provider on your subprocessor list. Shadow reduces what it receives; it does not remove it from the data flow. ## Rollout checklist - [ ] Write down which tools and features call which providers - [ ] Create a shared `shadow.toml` with your company, customers and internal domains - [ ] Install on every engineer laptop; source `env.sh` - [ ] Put the product feature behind a Shadow sidecar or service - [ ] Add `shadow status --quiet` to health checks - [ ] Run `shadow scan` on sample tickets, logs and prompts; add missed terms - [ ] Decide `remote_tool_targets` if you use provider-hosted tools - [ ] Note the control in your security policy and SOC 2 system description ## Gaps to know about :::note[Not there yet] - **No audit log export.** The proxy logs intent and counts per request; an optional local audit log is on the [roadmap](https://getshadow.dev/project/roadmap/). - **No published container image.** Build it from `packaging/Dockerfile` in CI and push to your own registry. - **Detection is best effort.** Internal customer IDs and dates are not detected. See the [benchmarks](https://getshadow.dev/benchmarks/). - **Not a full AI gateway.** No auth, budgets or model routing. See [Self-hosted AI gateway with PII masking](https://getshadow.dev/use-cases/self-hosted-ai-gateway-with-pii-masking/). ::: ## Related - [SaaS and technology](https://getshadow.dev/industries/saas-technology/) - [Protect secrets from AI coding assistants](https://getshadow.dev/use-cases/protect-secrets-from-ai-coding-assistants/) - [Redact PII before the OpenAI API](https://getshadow.dev/use-cases/redact-pii-before-sending-to-openai-api/) - [Mid-market](https://getshadow.dev/company-size/mid-market/), for when you outgrow a shared file in a repo - [How Shadow compares](https://getshadow.dev/compare/) ## Frequently asked questions ### Do startups need to worry about customer data in AI prompts? Yes, as soon as you have customers. Support replies, debugging sessions and AI product features all send customer data to your AI provider, and enterprise buyers ask about it in security reviews. Masking it early is cheaper than retrofitting it. ### Will Shadow help us pass SOC 2? It can be one documented control for data sent to AI subprocessors, which supports the confidentiality and privacy criteria. SOC 2 is an auditor's attestation of your whole control set; Shadow does not provide a report or certification. ### How do we add Shadow to our product's AI feature? Run Shadow as a sidecar or local service next to the backend and set the model client's base URL to the gateway, for example OPENAI_BASE_URL=http://127.0.0.1:8877/gw/openai/v1. Requests are masked and responses, streams and tool calls restored. ### Does Shadow replace an AI gateway like LiteLLM or Portkey? No. Shadow masks and restores data; it does not do routing across models, budgets, rate limits or key management. You can chain them: your app calls Shadow, and Shadow forwards to the gateway through a custom route. ### What happens if Shadow is down? Requests fail rather than bypass it: gateway calls to 127.0.0.1:8877 are refused and proxied tools cannot connect. Use shadow status --quiet as a health check and run it as a service that restarts on failure. --- # AI data privacy for small businesses: use ChatGPT-style tools without leaking customer data Source: https://getshadow.dev/company-size/small-business/ > A practical guide for SMBs: keep customer names, emails and payment details out of AI tools with free, self-hosted Shadow. Setup, limits and checklist. Small businesses use AI for the same things larger ones do: answering customer emails, summarising orders, drafting quotes, cleaning up spreadsheets. Shadow keeps the customer data in those requests on your computers by swapping it for realistic fakes before it reaches the AI provider, then putting the real details back in the answer. It is free, but it is a technical tool: it needs someone to install it, and it protects API-based AI tools, not the chatgpt.com website. ## What matters for a small business | Concern | Reality for an SMB | How Shadow fits | |---|---|---| | Budget | No money for enterprise DLP | Free, open source, no subscription | | Time | Nobody owns "AI security" full time | Install once per computer; runs in the background | | Customer trust | One leaked customer list can hurt more than a fine | Customer names, emails, phones, cards and IBANs replaced before leaving | | Everyday tools | Staff use whatever AI tool is easiest | Works when the tool uses an AI provider's API | | Regulation | GDPR, state privacy laws, PCI DSS if you take cards | Supports data minimisation; [never a compliance guarantee](#honest-limits) | ## The pattern that works Banning AI rarely works; people use it on their phones instead. The pattern that does work is to make the safe option the easy one: 1. **Pick one approved AI tool** that uses an API key: a desktop or web chat client you host or install, or an internal assistant, connected to OpenAI, Anthropic or Azure OpenAI. 2. **Run it through Shadow** on each computer, so customer details are masked automatically. 3. **Write a one-page policy**: use the approved tool for work, never paste customer data into personal AI accounts, ask before uploading files. The [shadow AI governance guide](https://getshadow.dev/use-cases/shadow-ai-governance/) explains the approach in more detail. ## Setting it up ### On each computer macOS or Linux: ```sh curl -fsSL https://raw.githubusercontent.com/Tiny-Bubble-Company/shadow/main/packaging/install.sh | sh shadow init --org "Northwind Bakery Ltd" shadow install --apply ``` Windows (PowerShell): ```powershell irm https://raw.githubusercontent.com/Tiny-Bubble-Company/shadow/main/packaging/install.ps1 | iex shadow init --org "Northwind Bakery Ltd" shadow install --apply ``` `shadow install` without `--apply` prints every change first, so whoever installs it can read the plan. With `--apply` it starts Shadow at login and trusts its local certificate. ### One shared settings file Write one `shadow.toml` with your business name, key customers and suppliers, and copy it into each computer's Shadow data folder (`~/Library/Application Support/shadow` on macOS, `%APPDATA%\shadow` on Windows, `~/.local/share/shadow` on Linux): ```toml organization = ["Northwind Bakery Ltd"] terms = ["org:Contoso Catering", "org:Fabrikam Events", "person:Jane Doe"] internal_domains = ["northwind.example"] [files] images = "block" # photos and scans are not masked, so don't send them [policy.always] secret = "redact" ``` Restart Shadow after changing it (or log out and back in). ### Connect the approved tool If the tool lets you set an "API base URL" or "OpenAI-compatible endpoint", use: ```text http://127.0.0.1:8877/gw/openai/v1 ``` Otherwise, if the tool follows the system proxy settings, point the system at Shadow's PAC file, which sends only AI traffic through Shadow: - **macOS:** `networksetup -setautoproxyurl "Wi-Fi" http://127.0.0.1:8877/proxy.pac` - **Windows:** *Settings → Network → Proxy → Use setup script*, address `http://127.0.0.1:8877/proxy.pac` Then check that `shadow status` shows the proxy as running. Details in [Proxy mode](https://getshadow.dev/integrate/proxy-mode/#system-wide-with-a-pac-file). ## Example A staff member asks the approved assistant to reply to a complaint: ```text They type: Reply to Jane Doe (jane.doe@contoso.example, +1 415 555 0134) about order 1182, refunded to card ending 1111. AI sees: Reply to Jane Doe (jane.doe@contoso.example, +1 415 555 0134) about order 1182, refunded to card ending 1111. They read: Dear Jane Doe, ... ``` Jane is the person the reply is for, so her name, email and phone are sent as written (and logged): the AI has to know who it is writing to. Anything else in the request, such as a colleague's name or a full card number, would be replaced with a fake and restored in the answer. The order number and the last four card digits are sent as they are; only full card numbers are detected. To hide the customer too, set `targets = "surrogate"` under `[policy.act]` in `shadow.toml`. ## Rollout checklist - [ ] Choose one approved, API-based AI tool - [ ] Sign the AI provider's business terms or data processing agreement - [ ] Install Shadow on each computer that uses the tool - [ ] Copy the shared `shadow.toml` into each data folder - [ ] Run `shadow scan` on a real customer email to see what is masked - [ ] Write and share a one-page AI policy - [ ] Recheck every few months: new staff, new computers, new tools ## Honest limits :::caution[Know these before you rely on Shadow] - **The chatgpt.com and claude.ai websites are not covered.** Nor are AI apps on phones. - **Photos and scanned documents are not masked.** Text inside images is not read. - **Unusual details are missed.** Order numbers, loyalty IDs and nicknames are not detected unless you add them as terms. - **No admin console.** Each computer is set up individually; there is no central dashboard. - **Not legal advice.** Shadow helps reduce what reaches AI providers. It does not make you compliant with GDPR, PCI DSS or any other law on its own. ::: ## Related - [How to stop employees pasting customer data into ChatGPT](https://getshadow.dev/use-cases/stop-pii-leaks-to-chatgpt/) - [E-commerce and retail](https://getshadow.dev/industries/ecommerce-retail/) and [nonprofits](https://getshadow.dev/industries/nonprofit/) - [Privacy for AI in customer support](https://getshadow.dev/use-cases/privacy-for-ai-in-customer-support/) - [Quick start](https://getshadow.dev/getting-started/quick-start/) - [How Shadow compares](https://getshadow.dev/compare/) ## Frequently asked questions ### Can a small business use ChatGPT safely with customer data? Safer use comes from three things: a short written policy, an approved tool, and a technical control that keeps identifiers out of prompts. Shadow is that control for API-based tools; it replaces customer names, emails, phone numbers, card numbers and IBANs with fakes and restores them in the answer. ### Does Shadow work with the ChatGPT website? No. Shadow protects traffic to AI provider APIs such as api.openai.com. The chatgpt.com web app is not a built-in host. Give staff an approved chat client that uses an API key and runs through Shadow. ### Do we need an IT person to set it up? Someone comfortable with a terminal, for about fifteen minutes per computer. If you use a managed service provider, they can script the install. There is no graphical installer or admin console yet. ### Is it really free for business use? Yes. Shadow is licensed under Apache-2.0, which allows commercial use. There is no subscription, account or usage limit. You still pay your AI provider as usual. ### Does Shadow help with GDPR for a small company? It supports data minimisation and pseudonymisation when staff use AI with customer data, which are GDPR principles. You still need a lawful basis, a data processing agreement with the AI provider and a record of the processing. --- # AI data protection for mid-market companies: roll out PII masking with your MDM Source: https://getshadow.dev/company-size/mid-market/ > Deploy Shadow across hundreds of laptops and internal AI apps: MDM scripts, managed configuration, pilot groups, sidecars and an honest list of gaps. At a few hundred people, AI use is everywhere and inconsistent: some teams have approved tools, some have API keys, and a few internal apps call models directly. Shadow fits this stage as a per-device background service pushed with your MDM, plus a gateway or sidecar for internal apps, all driven by one versioned `shadow.toml`. There is no central console, so plan to use the device and configuration management you already have. ## What matters at this size | Priority | What it means | |---|---| | Consistency | Every laptop protects the same customer names, domains and codenames | | Low-touch rollout | Pushed by IT, not installed by hand | | Internal apps | A few services call models with production customer data | | Proportionate evidence | Enough to answer customers, auditors and the board, without a project team | | Mixed fleet | macOS, Windows and some Linux | ## Recommended architecture | Component | Deployment | Notes | |---|---|---| | Laptops | MDM script per user: install, config, `shadow install --apply` | Proxy mode covers API-based assistants and scripts | | Internal apps on VMs | System-wide systemd unit with a dedicated `User=`, gateway URLs | See [Installation](https://getshadow.dev/getting-started/installation/#run-as-a-background-service) | | Internal apps in containers | Sidecar per app on `localhost` | See [Docker and Kubernetes](https://getshadow.dev/integrate/docker-kubernetes/) | | Extra recall where needed | Presidio or the NER sidecar on the same host or pod | Keep it on loopback: it receives plaintext | | Configuration | One `shadow.toml` in git, pushed by MDM and deployment pipelines | Review changes like code | ## Rolling out to laptops ### 1. Build and host the binary Release binaries are on the [roadmap](https://getshadow.dev/project/roadmap/). Until they exist for your platforms, build in CI from a pinned commit and host the binaries in your software distribution system, so laptops do not need a Rust toolchain: ```sh cargo build --release -p shadow-cli ``` ### 2. Write the managed configuration ```toml organization = ["Acme Holdings Ltd", "Acme Services GmbH"] terms = ["org:Globex", "org:Initech", "Project Falcon", "Nightjar"] internal_domains = ["acme.example", "acme.internal", "corp.acme.example"] allow_domains = ["docs.acme-public.example"] [policy.always] secret = "redact" [files] pdf = "text" images = "block" ``` ### 3. Push a per-user script The service and CA trust are per user (LaunchAgent and login keychain on macOS, systemd user unit on Linux, a Scheduled Task and the user certificate store on Windows), so run the setup in the user's context: ```sh #!/bin/sh # macOS example, run as the signed-in user DATA="$HOME/Library/Application Support/shadow" mkdir -p "$DATA" "$HOME/.local/bin" install -m 0755 /Library/Acme/shadow/shadow "$HOME/.local/bin/shadow" cp /Library/Acme/shadow/shadow.toml "$DATA/shadow.toml" "$HOME/.local/bin/shadow" init --org "Acme Holdings Ltd" "$HOME/.local/bin/shadow" install --apply ``` Adding a certificate to the login keychain may ask the user to confirm. Test the script on each OS in your pilot group, and add `source "/env.sh"` to the managed shell profile so terminal tools use the proxy. ### 4. Health check ```sh shadow status --quiet && echo ok || echo "shadow not running" ``` Use it as an MDM compliance or custom attribute script. ## Internal apps Point each app's model client at Shadow and nothing else changes: ```sh OPENAI_BASE_URL=http://127.0.0.1:8877/gw/openai/v1 ./support-assistant ``` For Azure OpenAI or an internal model server, add routes: ```toml [proxy.routes] azure = "https://acme-prod.openai.azure.com" llm = "https://llm.acme.internal" ``` If an app runs agents, decide how provider-hosted tools are treated (`[policy.act] remote_tool_targets`) and read [Tool calls and agents](https://getshadow.dev/concepts/tool-calls/). ## Rollout plan | Phase | Scope | Exit criteria | |---|---|---| | Pilot | 10–20 users across OSes, one internal app | No connectivity issues for a week; `shadow scan` reviewed on real samples | | Tune | Add missed terms, decide on Presidio for teams with free-text names | Terms list agreed with data owners | | Expand | Department by department via MDM | Health check passing on target devices | | Steady state | Config changes through review; quarterly scan of samples | Owner named for `shadow.toml` | ## Rollout checklist - [ ] Inventory AI tools and which provider APIs they call - [ ] Choose approved, API-based tools for each team - [ ] Build binaries from a pinned commit; host them internally - [ ] Agree the managed `shadow.toml` with security and data owners - [ ] Pilot on macOS, Windows and Linux; check certificate-pinning apps - [ ] Put internal apps behind a gateway or sidecar - [ ] Add `shadow status --quiet` to MDM health checks - [ ] Document the control in your AI policy and risk register ## Gaps to plan around :::note[Not available yet] - **No admin console, policy server or fleet dashboard.** MDM and configuration management are the control plane. - **No central logging or SIEM export.** Each instance logs intent and counts locally; an optional local audit log is a roadmap "Next" item. - **Per-user `shadow terms add` entries** live in each user's `terms.json` and are not synchronised. Put shared terms in `shadow.toml`. - **No coverage for consumer web chat** (chatgpt.com, claude.ai), mobile apps or vendor backends such as GitHub Copilot. Pair Shadow with your web filter or CASB for those. ::: ## Related - [Enterprise](https://getshadow.dev/company-size/enterprise/) - [Shadow AI governance](https://getshadow.dev/use-cases/shadow-ai-governance/) - [Self-hosted AI gateway with PII masking](https://getshadow.dev/use-cases/self-hosted-ai-gateway-with-pii-masking/) - [Configuration reference](https://getshadow.dev/reference/configuration/) - [Benchmarks](https://getshadow.dev/benchmarks/) and [comparisons](https://getshadow.dev/compare/) ## Frequently asked questions ### Can we deploy Shadow with Intune, Jamf or another MDM? Yes, with a script. Shadow has no MDM-specific package yet: the script installs the binary, writes the managed shadow.toml into the user's data directory, and runs shadow init and shadow install --apply as the signed-in user, because the service and CA trust are per user. ### Can IT change the policy centrally? Only by redistributing the configuration file. There is no admin console or policy server. Treat shadow.toml like any other managed configuration: version it, review changes and push it with your MDM or configuration management. ### How do we know Shadow is running on every laptop? Use shadow status --quiet in an MDM compliance or health script: it exits 0 when the proxy answers and 1 otherwise. There is no central fleet view. ### Should internal AI apps use the same setup as laptops? No. Servers and containers should use gateway mode: run Shadow as a systemd service or sidecar and set the app's base URL. No CA is needed and the data path is explicit. ### Can all laptops share one persona and vault? Not as a managed feature yet; shared team identities are on the roadmap. Each laptop generates its own persona, which is fine for privacy. You can copy persona.json, vault.bin and vault.key between machines if you need consistent fakes. --- # Enterprise LLM data protection: where Shadow fits next to DLP, SSE and AI gateways Source: https://getshadow.dev/company-size/enterprise/ > How enterprises use Shadow to mask PII and secrets in AI API traffic at scale: endpoints, sidecars, existing DLP and gateways, and the gaps to plan for. Enterprises already have DLP, a secure web gateway or SSE platform, and often an AI gateway. Shadow fills a specific gap between them: API traffic to AI providers from developer machines, internal applications, agents and pipelines, where blocking breaks the work and placeholders break the answers. It replaces sensitive values with consistent, reversible fakes on the endpoint or next to the workload, so detected values reach the provider only as fakes and the tools keep working. The only real values sent by design are the recipient of a message or action the user asks for, and those a provider-hosted action needs, each one logged, and you can turn both off. ## Where Shadow fits in the stack | Layer | Typical products | Covers | Shadow's role | |---|---|---|---| | Browser and SaaS DLP / SSE | Netskope, Zscaler, Palo Alto, Purview | Web chat apps, uploads, SaaS | Not a replacement. Shadow does not cover chatgpt.com or claude.ai | | AI gateway | LiteLLM, Kong, Portkey, cloud gateways | Routing, keys, budgets, rate limits | Complementary. Chain them: app → Shadow → gateway | | Endpoint API traffic | Few options | SDKs, CLIs, coding assistants, agents | **Primary use.** Proxy mode on laptops and build agents | | Workload API traffic | Gateway guardrails (usually block or redact) | Internal apps, RAG, agents | **Primary use.** Sidecar or service in gateway mode | See [LLM DLP without blocking](https://getshadow.dev/use-cases/llm-data-loss-prevention-dlp/) and the [comparison pages](https://getshadow.dev/compare/) for how specific products differ. ## What matters at enterprise scale - **Coverage you can reason about.** Know which traffic goes through Shadow and which does not. `shadow hosts` lists exactly which hosts and routes are intercepted. - **Consistency.** One reviewed configuration per population (developers, support, data science) and per workload. - **Separation of duties.** Shadow's data directory holds the vault key and CA key. On servers, run it as a dedicated user; in Kubernetes, keep `vault.key` in a secret. - **Egress control.** Make Shadow the only path to providers, so a misconfigured app fails instead of leaking. - **Fail-safe behaviour.** If Shadow is down, proxied and gateway traffic fails rather than bypassing it. Optional detectors fail open; the built-ins always run. ## Reference architecture | Population | Deployment | Configuration | |---|---|---| | Developer laptops | MDM per-user script, `shadow install --apply`, managed `env.sh` | Developer profile: `secret = "redact"`, internal domains, codenames | | Build agents and CI | `shadow run` as a service on the runner, gateway URLs | Same as developers, plus `images = "block"` | | Internal apps | Kubernetes sidecar per pod, `NetworkPolicy` egress | Per-app terms; `[policy.report]` for analytics workloads | | Agent platforms | Sidecar; local tool execution preferred | `remote_tool_targets` set by risk owner | | High-recall workloads | Presidio or the NER sidecar in the same pod | Loopback only | ### Kubernetes sidecar with egress control ```yaml containers: - name: app image: registry.example.com/support-assistant:2.4.1 env: - { name: OPENAI_BASE_URL, value: "http://127.0.0.1:8877/gw/openai/v1" } - name: shadow image: registry.example.com/shadow: args: ["run", "--listen", "127.0.0.1:8877"] readinessProbe: exec: { command: ["shadow", "status", "--quiet"] } volumeMounts: - { name: shadow-data, mountPath: /data } - { name: shadow-config, mountPath: /data/shadow.toml, subPath: shadow.toml } ``` Containers in a pod share a network namespace, so a pod-level `NetworkPolicy` cannot tell the app from the sidecar. Enforce "only Shadow reaches providers" with an egress proxy: set `[proxy] upstream_proxy` on the sidecar and allow provider hosts only from that proxy. For several replicas that should share one identity, use a `ReadWriteMany` volume or provision `persona.json`, `vault.key` and `vault.bin` from a secret. Full details in [Docker and Kubernetes](https://getshadow.dev/integrate/docker-kubernetes/). ### Chaining with an AI gateway ```toml [proxy.routes] llmgw = "https://llm-gateway.acme.internal" ``` The app calls `http://127.0.0.1:8877/gw/llmgw/v1/...`. Shadow masks, the gateway handles keys, quotas and routing, and the provider receives the masked request. Your gateway's logs hold the same masked content, not the real values Shadow detected. ## Rollout checklist - [ ] Map AI traffic: which populations and workloads call which providers, through which tools - [ ] Decide what Shadow covers and what your DLP/SSE covers; document the boundary - [ ] Build signed binaries and the container image from a pinned commit in your pipeline - [ ] Write per-population configuration; review with privacy, security and data owners - [ ] Pilot with a developer group and one production workload - [ ] Run `shadow scan` on representative samples; record misses and tune terms or detectors - [ ] Add egress controls so workloads cannot reach providers without Shadow - [ ] Add health checks (`shadow status --quiet`) to MDM and orchestration - [ ] Record the control, its limits and its owner in the risk register ## Gaps to plan for :::note[Roadmap items, not features] - **No central management plane.** No console, policy server, fleet inventory or RBAC. Configuration is a file distributed by your tooling. - **No SIEM integration or audit log yet.** Today: per-request log lines with intent and counts, and a log entry for every `keep`. An optional local, append-only audit log is a [roadmap](https://getshadow.dev/project/roadmap/) "Next" item; shipping it to your SIEM would be your own log pipeline. - **No shared team identity yet.** Each instance has its own persona unless you distribute the files. Team identities are a "Later" item. - **Per-host and per-tool policy** (different treatments per provider) is a "Later" item. - **Not covered:** consumer web chat, mobile, WebSocket and Realtime APIs, gRPC, SigV4-signed Bedrock requests, certificate-pinning apps, text in images. - **Detection quality** is best effort and should be measured on your data. See the [benchmarks](https://getshadow.dev/benchmarks/). ::: ## Related - [Regulated enterprise](https://getshadow.dev/company-size/regulated-enterprise/) - [Security model](https://getshadow.dev/reference/security/) - [Self-hosted AI gateway with PII masking](https://getshadow.dev/use-cases/self-hosted-ai-gateway-with-pii-masking/) - [Mask PII in LLM agents and tool calls](https://getshadow.dev/use-cases/mask-pii-in-llm-agents-and-tool-calls/) - [Financial services](https://getshadow.dev/industries/financial-services/) and [healthcare](https://getshadow.dev/industries/healthcare/) ## Frequently asked questions ### Does Shadow replace our DLP or SSE platform? No. Enterprise DLP and SSE products cover browsers, SaaS and web chat, and mostly block, warn or coach. Shadow covers API traffic from endpoints, servers and containers, and translates rather than blocks. Most enterprises would run both. ### Can Shadow scale to thousands of devices? Each instance is independent and lightweight, so scale is a rollout question rather than a capacity one. What does not exist yet is a central console, policy server or fleet view; you distribute configuration with existing tooling. ### How does Shadow work in Kubernetes? As a sidecar container in each pod that calls models. The app reaches it on 127.0.0.1 in gateway mode, Shadow keeps its persona and vault on a persistent volume, and a NetworkPolicy can make the sidecar the only path to AI providers. ### What does a security review of Shadow look like? The source is Apache-2.0 and public. Reviewers usually look at the local CA and TLS handling (rustls, path-length-0 CA, only listed hosts decrypted), the encrypted vault (ChaCha20-Poly1305), loopback defaults, absence of telemetry, and the documented limitations. ### Is there commercial support? Shadow is a community open-source project. Plan support as you would for any internal open-source dependency: pin versions, build from source in your pipeline and track the repository's releases and security advisories. --- # Generative AI controls for regulated enterprises: evidence-ready PII masking with Shadow Source: https://getshadow.dev/company-size/regulated-enterprise/ > For banks, insurers, health systems and public bodies: deploying Shadow as a documented AI data control, with risk assessment, testing and change control. In a regulated enterprise, a privacy control is only as useful as the evidence behind it. Shadow can be a documented technical measure for AI use: it pseudonymises detected identifiers before prompts, files and tool results reach an AI provider, keeps the mapping in your environment, and restores real values in the answer. This page covers how to deploy it so that risk, compliance and audit teams can rely on it, and where its limits must be written down. ## What regulators and auditors will ask | Question | How to answer it with Shadow | |---|---| | What data reaches the AI provider? | Masked content. Detected identifiers are replaced; see the coverage table in your assessment | | How do you know the control works? | `shadow scan` results on representative samples, recorded per release and per config change | | What is not covered? | The documented gaps: dates, internal IDs unless configured, images, consumer web chat, Realtime APIs | | Who can reverse the pseudonyms? | Only holders of the data directory: `vault.key` on the endpoint or in a Kubernetes secret | | What happens on failure? | Traffic fails closed. Optional detectors fail open to the built-ins, with a warning | | Is anything sent in clear text by design? | Only `keep` decisions: the recipient when a user asks the model to write to or act on someone, and provider-hosted tool targets, each logged with kind and reason; both configurable off | | How is the control changed? | `shadow.toml` in version control, reviewed, deployed by pipeline or MDM | ## How Shadow supports common requirements | Requirement | Shadow's contribution | |---|---| | GDPR Art. 5(1)(c), 25, 32 | Data minimisation and pseudonymisation by design for AI processing; mapping kept separately and encrypted | | HIPAA minimum necessary | Reduces identifiers in prompts; does not de-identify (dates are not masked) | | PCI DSS v4.0.1 | Card numbers detected (Luhn) and replaced before leaving; helps keep AI providers out of PAN flows | | DORA (since 17 Jan 2025) | Lowers the sensitivity of data shared with ICT third-party AI services; supports your third-party risk measures | | EU AI Act | Supports data governance for AI systems you deploy; high-risk obligations (Annex III) apply from 2 Dec 2027 under the 2026 Digital Omnibus | | ISO/IEC 27001, ISO/IEC 42001, SOC 2 | A documented technical control with configuration and test evidence | | NYDFS Part 500, GLBA Safeguards Rule | A safeguard for nonpublic information sent to AI service providers | :::caution[Not legal advice] Shadow is a technical control. It does not make any system compliant, and none of the above is legal advice. Pseudonymised data remains personal data for you, because you hold the key (GDPR Recital 26). Confirm your obligations with your legal, privacy and compliance teams. ::: ## Recommended deployment Start from the [enterprise architecture](https://getshadow.dev/company-size/enterprise/) and tighten it: ```toml organization = ["Acme Bank plc"] terms = ["org:Globex Holdings", "Project Falcon"] internal_domains = ["acmebank.example", "acmebank.internal"] [presidio] enabled = true url = "http://127.0.0.1:5002/analyze" entities = ["PERSON", "EMAIL_ADDRESS", "PHONE_NUMBER", "IBAN_CODE", "CREDIT_CARD", "US_SSN", "US_BANK_NUMBER", "UK_NINO"] [policy.act] targets = "surrogate" # fake message recipients too; the model writes to a stand-in remote_tool_targets = "surrogate" # never send action targets in clear; hosted actions may fail [policy.always] secret = "redact" national_id = "token" [files] pdf = "text" # or "block" where layout-dependent PDFs are not allowed images = "block" # no OCR, so images never leave ``` Additional controls: - **Build from source** in your pipeline from a reviewed, pinned commit; sign the binary and image with your own keys. - **Dedicated service accounts** on servers, with the hardened systemd unit and a data directory readable only by that account. - **Egress allow-listing**: providers are reachable only from Shadow instances, or through an egress proxy set as `upstream_proxy`. - **Full-disk encryption** on every endpoint that runs Shadow. - **Key handling**: `vault.key` and `ca/ca.key` treated as secrets; never in images or repositories. - **Prefer local tool execution** for agents, so actions on personal data never need `keep`. ## Testing and evidence 1. Build a test corpus of representative, synthetic or approved samples for each workload: prompts, tickets, documents, logs. 2. Run `shadow scan --json` on each sample and record detections by kind. 3. Compare against expected identifiers; record misses and decide: add terms, enable Presidio or the NER sidecar, change workflow, or accept and document. 4. Repeat on every Shadow upgrade and every configuration change, and keep the results with the change record. 5. During rollout, review the per-request log lines (intent, counts, keeps) for unexpected patterns. Public results on standard datasets are on the [benchmarks](https://getshadow.dev/benchmarks/) page; your own corpus matters more. ## Rollout checklist - [ ] DPIA or AI risk assessment that names Shadow as a measure, with its limits - [ ] Data flow diagram showing where masking happens and where keys live - [ ] Reviewed configuration per workload and population - [ ] Test corpus and recorded `shadow scan` results - [ ] Decision record for `targets` (message recipients), `remote_tool_targets`, secrets and files - [ ] Egress controls verified by a negative test (direct provider access fails) - [ ] Change-control process for `shadow.toml` and Shadow upgrades - [ ] AI provider contracts (DPA, BAA where relevant) still in place - [ ] Owner for the control and a review cycle ## Gaps to record in your assessment :::note[Current limitations and roadmap] - **No audit log yet.** Logs contain intent and counts, never values; an optional local append-only audit log is a [roadmap](https://getshadow.dev/project/roadmap/) "Next" item. - **No central console, RBAC or fleet inventory.** - **No certifications, attestations or validation packages.** - **Not detected:** dates, most internal identifiers (account, claim, policy, case numbers) unless listed as terms or found by Presidio or the NER sidecar; text in images and scanned PDFs. - **Not processed:** WebSocket and Realtime APIs, gRPC, SigV4-signed Bedrock requests (forwarded unmasked with a warning). - **Context can identify.** Masking values does not mask meaning. ::: ## Related - [Financial services](https://getshadow.dev/industries/financial-services/), [insurance](https://getshadow.dev/industries/insurance/), [healthcare](https://getshadow.dev/industries/healthcare/), [government](https://getshadow.dev/industries/government-public-sector/) - [GDPR](https://getshadow.dev/use-cases/gdpr-compliant-llm-usage/) and [HIPAA](https://getshadow.dev/use-cases/hipaa-safe-llm-usage/) guides - [Security model](https://getshadow.dev/reference/security/) - [Intent-aware policy](https://getshadow.dev/concepts/intent-policy/) - [How Shadow compares](https://getshadow.dev/compare/) ## Frequently asked questions ### Can Shadow be used as a control in a DPIA or AI risk assessment? Yes, as a technical measure that pseudonymises detected identifiers before data reaches an AI provider. Describe it accurately: detection is best effort, some data types are not covered, and the provider still receives masked content. Test it on your own data and record the results. ### What evidence can Shadow produce for auditors? Today: the versioned configuration, shadow hosts output showing what is intercepted, per-request log lines with intent and masking counts (never values), a log entry for every value sent in clear text, and your own shadow scan test results. A local append-only audit log is on the roadmap. ### Does using Shadow remove the AI provider from our third-party risk assessment? No. The provider still processes masked prompts and may receive context that identifies people or deals. Shadow lowers the sensitivity of what it receives, which can change the risk rating, but contracts, DPAs or BAAs and oversight still apply. ### Is Shadow itself a third party we need to assess? It is open-source software you run, not a service. It has no cloud component and no telemetry, so no data goes to its maintainers. Assess it as an internal software dependency: source review, pinned versions, your own builds and vulnerability tracking. ### Is Shadow validated for GxP or certified for any standard? No. Shadow has no certifications or validation packages. If your use requires computerised system validation, you would validate your configuration and intended use yourself. --- # Shadow reference: CLI, configuration, providers and security Source: https://getshadow.dev/reference/ > Reference documentation for Shadow: every CLI command, every configuration option, supported AI providers, the security model, troubleshooting and FAQ. Look up exact commands, options and guarantees. --- # Configuration Source: https://getshadow.dev/reference/configuration/ > Reference for every shadow.toml setting: persona, detectors, NER model, Presidio, policy, files, proxy and gateway routes, with defaults and examples. Shadow reads `shadow.toml` from the data directory, or from the path given by `--config` or `SHADOW_CONFIG`. **Every key is optional**: an empty file, or no file at all, is a valid configuration. `shadow init` writes a minimal one. [`shadow.example.toml`](https://github.com/Tiny-Bubble-Company/shadow/blob/main/shadow.example.toml) in the repository is an annotated template. ## Locations | | | |---|---| | Config file | `--config `, else `$SHADOW_CONFIG`, else `/shadow.toml` | | Data directory | `--data-dir`, else `data_dir` in the config, else `$SHADOW_HOME`, else the platform default | | macOS default | `~/Library/Application Support/shadow` | | Linux default | `~/.local/share/shadow` | | Windows default | `%APPDATA%\shadow` | When the config file has no `data_dir`, the data directory is the folder that contains the file. A relative `data_dir` is resolved against that folder. :::caution If you point `--config` or `SHADOW_CONFIG` at a shared file, for example one checked into a repository, set `data_dir` in it or pass `--data-dir`. Otherwise Shadow creates `vault.key` and the vault next to that file. ::: ## Top level | Key | Type | Default | Description | |---|---|---|---| | `data_dir` | path | see above | Where `persona.json`, `vault.bin`, `vault.key`, `terms.json` and `ca/` live | | `organization` | list of strings | `[]` | Legal names of your organisation(s). The first maps to the persona company. The bare name without its legal suffix is protected too | | `terms` | list of strings | `[]` | Dictionary terms: codenames, customers, projects, people. Optional kind prefix: `person:`, `org:`, `term:`, `hostname:`, `email:`, `username:` | | `internal_domains` | list of strings | `[]` | Domains always treated as internal and masked in URLs, hostnames and emails, including subdomains | | `allow_domains` | list of strings | `[]` | Public domains never masked, added to the built-in allow list | | `allow_terms` | list of strings | `[]` | Values never masked, even when detected (case-insensitive) | ```toml organization = ["Acme GmbH"] terms = ["Project Falcon", "Nightjar", "org:Globex", "person:Jane Doe"] internal_domains = ["acme.com", "acme.internal"] allow_domains = ["mycompany-public-docs.io"] allow_terms = [] ``` ## `[persona]` | Key | Type | Default | Description | |---|---|---|---| | `region` | string | `"auto"` | ISO code of the fake region: `CN`, `JP`, `KR`, `BR`, `US`, `DE`, `IN`, `AU`, `MX`, `ZA`, `CA`. `auto` picks a region far from the machine's timezone | | `company` | string | generated | Override the persona company name | | `domain` | string | generated | Override the persona company domain | The persona is generated once. Changing `region` later has no effect until you run `shadow init --force`. ## `[detectors]` All `true` by default. See [Detection](https://getshadow.dev/concepts/detection/). | Key | Detects | |---|---| | `email` | Email addresses | | `phone` | Phone numbers | | `person` | Person names | | `address` | Street addresses | | `iban` | IBANs (mod-97 checked) | | `credit_card` | Card numbers (Luhn checked) | | `national_id` | SSN, UK NINO, Aadhaar | | `ip` | IPv4 and IPv6 addresses | | `mac` | MAC addresses | | `hostname` | Non-public hostnames and internal domains | | `secret` | API keys, tokens, private keys, password assignments | | `path` | Usernames in home-directory paths | | `org` | Organisation names with a legal form (`GmbH`, `Ltd`, `S.A.`...) | | `username` | Usernames from context (`ssh user@host`, `username=`, URL userinfo) | | `date_of_birth` | Dates in a birth context ("born", "DOB", "Geburtsdatum") | | `fields` | Labelled fields (`Name:`, `Address:`, `Tel:`), CSV columns and signature names | | `public_allowlist` | Keeps well-known public names (companies, places, public figures) that name detectors would otherwise mask | | `local_facts` | OS user, hostname, git `user.name` / `user.email`, local domain | ## `[model_detector]` The optional local NER sidecar in `ml/`. | Key | Type | Default | Description | |---|---|---|---| | `enabled` | bool | `false` | Call the sidecar for every scanned string | | `url` | string | `"http://127.0.0.1:8878/detect"` | Must be a loopback URL | | `timeout_ms` | integer | `300` | Per-call timeout. On timeout or error Shadow continues without model results | | `min_score` | float | `0.6` | Minimum entity score to accept | ## `[presidio]` A [Microsoft Presidio](https://getshadow.dev/concepts/detection/#presidio) Analyzer as an extra detector. | Key | Type | Default | Description | |---|---|---|---| | `enabled` | bool | `false` | Call Presidio for every scanned string | | `url` | string | `"http://127.0.0.1:5002/analyze"` | The Analyzer's `/analyze` endpoint | | `language` | string | `"en"` | Language passed to Presidio | | `min_score` | float | `0.5` | Minimum score to accept a Presidio finding | | `entities` | list of strings | `[]` | Presidio entity types to request; empty means all | Shadow fails open if Presidio is unreachable: it logs a warning and uses its built-in detectors. ## `[policy]` The [intent-aware policy](https://getshadow.dev/concepts/intent-policy/). Treatments are `surrogate`, `token`, `keep` and `redact`. | Key | Type | Default | Description | |---|---|---|---| | `intent` | bool | `true` | Classify each request as `act`, `report`, `code` or `converse`. When `false`, every request is handled as `converse` | ### `[policy.act]` | Key | Type | Default | Description | |---|---|---|---| | `targets` | `keep` \| `surrogate` | `"keep"` | Treatment of the recipient of a message or action (the person you ask the model to write to or act on): names, emails, phones, companies, addresses, IBANs and usernames in the recipient phrase. `keep` passes them through so the model writes to the right person; each keep is logged. `surrogate` sends a stand-in and restores the real recipient in the answer and in tool-call arguments | | `remote_tool_targets` | `surrogate` \| `keep` | `"keep"` | Treatment of what an action needs when the tool runs in the provider's cloud (hosted MCP, server-side tools): the contact details, people, companies, addresses, usernames and IBANs named in the instruction (and, for a follow-up that names no email or phone, the email, phone and person values anywhere in the request, including pasted documents and tool results). Secrets, cards, national IDs, hostnames and IPs stay masked. Each keep is logged | ### `[policy.report]` Per-kind treatments for `report` requests. Keys are detector kinds (`email`, `phone`, `person`, `org`, `term`, `address`, `iban`, `credit_card`, `national_id`, `ip`, `mac`, `hostname`, `secret`, `username`) plus `default`. | Key | Default | |---|---| | `person` | `"token"` | | `email` | `"token"` | | `phone` | `"token"` | | `address` | `"token"` | | `default` | `"surrogate"` | ### `[policy.always]` Per-kind treatments that apply to every request, regardless of intent. Empty by default. Example: ```toml [policy.always] secret = "redact" # never send secrets in any form; irreversible [REDACTED_SECRET] ``` ## `[files]` [File masking](https://getshadow.dev/concepts/file-masking/) for uploads and embedded files. | Key | Type | Default | Description | |---|---|---|---| | `enabled` | bool | `true` | Mask multipart uploads, base64 file payloads and `data:` URIs | | `pdf` | `text` \| `block` \| `pass` | `"text"` | Send extracted, masked text; refuse PDFs; or pass them unmasked | | `images` | `pass` \| `block` | `"pass"` | Images are not OCR'd. `block` refuses requests that carry images | | `max_bytes` | integer | `52428800` | Largest file Shadow will process (50 MiB) | | `block_oversize` | bool | `false` | Refuse requests with files over `max_bytes` instead of forwarding them unmasked with a warning | ## `[activity]` The [activity dashboard](https://getshadow.dev/concepts/activity-dashboard/) feed. It records counts and categories only, never real values, fakes, prompts or file names. | Key | Type | Default | Description | |---|---|---|---| | `enabled` | bool | `true` | Record activity events | | `persist` | bool | `true` | Keep history in `/activity/activity.jsonl` (mode 0600) | | `retention_days` | integer | `30` | Drop events older than this | | `max_file_mb` | integer | `16` | Rotate the log past this size; one previous file is kept | | `allow_remote` | bool | `false` | Serve the dashboard and activity API to non-loopback clients | ## `[proxy]` | Key | Type | Default | Description | |---|---|---|---| | `listen` | string | `"127.0.0.1:8877"` | Address for the forward proxy and the gateway. Shadow warns if it is not loopback | | `extra_hosts` | list of globs | `[]` | Extra hosts to intercept in proxy mode, e.g. a self-hosted LLM gateway | | `bypass_hosts` | list of globs | `[]` | Hosts never intercepted (blind tunnel), even if built in | | `upstream_proxy` | string | none | Egress relay for intercepted traffic: `http://`, `socks5://` or `socks5h://` | | `strip_headers` | list of strings | see below | Request headers removed before forwarding (case-insensitive) | Default `strip_headers`: ```toml strip_headers = [ "openai-organization", "x-forwarded-for", "x-real-ip", "forwarded", "via", "x-client-ip", "x-stainless-os", "x-stainless-arch", "x-stainless-runtime-version", ] ``` Remove `"openai-organization"` from the list if you rely on a non-default OpenAI organisation. Setting `strip_headers` replaces the default list. ### `[proxy.persona_headers]` Request headers whose value is replaced with a persona value, **when the header is present**. Placeholders: `{user_id}`, `{hostname}`, `{company}`, `{domain}`, `{username}`. ```toml [proxy.persona_headers] "x-request-origin" = "{hostname}" "x-user-id" = "{user_id}" "x-client-hostname" = "{hostname}" ``` These three are the defaults. ### `[proxy.routes]` Extra gateway routes: `http://127.0.0.1:8877/gw//` → `/`. A route with a built-in name overrides it. See [Gateway mode](https://getshadow.dev/integrate/gateway-mode/#built-in-routes). ```toml [proxy.routes] azure = "https://my-resource.openai.azure.com" bedrock = "https://bedrock-runtime.eu-central-1.amazonaws.com" ``` ## Environment variables | Variable | Effect | |---|---| | `SHADOW_HOME` | Data directory | | `SHADOW_CONFIG` | Config file path (same as `--config`) | | `RUST_LOG` | Log filter, e.g. `info`, `debug`, `shadow=debug`. `shadow run` defaults to `info`, other commands to `warn` | ## A complete example ```toml organization = ["Acme GmbH"] terms = ["Project Falcon", "org:Globex", "person:Jane Doe"] internal_domains = ["acme.com", "acme.internal"] [persona] region = "JP" [detectors] mac = false [presidio] enabled = true url = "http://127.0.0.1:5002/analyze" [policy] intent = true [policy.act] remote_tool_targets = "keep" [policy.report] person = "token" email = "token" default = "surrogate" [policy.always] secret = "redact" [files] pdf = "text" images = "block" [proxy] listen = "127.0.0.1:8877" bypass_hosts = ["api.cohere.com"] [proxy.routes] azure = "https://my-resource.openai.azure.com" ``` --- # CLI Source: https://getshadow.dev/reference/cli/ > Reference for the shadow command line: init, run, install, status, mask, unmask, scan, vault, terms, hosts, top, activity and dashboard, with every subcommand and flag. ```text shadow [--config ] [--data-dir ] ``` | Global flag | Description | |---|---| | `--config ` | Path to `shadow.toml`. Default `/shadow.toml`. Also read from `SHADOW_CONFIG`. An explicit path that doesn't exist is an error | | `--data-dir ` | Data directory. Default `$SHADOW_HOME` or the platform data directory | | `-h`, `--help` | Help for any command | | `-V`, `--version` | Print the version | Logs go to stderr. `shadow run` logs at `info` by default, every other command at `warn`. Override with `RUST_LOG`. ## Commands at a glance | Command | Purpose | |---|---| | [`init`](#shadow-init) | Create the persona, vault key and local CA | | [`run`](#shadow-run) | Run the proxy and gateway in the foreground | | [`install`](#shadow-install) / [`uninstall`](#shadow-uninstall) | Background service and CA trust (dry run by default) | | [`status`](#shadow-status) | Configuration, persona, vault and proxy health | | [`identity`](#shadow-identity) | Show the persona | | [`mask`](#shadow-mask) / [`unmask`](#shadow-unmask) | Translate stdin to stdout | | [`scan`](#shadow-scan) | Report sensitive values in a file | | [`vault`](#shadow-vault) | Inspect, export or edit the vault | | [`terms`](#shadow-terms) | Manage protected dictionary terms | | [`hosts`](#shadow-hosts) | List intercepted hosts and gateway routes | | [`top`](#shadow-top) | Live activity view in the terminal | | [`activity`](#shadow-activity) | Print an activity summary | | [`dashboard`](#shadow-dashboard) | Open the web activity dashboard | ## `shadow init` Create the persona, vault key and local CA. Writes a minimal `shadow.toml` if none exists. ```sh shadow init [--region ] [--org ]... [--force] ``` | Flag | Description | |---|---| | `--region ` | Fake region: `CN`, `JP`, `KR`, `BR`, `US`, `DE`, `IN`, `AU`, `MX`, `ZA`, `CA`, or `auto`. Ignored if a persona exists (use `--force`) | | `--org ` | Organisation name to protect. Repeatable. The first maps to the persona company | | `--force` | Delete the existing persona and vault and start over | Also writes `ca/bundle.pem` (system roots + Shadow CA). Safe to run again: existing files are kept unless `--force` is given. ## `shadow run` Run the forward proxy and the `/gw` gateway in the foreground until Ctrl-C. ```sh shadow run [--listen ] ``` | Flag | Description | |---|---| | `--listen ` | Listen address. Default `[proxy] listen`, i.e. `127.0.0.1:8877`. Warns if not loopback | On start it prints the persona, the gateway URL pattern, the proxy setting and the CA path, and the upstream proxy if one is configured. ## `shadow install` Install the background service and trust the local CA. Prints the plan; performs it only with `--apply`. ```sh shadow install [--apply] [--listen ] ``` | Flag | Description | |---|---| | `--apply` | Perform the steps. Without it, nothing changes | | `--listen ` | Listen address for the service | Creates the persona, key and CA first if needed. After the plan, it prints how to point tools at Shadow (gateway URLs, the proxy environment, and the macOS PAC command). With `--apply`, it also writes `env.sh` (`env.ps1` on Windows) to the data directory. See [Installation](https://getshadow.dev/getting-started/installation/#run-as-a-background-service) for the per-OS steps. ## `shadow uninstall` Remove the background service and the CA trust. Same flags as `install`, also a dry run unless `--apply`. The data directory is left in place. ## `shadow status` ```sh shadow status [--quiet] ``` Shows whether the proxy answers on the configured address, the data directory, the persona, the vault size and creation date, the CA path and SHA-256 fingerprint, the model detector, and the upstream proxy. | Flag | Description | |---|---| | `--quiet` | Print nothing; exit 0 if the proxy answers, 1 otherwise. Useful for health checks | ## `shadow identity` ```sh shadow identity [--json] ``` Show the persona: company and domain, primary person and email, location, timezone and locale, user id, username, hostname and colleagues. `--json` prints the full `persona.json` record. ## `shadow mask` Mask stdin to stdout. Uses and updates the same vault as the proxy. ```sh echo "Mail jane@acme.com" | shadow mask shadow mask --json < request.json ``` | Flag | Description | |---|---| | `--json` | Parse stdin as JSON and mask string values only (protocol keys are skipped). Output is pretty-printed | ## `shadow unmask` Unmask stdin to stdout. ```sh shadow unmask < answer.txt shadow unmask --json < response.json ``` | Flag | Description | |---|---| | `--json` | Parse stdin as JSON and unmask every string value | ## `shadow scan` Report sensitive values in a file without changing anything or touching the vault. ```sh shadow scan [--reveal] [--json] ``` | Flag | Description | |---|---| | `--reveal` | Print full values instead of redacted previews (`ja*********om`) | | `--json` | Output a JSON array of `{kind, line, column, detector, score, value}` | The table output shows `KIND`, `AT` (line:column), `VALUE` and `DETECTOR`. ## `shadow vault` ```sh shadow vault stats shadow vault export --i-understand shadow vault forget ``` | Subcommand | Description | |---|---| | `stats` | Entry counts by kind and the vault's creation time | | `export --i-understand` | Print every real↔fake pair as JSON. Contains real values in clear text, so the flag is required | | `forget ` | Remove the mapping for a real or a fake value | ## `shadow terms` ```sh shadow terms add ... shadow terms list ``` | Subcommand | Description | |---|---| | `add ...` | Protect one or more terms. Prefix with `person:`, `org:`, `term:`, `hostname:`, `email:` or `username:` to set the kind. Prints the fake each term maps to. Stored in `terms.json` | | `list` | List all protected terms (from config, `organization` and `terms.json`) with their kinds | ```text $ shadow terms add "Project Falcon" "person:Jane Doe" ✓ Project Falcon → Lumeth Draane ✓ person:Jane Doe → Xiu Wang ``` ## `shadow hosts` List the hosts intercepted in proxy mode (built-ins plus `extra_hosts`, with `bypass_hosts` marked) and the gateway routes (built-ins plus `[proxy.routes]`). ```sh shadow hosts ``` ## `shadow top` A live, full-screen view of the running proxy's [activity](https://getshadow.dev/concepts/activity-dashboard/): totals, a timeline, and breakdowns by data type, destination and app. `--demo` shows synthetic data instead. ```sh shadow top [--demo] ``` ## `shadow activity` Print the activity summary as plain tables, or as JSON with `--json`. `--window` (`-w`) is `1h`, `24h` (default), `7d`, `30d` or `today`. `--demo` summarises synthetic data. ```sh shadow activity [--window ] [--json] [--demo] ``` ## `shadow dashboard` Open the web dashboard (`http://127.0.0.1:8877/shadow/ui`) in your browser. `--print` prints the URL instead; `--demo` opens it with synthetic data. ```sh shadow dashboard [--demo] [--print] ``` ## Exit codes | Code | Meaning | |---|---| | `0` | Success | | `1` | Error (message printed to stderr as `error: ...`), or `status --quiet` with the proxy down | | `2` | Invalid command-line usage | --- # Supported providers and hosts Source: https://getshadow.dev/reference/providers/ > AI providers Shadow supports: OpenAI, Anthropic, Gemini, Azure OpenAI, Bedrock, Mistral and more. Intercepted hosts, gateway routes and API formats. ## Intercepted hosts (proxy mode) In proxy mode, Shadow terminates TLS only for these hosts. Every other host is tunnelled without decryption. `*` matches any run of characters; matching is case-insensitive and exact otherwise (`openai.azure.com.evil.com` does **not** match `*.openai.azure.com`). | Provider | Host pattern | |---|---| | OpenAI | `api.openai.com` | | Anthropic | `api.anthropic.com` | | OpenRouter | `openrouter.ai` | | Google Gemini API | `generativelanguage.googleapis.com` | | Google Vertex AI | `aiplatform.googleapis.com`, `*-aiplatform.googleapis.com` | | Mistral | `api.mistral.ai` | | Groq | `api.groq.com` | | Together AI | `api.together.xyz`, `api.together.ai` | | DeepSeek | `api.deepseek.com` | | xAI | `api.x.ai` | | Azure OpenAI | `*.openai.azure.com` | | Azure AI services | `*.services.ai.azure.com`, `*.cognitiveservices.azure.com` | | AWS Bedrock runtime | `bedrock-runtime.*.amazonaws.com` | | Cohere | `api.cohere.com`, `api.cohere.ai` | | Perplexity | `api.perplexity.ai` | | Fireworks AI | `api.fireworks.ai` | | Cerebras | `api.cerebras.ai` | | Voyage AI | `api.voyageai.com` | Add hosts with `[proxy] extra_hosts` and exclude hosts with `bypass_hosts`. `shadow hosts` shows the effective list. The [PAC file](https://getshadow.dev/integrate/proxy-mode/#system-wide-with-a-pac-file) at `/proxy.pac` is generated from the same list. ## Gateway routes | Route | Upstream | |---|---| | `openai` | `https://api.openai.com` | | `anthropic` | `https://api.anthropic.com` | | `openrouter` | `https://openrouter.ai/api` | | `gemini` | `https://generativelanguage.googleapis.com` | | `google` | `https://generativelanguage.googleapis.com` | | `mistral` | `https://api.mistral.ai` | | `groq` | `https://api.groq.com/openai` | | `together` | `https://api.together.xyz` | | `deepseek` | `https://api.deepseek.com` | | `xai` | `https://api.x.ai` | | `cohere` | `https://api.cohere.com` | | `perplexity` | `https://api.perplexity.ai` | | `fireworks` | `https://api.fireworks.ai/inference` | | `cerebras` | `https://api.cerebras.ai` | Azure OpenAI, Bedrock, Vertex AI and self-hosted endpoints have per-account URLs, so add them under `[proxy.routes]`. See [Gateway mode](https://getshadow.dev/integrate/gateway-mode/#custom-routes). ## API formats Request bodies in any JSON shape are masked. These formats get additional handling: | Format | Request | Streaming restore with split-fake reassembly | Identifier scrubbing | |---|---|---|---| | OpenAI Chat Completions | ✓ | ✓ (content and tool-call arguments) | `user`, `safety_identifier` | | OpenAI Responses | ✓ | ✓ | `user`, `safety_identifier` | | Anthropic Messages | ✓ | ✓ (text, thinking and tool-input JSON deltas) | `metadata.user_id` | | Google Gemini `generateContent` / `streamGenerateContent` | ✓ | ✓ | | | Cohere Chat | ✓ | ✓ | | | Ollama-style NDJSON streams | ✓ | ✓ | | | Other OpenAI-compatible APIs (Groq, Mistral, Together, DeepSeek, xAI, OpenRouter, Fireworks, Cerebras, Perplexity) | ✓ | ✓ (OpenAI format) | `user` | Compressed (`gzip`, `deflate`) request bodies are decoded before masking. [File uploads](https://getshadow.dev/concepts/file-masking/) are masked for OpenAI, Anthropic and Gemini payload shapes. ## Not supported | | Why | |---|---| | WebSocket and Realtime APIs | Not parsed; traffic is passed through | | gRPC | Not parsed | | AWS SigV4-signed requests (Bedrock with IAM) | Modifying the body breaks the signature. Forwarded unmasked with a warning. Bedrock API keys (bearer) are masked normally | | Apps with certificate pinning | They reject the local CA in proxy mode. Use gateway mode | --- # Security model Source: https://getshadow.dev/reference/security/ > Shadow's security model: what it protects from AI providers, what it trusts, how the vault and local CA are secured, and its known limitations. Shadow's job is narrow and specific: **keep the sensitive values in AI requests away from AI providers**, and from everyone downstream of them, while keeping the tools that send those requests useful. It protects the values it detects, in traffic to the AI hosts it intercepts; the limitations below say exactly where that stops. ## What Shadow protects | Asset | Protected from | |---|---| | Personal data in prompts, history, tool results and uploaded files | The provider, its subprocessors, its logs, abuse review, training pipelines, and breaches of any of them | | Your organisation's names, codenames, customers | Same | | Secrets pasted into prompts | Same (as fakes by default, or [redacted](https://getshadow.dev/concepts/intent-policy/#example-a-secret-with-redaction-turned-on)) | | Machine identity: OS user, hostname, git identity, paths | Same | | Request metadata: OpenAI `user`, Anthropic `metadata.user_id`, organisation and OS headers | Same | ## Trust boundaries **Trusted**: the endpoint running Shadow, the user account it runs as, and anything that can read its data directory. **Not trusted**: the network between Shadow and the provider (TLS is used), the provider itself, and anything that receives data from the provider. **Out of scope**: an attacker who already controls your machine or user account. They can read the vault key, the vault and the CA key, and they can read your prompts before Shadow does. ## Design properties - **Local only.** No hosted component, no account, no telemetry. Shadow sends nothing except the masked requests you were already sending, to the hosts you were already sending them to. - **Encrypted vault.** ChaCha20-Poly1305 with a key in `vault.key` (mode `0600`), atomic writes. See [The vault](https://getshadow.dev/concepts/vault/). - **Minimal interception.** In proxy mode only listed AI hosts are decrypted; everything else is a byte-for-byte tunnel. Bypassed hosts are never decrypted. - **Constrained CA.** The local CA has path length 0, its key is created `0600`, it is unique per machine, and `shadow uninstall --apply` removes its trust. - **Loopback by default.** Proxy and gateway listen on `127.0.0.1`. Shadow warns when you bind anything else. The NER sidecar URL must be loopback, and the sidecar refuses non-loopback binds unless forced. - **No values in logs.** Logs contain methods, URLs, counts, intents and kinds, never real or fake values. - **Fail closed on transport, fail open on optional detectors.** If Shadow is down, proxied tools fail to connect rather than bypass it. If the NER sidecar or Presidio is down, the built-in detectors still run. - **Explicit decisions.** Values sent in clear text through `keep` (message recipients and provider-hosted action targets) are logged with kind and reason. Installation steps are printed before they run. - **Memory-safe implementation.** Rust, rustls for TLS, no OpenSSL. ## Limitations These are inherent to the approach or not yet addressed. Read them before relying on Shadow. - **Your IP address and account are visible.** Translation hides content, not who you are to the provider. The API key identifies your account; the source IP identifies your network. `upstream_proxy` can route AI traffic through a relay, but the key still identifies the account. - **Detection is best effort.** Regexes, checksums and dictionaries miss unusual formats and names they don't know. Add your own terms, enable the NER sidecar or Presidio, and test with `shadow scan`. - **Context can identify.** Masking values does not mask meaning. A prompt that describes a unique event, product or situation can identify a company even with every name replaced. - **Not scanned:** text inside images (no OCR), audio, WebSocket and Realtime APIs, gRPC, files above `max_bytes`, files referenced by an earlier upload id. - **`keep` sends real values.** By default, when you ask the model to write to or act on someone ("email Jane", "draft a reply to Jane Doe at Acme GmbH"), the recipient phrase is sent as written: names, emails, phones, companies, addresses, IBANs and usernames in it, each logged. Other people and sensitive values in the same request stay masked. Set `[policy.act] targets = "surrogate"` to send a stand-in recipient instead. Also by default, when an action runs in a provider-hosted tool (hosted MCP, web search, code execution), what it needs is sent in clear text so the action works: the contact details, people, companies, addresses, usernames and IBANs named in the instruction, and for a follow-up that names no email or phone, the email, phone and person values anywhere in the request (earlier messages, pasted documents, tool results). Each keep is logged. Secrets, cards, national IDs, hostnames and IPs stay masked. Set `remote_tool_targets = "surrogate"` if that is not acceptable. - **Only listed hosts are intercepted.** Traffic to hosts outside the [intercept list](https://getshadow.dev/reference/providers/) and `extra_hosts` is tunnelled unmasked. That includes AI web apps in a browser (chatgpt.com, claude.ai, gemini.google.com), phone apps, and tools that talk to their vendor's own backend. - **Whole values, plus name parts.** Restoration replaces whole fakes and a fake person's first or last name on its own. A name part is not restored if it is under 3 characters, shared by two fake people, or itself a real name in the vault. Other fragments (the last four digits of a card, part of an email or address, a company name without its legal suffix) stay fake in the answer. - **Signed requests** (AWS SigV4) are forwarded unmasked with a warning. - **Certificate-pinning apps** reject the local CA in proxy mode. - **Meaning can shift.** The model reasons about the persona: a persona in Shenzhen gets answers about Chinese holidays. Pick a region that suits your work. - **Coincidental fakes.** If a model produces text that happens to equal a fake on its own, it is translated back too. This is rare, and harmless in practice. - **Surrogate secrets look real.** A fake API key has the same shape as a real one. That keeps code and configs coherent, but if you need a guarantee that no secret-shaped value is sent, use `[policy.always] secret = "redact"`. ## Operational guidance - Use full-disk encryption on machines that run Shadow. - Never share the data directory. It contains the vault key and the CA key. - On servers, run Shadow as a dedicated user with the systemd unit's hardening, and restrict the data directory to that user. - Keep the proxy and gateway on loopback or inside the pod. Anyone who can reach the port can use your persona and have responses unmasked with your vault. - Review the per-request log during rollout and run `shadow scan` on representative content. ## Reporting a vulnerability Please report security issues privately. See [`SECURITY.md`](https://github.com/Tiny-Bubble-Company/shadow/blob/main/SECURITY.md). --- # FAQ Source: https://getshadow.dev/reference/faq/ > Answers to common questions about Shadow: latency, streaming, message recipients, agents, files, answer quality, Presidio, DLP, telemetry and licensing. ### Does Shadow send anything anywhere? Only the masked requests you were already sending, to the providers you were already using. There is no telemetry, no account and no hosted service. Optional detectors (the NER sidecar, Presidio) are called on addresses you configure, normally loopback. ### Does the model's answer get worse? Usually not noticeably, which is the point of realistic fakes. A fake email is still an email, a fake card number still passes Luhn, and the persona keeps names, domains and regions consistent with each other. Answers can shift where the persona's region matters (holidays, phone formats, legal context); pin `[persona] region` if that matters to you. For bulk data in reports, typed tokens avoid the model inventing facts about fake people. ### Can the provider reverse the fakes? No. Fakes are generated randomly from the persona. The mapping exists only in your encrypted vault. ### What happens if Shadow is down? In gateway mode, requests to `127.0.0.1:8877` fail. In proxy mode, tools fail to connect. Shadow never falls back to sending a request direct and unmasked. (Hosts that are not on the [intercept list](https://getshadow.dev/reference/providers/), and anything in `NO_PROXY`, always go direct by design.) The background services restart on failure. To bypass Shadow on purpose, unset `HTTPS_PROXY` or the base URL. ### How much latency does it add? Masking is in-process string work on the request: about 7.5 ms per document with the built-in rules on the [benchmark](https://getshadow.dev/benchmarks/) set, small next to model latency. Streams are delayed by at most a few characters (the length of the longest fake). Optional detectors add their own call time: the NER sidecar is capped by `timeout_ms`, and Presidio adds a local HTTP round trip. ### Can I see what was replaced? `shadow run` logs one line per request with the number of values masked, never the values. `shadow scan ` shows what would be replaced in a file. `shadow vault export --i-understand` prints the full mapping. The [activity dashboard](https://getshadow.dev/concepts/activity-dashboard/) shows counts by data type, destination and app. ### Does it work with streaming? Yes. Server-sent events and NDJSON streams are restored on the fly, including fakes split across events and tool-call arguments streamed in pieces. See [Streaming hold-back](https://getshadow.dev/concepts/how-it-works/#streaming-hold-back). ### What if I ask the model to write to someone? The recipient passes through. If the goal is to email, message, call, invite or write a letter to a person ("Draft a reply to Jane Doe at Acme GmbH"), faking that person would make the model greet the wrong name or research the wrong company, so the names, emails, phones, companies and addresses in the recipient phrase are sent as written and each keep is logged. Everything else in the request (other people, health data, IDs, cards, secrets) is still masked. To hide the recipient from the provider too, set `[policy.act] targets = "surrogate"`: the model writes to a stand-in and Shadow restores the real recipient in the answer. See [Intent-aware policy](https://getshadow.dev/concepts/intent-policy/). ### Does it work with agents and tools? Yes. The recipient of an action is passed through by default, and any other fakes in tool-call arguments are restored on your machine before your tools run. Shadow cannot restore inside the provider, so for tools the provider runs itself (hosted MCP, web search, code execution) it keeps real, by default, the contact details, people, companies, addresses, usernames and IBANs the action needs, keeps secrets, cards and IDs masked, and logs each keep. See [Tool calls and agents](https://getshadow.dev/concepts/tool-calls/). ### Does it work with images, PDFs and Office files? Text, CSV, JSON, HTML, XML, DOCX, XLSX and PPTX uploads are masked. PDF text is extracted, masked and sent as text. Images pass through unchanged (no OCR yet) unless you block them. See [File masking](https://getshadow.dev/concepts/file-masking/). ### Can several machines share one identity? Yes. Copy `persona.json`, `vault.bin` and `vault.key` to each machine, or point several processes at one data directory. Concurrent writers are merged on save. ### Why does the persona live far away? So that the fake identity is not a near miss of the real one. A machine in Germany presenting as a Berlin company with different names would still leak a lot. You can pin any supported region. ### Why not just use Presidio? Presidio is an excellent detector, and Shadow can use it. But detection is half the job. Presidio's anonymizer replaces values with placeholders or hashes; Shadow adds consistent, format-preserving fakes from a persona, an encrypted persistent mapping, response restoration (including streams and tool calls), an HTTPS proxy and gateway, and intent-aware policy. Use them together: Presidio for recall, Shadow for everything around it. ### Is this a DLP product? It overlaps with DLP but behaves differently. DLP gateways usually block or redact. Shadow translates, so the tool keeps working and people have no reason to route around it. ### Can Shadow run in a browser or on a phone? No. Shadow runs on macOS, Linux and Windows hosts, servers and containers. Browsers on those machines can use it through the system proxy or PAC file, but only for requests to the [intercepted AI API hosts](https://getshadow.dev/reference/providers/). What people type into chatgpt.com, claude.ai or gemini.google.com is not masked; give them an API-based client that runs through Shadow instead. ### What licence is Shadow under? Apache-2.0. --- # Troubleshooting Source: https://getshadow.dev/reference/troubleshooting/ > Fix common Shadow problems: untrusted certificates, connection refused, gateway 404s, values not masked, fakes left in answers, streaming and vault errors. Start with: ```sh shadow status RUST_LOG=debug shadow run ``` `shadow status` tells you whether the proxy answers, where the data directory is, and which CA is in use. Debug logs show each connection and whether it was intercepted or tunnelled. ## Certificates ### `certificate verify failed`, `self signed certificate in certificate chain`, `UNABLE_TO_GET_ISSUER_CERT_LOCALLY` The client does not trust Shadow's CA. - Run `shadow install --apply`, or trust `ca/ca.pem` by hand ([how](https://getshadow.dev/integrate/proxy-mode/#2-trust-the-local-ca)). - Node.js: set `NODE_EXTRA_CA_CERTS` to `ca/ca.pem`. - Python `requests`: set `REQUESTS_CA_BUNDLE` to `ca/bundle.pem`. `httpx` and OpenSSL-based tools: `SSL_CERT_FILE`. - curl: `CURL_CA_BUNDLE`. - Java: import `ca.pem` into the JVM's truststore with `keytool -importcert`. - Or switch the tool to [gateway mode](https://getshadow.dev/integrate/gateway-mode/), which needs no CA. ### Other HTTPS sites broke after setting the proxy They should not: non-AI hosts are tunnelled without TLS interception. If one did, check that its host is not matched by an `extra_hosts` glob, and add it to `bypass_hosts`. ### An app still fails with the CA trusted It probably pins certificates. Add its host to `bypass_hosts` (it will not be masked) or use a client that supports a base URL. ### I regenerated the CA and things broke `shadow init --force` does not recreate the CA, but deleting `ca/` does. Run `shadow install --apply` again to trust the new one, and re-point `NODE_EXTRA_CA_CERTS` and friends. ## Proxy and gateway ### `Connection refused` on 127.0.0.1:8877 Shadow is not running. Start it with `shadow run`, or check the service: `launchctl print gui/$(id -u)/dev.shadow.proxy` (macOS), `systemctl --user status shadow` (Linux), `schtasks /Query /TN Shadow` (Windows). ### `address already in use` Another process (often a second Shadow) holds the port. Stop it, or use `--listen 127.0.0.1:8878` and update your settings. ### `unknown gateway route "..."` The first path segment after `/gw/` must be a route name. Check `shadow hosts` and add custom routes under `[proxy.routes]`. ### 404 from the provider through the gateway The path after the route is appended to the route's base URL as is. Compare with the [route table](https://getshadow.dev/integrate/gateway-mode/#built-in-routes): for example the OpenAI SDK needs `/gw/openai/v1`, while the Anthropic SDK needs `/gw/anthropic` because it adds `/v1` itself. ### Requests bypass Shadow - The tool ignores `HTTPS_PROXY`. Use its own proxy setting, the system proxy / PAC file, or gateway mode. - `NO_PROXY` matches the provider host. Remove it from `NO_PROXY`. - The host is not on the intercept list. Add it with `extra_hosts`. ## Detection ### A value was not masked 1. Put the text in a file and run `shadow scan file.txt --reveal`. 2. If it is a name, codename or customer, add it: `shadow terms add "person:Jane Doe"`. 3. If it is a domain of yours, add it to `internal_domains`. 4. Check it is not on `allow_terms` or `allow_domains`, and that its detector is enabled. 5. For free-text names, enable the NER sidecar or Presidio. ### Something was masked that shouldn't be Add it to `allow_terms` (exact values) or `allow_domains` (public domains), or disable the detector under `[detectors]`. ### The answer contains a fake instead of the real value The response contained a variant of the fake that is not in the vault, for example a changed case, a fake email split across lines, or a fake that the model abbreviated. Exact and case variants are restored; paraphrases cannot be. Typed tokens for reports avoid most of this. ### Presidio or the NER sidecar seems ignored Both fail open with a warning in the log. Check the URL, that the service is running (`curl http://127.0.0.1:5002/health` for Presidio, `curl http://127.0.0.1:8878/healthz` for the sidecar), and the `min_score`. ## Streaming ### Streams arrive in larger chunks than before Shadow holds back at most the length of the longest fake, so a fake split across events can be restored. That is a few characters, not a buffering of the whole response. If a stream stalls entirely, run with `RUST_LOG=debug` and report it. ## Vault ### `vault.key` permission or decryption errors The key does not match `vault.bin`, or the file was modified. Restore both from the same backup. If neither can be recovered, move them away and Shadow starts a new vault; old fakes will no longer be restored. ## Getting help Open an issue with the output of `shadow --version`, `shadow status`, your OS, the client you used, and a `RUST_LOG=debug` log with any sensitive content removed. Security issues go through [`SECURITY.md`](https://github.com/Tiny-Bubble-Company/shadow/blob/main/SECURITY.md). --- # Shadow project: roadmap, changelog and contributing Source: https://getshadow.dev/project/ > Shadow is free, open-source software under Apache-2.0. See the roadmap and changelog, and learn how to contribute code, detectors, docs and benchmarks. Shadow is developed in the open on GitHub under the Apache-2.0 licence. Contributions of code, detectors, translations, docs and benchmark data are welcome. --- # Roadmap Source: https://getshadow.dev/project/roadmap/ > What is planned for Shadow, the open-source privacy layer for AI: shipped features and upcoming directions. Directions, not promises. Shadow is young. This page lists what we intend to work on next, roughly in priority order. It is a statement of direction, not a commitment to dates. Suggestions and pull requests are welcome: open an issue to discuss before starting on anything large. ## Shipped - Reversible, format-preserving translation with a persistent persona and encrypted vault - HTTPS forward proxy with a local CA, gateway mode, PAC file - SSE and NDJSON restoration with split-fake reassembly, tool-call argument restoration - Built-in regex, checksum, dictionary and local-facts detectors; optional NER sidecar - Intent-aware policy: `act`, `report`, `code`, `converse` with surrogate, token, keep and redact treatments - File masking: text formats, DOCX, XLSX, PPTX in place, PDF as text - Presidio as an additional detector - Service installation on macOS, Linux and Windows; Docker image - Activity dashboard (web UI, `shadow top`, `shadow activity`) recording counts only, and a menu-bar / tray app for macOS and Windows - A reproducible detection benchmark on a held-out test set - Documentation site ## Next - **Release binaries** for macOS (arm64, x86_64), Linux (x86_64, aarch64) and Windows, built and signed in CI, so the install scripts no longer fall back to `cargo`. - **Published container image** on a public registry. - **OCR for images and scanned PDFs**, local and optional, so text in screenshots can be masked. - **WebSocket and Realtime API** support. - **Audit log**: an optional local, append-only record of per-request decisions (intent, counts by kind and treatment, keeps), never values. - **More regions** for personas, and more national ID and phone formats. ## Later - **Team identities**: a shared persona and vault for a group of machines, with key distribution. - **Policy per host and per tool**: different treatments for different providers or tools. - **Language bindings** for `shadow-core` (Python, Node.js) for in-process masking outside Rust. - **Quick bypass** from the menu-bar / tray app. - **Per-detector precision and recall** numbers in the benchmark. ## Not planned - A hosted Shadow service. Shadow is local by design. - Blocking-only DLP features. Shadow translates; blocking is available only where translation cannot work (for example `pdf = "block"`, `images = "block"`). --- # Contributing Source: https://getshadow.dev/project/contributing/ > How to contribute to Shadow: build from source, run the tests, work on the docs and detectors, and send a pull request. Thanks for your interest in Shadow. Bug reports, detector improvements, new provider formats, documentation fixes and test cases are all welcome. The canonical guide is [`CONTRIBUTING.md`](https://github.com/Tiny-Bubble-Company/shadow/blob/main/CONTRIBUTING.md) in the repository; this page summarises it. ## Repository layout ```text crates/shadow-core detection, persona, vault, policy, translation (library) crates/shadow-proxy CA, forward proxy, gateway, files, SSE translation (library) crates/shadow-cli the `shadow` binary ml/ optional PyTorch NER sidecar packaging/ install scripts, launchd / systemd / Windows service, Dockerfile docs-site/ this documentation (Astro Starlight) assets/ logo, banner, diagrams ``` ## Build and test You need Rust 1.88 or newer. Nothing else. ```sh cargo build --workspace cargo test --workspace # unit and integration tests cargo clippy --workspace --all-targets -- -D warnings # lints, as in CI cargo fmt --all # formatting ``` The integration tests run the proxy against a mock upstream, covering gateway routing, TLS interception and SSE restoration. They need no network access and no API keys. The optional NER sidecar has its own tests: ```sh cd ml && python -m unittest discover -s tests ``` ## Documentation The docs site lives in `docs-site/` and uses [Starlight](https://starlight.astro.build/). You need Node.js 22 or newer and pnpm (or npm). ```sh cd docs-site pnpm install pnpm dev # http://localhost:4321/ pnpm build # static site in docs-site/dist ``` The site is published at [getshadow.dev](https://getshadow.dev/) and served from the domain root. `SITE` (default `https://getshadow.dev`) sets canonical URLs, the sitemap and structured data; `BASE` builds for a sub-path. `docs-site/deploy/` has a Docker and Caddy setup for self-hosting a copy. Pages are Markdown or MDX files under `docs-site/src/content/docs/`. The sidebar is configured in `docs-site/astro.config.mjs`. Every page has an *Edit page* link to its source on GitHub. ## Pull requests - Open an issue first for large changes, so we can agree on the approach. - Keep pull requests focused. Add tests for behaviour changes; detector changes need positive and negative cases. - CI must pass: `fmt`, `clippy -D warnings` and `test` on Linux, macOS and Windows. - Update the docs and `CHANGELOG.md` when behaviour changes. - Never put real personal data or real secrets in tests, fixtures or issues. Use obviously fake values. ## Security issues Do not open public issues for vulnerabilities. Follow [`SECURITY.md`](https://github.com/Tiny-Bubble-Company/shadow/blob/main/SECURITY.md). ## Code of conduct This project follows the [Contributor Covenant 2.1](https://github.com/Tiny-Bubble-Company/shadow/blob/main/CODE_OF_CONDUCT.md). --- # Changelog Source: https://getshadow.dev/project/changelog/ > Release notes for Shadow: notable changes, new features and fixes in each version, following Keep a Changelog and Semantic Versioning. All notable changes to Shadow are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project uses [Semantic Versioning](https://semver.org/). The source of truth is [`CHANGELOG.md`](https://github.com/Tiny-Bubble-Company/shadow/blob/main/CHANGELOG.md). ## Unreleased ### Added - **Intent-aware policy.** Each request is classified as `act`, `report`, `code` or `converse`, and every detected value gets a treatment: `surrogate`, `token` (typed placeholders such as `[PERSON_0001]`), `keep` (for message recipients and action targets in provider-hosted tools, always logged) or `redact` (opt-in, irreversible). Configured under `[policy]`. See [Intent-aware policy](https://getshadow.dev/concepts/intent-policy/). - **Session API** in `shadow-core`: `shadow.session_for(&request_json)` and `shadow.session(RequestContext { .. })` with `mask`, `mask_json`, `unmask`, `unmask_json`, `stream_unmasker`, `classification` and `summary`. - **File masking.** Multipart uploads, base64 file payloads (OpenAI `input_file`, Anthropic `document`, Gemini `inline_data`) and `data:` URIs. Text, CSV, JSON, HTML and XML masked as text; DOCX, XLSX and PPTX masked in place; PDF text extracted and masked; images passed through or blocked. Configured under `[files]`. See [File masking](https://getshadow.dev/concepts/file-masking/). - **Activity dashboard.** A privacy-safe activity feed (counts and categories only, never values) served on loopback at `/shadow/ui`, plus `shadow top`, `shadow activity` and `shadow dashboard`, and a menu-bar / tray app in `apps/shadow-tray`. See [Activity dashboard](https://getshadow.dev/concepts/activity-dashboard/). - **Name-part restoration.** A fake person's first or last name on its own is restored too ("Dear Hui," becomes "Dear Jane,"), except parts under 3 characters, parts shared by two fake people and parts that are real names in the vault. Other fragments (card endings, email parts, partial addresses) are still not restored. See [Security](https://getshadow.dev/reference/security/). - **Presidio integration.** A Microsoft Presidio Analyzer can be used as an additional detector, merged with the built-in detectors and failing open. Configured under `[presidio]`. - **Documentation site** built with Starlight, plus a new README, brand assets, contributing guide, security policy and code of conduct. ### Changed - **Message recipients are passed through.** A request whose goal is to contact or act on someone is now `act`, including compose requests ("draft/write/compose/prepare a reply, email, letter, reminder, invitation..."). The recipient phrase (the words after "to", "for" or "at" following the verb, or right after "email", "call" and similar) is kept: names, emails, phones, companies, addresses, IBANs and usernames in it reach the provider as written, so the model greets, researches and addresses the right person. Each keep is logged. Everything else in the request (other people, health data, IDs, cards, secrets, hostnames) is still masked. `[policy.act] targets` now defaults to `"keep"`; set `targets = "surrogate"` for the previous behaviour, where the model sees a stand-in recipient and Shadow restores the real one in the answer and in tool-call arguments. See [Intent-aware policy](https://getshadow.dev/concepts/intent-policy/). - **Goal-preserving intent.** Action verbs are also detected after "to", "and", "then" and "you". For provider-hosted actions with `remote_tool_targets = "keep"`, usernames and IBANs named in the instruction are kept real, and a follow-up that names no email or phone keeps the emails, phones and people anywhere in the request. Secrets, cards, national IDs, hostnames and IPs stay masked; every keep is logged. See [Intent-aware policy](https://getshadow.dev/concepts/intent-policy/). ## 0.1.0 Initial release. ### Added - `shadow-core`: detection (email, phone, person, address, IBAN with mod-97, cards with Luhn, SSN / NINO / Aadhaar, IP, MAC, hostnames, secrets, home-directory usernames, local machine facts), dictionary terms, persistent persona with 11 regions, format-preserving fakes, ChaCha20-Poly1305 encrypted vault with atomic, multi-process-safe writes, JSON and streaming unmaskers, custom detector trait. - `shadow-proxy`: HTTPS forward proxy with a local CA that intercepts only known AI hosts, `/gw/` gateway with 14 built-in routes, PAC file, SSE and NDJSON restoration with split-fake reassembly for OpenAI, Anthropic, Gemini, Cohere and Ollama-style streams, identifier and header scrubbing, upstream proxy support, SigV4 detection. - `shadow` CLI: `init`, `run`, `install` / `uninstall` (dry run by default), `status`, `identity`, `mask`, `unmask`, `scan`, `vault`, `terms`, `hosts`. - Optional PyTorch NER sidecar in `ml/`. - Packaging: install scripts for macOS, Linux and Windows; launchd, systemd and Scheduled Task integration; Dockerfile.