A lightweight framework for redacting and revealing Personally Identifiable Information (PII).
Modern AI systems touch sensitive data every day.
VeilData makes it easy to redact, anonymize, and later restore information.
From PyPI
pip install veildataRun from Docker
docker build -t veildata .
alias veildata="docker run --rm -v \$(pwd):/app veildata"
veildata redact data/input.csv --out data/redacted.csvRunning in Docker
docker build -t veildata .
docker run -it ghcr.io/veildata/veildata:latestFor Development
git clone https://github.com/VeilData/veildata.git
cd veildata
curl -LsSf https://astral.sh/uv/install.sh | sh
uv venv
uv syncMark sensitive data
veildata redact input.txtExample config.yaml
patterns:
EMAIL: "\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}\\b"Reveal previously redact data
veildata reveal redacted.txt** Using Docker**
docker run --rm -v $(pwd):/app veildata redact input.txt --out redacted.txtRedact a file, save the output, and keep a token store for reversibility:
veildata redact input.txt --output redacted.txt --store store.jsonReveal the file using the stored tokens:
veildata reveal redacted.txt --store store.jsonRegex-based Redaction
from veildata import Compose, RegexRedactor, TokenStore
# Create a shared TokenStore for reversible Redaction
store = TokenStore()
# Define your Redaction pipeline with the shared store
redactor = Compose([
RegexRedactor(r"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}", store=store), # email
RegexRedactor(r"\b\d{3}-\d{3}-\d{4}\b", store=store), # phone
])
text = "Contact John at john.doe@example.com or call 123-456-7890."
# --- redact the data ---
redacted_text = redactor(text)
print(redacted_text)
# -> Contact John at [REDACTED_1] or call [REDACTED_2].
# --- reveal it later ---
revealed_text = store.reveal(redacted_text)
print(revealed_text)
# -> Contact John at john.doe@example.com or call 123-456-7890.spaCy Named Entity Recognition
# Requires `pip install veildata[spacy]`
from veildata.redactors.ner_spacy import SpacyNERRedactor
from veildata import TokenStore
# Shared token store for reversible revealing
store = TokenStore()
redactor = SpacyNERRedactor(
entities=["PERSON", "ORG"],
store=store
)
text = "Apple was founded by Steve Jobs in Cupertino."
redacted = redactor(text)
print(redacted) # -> [REDACTED_1] was founded by [REDACTED_2] in Cupertino.# Requires `pip install veildata[bert]`
from veildata.redactors.ner_bert import BERTNERRedactor
from veildata import TokenStore
store = TokenStore()
redactor = BERTNERRedactor(
model_name="dslim/bert-base-NER",
store=store
)
text = "John Smith works at Google in New York."
redacted = redactor(text)
print(redacted) # -> [REDACTED_1] works at [REDACTED_2] in [REDACTED_3].from pathlib import Path
from veildata import Compose, RegexRedactor, TokenStore
# Setup redactor
store = TokenStore()
redactor = Compose([
RegexRedactor(r"\b\d{3}-\d{3}-\d{4}\b", store=store)
])
# Read from file
input_path = Path("input.txt")
if input_path.exists():
text = input_path.read_text()
# redact
redacted_text = redactor(text)
# Write to file
Path("redacted.txt").write_text(redacted_text)
# Save store for later revealing
store.save("store.json")ml:
spacy:
enabled: true
model: "en_core_web_lg"
pii_labels:
- PERSON
- ORG
- GPE
- LOC
- NORPml:
bert:
enabled: true
model_path: "models/pii-bert-base"
threshold: 0.5
label_mapping:
EMAIL: ["B-EMAIL", "I-EMAIL"]
PHONE: ["B-PHONE", "I-PHONE"]
SSN: ["B-SSN", "I-SSN"]When using --detect-mode hybrid:
- Run regex rules on text → produce spans with types
- Run ML/NLP engines (spaCy + BERT) → produce spans with types + scores
- Merge spans:
- If spans overlap with same type → keep the union
- If spans overlap with different types → configurable resolution
options:
detect_mode: hybrid # default: rules
hybrid:
prefer: ml # ml | rules | longest_span- CI: .github/workflows/ci.yml runs linting, formatting, build, and tests on every push or PR.
- Publish: .github/workflows/publish.yml auto-publishes to PyPI when a new v* tag or release is created.