Protect sensitive information with privacy tags stripped at multiple layers
Engram provides defense-in-depth privacy protection by stripping sensitive content at two layers: the plugin layer (before data leaves the process) and the store layer (before any database write).
The SQLite store layer strips privacy tags before any database write, regardless of how the data arrives (HTTP API, MCP stdio, CLI).Implementation (internal/store/store.go):
var privateTagRegex = regexp.MustCompile(`(?is)<private>.*?</private>`)func stripPrivateTags(s string) string { result := privateTagRegex.ReplaceAllString(s, "[REDACTED]") result = strings.TrimSpace(result) return result}
When it runs:
Inside AddObservation() — strips title and content before INSERT
Inside UpdateObservation() — strips title and content before UPDATE
Inside AddPrompt() — strips content before INSERT
Inside passive capture — strips learning items before save
Why? Guarantees that the database never contains sensitive data, even if called directly via API or CLI without a plugin.
**What**: Added Stripe integration**Why**: Process payments for premium features**Where**: src/payments/stripe.ts**Learned**: Test mode key is <private>sk_test_abc123</private>, live key is <private>sk_live_xyz789</private>
Stored as:
**What**: Added Stripe integration**Why**: Process payments for premium features**Where**: src/payments/stripe.ts**Learned**: Test mode key is [REDACTED], live key is [REDACTED]
**What**: Set up production database**Why**: Deploy to AWS RDS**Where**: .env.production**Learned**: Connection string format is <private>postgresql://admin:[email protected]:5432/myapp</private>
Stored as:
**What**: Set up production database**Why**: Deploy to AWS RDS**Where**: .env.production**Learned**: Connection string format is [REDACTED]
**What**: Fixed bug in password reset flow**Why**: User <private>[email protected]</private> reported they couldn't reset their password**Where**: src/auth/reset-password.ts**Learned**: Token expiry was set to 1 hour instead of 24 hours
Stored as:
**What**: Fixed bug in password reset flow**Why**: User [REDACTED] reported they couldn't reset their password**Where**: src/auth/reset-password.ts**Learned**: Token expiry was set to 1 hour instead of 24 hours
Team collaboration consideration: If you’re using Git Sync to share memories with your team, privacy tags prevent sensitive data from being committed to the repository.Always wrap secrets in <private> tags before saving to memory.
Secrets in file paths: If you save Fixed bug in /home/user/.ssh/id_rsa, the path is stored as-is. Wrap it: Fixed bug in <private>/home/user/.ssh/id_rsa</private>
Secrets in tool output: If an agent runs a command that outputs secrets to its context, those won’t be automatically redacted unless the agent wraps them in privacy tags before saving.
Inference from context: If you write “API key ends in 123”, an attacker with access to your memory could narrow down the key. Be mindful of indirect leakage.