The problem
Why it needed building
A small dev team usually spreads its work across seven or eight tools: GitHub for code, Vercel for deploys, Google Calendar for meetings, a task board, a notes app, a time tracker and a password manager for API keys. Each switch costs attention, and nothing links a task to its branch, its meetings or the time spent on it. Bringing all of that into one app meant real team permissions enforced by the database, secrets the server can never read, and two-way calendar sync that never loses data.
My role
What I owned
- Product and UI design across 16 modules, with a plain-CSS design system and runtime themes
- React 19 + Vite front end, hosted on Vercel
- Supabase backend: 120+ migrations of schema, RPC functions, triggers and pg_cron jobs
- 7 Supabase Edge Functions for Google Calendar, Cloudinary signing, email and the admin API
- Browser-side encrypted secrets vault
- A separate admin console for users, workspaces and broadcasts
The approach
Key decisions
- 1
One workspace, 16 modules
Projects, a Kanban board with custom statuses, subtasks, comments, attachments and time tracking; a calendar with two-way Google sync; GitHub and Vercel hubs; Markdown notes; a learning tracker; a Project Hub for scope, invoices and contracts; and analytics. A task can have its own GitHub branch, logged time and linked notes.
- 2
Every read and write through Postgres RPCs
The client never queries tables directly. Each RPC checks workspace membership and role, so the Owner, Admin, Member and Viewer roles and a 20+ key permission matrix (editable from the UI) hold even if someone calls the API directly.
- 3
A vault the server can’t read
Secrets are encrypted in the browser with AES-256-GCM, using a key derived from a master password with PBKDF2 at 310,000 iterations. Supabase only stores ciphertext, and an encrypted verifier checks the password without storing it.
- 4
Automations in the database
Owners set “when X, then Y” rules: status-change alerts, a time summary when a task is done, and daily nudges for stale tasks. They run from a Postgres trigger, so they fire no matter which screen changed the task.
- 5
Keyboard-first, flag-controlled
A ⌘K command palette, shortcuts and global search make the app fast to move around. Firebase Remote Config can turn each module on or off without a redeploy, and every module defaults to on if the fetch fails.
Challenges
The hard parts
Two-way Google Calendar sync
Orbit pushes changes with etag If-Match, retries on a 412 conflict with a fresh etag, recreates events deleted in Google, and pulls Google-side edits back by timestamp. When a token refresh fails, the user gets a “reconnect Google Calendar” notification.
Keeping third-party secrets off the browser
GitHub and Google tokens are stored AES-GCM encrypted and used only inside edge functions, and Cloudinary uploads use server-signed URLs so the API secret never reaches the client.
Reminders with nobody online
A pg_cron job runs every minute and turns due calendar reminders into in-app notifications, even when no one has the app open.
Nudges that don’t nag
A task’s last activity counts edits, comments and logged time, and a nudge log makes sure the same nudge is never sent twice.
Hardening the database
Fixed every function’s search_path, removed anonymous execute rights on security-definer functions, switched functions to invoker where possible, and indexed unindexed foreign keys.
Results
What it achieved
- A 16-module team platform built and shipped solo in about a month
- ~27k lines of React, 120+ database migrations and 7 edge functions
- Live at orbit-sand-alpha.vercel.app
- Replaces a daily set of tools: task board, calendar, notes, timer, deploy dashboard and password manager