Compliance-first SaaS for Philippine private-security agencies. The wedge — and the current MVP slice — is the license & certification expiry tracker: guards operating on expired LISG/NBI/clearances expose agencies to PNP sanctions and lost contracts, so the system flags every credential at 30 / 7 / 1 days before it lapses.
Working product name: Bantay (Filipino: to guard / watch over).
| Layer | Choice |
|---|---|
| Framework | Next.js 16 (App Router) · React 19 · TypeScript |
| UI | Tailwind v4 + shadcn/ui (Base UI primitives) |
| Design | Notion-inspired tokens — see DESIGN.md |
| Backend | Supabase — Postgres + Auth + data via @supabase/supabase-js (PostgREST), no ORM |
| Auth | Supabase Auth (email + password, cookie sessions via @supabase/ssr) |
| Tenant isolation | Postgres Row Level Security + app-level requireAgency() scoping |
| Validation | Zod |
-
Install
pnpm install
-
Create a Supabase project, then copy
.env.example→.env.localand setNEXT_PUBLIC_SUPABASE_URLandNEXT_PUBLIC_SUPABASE_ANON_KEY(Settings → API). For local dev without SMTP, disable Authentication → Email → Confirm email. -
Apply the schema — run the migrations in order in the Supabase SQL editor (or
supabase db push):0001_init.sql— tables, indexes, RLS.0002_profiles_roles_provisioning.sql—profiles, roles,is_superadmin(), thehandle_new_usersignup trigger (provisions profile + agency), superadmin RLS bypass, and backfills.0003_promote_superadmin.sql— promotes the platform owner tosuperadmin.
Roles:
platform_roleisuser|superadmin. The superadmin allowlist email is hardcoded in0002/0003(currentlyrgcadavos@gmail.com) — change it there if needed. Break-glass (promote any user as the service role / SQL editor):update public.profiles set role='superadmin' where id = (select id from auth.users where lower(email)=lower('<owner>')); -
Run
pnpm dev
Sign up, then add guards and their credentials. To preview the dashboard with sample data, set
SUPABASE_SERVICE_ROLE_KEYandSEED_OWNER_USER_ID(your Supabase user id) in.env.localand runpnpm db:seed.
| Script | Purpose |
|---|---|
pnpm dev / build / start |
Next.js dev / production build / serve |
pnpm lint / pnpm typecheck |
ESLint / tsc --noEmit |
pnpm db:seed |
Seed demo guards + credentials (uses the service-role key) |
- Multi-tenancy. Every row carries an
agency_id. For the MVP an agency maps to its owner (agencies.owner_user_id= Supabase user id). Isolation is enforced twice: RLS policies in the DB, andrequireAgency()(src/lib/auth.ts) scoping inside every Server Action and query. - Data access.
@supabase/supabase-js(PostgREST). PostgREST returns snake_case;src/lib/queries.tsmaps rows to the camelCase domain types the UI uses. Enum tuples live insrc/lib/constants.ts, row types insrc/lib/db-types.ts. proxy.ts(Next 16's renamed middleware) does Supabase session refresh and emits the per-request strict CSP nonce.- Compliance logic lives in
src/lib/compliance.ts— the 30/7/1-day status ramp and labels. - See
SECURITY.mdfor the hardening checklist and the securityheaders.com target.
Duty Detail Orders (per-client + internal) · timesheet → DOLE payroll prep · per-client SOA billing (PDF) · incident logging · guard mobile app with geofenced GPS check-in · SMS (Twilio) alerts.