Trust
Security & access
No passwords, no OAuth, no signup forms. Authentication is a magic link to your inbox; authorization is a Postgres RLS policy. Here's exactly what that protects, and what it doesn't.
Auth flow
We use Supabase Auth's magic-link flow end-to-end. You enter an email, Supabase emails a one-time link, the link grants a signed JWT. That JWT is the only thing the app ever checks. There is no password anywhere in the codebase — not stored, not hashed, not hinted at.
Identity
Once authenticated, the app caches your chosen member context in localStorage under the key taskflow-identity:
- memberId
- UUID of your row in the members table.
- name
- Your display name. Also stored on the members row.
- workspaceId
- The workspace you last opened. You can belong to more than one.
localStorage is a cache, not a credential. If a browser clears it, re-authenticating with the same email restores everything — your member row is keyed by email, not by the cached UUID.
Row-level security
Every workspaced table has an RLS policy that compares the row's workspace_id to the workspace the JWT belongs to. Anything that doesn't match returns zero rows — not 403, zero rows. Queries are safe to run even if the client guessed a workspace UUID.
What we store
- Stored on the members row. Used to match authenticated users back to workspaces. Never shown to other members.
- Name
- Your display name on the members row. Visible to teammates.
- Task content
- Title, description, acceptance criteria, importance, and status. Visible to every member of the workspace.
- Notes & feed
- Markdown notes per project and activity feed entries. Workspace-scoped.
- Images
- Task attachments in the task-images storage bucket, path-scoped by task ID.
- Stats
- XP, streaks, and completion counts in member_stats. Visible as a leaderboard.
What we don't store
- No passwords. Ever. There is no password column anywhere in the schema.
- No location, IP history, or device fingerprints beyond what the hosting provider logs.
- No third-party analytics SDKs, no ad pixels, no session replay.
- No emails from teammates in your browser — only your own, via the Supabase session.
Threat model · what RLS protects
memberId in localStorage does not grant access — the server checks the JWT, not the cache.Threat model · what RLS does not
Reporting issues
If you find something that looks like it bypasses the isolation model above, please don't post it publicly. Open a private security advisory on the repository or email the maintainer. Fixes go out as soon as we can reproduce.
That's the tour
You've seen the flows, the architecture, the gamification layer, and the security model. The fastest way to get the rest is to click around the demo.
Open the demo workspace →