Documentation
Scan. Rescan. Verify. Any LLM.
Shutapp is a model-agnostic PII and prompt-security firewall. Scan the prompt on the way out, send the redacted version to whichever LLM you like with your own provider key, then rescan the response on the way back. Shutapp never sees your provider key or the raw model output.
If you can call the OpenAI SDK, you already know how to use Shutapp. The two extra calls take three lines.
Install
Thin HTTP client. Two light deps (httpx, cryptography). Python 3.9+. Read the SDK before you trust it.
pip install shutappGet a key
Hit the button on the landing pageand put the key in an environment variable. Stateless signed JWT, valid for 30 days. No accounts, no portal, no “set up your workspace” flow.
export SHUTAPP_API_KEY="sa_live_..."The three lines
Works with any LLM. Shutapp only processes text — it never sees your provider key or the raw response unless you hand it to rescan.
from shutapp import Shutapp
s = Shutapp(api_key="<YOUR_KEY>")
# 1. PII out
safe, session = s.scan(user_input)
# 2. you call the LLM with YOUR provider key
# (we never see it)
reply = call_your_llm(safe)
# 3. PII back, leakage checked
final = s.rescan(reply, session=session).cleaned_textThe scan response includes a decision (allow / flag / block), a risk block with the prompt-injection score, a cost block with cumulative quota, and a signed receipt. You decide what to do with each one. We don't.
Auto-wrap for OpenAI
One line of sugar. Your existing OpenAI code doesn't change. Under the hood guard() scans outbound messages, lets OpenAI respond with your key, and rescans the response before your code sees it.
from openai import OpenAI
from shutapp import guard
# One call. Every chat.completions.create is scanned and rescanned.
# Your existing code doesn't change.
client = guard(OpenAI(), api_key="<YOUR_KEY>")
client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "My email is john@acme.com"}],
)Auto-wrap for Anthropic
from anthropic import Anthropic
from shutapp import guard
client = guard(Anthropic(), api_key="<YOUR_KEY>")
client.messages.create(
model="claude-3-5-sonnet-latest",
max_tokens=512,
messages=[{"role": "user", "content": "My email is john@acme.com"}],
)Detect system-prompt leakage
Register a MinHash fingerprint of your system prompt once. Shutapp stores the digest; the text is discarded. Every rescan can then flag near-verbatim leakage in the response — useful when somebody finally figures out the “repeat your instructions” trick on your support bot.
fp_id = s.register_system_prompt("support-bot", SYSTEM_PROMPT)
# later, on every rescan:
result = s.rescan(llm_response, session=sess, system_fingerprint_id=fp_id)
if result.findings["system_leakage"]["matched"]:
# log, alert, or retry with a tighter system prompt — your call
...Raw HTTP (any language)
Two endpoints. Both return a signed receipt in the response body. If your stack isn't Python, this is the whole API.
# 1. scan
curl https://api.shutapp.world/v1/scan \
-H "X-Shutapp-Key: <YOUR_KEY>" \
-H "Content-Type: application/json" \
-d '{"text":"My email is john@acme.com"}'
# 2. call your LLM yourself, with your own key
# 3. rescan
curl https://api.shutapp.world/v1/rescan \
-H "X-Shutapp-Key: <YOUR_KEY>" \
-H "Content-Type: application/json" \
-d '{"text":"<LLM response>","session_map":{}}'
# verify a receipt offline, no network needed
shutapp verify receipt.json --base-url https://api.shutapp.world// 1. scan on the way out
const scan = await fetch("https://api.shutapp.world/v1/scan", {
method: "POST",
headers: {
"X-Shutapp-Key": "<YOUR_KEY>",
"Content-Type": "application/json",
},
body: JSON.stringify({ text: "My email is john@acme.com" }),
});
const { redacted_text, map, receipt, decision } = await scan.json();
// 2. call the LLM yourself with redacted_text and YOUR provider key
// 3. rescan on the way back
const rescan = await fetch("https://api.shutapp.world/v1/rescan", {
method: "POST",
headers: {
"X-Shutapp-Key": "<YOUR_KEY>",
"Content-Type": "application/json",
},
body: JSON.stringify({ text: llm_response, session_map: map }),
});
const { cleaned_text, findings } = await rescan.json();Verify a receipt offline
Every scan and rescan response includes an Ed25519-signed receipt. The public key is published at /v1/pubkey. Pin its fingerprint and you can verify any receipt without touching the network again. When the auditor asks, you hand them a file and the public key. That's the audit trail.
ok = s.verify(receipt) # True / False
# or, from a shell:
# shutapp verify path/to/receipt.json --base-url https://api.shutapp.worldReceipts in your terminal
After every scan and rescan, the SDK prints one line to stderr. No dashboard. No 11th tab. Where you already are.
$ python my_chatbot.py
[shutapp] scan ok | 11ms | EMAIL_ADDRESS x1, PHONE_NUMBER x1 | rcpt_8f3a2b
[shutapp] scan ok | 14ms | no PII detected | rcpt_91ac02
Assistant: Sure, I'll email john@acme.com.Configure via Shutapp(receipts="stderr" | "stdout" | "silent" | callable).
What Shutapp does NOT do
Prompt injection is unsolved. We detect attacks with a heuristic layer plus ProtectAI's deberta-v3 classifier, and we score them. We do not claim to prevent them and you should be wary of anyone who does.
We do not address training-data poisoning, supply-chain model tampering, RAG retrieval weaknesses, or hallucination — those are different problems and different products. See the coverage matrixfor the honest picture, including the rows where we said “out of scope.”
Need more
Need a self-hosted build, an on-prem deployment, or a custom recognizer for that one weird internal ID format you have? hi@shutapp.world. A human will reply.