Usage Guide
Complete guide to CloakLLM — installation, configuration, middleware integration, audit logs, and more.
PII protection middleware for LLMs — detect, tokenize, and audit before prompts leave your infrastructure.
Table of Contents
- Installation
- Quick Start
- How It Works
- Configuration Reference
- Multi-Turn Conversations
- Custom Patterns
- LLM-Powered Detection (Ollama)
- Entity Detection Reference
- CLI
- Audit Logs
- Disabling / Re-enabling
Installation
Python
For LiteLLM middleware integration:
Requires Python 3.10+.
JavaScript
Zero runtime dependencies. Requires Node.js 18+.
MCP Server
Depends on cloakllm (the Python SDK).
Quick Start
Python — LiteLLM
One line to protect all your LLM calls:
Python — Standalone Shield
Use the Shield directly without any LLM framework:
JavaScript — OpenAI SDK
One line to wrap your OpenAI client:
JavaScript — Vercel AI SDK
Use as language model middleware:
JavaScript — Standalone Shield
Use the Shield directly without any LLM framework:
MCP — Claude Desktop
Add CloakLLM to your claude_desktop_config.json:
Or using uvx:
The MCP server exposes 3 tools:
sanitize — Detect and cloak PII, returns sanitized text + token map ID.
desanitize — Restore original values using a token map ID.
analyze — Detect PII without cloaking.
How It Works
CloakLLM uses a multi-pass detection pipeline to find PII before it reaches an LLM provider.
3-Pass Detection
-
Regex (both SDKs) — High-precision pattern matching for structured data: emails, SSNs, credit cards, phone numbers, IP addresses, API keys, AWS keys, JWTs, IBANs.
-
spaCy NER (Python only) — Named entity recognition for names, organizations, and locations (PERSON, ORG, GPE). The JS SDK does not include spaCy; instead, these categories are handled by the optional Ollama LLM pass.
-
Ollama LLM (opt-in, both SDKs) — Local LLM-based semantic detection for contextual PII: addresses, dates of birth, medical terms, financial data, and more. Data never leaves your machine.
Tokenization
Detected entities are replaced with deterministic tokens in [CATEGORY_N] format:
john@acme.com→[EMAIL_0]Sarah Johnson→[PERSON_0]123-45-6789→[SSN_0]
Tokens are deterministic — the same input produces the same token within a session. A TokenMap stores the bidirectional mapping and can be reused across multi-turn conversations.
Token injection is prevented by escaping fullwidth brackets in user input.
Audit Logs
Every sanitize/desanitize operation is logged to hash-chained JSONL files:
- No PII stored — only hashes and token counts
- Tamper-evident — each entry's
prev_hashlinks to the previous entry'sentry_hash(SHA-256) - Genesis hash — first entry links to
"0" * 64 - Designed for EU AI Act Article 12 compliance
Configuration Reference
Python ShieldConfig
| Option | Type | Default | Env Var | Description |
|---|---|---|---|---|
spacy_model | str | "en_core_web_sm" | CLOAKLLM_SPACY_MODEL | spaCy model for NER |
ner_entity_types | set[str] | {"PERSON", "ORG", "GPE", "LOC", "FAC", "NORP", "EMAIL", "PHONE"} | — | Entity types for spaCy NER |
detect_emails | bool | True | — | Detect email addresses |
detect_phones | bool | True | — | Detect phone numbers |
detect_ssns | bool | True | — | Detect Social Security Numbers |
detect_credit_cards | bool | True | — | Detect credit card numbers |
detect_api_keys | bool | True | — | Detect API keys |
detect_ip_addresses | bool | True | — | Detect IP addresses |
detect_iban | bool | True | — | Detect IBAN numbers |
custom_patterns | list[tuple[str, str]] | [] | — | Custom (name, regex) patterns |
llm_detection | bool | False | CLOAKLLM_LLM_DETECTION | Enable Ollama LLM detection |
llm_model | str | "llama3.2" | CLOAKLLM_LLM_MODEL | Ollama model name |
llm_ollama_url | str | "http://localhost:11434" | CLOAKLLM_OLLAMA_URL | Ollama server URL |
llm_timeout | float | 10.0 | — | LLM request timeout (seconds) |
llm_confidence | float | 0.85 | — | Confidence threshold for LLM detections |
descriptive_tokens | bool | True | — | [PERSON_0] vs [TKN_A3F2] |
preserve_format | bool | False | — | Preserve format in replacement |
audit_enabled | bool | True | — | Enable audit logging |
log_dir | Path | ./cloakllm_audit | CLOAKLLM_LOG_DIR | Audit log directory |
log_original_values | bool | False | — | Log original PII values (not recommended) |
otel_enabled | bool | False | CLOAKLLM_OTEL_ENABLED | Enable OpenTelemetry |
otel_service_name | str | "cloakllm" | OTEL_SERVICE_NAME | OTel service name |
auto_mode | bool | True | — | Auto-sanitize in middleware |
skip_models | list[str] | [] | — | Model prefixes to skip |
JavaScript ShieldConfig
| Option | Type | Default | Env Var | Description |
|---|---|---|---|---|
detectEmails | boolean | true | — | Detect email addresses |
detectPhones | boolean | true | — | Detect phone numbers |
detectSsns | boolean | true | — | Detect Social Security Numbers |
detectCreditCards | boolean | true | — | Detect credit card numbers |
detectApiKeys | boolean | true | — | Detect API keys |
detectIpAddresses | boolean | true | — | Detect IP addresses |
detectIban | boolean | true | — | Detect IBAN numbers |
customPatterns | Array<{name, pattern}> | [] | — | Custom regex patterns |
llmDetection | boolean | false | CLOAKLLM_LLM_DETECTION | Enable Ollama LLM detection |
llmModel | string | "llama3.2" | CLOAKLLM_LLM_MODEL | Ollama model name |
llmOllamaUrl | string | "http://localhost:11434" | CLOAKLLM_OLLAMA_URL | Ollama server URL |
llmTimeout | number | 10000 | — | LLM request timeout (ms) |
llmConfidence | number | 0.85 | — | Confidence threshold for LLM detections |
descriptiveTokens | boolean | true | — | [PERSON_0] vs opaque tokens |
auditEnabled | boolean | true | — | Enable audit logging |
logDir | string | "./cloakllm_audit" | CLOAKLLM_LOG_DIR | Audit log directory |
logOriginalValues | boolean | false | — | Log original PII values (not recommended) |
autoMode | boolean | true | — | Auto-sanitize in middleware |
skipModels | string[] | [] | — | Model prefixes to skip |
Environment Variables
These work across all three SDKs:
| Variable | Default | Description |
|---|---|---|
CLOAKLLM_LOG_DIR | ./cloakllm_audit | Audit log directory |
CLOAKLLM_LLM_DETECTION | false | Enable LLM-based detection |
CLOAKLLM_LLM_MODEL | llama3.2 | Ollama model for LLM detection |
CLOAKLLM_OLLAMA_URL | http://localhost:11434 | Ollama server URL |
CLOAKLLM_SPACY_MODEL | en_core_web_sm | spaCy model (Python only) |
CLOAKLLM_AUDIT_ENABLED | true | Enable/disable audit logging (MCP) |
CLOAKLLM_OTEL_ENABLED | false | Enable OpenTelemetry (Python only) |
OTEL_SERVICE_NAME | cloakllm | OpenTelemetry service name (Python only) |
Multi-Turn Conversations
Reuse the token map across turns so the same entities always map to the same tokens.
Python
JavaScript
Custom Patterns
Add your own regex patterns to detect domain-specific PII.
Python
JavaScript
LLM-Powered Detection (Ollama)
Both SDKs support an optional local LLM pass via Ollama for detecting PII that requires contextual understanding.
Enabling
Or via environment variable:
What It Catches
| Category | Examples |
|---|---|
ADDRESS | 742 Evergreen Terrace, Springfield |
DATE_OF_BIRTH | born January 15, 1990 |
MEDICAL | diabetes mellitus, blood type A+ |
FINANCIAL | account 4521-XXX, routing 021000021 |
NATIONAL_ID | TZ 12345678 |
BIOMETRIC | fingerprint hash F3A2... |
USERNAME | @johndoe42 |
PASSWORD | P@ssw0rd123 |
VEHICLE | plate ABC-1234 |
In the JS SDK, the LLM pass also detects PERSON, ORG, and GPE (since JS has no spaCy NER).
Configuration
| Option | Python | JavaScript | Default |
|---|---|---|---|
| Model | llm_model | llmModel | "llama3.2" |
| Server URL | llm_ollama_url | llmOllamaUrl | "http://localhost:11434" |
| Timeout | llm_timeout | llmTimeout | 10.0s / 10000ms |
| Confidence | llm_confidence | llmConfidence | 0.85 |
If Ollama is not running, the LLM pass is silently skipped.
Entity Detection Reference
| Category | Examples | Detection Method |
|---|---|---|
EMAIL | john@acme.com | Regex |
PHONE | +1-555-0142, 050-123-4567 | Regex |
SSN | 123-45-6789 | Regex |
CREDIT_CARD | 4111111111111111 | Regex |
IP_ADDRESS | 192.168.1.100 | Regex |
API_KEY | sk-abc123..., AKIA... | Regex |
AWS_KEY | AKIA1234567890ABCDEF | Regex |
JWT | eyJhbGciOi... | Regex |
IBAN | DE89370400440532013000 | Regex |
| Custom | (your patterns) | Regex |
PERSON | John Smith, Sarah Johnson | spaCy NER (Python) / Ollama LLM (JS) |
ORG | Acme Corp, Google | spaCy NER (Python) / Ollama LLM (JS) |
GPE | New York, Israel | spaCy NER (Python) / Ollama LLM (JS) |
ADDRESS | 742 Evergreen Terrace | Ollama LLM |
DATE_OF_BIRTH | 1990-01-15 | Ollama LLM |
MEDICAL | diabetes mellitus | Ollama LLM |
FINANCIAL | account 4521-XXX | Ollama LLM |
NATIONAL_ID | TZ 12345678 | Ollama LLM |
BIOMETRIC | fingerprint hash | Ollama LLM |
USERNAME | @johndoe42 | Ollama LLM |
PASSWORD | P@ssw0rd123 | Ollama LLM |
VEHICLE | plate ABC-1234 | Ollama LLM |
CLI
Both SDKs include a CLI for scanning text, verifying audit logs, and viewing statistics.
Python
JavaScript
Example Output
scan:
verify:
stats:
Audit Logs
File Format
Audit logs are stored as JSONL files in the configured log directory:
Entry Structure
Each line is a JSON object with these key fields:
| Field | Description |
|---|---|
event_id | Unique event ID (UUID4) |
seq | Sequence number within the file |
timestamp | ISO 8601 timestamp |
event_type | "sanitize", "desanitize", "shield_enabled", or "shield_disabled" |
entity_count | Number of entities detected |
categories | Map of category → count |
prompt_hash | SHA-256 hash of the original text |
sanitized_hash | SHA-256 hash of the sanitized text |
model | LLM model name (if provided) |
provider | LLM provider name (if provided) |
tokens_used | List of tokens used (no original values) |
latency_ms | Processing time in milliseconds |
metadata | Additional context (e.g., user_id, session_id) |
prev_hash | SHA-256 hash of the previous entry |
entry_hash | SHA-256 hash of this entry |
No original PII is stored in audit logs — only hashes, token counts, and categories.
Verification
Python:
JavaScript:
CLI:
Tamper Detection
The hash chain makes tampering evident. Each entry's entry_hash is computed from its contents including prev_hash. If any entry is modified, deleted, or reordered, the chain breaks and verify_audit() / verifyAudit() reports the specific entries that fail validation.