Security & privacy

Personal data

What we keep, what we redact, and what happens when you leave.

Deletion is effective, logs are scrubbed, and our error-reporting pipeline strips PII before it leaves the server. Guest accounts that sit unused disappear on their own. You can exercise every GDPR right by writing to [email protected].

Effective deletion

When you delete your account, your credentials, tokens, passwords and API keys are nulled immediately and your active sessions revoked. The row may be retained to preserve the integrity of linked data (conversations, household members), but nothing identifying you remains.

Scrubbed logs

Our logs automatically filter more than 60 categories of sensitive fields: tokens, confirmation codes, API keys, cookies, IBANs, card numbers, voice and face embeddings.

PII-free monitoring

Our error-reporting tool (Sentry) does not collect personally identifiable information by default, and a second filter is applied before any event is sent to make sure no token or secret leaves our environment.

GDPR rights

As a publisher based in Europe we comply with the General Data Protection Regulation. You can exercise your rights of access, rectification, erasure, portability, restriction and objection by writing to [email protected].

Data minimisation

We only collect what each feature requires. The signup form asks for email and password — not a phone number, not a physical address, not a birthday. Every optional field is clearly marked and stays empty if you don't fill it in.

Inactive guest purge

Guest accounts — those created via POST /users/guest — are soft-deleted automatically if they remain dormant for 30 days. The purge runs nightly at 03:30 UTC.

Export / portability

On request, we provide a machine-readable export of your personal data (conversations, members, device list, connected services). Turnaround under a month, usually within a week.

Encrypted third-party credentials

If you connect a third-party service (Gmail, Google Calendar, Spotify, a bank via Nordigen, your own OpenAI key), its access tokens live in a separate encrypted column with a deterministic key distinct from the primary encryption key — so a leak of one doesn't compromise the other.

Deep dive

Deletion mechanics

When you delete your account, we atomically null out the authenticator fields (encrypted_password, auth_token, email, unconfirmed_email, confirmation_code), revoke every AuthSession, and set status='deleted'.

Conversations and household rows are preserved because they carry references from other members of the household, but the user foreign-key has no way back to you — your content becomes orphan rows. After 30 days the orphan rows are garbage-collected. If you want deletion to be literal and immediate (rather than soft-delete + GC), write to [email protected] with your request.

Scrubbed logs

Rails' config.filter_parameters is extended with a list of around 60 field names covering:

  • Tokens: auth_token, user_token, device_token, access_token, refresh_token, bearer, id_token, jwt, authorization
  • API keys: *_api_key (OpenAI, Anthropic, Gemini)
  • Secrets: cookie, card, cvv
  • Financial IDs: iban, rib
  • Codes: pairing_code, confirmation_code
  • Biometric fields: face_embedding, voice_profile, audio, embedding

The filter applies to request logs AND to params inspect output — any value under one of these keys is replaced with [FILTERED] before anything is written to disk.

Inactive guest purge

Guest accounts are created by users who try out Twoody without signing up — they get a single-use session, no password, no real email. Left alone they'd clutter the database indefinitely.

A nightly job (PurgeInactiveGuestsJob) targets guests whose creation and last-seen timestamps are both older than 30 days, sets status='deleted', nulls encrypted_password and auth_token, but keeps the row so FKs from queries/discussions stay valid. No user can retrospectively point at a row you've held — the row is still there, but not as you.

Our target is: if you leave, your leaving is visible in our SQL as blanks, not as something we could use. Data minimisation is a design goal, not a checklist — we don't collect what we don't need, and we don't keep what we don't use.

Related topics