Skip to main content
For applications that need to persist PII maps across requests or page reloads (like chat applications), Rehydra provides session-based storage.

Storage Providers

ProviderEnvironmentPersistenceUse Case
InMemoryPIIStorageProviderAllNoneDevelopment, testing
SQLitePIIStorageProviderNode.js, BunFile-basedServer-side applications
IndexedDBPIIStorageProviderBrowserBrowser storageClient-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