Veil is a high-performance, developer-first library that prevents PII (Personally Identifiable Information) from leaking into LLMs, logs, and vector databases.
It detects sensitive data locally, masks it deterministically before it leaves your infrastructure, and restores it seamlessly in the response.
Sending raw customer data (Emails, Credit Cards, National IDs) to OpenAI or Anthropic is a security risk and a compliance nightmare.
- ❌ The Old Way: Regex spaghetti code scattered across your backend.
- ❌ The "Hard" Way: Buying enterprise DLP appliances that block your AI features.
- ✅ The Veil Way: A simple wrapper that masks data before the call and restores it after, preserving the LLM's context.
Veil was built for high-throughput environments (API Gateways, Stream Processors).
- ⚡ Blazing Fast: Processed 10,000 requests in ~40ms in benchmarks. Overhead is negligible (< 2µs/op).
- 🔒 Thread-Safe: Fully concurrent design. Validated with massive stress tests (10k+ goroutines).
- 💾 Low-Allocation Logic: Validators for CPF, CNPJ, and Credit Cards use byte-level arithmetic to minimize GC pressure.
- 🎯 Deterministic:
john@example.combecomes<<EMAIL_1>>consistently within a session, allowing the LLM to track references.
go get github.com/veil-services/veil-goVeil replaces PII with tokens like <<EMAIL_1>>. This allows the LLM to understand what the data is without seeing the value.
package main
import (
"fmt"
"github.com/veil-services/veil-go"
)
func main() {
// 1. Initialize with desired detectors
// Error handling omitted for brevity
v, _ := veil.New(
veil.WithEmail(),
veil.WithCreditCard(),
veil.WithConsistentTokenization(true),
)
// 2. Input with sensitive data
prompt := "Check order for john.doe@example.com using card 4111 1111 1111 1111."
// 3. Mask it locally
safePrompt, ctx, _ := v.Mask(prompt)
fmt.Println("Sent to LLM:", safePrompt)
// Output: "Check order for <<EMAIL_1>> using card <<CREDIT_CARD_1>>."
// 4. Simulate LLM Response (The LLM uses the tokens in its logic)
llmResponse := "The card <<CREDIT_CARD_1>> belonging to <<EMAIL_1>> was charged."
// 5. Restore the original values locally
finalResponse, _ := v.Restore(llmResponse, ctx)
fmt.Println("Client sees:", finalResponse)
// Output: "The card 4111 1111 1111 1111 belonging to john.doe@example.com was charged."
}Never leak PII in your observability stack again. The Sanitize helper is a one-way mask.
logger.Info("Incoming request", "body", v.Sanitize(requestBody))
// Logs: "Incoming request body={"user": "<<NAME_1>>", "card": "<<CREDIT_CARD_1>>"}"Veil exports typed errors for robust control flow.
_, err := v.Restore(input, nil)
if errors.Is(err, veil.ErrContextInvalid) {
// Handle missing context gracefully
}| Type | Token | Logic |
|---|---|---|
<<EMAIL_N>> |
Custom Parser (RFC 5322 subset) | |
| Credit Card | <<CREDIT_CARD_N>> |
Luhn Algorithm Validation (Zero-Alloc) |
| IPv4 | <<IP_N>> |
net.ParseIP Validation |
| Global Phone | <<PHONE_N>> |
E.164 Format (+1 555...) |
| UUID | <<UUID_N>> |
Standard Hex Format |
| CPF (Brazil) | <<CPF_N>> |
Mod11 Algorithm Validation (Zero-Alloc) |
| CNPJ (Brazil) | <<CNPJ_N>> |
Mod11 Algorithm Validation (Zero-Alloc) |
Run them yourself: go test -bench=.
BenchmarkParallelMask-12 576781 1963 ns/op (500k+ ops/sec)
TestConcurrency_Massive 10000 41.36 ms (Zero Race Conditions)
Command: go test ./detectors -bench Benchmark -benchmem
Environment: AMD Ryzen 5 5600, Go 1.21
| Benchmark | ns/op | B/op | allocs/op |
|---|---|---|---|
BenchmarkEmailDetector_LongText |
427.7 | 848 | 4 |
BenchmarkPhoneDetector_LongPrompt |
560.0 | 1872 | 5 |
BenchmarkIPDetector_Log |
819.5 | 1872 | 5 |
BenchmarkCreditCardDetector_LongText |
331.1 | 400 | 3 |
BenchmarkCPFDetector_LongText |
241.1 | 400 | 3 |
BenchmarkCNPJDetector_LongText |
290.8 | 400 | 3 |
BenchmarkUUIDDetector_LongText |
241.0 | 400 | 3 |
These numbers are our regression targets—any change to a detector should stay at or below the listed allocations and latencies.
MIT © Veil Services
