Data Privacy
Selva minimizes the personal data it holds, but it does hold some, and the operator is the data controller, responsible for residency, retention, and erasure requests.
This page is the full inventory: what is stored, who owns the identity data, what erasure reaches, and where it cannot follow.
What every deployment stores
- Opaque session tokens (cookies)
- User id + permissions
- Display names (
user_profiles.display_name) - Invite email addresses (
invites.email), retained after accept or expiry - Audit-event payloads (
audit_events.data), which embed an email forinvite.created(see theDomainEventunion inpackages/platform/src/events/interface.ts) - Solve telemetry (
solve_metrics), keyed byactor_idand deliberately not FK-cascaded, so it survives deletion of the definition or user it refers to
Login IPs are processed by the rate limiter but stay in memory, expire within the rate-limit window, and are never persisted.
Who owns the identity data
How much the auth provider owns depends on which one runs:
- Supabase: credentials and identity live in Supabase
auth.users; Selva holds only the authorization data above. This is the case the “provider owns it” framing describes. - Local: Selva is the auth provider.
auth-users.jsonholds email addresses and PBKDF2 password hashes on the deployment’s own disk (packages/providers/local/src/auth/users.ts). No third party, no credential-isolation claim.
See providers for the full trust boundary.
Erasure
SupabaseDataProvider.onUserDeleted(ctx, userId, { email }) scrubs what FK cascade doesn’t
reach: deletes audit_events the user authored (keyed by plain-text actor_id) and invites addressed to their email, redacts that email from surviving invite.created payloads (redact_audit_event_email), and tombstones solve_metrics.actor_id to 'deleted' so the row survives for capacity aggregates while the person does not. The
admin delete handler captures the email before deleteUser and passes it through.
Open gap: no time-based retention on audit_events or solve_metrics; rows live
until a subject is erased.
Logs are the escape hatch erasure cannot follow
onUserDeleted scrubs rows; it has no reach into stdout, which on a real deployment has
already shipped to a collector and may be indexed by a third party. A log line carrying
personal data outlives every guarantee above, hence the project rule against logging whole
domain objects.
The pino redaction list (packages/server/src/logging/PinoLogger.ts) scrubs by credential field name (token, apiKey, …) and will not catch an email nested in a
payload; it is a backstop for accidents, not a licence to log objects. See security & limits § Logging and personal data for exactly which field names are matched and at what depth.
Next
- Security & limits: secrets, cookies, and the logging contract.
- Providers: where identity and credentials actually live.
- Admin guide: the user-deletion flow in the UI.