Streaming Redaction
Overview
VeilData supports chunk-based streaming redaction, enabling efficient processing of large files and real-time streams while correctly detecting entities that span across chunk boundaries.
Features
Streaming Buffer: Process text in chunks with minimal memory footprint
Cross-Chunk Detection: Detect entities split across chunk boundaries using sliding window
REST API: FastAPI endpoint for streaming HTTP requests/responses
CLI Support:
--streamflag for processing large filesReversible: Full TokenStore integration for reveal operations
Quick Start
Python API
from veildata.detectors import RegexDetector
from veildata.pipeline import DetectionPipeline
from veildata.streaming_buffer import StreamingRedactionBuffer
# Setup
patterns = {"EMAIL": r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}"}
detector = RegexDetector(patterns)
pipeline = DetectionPipeline(detector)
buffer = StreamingRedactionBuffer(pipeline, overlap_size=512)
# Process chunks
for chunk in ["My email is john.do", "[email protected] here"]:
output = buffer.add_chunk(chunk)
print(output, end="")
print(buffer.finalize())
# Output: My email is [REDACTED_1] here
CLI Streaming
# Stream large file
veildata redact large_file.txt --stream --output redacted.txt
# Custom chunk and overlap sizes
veildata redact input.txt --stream \
--chunk-size 8192 \
--overlap 1024 \
--output output.txt \
--store store.json
# With timing
veildata redact input.txt --stream --time --verbose
REST API
Start the API server:
pip install veildata[api]
uvicorn veildata.api.app:app --reload
Stream redaction:
curl -X POST "http://localhost:8000/v1/redact/stream?overlap_size=256" \
-H "Content-Type: text/plain" \
--data-binary @input.txt > output.txt
Get token store:
curl -X POST "http://localhost:8000/v1/redact/stream?return_store=true" \
-H "Content-Type: text/plain" \
--data-binary @input.txt \
-v 2>&1 | grep x-token-store
How It Works
Sliding Window Algorithm
The streaming buffer uses a sliding window approach:
Buffer Accumulation: Chunks are appended to an internal buffer
Safe Zone Calculation: Text before the overlap region is “safe” to output
Cross-Boundary Detection: Entities in the overlap may span to next chunk
Progressive Output: Safe text is redacted and yielded immediately
Overlap Retention: Last N characters are kept for the next iteration
Chunk 1: "My email is john.do"
|<---safe--->|<-overlap->|
"My email is" "john.do" <- kept
Chunk 2: "[email protected] and more"
Buffer: "[email protected] and more"
|<-------safe-------->|<-overlap->|
Output: "[REDACTED_1] and" "more" <- kept
Finalize: "more"
Output: "more"
Entity Detection
Entities are detected using the full buffer (safe + overlap), but only entities entirely within the safe zone are redacted in the current iteration. This ensures:
✅ Entities split across chunks are detected when reassembled
✅ No partial entity text is output unredacted
✅ Minimal memory usage (only overlap region retained)
Configuration
Buffer Parameters
- overlap_size (default: 512)
Characters to retain between chunks
Should be ≥ longest expected entity
Larger = better detection, more memory
Smaller = less memory, may miss long entities
- chunk_size (CLI only, default: 4096)
Bytes to read per chunk
Affects I/O performance
Does not affect detection accuracy
API Parameters
POST /v1/redact/stream
Query parameters:
method: Redaction method (regex,ner_spacy,ner_bert)detect_mode: Detection mode (rules,ml,hybrid)overlap_size: Overlap for cross-chunk detectionchunk_size: Request body chunk sizereturn_store: Include token mappings in response header
Response headers:
X-Redaction-Method: Method usedX-Overlap-Size: Overlap size usedX-Token-Store: JSON token mappings (if requested)
Performance
Benchmarks
Streaming mode provides constant memory usage regardless of file size:
File Size |
Memory (Stream) |
Memory (Batch) |
Speedup |
|---|---|---|---|
1 MB |
~2 MB |
~3 MB |
1.0x |
10 MB |
~2 MB |
~15 MB |
0.95x |
100 MB |
~2 MB |
~150 MB |
0.9x |
1 GB |
~2 MB |
OOM |
∞ |
Note
Benchmarks with overlap_size=512, chunk_size=4096
When to Use Streaming
Use streaming when:
✅ Processing files > 100 MB
✅ Real-time stream processing
✅ Memory-constrained environments
✅ Need progressive output
Use batch mode when:
✅ Small files (< 10 MB)
✅ Maximum performance needed
✅ Need explain mode (not supported in streaming)
Limitations
Warning
Explain mode: Not available in streaming (entities not tracked by position)
Overlap size: Must be ≥ longest entity for reliable detection
Performance: ~10% slower than batch for small files due to buffer overhead
Examples
See Examples for complete, working examples including:
Basic buffer usage
File streaming with progress tracking
API integration
CLI usage patterns
Memory-efficient pipelines
Testing
Run streaming tests:
pytest tests/test_streaming_buffer.py -v # Buffer tests (27 tests)
pytest tests/test_api.py -v # API tests (requires fastapi)
Architecture
Core Components
StreamingRedactionBufferMain buffer with sliding window logic
ChunkMetadataTracking for each processed chunk
stream_redact()Convenience wrapper for generators
app.pyFastAPI application with streaming endpoint
Integration Points
Compatible with all existing
DetectorimplementationsUses standard
DetectionPipelinefor processingFull
TokenStoresupport for reversible redactionDrop-in replacement for batch processing in CLI
API Reference
- class veildata.streaming_buffer.StreamingRedactionBuffer(pipeline: DetectionPipeline, overlap_size: int = 512, store: TokenStore | None = None, redaction_format: str = '[REDACTED_{counter}]')[source]
Bases:
objectBuffer for streaming redaction with cross-chunk entity detection.
This class maintains a sliding window to ensure entities that span chunk boundaries are correctly detected and redacted. It progressively yields sanitized output while maintaining minimal state.
Example
>>> from veildata.detectors import RegexDetector >>> from veildata.pipeline import DetectionPipeline >>> detector = RegexDetector({"EMAIL": r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}"}) >>> pipeline = DetectionPipeline(detector) >>> buffer = StreamingRedactionBuffer(pipeline, overlap_size=20) >>> for chunk in ["My email is john.do", "[email protected] and more"]: ... output = buffer.add_chunk(chunk) ... print(output, end="") >>> print(buffer.finalize())
- Parameters:
pipeline – DetectionPipeline to use for entity detection
overlap_size – Number of characters to retain between chunks for boundary detection
store – Optional TokenStore to record redacted mappings
redaction_format – Format string for redaction tokens (default: “[REDACTED_{counter}]”)
- add_chunk(chunk: str) str[source]
Add a chunk of text and return the redacted output for the safe zone.
The safe zone is the portion of the buffer that is guaranteed not to contain any entities that might continue in the next chunk.
- Parameters:
chunk – New text chunk to process
- Returns:
Redacted text for the safe portion of the buffer
- finalize() str[source]
Process any remaining text in the buffer.
This should be called after all chunks have been added to ensure the overlap region is processed.
- Returns:
Redacted text for the remaining buffer contents
- get_metadata() List[ChunkMetadata][source]
Get metadata about all processed chunks.
- Returns:
List of ChunkMetadata objects
- class veildata.streaming_buffer.ChunkMetadata(chunk_index: int, input_size: int, output_size: int, buffer_size: int, entities_detected: int)[source]
Bases:
objectMetadata about a processed chunk for debugging and tracking.
- veildata.streaming_buffer.stream_redact(chunks: Generator[str, None, None], pipeline: DetectionPipeline, overlap_size: int = 512, store: TokenStore | None = None) Generator[str, None, None][source]
Convenience function for streaming redaction.
This is a simple wrapper around StreamingRedactionBuffer for common use cases.
- Parameters:
chunks – Generator yielding text chunks
pipeline – DetectionPipeline to use for entity detection
overlap_size – Number of characters to retain between chunks
store – Optional TokenStore to record redacted mappings
- Yields:
Redacted text chunks
Example
>>> def read_file_chunks(filepath, chunk_size=4096): ... with open(filepath, 'r') as f: ... while True: ... chunk = f.read(chunk_size) ... if not chunk: ... break ... yield chunk >>> >>> from veildata.engine import build_redactor >>> redactor, store = build_redactor(method="regex") >>> for redacted_chunk in stream_redact(read_file_chunks("input.txt"), redactor): ... print(redacted_chunk, end="")
Future Enhancements
Async streaming buffer for concurrent processing
Adaptive overlap sizing based on detected entity lengths
Streaming reveal endpoint
WebSocket support for real-time streams
Progress callbacks for large files
Examples
See examples/streaming_example.py for complete working examples.