Capabilities
Compliance & privacy
GDPR Article 17 and 20 endpoints, per-user crypto-shredding with AES-256-GCM, retention sweeps with hash-chain-aware redaction, and six shipped languages behind a localizer port.
Privacy law turned “delete this user” from a SQL statement into an engineering discipline. Sentinel builds the discipline in: erasure that actually erases, export that actually exports, retention that doesn’t break your audit chain, and user-facing text that speaks the user’s language.
Crypto-shredding: one key per person
PII at rest is encrypted per data subject with AES-256-GCM
(SentinelCryptoShredder, packing nonce | tag | ciphertext). Keys live behind
ISentinelCryptoKeyStore (EF-backed in sentinel_subject_keys), one key per user.
Erasure destroys the subject’s key. From that moment every ciphertext belonging to that
person — including copies in backups you will never enumerate — is noise.
TryDecryptAsync returns null and callers degrade gracefully. This is the same pattern
Relay uses for event-sourced PII, reimplemented in Sentinel.Core without a Relay
dependency.
Erasure and export
MapSentinelPrivacy() mounts the two GDPR workhorses under /sentinel-admin/privacy,
both fenced to realm admins (sentinel:global:manage) and both audited:
POST /export/{userId} (Art. 20) returns the subject’s data as a structured
document: profile, sessions, linked identities, and security events (capped at 1000).
POST /erase/{userId} (Art. 17) runs a fixed order designed so a crash partway
leaves you more erased, never inconsistently half-erased:
- Destroy the crypto key — the point of no return.
- Revoke every session and refresh family; notify OIDC clients via back-channel logout.
- Anonymize the user row (the id survives for referential integrity and audit).
- Delete authenticators, passkeys and linked identities.
- Redact the subject’s ledger entries — chain-aware, below.
The ErasureResult reports exactly what happened (SessionsRevoked,
SecurityEventsRedacted, AuditEntriesRedacted), and user.erased lands in the ledger.
Retention, without sawing through the audit chain
RetentionService sweeps on a schedule (daily by default): security events beyond their
retention (365 days by default) are deleted; admin-audit entries are redacted, not
deleted — payloads nulled while the separately-stored digests keep the
tamper-evident hash chain verifying. You can honor a retention
policy and still prove nobody rewrote history; retention.swept records each pass.
Register the hosted sweeper with AddSentinelRetentionService() or drive
RunOnceAsync() from your own scheduler.
Localization
Every user-facing string — emails, OTP texts, error messages — flows through the
ISentinelLocalizer port. Six locales ship in v1: en, de, fr, es, it, ro, as
embedded resource catalogs with per-locale overridable email templates
(mail.password_reset.subject, mail.email_otp.body, …). Locale negotiation follows
Accept-Language with a per-user override (User.Locale); HTTP error bodies carry
localized messages next to their stable machine-readable codes. Register with
AddSentinelLocalization().
The parts that stay yours
Sentinel erases and exports what Sentinel stores. Your application’s own tables keyed by
the user id are your Article 17 homework — the erasure flow is the hook to trigger it
(subscribe to user.erased via webhook or event sink), not a substitute for it. And
retention numbers are policy decisions: Sentinel enforces whatever you configure, it
doesn’t tell you what your regulator expects.
Migration note
Erasure interacts with migration: imported users get crypto keys on first touch, so people who arrived via an importer are just as erasable as the ones who registered natively.
Learn by building
The tutorials for this area, in order — each with a runnable sample.