A coordination-light social activity network. People post what they're already doing or planning, and others decide whether to join with one tap. No organizing, no RSVP wrangling, no plans to build. Built for the Edge Esmeralda / Edge City community as the launch group.
Brand: Hangs (live at irlhangs.app), set via
NEXT_PUBLIC_APP_NAME. The repo/package slugopen-invitationsis a legacy technical identifier. Seedocs/PRD.md.
- Next.js 16 (App Router, React 19) + Tailwind v4, built as an installable PWA with Web Push.
- Supabase (Postgres, Auth, Row-Level Security, Realtime, Edge Functions) as the backend.
- Magic-link email auth; group-gated one-way follows; row-level security enforces the closed-network visibility model server-side.
Prerequisites: Node 20.9+, pnpm, Docker (for local Supabase).
pnpm install
pnpm supabase start # boots local Postgres/Auth/etc in Docker, applies migrations
cp .env.example .env.local # then fill from `pnpm supabase status` + `pnpm exec web-push generate-vapid-keys`
pnpm dev # http://localhost:3737
node scripts/seed-dev.mjs # optional: demo users, a group, and activitiesLocal email (magic links) is captured by Mailpit at http://localhost:54324. Supabase Studio is at http://localhost:54323.
supabase/migrations/— schema, RLS policies, triggers, RPCs (the source of truth for data rules).src/lib/— the shared "spine":supabase/clients,queries.ts(reads),actions/(server actions),push.ts,recurrence.ts,auth.ts.src/components/— design-system primitives (ui/) and feature components (activity/,social/,notifications/).src/app/— routes.(app)/is the authenticated shell (gated byproxy.ts+ onboarding).docs/PRD.md— the product requirements.docs/DEPLOYMENT.md— how to ship it.
- Closed network, server-enforced. Visibility is row-level security scoped by
follows(active only) andgroup_members, filtered byblocks. A user can only ever read what RLS permits, never just what the client hides. - Group-gated follows (FR-13). Sharing a group makes a follow active instantly; otherwise it's a pending request. Enforced by a DB trigger, not the client.
- Participation is private (FR-24). Who's "in" on an occurrence is visible only to the host and other joiners.
- Notifications. A DB trigger fans out in-app
new_activityrows to eligible followers (minus muted/blocked); the create action then delivers Web Push to each recipient's devices via the service-role client. - Recurrence. Recurring activities materialize concrete
activity_occurrences; "I'm in" always attaches to one occurrence.
node scripts/seed-dev.mjsseeds demo data.- The multi-user RLS/trigger behavior is exercised by an ad-hoc script during development; run the app against local Supabase and verify flows in a real browser before deploying.
See docs/DEPLOYMENT.md. Recommended: the app on your own server (e.g. Hetzner behind nginx) + Supabase Cloud for the backend. Vercel + Supabase Cloud is the lowest-ops alternative. Both are reversible; the code is identical across hosts.