For applications that need to persist PII maps across requests or page reloads (like chat applications), Rehydra provides session-based storage.
Storage Providers
| Provider | Environment | Persistence | Use Case |
|---|
InMemoryPIIStorageProvider | All | None | Development, testing |
SQLitePIIStorageProvider | Node.js, Bun | File-based | Server-side applications |
IndexedDBPIIStorageProvider | Browser | Browser storage | Client-side applications |
SQLitePIIStorageProvider is not available in browser builds. Use IndexedDBPIIStorageProvider for browser applications.
Important: Storage Requires Sessions
The storage provider is only used when you call anonymizer.session(). Direct anonymize() calls return the PII map but don’t save it:
// ❌ Storage NOT used - you must handle the PII map yourself
const result = await anonymizer.anonymize('Hello John!');
// result.piiMap is returned but NOT saved to storage
// ✅ Storage IS used - auto-saves and auto-loads
const session = anonymizer.session('conversation-123');
const result = await session.anonymize('Hello John!');
// result.piiMap is automatically saved to storage
Session Example
import {
createAnonymizer,
InMemoryKeyProvider,
SQLitePIIStorageProvider, // or IndexedDBPIIStorageProvider for browser
} from 'rehydra';
// 1. Setup storage (once at app start)
const storage = new SQLitePIIStorageProvider('./pii-maps.db');
await storage.initialize();
// 2. Create anonymizer with storage and key provider
const anonymizer = createAnonymizer({
ner: { mode: 'quantized' },
keyProvider: new InMemoryKeyProvider(),
piiStorageProvider: storage,
});
await anonymizer.initialize();
// 3. Create a session for each conversation
const session = anonymizer.session('conversation-123');
// 4. Anonymize - auto-saves to storage
const result = await session.anonymize('Hello John Smith from Acme Corp!');
console.log(result.anonymizedText);
// "Hello <PII type="PERSON" id="1"/> from <PII type="ORG" id="1"/>!"
// 5. Later (even after app restart): rehydrate - auto-loads and decrypts
const translated = await translateAPI(result.anonymizedText);
const original = await session.rehydrate(translated);
console.log(original);
// "Hello John Smith from Acme Corp!"
Multiple Conversations
Each session ID maps to a separate stored PII map:
// Different chat sessions
const aliceChat = anonymizer.session('user-alice-chat');
const bobChat = anonymizer.session('user-bob-chat');
await aliceChat.anonymize('Alice: Contact me at [email protected]');
await bobChat.anonymize('Bob: My number is +49 30 123456');
// Each session has independent storage
await aliceChat.rehydrate(translatedText1); // Uses Alice's PII map
await bobChat.rehydrate(translatedText2); // Uses Bob's PII map
Multi-Message Conversations
Within a session, entity IDs are consistent across multiple anonymize() calls:
const session = anonymizer.session('chat-123');
// Message 1: User provides contact info
const msg1 = await session.anonymize('Contact me at [email protected]');
// → "Contact me at <PII type="EMAIL" id="1"/>"
// Message 2: References same email + new one
const msg2 = await session.anonymize('CC: [email protected] and [email protected]');
// → "CC: <PII type="EMAIL" id="1"/> and <PII type="EMAIL" id="2"/>"
// ↑ Same ID (reused) ↑ New ID
// All messages can be rehydrated correctly
await session.rehydrate(msg1.anonymizedText); // ✓
await session.rehydrate(msg2.anonymizedText); // ✓
Session Interface
interface AnonymizerSession {
readonly sessionId: string;
// Anonymize text and save to storage
anonymize(
text: string,
locale?: string,
policy?: Partial<AnonymizationPolicy>
): Promise<AnonymizationResult>;
// Load PII map and restore original values
rehydrate(text: string): Promise<string>;
// Load stored PII map
load(): Promise<StoredPIIMap | null>;
// Delete session from storage
delete(): Promise<boolean>;
// Check if session exists
exists(): Promise<boolean>;
}
SQLite Provider (Node.js / Bun)
import { SQLitePIIStorageProvider } from 'rehydra';
// File-based database
const storage = new SQLitePIIStorageProvider('./data/pii-maps.db');
await storage.initialize();
// Or in-memory for testing
const testStorage = new SQLitePIIStorageProvider(':memory:');
await testStorage.initialize();
Dependencies:
- Bun: Uses built-in
bun:sqlite (no additional install)
- Node.js: Requires
better-sqlite3:
npm install better-sqlite3
IndexedDB Provider (Browser)
import {
createAnonymizer,
InMemoryKeyProvider,
IndexedDBPIIStorageProvider,
} from 'rehydra';
// Custom database name (defaults to 'rehydra-pii-storage')
const storage = new IndexedDBPIIStorageProvider('my-app-pii');
const anonymizer = createAnonymizer({
ner: { mode: 'quantized' },
keyProvider: new InMemoryKeyProvider(),
piiStorageProvider: storage,
});
await anonymizer.initialize();
// Use sessions as usual
const session = anonymizer.session('browser-chat-123');
Data Retention
Entries persist forever by default. Manage data with:
// Delete entries older than 7 days
const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
const deletedCount = await storage.cleanup(sevenDaysAgo);
// Delete specific session
await session.delete();
// List all stored sessions
const sessionIds = await storage.list();
Storage Provider Interface
Implement custom storage:
interface PIIStorageProvider {
initialize(): Promise<void>;
save(sessionId: string, data: StoredPIIMap): Promise<void>;
load(sessionId: string): Promise<StoredPIIMap | null>;
delete(sessionId: string): Promise<boolean>;
exists(sessionId: string): Promise<boolean>;
list(options?: ListOptions): Promise<string[]>;
cleanup(olderThan: Date): Promise<number>;
close(): Promise<void>;
}
Without Storage (Simple One-Off Usage)
For simple use cases without persistence:
import {
createAnonymizer,
decryptPIIMap,
rehydrate,
InMemoryKeyProvider
} from 'rehydra';
const keyProvider = new InMemoryKeyProvider();
const anonymizer = createAnonymizer({
ner: { mode: 'quantized' },
keyProvider,
});
await anonymizer.initialize();
// Anonymize
const result = await anonymizer.anonymize('Hello John Smith!');
// Translate
const translated = await translateAPI(result.anonymizedText);
// Rehydrate manually
const key = await keyProvider.getKey();
const piiMap = await decryptPIIMap(result.piiMap, key);
const original = rehydrate(translated, piiMap);
Next Steps