Appearance
Architecture
Browser: React 18 · Vite · TypeScript · Tailwind + shadcn/ui · TanStack Query · React Router
│
├── supabase-js (publishable key + the user's session) ── PostgREST ──┐
│ ▼
└── Edge Functions (Deno) ─────────────────────────────────────► Postgres
auth-resolve, student-data, library-action, store-action, ├─ row-level security, generated from a manifest
api-v1, set-staff-password, create-tenant-admin, … ├─ SECURITY DEFINER RPCs
├─ triggers
Cloudflare Worker (site-router) ── *.qurtuba.in, custom domains └─ storage| Repository | Serves | Hosting |
|---|---|---|
qurtuba-portal | app.qurtuba.in, qurtuba.in, every entity public site | Vercel + Cloudflare Worker |
Fest-Management (Campus Fest) | fest.qurtuba.in | Vercel |
qurtuba-portal/docs-site | docs.qurtuba.in | Vercel |
All three share one Supabase project.
Identity: the auth spine
Everyone who signs in is a principal, a GoTrue user. Login aliases map an admission number, username, email or phone to the principal. Grants give a principal a role within a scope (platform, institution, library, store). On sign-in, the access-token hook writes the principal's scopes and a permission bitmap per scope into the JWT.
Authorisation
- Privileges.
anonandauthenticatedhold nothing by default; access is granted on purpose. - Row-level security is generated from
app.auth_table_manifest. Each table declares its scope column, the page whose permissions gate it, and how assigned and own narrowing apply. One manifest row produces the table's four policies. - Features clear the permission bits of disabled pages in the token, so a switched-off feature is denied everywhere.
Scope
Most data belongs to an institution (tenant_id). Libraries, stores and fests are platform entities with an access scope (TENANT, SHARED with allowed_tenants, or PUBLIC/universal).