Privacy & Sync Security

A plain, honest account — written to be clear, not to impress.

Again is local-first: the app works fully offline, with no account, no email, and no password. Sync between your own devices is optional and opt-in — nothing leaves your device unless you turn it on.

In short

Identity model

When you enable sync for the first time, your device generates locally:

Both secrets are kept in your device's secure storage — the iOS Keychain directly, and on Android an encrypted store sealed with a key held in the hardware-backed Android Keystore. Server-side, only SHA-256 hashes of each secret are ever persisted — never the secrets themselves.

"Pseudo-anonymous", not "anonymous": Cloudflare logs the client IP per request (their default, retained briefly). A determined adversary who could subpoena Cloudflare could in principle correlate a User ID with an IP. We don't claim more than that.

What's encrypted, what isn't

Each sync event has metadata that stays in the clear — id, user id, device id, event type, a version number, and timestamps — plus a payload field carrying the content (the loop's title, notes, emoji, colour, schedule, completion dates…). That payload is wrapped in an AES-GCM-256 envelope; the 32-byte key is derived on each device from the sync secret via HKDF-SHA256.

HKDF is one-way: even someone who recovered the key could not work back to the sync secret. The key is never stored or transmitted — every device re-derives it on demand, which is how two devices holding the same secret read each other's events.

What the server (or anyone with read access) can see: how many events of each type exist, their timestamps, which device created each, and your overall activity cadence. What it cannot see: your titles, notes, emoji, colours, schedules, or completion dates — and we never collect your email, name, phone, contacts, location, or photos at all.

Linking a second device

QR scan. The new device generates an ephemeral key pair and shows a QR carrying a random pairing id plus its public key — no secret. The existing (already-enrolled) device scans it, performs ECDH against that public key, and AES-GCM-seals your sync secret + user id under the derived key. It posts that sealed blob to a single-use, 60-second relay; the new device claims and opens it with its private key. Both devices derive and display the same six-digit confirmation code from the shared secret. Compare those codes before accepting the link. The server only ever holds public keys and ciphertext, so it cannot derive the shared key. The QR does not contain a secret, but someone who captures it can attempt to substitute a different pairing; a mismatched confirmation code exposes that attempt. (The web companion uses.)

No other device to hand? Settings → Sync → Restore from sync key rejoins from your saved key directly — no second device, no code.

Screen-leak defence

The screens that show the QR or the raw sync key set the platform's secure-window protection — FLAG_SECURE on Android and a secure-layer overlay on iOS — so while they're open, the system blocks screenshots, recents thumbnails, screen recordings, and most screen-mirroring. A separate biometric prompt gates those screens, so even on an unlocked phone you must re-authenticate before anything is revealed.

This closes the easy, accidental exfiltration paths. It is not a defence against an attacker holding your unlocked device — see below.

Data retention & deletion

Cloudflare retains request metadata (including IP) briefly per their own defaults; we have no admin control over that.

Threat model — what we defend, what we don't

Defended:

Not defended (and we won't pretend otherwise):

This is not a marketing document — it's a plain description of how sync actually works, caveats included. If something here doesn't match what you see, the contact address is on the privacy page.