Trust & Security
How CivicLoop County OS protects County and resident data. Written for a County security reviewer.
Architecture
- Next.js 14 (App Router) on Netlify; Supabase (Postgres + Auth) backend.
- All data access is server-side. The browser never holds the service-role key.
- Security headers set in both
next.config.mjs and netlify.toml: HSTS (preload), X-Frame-Options: SAMEORIGIN, X-Content-Type-Options: nosniff, Referrer-Policy: strict-origin-when-cross-origin, scoped Permissions-Policy.
Authentication
- Supabase Auth (email + password) for staff. Sessions are httpOnly cookies.
- TOTP MFA (
/mfa): staff enrol with a QR/secret; requireStaff can gate sensitive surfaces on AAL2; the staff bar nudges anyone not enrolled.
- Residents file 311 requests without an account; they receive a tracking number.
Authorization (RBAC)
- Five roles:
agent, supervisor, department_head, director, county_admin, enforced by requireStaff(locale, minRole) server-side on every gated route.
- Authorization is also enforced in the database via Row-Level Security, so a bug in the app layer cannot leak another county's or another role's data. See AUTH_AND_RBAC.md.
Data protection
- Encryption in transit: TLS everywhere (HSTS preload).
- Encryption at rest: Supabase encrypts Postgres storage and backups at rest (AES-256).
- RLS on every table: reads are scoped to the signed-in staffer's county; writes go through the service-role admin client only inside server actions/routes that first call
requireStaff.
- Secrets live in environment variables, never in the repo. AI, automation, and ingest endpoints fail safe (503 / no-op) when their secret is unset.
Privacy
- No resident PII appears on any
/public page - public-record aggregates only (counts, medians, supplier diversity, contract values, coordinates without identity). See DATA_PRIVACY.md.
- The County AI assistant answers from a County-controlled knowledge base and is instructed never to invent specific facts.
Observability & audit
- Sentry error tracking (server + client + edge) once a DSN is configured.
- Synthetic uptime/latency monitor records to
synthetic_checks and flags 5xx.
- Automations write an audit trail (
automation_runs, automation_flags); flags are human-resolved.
- The agent co-pilot and AI bid scoring are human-in-the-loop: the AI drafts/scores, a person sends/awards.
Secure development
- Build-before-push; every-button UI sweep; production smoke; no
.test accounts on prod.
- RLS is never disabled to "fix" a read; the correct policy/grant is added instead.
information_schema / schema dump consulted before any SQL.