SCOPE: everything in progressdocs/soc2-readiness.md · build 40bb41bdb460

SOC 1 / SOC 2 readiness

43% toward the Type I audit·43% including Type II

Every gap from Kyle's SOC 2 roadmap (27 Oct 2026), checked against the SCOPE code on 27 Oct. Done and Partial say how much of the fix is in the code, Not started means none of it is, and Org task is an account, setting, document or contract. The SOC 2 column is the AICPA's own numbering of the Trust Services Criteria (CC = common criteria, A = Availability, C = Confidentiality); the ID is ours, one per gap or task. Click a column heading to sort, or By priority to put both tables soonest first. Tick an item when it is done: the tick is saved for everyone with your name and the date, and unticking puts our assessment back. SOC 1 is not planned; SCOPE touches nobody's financial reporting. The items that are not code have drafts and owners on SOC 2: the work outside the code.

Loading ticks…

Gaps against SOC 2

Step 0, the 39 gaps to close before the Type I audit, and the 12 for the Type II phase.

DoneIDGap and fixEvidence
OW-1

Code lives in developers' personal GitHub accounts

Fix: Transfer every SCOPE repo into the lucent-axis GitHub org; developers get access through teams only

Step 0: Lucent Axis-owned accounts

Compliance ownerStep 0DoneBoundary (28 Oct): only Lucent Axis accounts are audited. The code is in lucent-axis/scope, private, the origin remote since 28 Oct; access through the org. Dan's personal repo daniel-sgalia/scope-safety-poc is a mirror outside the boundary that Paul demos from, archived after his switch
OW-2

Vercel projects sit on a developer's account

Fix: Move them to a Lucent Axis Vercel team on company billing; developers invited with least-privilege roles

Step 0: Lucent Axis-owned accounts

App developerStep 0DoneBoundary (28 Oct): only Lucent Axis accounts are audited. The Lucent Axis Vercel team lucent-axis (created by Kyle 27 Oct, Pro, company billing, MFA enforced) holds scope-web, built from lucent-axis/scope, functions in iad1; Kyle Owner, Dan Member (read 28 Oct). What is left on it is not this item: Production env vars (OW-3, ORG-11), a second owner (OW-6), billing after the trial (Kyle). The personal scope-safety-poc and scope-novvia are outside the boundary
OW-3

Supabase projects sit in a developer's organization

Fix: Transfer the projects to a Lucent Axis Supabase organization on company billing; members by role

Step 0: Lucent Axis-owned accounts

Data / platform engineerStep 0DoneBoundary (28 Oct): scope-web's own Supabase project (ref zvxabodonzynkxmcpbji, us-east-1, Postgres 17, SSL available but not yet enforced, DB-7), connected by Kyle's integration on the Lucent Axis team. It began as a copy of the personal demo database as of 27 Oct, and on 28 Oct (Dan's decision to keep the copy) it was brought current: a full backup first; the 10 pending migrations applied (the first needed set constraints all immediate before its ALTER, because the copy holds data); app.demo_baseline_drift restored to its newest body; the personas add-on installed; and **every table's grants restored**, because the copy had reset 83 tables and 26 column grants to Supabase's defaults (signed-in users could, for example, update action and incident directly). All 23 verify_*.sql scripts pass against it, and its permissions match the personal database's plus the intended tightening. Read in the dashboard 28 Oct: the project (named scope-dev in the dashboard) is the only one in the **Lucent Axis** Supabase organization, on the **Pro** plan, Nano compute, linked to lucent-axis/scope; members Kyle (Owner) and Dan (Developer), both with MFA on. scope-web signs in against it with the rotated password (checked as the VP persona, 28 Oct). What remains on the project is DB-7, ID-1, ID-7 and AV-2
OW-4

Domain and DNS for the SCOPE app may be on a personal account

Fix: Move them to the company registrar and DNS

Step 0: Lucent Axis-owned accounts

Compliance ownerStep 0DoneBoundary (28 Oct): only Lucent Axis accounts are audited. No domain is used: scope-web is served on *.vercel.app under the Lucent Axis team, so nothing sits on a personal registrar. A company domain, if bought, goes in a company registrar account (Kyle or the Business lead)
OW-5

Other services (email sending, error tracking, any others) may be on personal accounts

Fix: Inventory every service SCOPE uses and move each to a company account

Step 0: Lucent Axis-owned accounts

App developerStep 0PartialBoundary (28 Oct): only Lucent Axis accounts are audited. In it: GitHub, Vercel and Supabase under Lucent Axis; client builds' AI on AWS Bedrock in a Lucent Axis AWS account, which does not exist yet. No email or error-tracking service is installed. The personal demo's Anthropic key is outside the boundary. Inventory: docs/compliance/system-inventory.md
OW-6

Owner rights and recovery codes held by individuals

Fix: Lucent Axis holds owner on every account, at least two owners, recovery codes in the company vault

Kyle decides · Owner switch: Make Dan a second Owner on the GitHub org, the Vercel team and the Supabase organization, and accept the vault invite (Bitwarden free)

Step 0: Lucent Axis-owned accounts

Compliance ownerStep 0Org taskBoundary (28 Oct): only Lucent Axis accounts are audited. Still one owner on each: the lucent-axis GitHub org (Kyle; Dan a member, re-read 28 Oct) and the lucent-axis Vercel team (Kyle; Dan is Member, re-read from the API 28 Oct). No company vault yet for recovery codes. Owner: Kyle. Supabase too: Kyle is the only Owner of the Lucent Axis organization, Dan is Developer
OW-7

No list of the systems and accounts SCOPE runs on

Fix: System inventory: each account, owner, billing and who has access; feeds the system description

Step 0: Lucent Axis-owned accounts

Compliance ownerStep 0DoneBoundary (28 Oct): only Lucent Axis accounts are audited. Drafted: docs/compliance/system-inventory.md and docs/compliance/system-description.md, now describing the Lucent Axis accounts. The Supabase organization, plan, region and members were read in its dashboard on 28 Oct and are in the inventory; it is reviewed each quarter with the access review
OW-8

Keys and secrets were created under the old accounts

Fix: Regenerate every key and secret as the last step of each move (this also closes SC-1)

Step 0: Lucent Axis-owned accounts

Data / platform engineerStep 0PartialBoundary (28 Oct): scope-web's keys were made by its own Supabase project. On 28 Oct the copied sign-ins got a fresh 24-character demo password (the six personas by gen_demo_auth.sql, the two View-as logins the same), every copied session and refresh token was deleted, and scope-web's Production NEXT_PUBLIC_DEMO_PASSWORD set to match; the password lives in the git-ignored .env.company and Vercel only. The hub's shared sales login keeps its password (a team password people type; rotate it with create_sales_login.sql when the team switches). Left: the database password into the vault and the unused service-role, secret and JWT values removed from the project (DB-7)
SC-1

Shared demo password in the code and git history; Supabase anon and service-role keys not rotated

Fix: Rotate both keys, purge the history, treat everything as compromised

1. Source code and CI (GitHub)

App developerBefore customer dataPartialThe demo password is NEXT_PUBLIC_DEMO_PASSWORD, so every build ships it to the browser (apps/web/lib/personas.ts); rotation cannot be seen from the repo. Gitleaks in CI finds nothing in history. 28 Oct (5b77b6d): the persona picker, the demo password and View as anyone are now a demo-only add-on (apps/web/addons/personas, supabase/addons/personas), and apply-tenant refuses NEXT_PUBLIC_DEMO_PASSWORD for a tenant without it, so a client build cannot carry the password. Left: the demo build in the Lucent Axis team still ships it by design, with a fresh password of its own (OW-8), and a client build needs its own sign-in (ID-2, ID-3)
SC-2

Repo was public

Fix: Make it private and move it into the lucent-axis org

1. Source code and CI (GitHub)

Compliance ownerBefore customer dataDoneBoundary (28 Oct): only Lucent Axis accounts are audited. lucent-axis/scope is private and in the lucent-axis org
SC-3

No secret scanning; no check of the built code for keys

Fix: GitHub secret scanning + push protection; CI scan of the build output

1. Source code and CI (GitHub)

App developerBefore customer dataDone28 Oct, the free route (ORG-03, done): gitleaks over the whole history in the required secrets CI job, and gitleaks before every commit through the pre-commit hooks installed by default. GitHub's own secret scanning and push protection stay off by decision (paid on Team). 29 Oct: the built output is scanned too. scripts/security/scan-bundle.mjs reads apps/web/.next/static, what every visitor downloads, after the build CI job, and fails on a Supabase JWT whose role is not anon, seven key shapes (Supabase secret, Anthropic, AWS, GitHub, Stripe, private key blocks, database URLs with a password), and the VALUE of any non-NEXT_PUBLIC_ variable named like a secret present at build time; a canary secret is set for the build so that check always has one to find. Proved on a production build (64 files, clean) and on planted fakes (all three caught). The demo password is in the demo bundle by design and stays SC-1
SC-4

Nothing stops a new table going live without RLS

Fix: CI test fails the build if any API-exposed table has RLS off

1. Source code and CI (GitHub)

App developerBefore customer dataDone28 Oct: three layers. The new-table guard (scripts/guards/new_table.py) refuses a migration whose table lacks RLS or audit columns, and runs in the pre-commit CI job, which the ruleset makes a required check, so an RLS-less table blocks a merge. scripts/db/verify_rls.sql fails on a public table with RLS off or a view or materialized view that reads around it, and scripts/db/verify_anon.sql on anything callable or writable signed out; the nightly database job runs both on a database built from every migration, green at run 40725900178 (28 Oct) with the anon migrations
SC-5

Main branch not protected; changes can merge without review

Fix: Branch protection for admins too, 1 approving review, required checks, emergency path

1. Source code and CI (GitHub)

Compliance ownerBefore Type IDoneApplied 28 Oct: ruleset main (id 26059181) on lucent-axis/scope, active, from .github/rulesets/main.json: pull request required, 0 approvals, merge commits only, no force-push or deletion, NO bypass for anyone, required checks check, secrets, pre-commit, build, data. A direct push to main is refused (GH013, seen 28 Oct); PRs #2 and #3 merged only after all five passed. 0 approvals instead of the roadmap's 1 is deliberate (one developer): the compensating control is Kyle's monthly review of merged PRs, in docs/compliance/change-management-policy.md. Emergency path: an owner sets the ruleset to Disabled, which the audit log records
SC-6

No single migration chain across dev, staging and prod

Fix: Supabase CLI migrations in Git, applied only by CI

1. Source code and CI (GitHub)

Data / platform engineerBefore customer dataPartialBuilt, switched off. Every migration is in Git (supabase/migrations/), and the deploy workflows (4d0c3e8: deploy.yml, called by deploy-dev.yml on a green main, deploy-demo.yml on a published release, deploy-release.yml for a client's staging then production) apply them with supabase db push inside the deploy run, only on a commit whose five required checks passed, one run per database at a time, skipped on a rollback. They run only on lucent-axis/scope with the variable DEPLOYS_ENABLED, which is not set, and no GitHub environment or SUPABASE_DB_URL secret exists yet (read 28 Oct). So today every migration is still applied by hand: the Lucent Axis database was brought current that way on 28 Oct and its history matches the repository. Left: create the environments and their secrets, and set DEPLOYS_ENABLED; Novvia's clone gets the same workflows
SC-7

No dependency or code scanning

Fix: Dependabot alerts and updates, code scanning; critical fixes within 7 days

1. Source code and CI (GitHub)

App developerBefore Type IDone28 Oct: Dependabot alerts on for lucent-axis/scope, .github/dependabot.yml on main, and its first update (PR #1, nine minor and patch bumps) merged through the required checks and brought into the personal repo; Semgrep CE runs as the code-scan CI job (45566fb), with its one finding silenced inline with the reason, not required because registry rules can change. The 7-day rule for criticals is in the information security policy (draft, ORG-26); the monthly review checks it is kept
VE-1

One shared database serves every demo; no separate customer production or staging

Fix: Separate Supabase projects for production, staging and demo, each with its own Vercel environment

2. Environments and hosting (Vercel)

Data / platform engineerBefore customer dataPartialThe code half is built; the accounts half is not. Decided and written (docs/internal/release-and-deploy-plan.md, 28 Oct): dev, demo, and per client a staging and a production, each its own Supabase project, the client's staging on synthetic data only. The deploy workflows (SC-6) already deploy by environment (dev, demo, <client>-staging, <client>-production), each holding its own database secret, and production only after the same release went to that client's staging, run by someone else. Not yet: in the Lucent Axis accounts there is still one Vercel project (scope-web) and one Supabase project behind it (re-read 28 Oct), so previews, dev and the demo share it, and no client project exists (ORG-11). The one shared database in CLAUDE.md is the personal demo's, outside the boundary
VE-2

No secrets inventory; the service-role key must never reach the browser

Fix: Service-role key server-side only, Vercel sensitive env vars per environment, a secrets inventory with a rotation schedule

2. Environments and hosting (Vercel)

App developerBefore customer dataPartialNo service-role key anywhere in apps/ or packages/: the app runs on the anon key and RLS. The secrets inventory, with a rotation rule per secret, is in docs/compliance/system-inventory.md (28 Oct). The deploy workflows keep each environment's database URL as that GitHub environment's own secret (SC-6). Left: the demo password is still a public env var on the demo (SC-1). The Vercel Supabase integration put SUPABASE_SERVICE_ROLE_KEY and SUPABASE_SECRET_KEY into scope-web's Preview and Development variables: server-side, not NEXT_PUBLIC_, unused by the app; removed with the DB-7 rotation
VE-3

Preview deployments may reach production data and be publicly reachable

Fix: Previews use staging only; Vercel Deployment Protection on previews

2. Environments and hosting (Vercel)

App developerBefore customer dataPartialBoundary (28 Oct): the Lucent Axis Vercel team puts Vercel Authentication on every deployment except custom domains (read from the API), and a scope-web deployment URL redirects to Vercel's login (checked 28 Oct), so previews are not public. Left: previews still point at the same Supabase project as production (VE-1); the release plan puts them on scope-dev, sample data only, once that project exists. Note for Paul's switch: if the production address is behind the same login, a custom domain or a changed setting opens it
VE-4

Vercel team access not controlled or reviewed

Fix: Enforce MFA on the Vercel team, least-privilege roles, quarterly review

Kyle decides · Owner switch: Vercel team roles, and the first quarterly review of its members

2. Environments and hosting (Vercel)

Compliance ownerBefore Type IPartialAfter OW-2. MFA steps: docs/compliance/this-week.md; quarterly review template: docs/compliance/onboarding-offboarding.md. Owner: Dan. 28 Oct: MFA is enforced on the Lucent Axis Vercel team (set by Kyle 27 Oct). Roles and the first quarterly review remain; Dan is Member, Kyle the only Owner (OW-6, read 28 Oct)
ID-1

Supabase organization access not locked down for our team

Fix: Enforce MFA on the Supabase org, least-privilege member roles, quarterly review

Kyle decides · Owner switch: Switch on "Require MFA to access organization" in Supabase

3. Identity and sign-in (Supabase Auth)

Compliance ownerBefore customer dataPartialAfter OW-3. MFA steps: docs/compliance/this-week.md; quarterly review template: docs/compliance/onboarding-offboarding.md. Owner: Dan. Read in the dashboard 28 Oct: both members of the Lucent Axis Supabase organization have MFA on, but "Require MFA to access organization" is off, and only the Owner (Kyle) can switch it on. Blocked: Kyle (Owner switch), or Dan once made Owner
ID-2

No SSO for customer users

Fix: OneLogin via SAML/OIDC, sign-in configured per tenant

3. Identity and sign-in (Supabase Auth)

App developerBefore customer dataPartial30 Oct: novvia's SSO Phases 0-3 are in this repository, SWITCHED OFF (features.sso in the tenant, app.feature_enabled('sso') in the database; no tenant or migration turns either on, so every demo signs in through the persona picker exactly as before). In place: SCIM as the only way a client account is created (/scim/v2), an identity keyed on the IdP's immutable id, and app.bind_login(), the database half of SAML sign-in that refuses anyone not provisioned, pending, suspended or deactivated (20261030120000-20261030150000, verify_identity.sql 0-28, verify_scim.mts, docs/sso-access-plan.md). Not built: Phase 5, the SAML handshake itself, on hold until Patrick decides the client platform (decision S14); then Phase 6, the OneLogin guide (docs/client/onelogin-setup.md, draft) and the end-to-end pass. SAML only: OIDC is out of scope (decision S2)
ID-3

Employee number + PIN: sequential numbers let anyone guess or lock out a whole shift

Fix: Rate limits per account, device and IP; alert on lockout spikes

3. Identity and sign-in (Supabase Auth)

App developerBefore customer dataNot startedThe PIN sign-in is not built yet, and the code has no rate limit or lockout alert; build both with it
ID-4

Sessions don't end with the shift; leavers stay signed in

Fix: Driver sessions end at shift end; leaver sessions expire within 1 working day

3. Identity and sign-in (Supabase Auth)

App developerBefore customer dataPartial30 Oct: supabase/config.toml time-boxes every session at 12 hours for the local stack and CI; a client's hosted project is set the same in its dashboard (docs/client/provisioning.md), the demo's left as it is. Suspension or a SCIM deactivation removes the access rows in the same transaction, so RLS returns nothing to a session still open (verify_identity.sql 17, verify_scim.mts 8); ending that session at the identity provider waits for Phase 5. Both switched off until a client is provisioned (ID-2). Sessions are still supabase-js in localStorage (apps/web/lib/supabase.ts). Ending a driver's session at the shift's own end, rather than 12 hours on, needs the shift times
ID-7

Password rules for non-SSO accounts not set

Fix: Minimum length, leaked-password protection and MFA option in Supabase Auth

Kyle decides · Owner switch: Supabase sign-in: passwords of 12 or more with letters and digits, leaked-password protection on

3. Identity and sign-in (Supabase Auth)

App developerBefore Type IPartial28 Oct: supabase/config.toml now asks for 12 characters with letters and digits; 30 Oct: sign-up through the Auth API off and re-authentication before a password change (local stack and CI; the hosted settings are in docs/client/provisioning.md). The hosted project's settings and leaked-password protection are clicks for the company copy (docs/compliance/this-week.md). Owner: Dan, with OW-3. Read in the dashboard 28 Oct: the company project still allows 6-character passwords with no requirements and leaked-password protection off. Setting 12, letters and digits, and leaked-password protection was refused for Dan's Developer role ("You need additional permissions to update provider settings"); nothing was changed. Blocked: Kyle
DB-1

RLS is the only boundary; the public API exposes every table in the exposed schema

Fix: Move internal tables out of the exposed schema; review every policy; RLS check in CI (SC-4)

4. Database (Supabase Postgres)

Data / platform engineerBefore customer dataPartialEvery public table has RLS (scripts/db/verify.sql), internal helpers live in the unexposed app schema, and scripts/db/verify_raw_writes.sql proves a raw token cannot do what the RPCs refuse. Every table in public is still reachable through the API. 28 Oct: found that 56 of the 104 functions in public could be called with no sign-in, because Supabase's project default grants anon directly and the migrations' revoke ... from public never reached it; one, next_incident_number, told a stranger how many incidents a database holds on any date. 20261028120000_anon_cannot_execute.sql revokes them all, scripts/db/verify_anon.sql fails on any function anon can call and on any authenticated lost; proved together in a rolled-back transaction on the shared database. And 20261028130000_anon_no_table_writes.sql: anon held INSERT, UPDATE, DELETE and TRUNCATE on 33 tables and authenticated TRUNCATE on 53, none reachable (RLS refuses anon's writes; TRUNCATE cannot go through the API), all revoked except the hub's own four tables' writes, and the default closed for tables made later. Both are applied to the Lucent Axis database and verify_anon.sql passes there (28 Oct); the nightly job proves them on a fresh database each night. A stricter pass (keep a table grant only where a policy covers that role and command, with a drift check) was written in the set-aside lucent-axis/novvia repo (20261015110000_revoke_unused_grants.sql), to port. 29 Oct: 20261029140000_anon_no_table_reads.sql takes SELECT from anon on every table and view in public but the hub's four (36 had it; 32 had no policy naming anon, so a signed-out read already returned nothing, but the grant left their columns readable through REST and GraphQL), and closes the default for new tables; verify_anon.sql now fails on any anon read grant beyond the four, and proved failing before and passing after on a fresh local database. Left: signed in, every table in public is still reachable through the API behind RLS; moving tables behind an API tier is NP-1
DB-2

RLS can't hide columns: a driver can read an investigation's root cause

Fix: Column grants or security_invoker views for investigation data; check every sensitive table

4. Database (Supabase Postgres)

Data / platform engineerBefore customer dataNot startedNo column grants in any migration; a driver's token can read root_cause on any incident they were involved in (CLAUDE.md, next step 3). 28 Oct: column grants cannot do it, since every login is the one authenticated role; the plan (move the three columns to an incident_finding table with its own RLS, switch five readers, a check as a driver) is in docs/compliance/data-classification-and-retention.md. The company database exists now (OW-3), but the code on main also serves Paul's demo, whose frozen database depends on today's reads; so this waits for Paul's switch, or for the change to be made backward-compatible, or for Novvia's clone, which has no frozen demo behind it
DB-3

No tenant_id: nothing separates one customer's rows from another's

Fix: tenant_id NOT NULL on every table, in every policy and index; composite foreign keys; cross-tenant CI test

4. Database (Supabase Postgres)

Data / platform engineerBefore customer dataNot startedNo tenant_id in any migration (re-checked 28 Oct). The release plan separates clients by giving each its own Supabase project (VE-1, NP-4), which separates their rows without it; this row stays open only if clients are ever to share a database
DB-4

No audit trail: modified_by keeps only the last change, and who/when are not set by triggers

Fix: Triggers fill who and when, with a named system actor; append-only history (trigger table or pgaudit)

4. Database (Supabase Postgres)

Data / platform engineerBefore customer dataDone20261015200000_audit_columns.sql gives every table created and modified at and by, set by the zz_audit_stamp trigger on the real clock, and it is applied to the Lucent Axis database (OW-3; verify_audit_columns.sql among the 23 that pass there). Append-only history exists for incidents (incident_event), training (training_event), investigation requests and the rule mapping. 29 Oct, for every table: 20261029120000_row_history.sql adds app.row_change, one row per UPDATE or DELETE of any row in public, app and sales (and per INSERT on the two access tables): when (real clock), who (the person, or system / anon named as such rather than left blank), the record by its business key, and each changed column as old and new, or the whole row on a delete. Append-only from the owner too, unreachable through the API, read by a VP through report_change_log(). A new table gets it through app.add_audit_columns(); scripts/db/verify_row_history.sql (12 checks, as real logins) fails on a table without it. Applied to the Lucent Axis database 29 Oct, where verify_row_history.sql passes 12 of 12 (evidence: docs/compliance/evidence/2026/2026-10-29-change-history-applied.md)
DB-5

Sensitive fields not classified (CDL, DOT medical cards, audiograms, injury details)

Fix: Classify data, mark Confidential columns, limit them to the roles that need them

4. Database (Supabase Postgres)

Data / platform engineerBefore Type IPartial28 Oct: classes and every Confidential column named in docs/compliance/data-classification-and-retention.md (draft). Left: limiting those columns to the roles that need them (DB-2), which waits for the company database
DB-6

No retention, soft delete, legal hold or disposal process

Fix: deleted_at soft deletes, 5-year OSHA 300/301 retention, legal hold, secure deletion at contract end

4. Database (Supabase Postgres)

Data / platform engineerBefore Type IPartial28 Oct: retention periods (OSHA, FMCSA), legal hold and disposal at contract end written in docs/compliance/data-classification-and-retention.md (draft). 29 Oct, in the schema: 20261029130000_legal_hold.sql. A VP places a hold on an incident, a person, a site or everything, by business key, with who asked and why; another VP lifts it, with a reason, once; a hold is never edited or deleted; while it is live the database refuses to delete what it covers (incident and person, whose children go only by cascade), from the owner too. report_retention() shows per class (OSHA injury records, FMCSA qualification files, DVIRs) how many records are past their legal minimum and how many of those are held. Nothing deletes on its own: disposal stays the client's written request. scripts/db/verify_legal_hold.sql (10 checks), all passing on the Lucent Axis database, where it was applied 29 Oct (evidence: docs/compliance/evidence/2026/2026-10-29-change-history-applied.md). Left: deleted_at soft deletes, which would change every reader, and a screen for holds (today a VP calls the functions)
DB-7

Direct database connections not restricted

Fix: Enforce SSL, Supabase network restrictions, a rotated database password held only in the vault

Kyle decides · Owner switch: Enforce SSL and network restrictions on the Supabase project

4. Database (Supabase Postgres)

Data / platform engineerBefore Type IOrg taskSteps (SSL, network restrictions, password in the vault): docs/compliance/this-week.md. Owner: Dan, after OW-3 and the vault. 28 Oct: also waits on the vault (Bitwarden's free organization, which Dan can create now). The Vercel integration also copied the new database password and service-role key into scope-web's Preview and Development variables; they are removed when the password is rotated. Read in the dashboard 28 Oct: on the company project SSL is NOT enforced and the database accepts connections from any address; Dan's Developer role cannot change either (the switches are greyed out). Blocked: Kyle (Owner switch), or Dan once made Owner or Administrator. Before restricting addresses, keep the ones the release workflow will migrate from (GitHub Actions) in mind
ST-1

Storage paths not prefixed by tenant; bucket policies not tied to tenant and role

Fix: Tenant-prefixed paths; storage policies per tenant and role

5. File storage (Supabase Storage)

Data / platform engineerBefore customer dataPartialStorage policies on all five buckets check the reader's role and the record a file belongs to; there is no tenant prefix because there is no tenant yet (DB-3)
ST-2

Buckets not confirmed private; files may be reachable by URL

Fix: Private buckets only, short-lived signed URLs

5. File storage (Supabase Storage)

App developerBefore customer dataDoneAll five buckets are created public = false (incident-attachments, training-material, person-documents, training-evidence, site-documents), and files are served only through signed URLs that expire within an hour (packages/db/src/attachment.ts, site-documents.ts)
AV-1

No restore has been proven; RPO and RTO not measured

Fix: A timed full restore; measured RPO/RTO in the contract; restore tests on a schedule

6. Backups, availability and recovery

Data / platform engineerBefore customer dataOrg taskRestore test record and targets: docs/compliance/bc-dr-plan.md. Owner: Dan; restore into a new project, never the live one
AV-2

Point-in-time recovery not confirmed on the Supabase plan

Fix: Confirm the plan, the PITR add-on and backup retention days

Kyle decides · Paid service: Buy Supabase point-in-time recovery before client data, or write "up to 24 hours of data can be lost" into the plan?

6. Backups, availability and recovery

Data / platform engineerBefore customer dataPartialA plan setting; blank in docs/compliance/system-inventory.md until Dan reads the dashboard. Owner: Dan. Read in the dashboard 28 Oct: the company project (Pro) takes a daily physical backup (latest 28 Oct 06:46 UTC); Point-in-Time Recovery is a paid add-on and is OFF. So today's RPO is up to 24 hours, not the 5 minutes in the BC/DR plan. Decision: buy PITR before client data, or write 24 hours into the plan for the demo (Free first)
AV-3

No business continuity or disaster recovery plan

Fix: Written BC/DR plan covering a Supabase or Vercel outage; yearly tabletop

6. Backups, availability and recovery

Compliance ownerBefore Type IOrg taskDraft: docs/compliance/bc-dr-plan.md (targets, outage scenarios, tabletop). Owner: Kyle
AV-4

No uptime monitoring, status page, support hours or SLA

Fix: External uptime monitor, status page, support hours and after-hours response time in the contract

Kyle decides · Outside service: Which uptime monitor and status page: UptimeRobot or Better Stack (free tiers), under a Lucent Axis account

6. Backups, availability and recovery

App developerBefore Type IPartial/api/health (28 Oct) answers 200 when the app and the database's auth service are up and 503 when the database cannot be reached, for an external monitor to call. Left: the monitor and status page themselves (UptimeRobot or Better Stack free tier, Free first), support hours and the SLA in the contract
AV-5

No capacity monitoring

Fix: Alerts on database disk, connections and CPU; keep reporting off production

6. Backups, availability and recovery

Data / platform engineerBefore Type IPartialNo capacity alerts, and the reports and /cofi read the production database. 28 Oct, free route until alerts exist: a monthly look at Supabase's database usage (disk, CPU, connections) is in docs/compliance/control-calendar.md; Supabase also emails the owners when a project nears a plan limit. 29 Oct: scripts/compliance/capacity.sql reads the same from the database itself, per client project: disk against the plan's included size, connections against the limit, the cache hit rate, the ten largest tables with their dead rows, anything running over five minutes; each line OK or WATCH at 70%. Left: alerts, and CPU, which only the dashboard shows
LM-1

Supabase and Vercel log retention depends on plan and may be only days

Fix: Send app, auth and database logs to a store kept for 12 months

Kyle decides · Paid service: Pay for log drains to keep logs 12 months, or accept what Pro keeps?

7. Logging, monitoring and incident response

Data / platform engineerBefore Type IOrg taskBoth accounts are now on Pro (OW-2, OW-3), and both offer drains on Pro, so this is a spending decision, not a plan change: Vercel Pro keeps runtime logs 1 day (30 with Observability Plus) and drains are billed per GB; Supabase Pro keeps 7 days and a drain is a paid add-on per project (vendor pricing pages, read 28 Oct; confirm on the billing page the day it is bought). The database's own audit trail (audit columns, incident_event, training_event) is already kept with the backups. Owner: Dan
LM-2

No alerts on security events

Fix: Alerts on failed sign-ins, lockout spikes, admin and role changes, and service-key use

7. Logging, monitoring and incident response

App developerBefore Type IPartial28 Oct, the free route: a monthly review in place of real-time alerts. scripts/compliance/app_access.sql lists every app login with its level and flags new ones and ones needing attention (its first run, on the personal demo database outside the boundary, found one login with no person); sign-in failures are read in Supabase's auth logs, which the database does not hold. Both in docs/compliance/control-calendar.md. app_access.sql counts the hub's shared sales login apart, so the first company run reads 0 unexplained logins. 29 Oct: the append-only log of admin and role changes exists. app.row_change (DB-4) logs every insert, update and delete on app.user_account and app.user_site, so a login granted, a level changed, a GM's site added or removed are each a row with who did it; app_access.sql now ends with every such change in the last 35 days, for the monthly review. 30 Oct: app.access_event is in this repository (ID-5), append-only from the owner too: on a client database it records every approval, level or site change, suspension, admin grant, vendor grant, SCIM authentication failure and rate-limit, refused sign-in and token change, read on /admin/users and exported with "Prepared by". Left: real-time alerts; lockout-spike alerts come with the PIN sign-in (ID-3)
LM-3

No incident response plan or breach-notification process

Fix: IR plan, on-call contact, customer notice within the contract window (24-72 hours), yearly tabletop

7. Logging, monitoring and incident response

Compliance ownerBefore Type IOrg taskDraft: docs/compliance/incident-response-plan.md (roles, severity, steps, customer notice, tabletop). Owner: Kyle
LM-4

No pen test or vulnerability scan of the app

Fix: Third-party pen test before the Type I; findings tracked to closure

7. Logging, monitoring and incident response

Compliance ownerBefore Type IOrg taskPen test scope drafted in docs/compliance/vendors-and-counsel.md. Owner: Kyle; many compliance platforms resell one. 28 Oct, decided: for the Type I an automated scan (OWASP ZAP) plus the RLS proofs (scripts/db/verify_*.sql); a third-party pen test before the first enterprise client go-live
VN-1

Vendor SOC 2 reports not collected or reviewed

Fix: Collect Supabase, Vercel, GitHub and Google reports yearly; record the review in the platform

Kyle decides · Paid service: How to get Supabase's SOC 2 report, which Pro does not include: ask under NDA, a month on Team each year, or Team throughout?

8. Vendors and subprocessors

Compliance ownerBefore Type IOrg taskWhich report from whom, and where: docs/compliance/subprocessors.md. Owner: Kyle. 28 Oct: where each report is got, recorded in docs/compliance/subprocessors.md. Supabase gives its SOC 2 report only on Team and Enterprise plans, so on Pro the free route cannot reach the one report the carve-out relies on; options there (ask under NDA, a month on Team each year, or Team throughout), decided with OW-3
VN-2

Complementary user entity controls (CUECs) not mapped

Fix: Each vendor report lists controls we must run (MFA, RLS, key handling); map them to ours

8. Vendors and subprocessors

Compliance ownerBefore Type IOrg taskExpected CUECs mapped to our controls in docs/compliance/subprocessors.md; replace with each report's wording. Owner: Kyle
VN-3

No subprocessor list

Fix: List Supabase (on AWS), Vercel, the email provider and any AI tooling that touches code or data

8. Vendors and subprocessors

Compliance ownerBefore customer dataOrg taskDraft: docs/compliance/subprocessors.md (Supabase on AWS, Vercel, AWS Bedrock in client builds, Anthropic in the demo only). Owner: Kyle
VN-4

Data region not confirmed

Fix: Confirm the Supabase project and Vercel functions run in the US

8. Vendors and subprocessors

Data / platform engineerBefore customer dataDoneBoundary (28 Oct): scope-web's Vercel functions run in iad1 (Washington, D.C.) and its Supabase project in us-east-1 (N. Virginia), both US, read from the Vercel API and the project's connection host on 28 Oct
VN-5

No data processing agreement

Fix: DPA from counsel covering subprocessors, breach notice and deletion

Kyle decides · Outside service: The DPA, once counsel is named (ORG-36)

8. Vendors and subprocessors

Compliance ownerBefore customer dataOrg taskBrief for counsel drafted in docs/compliance/vendors-and-counsel.md. Owner: Kyle or Business lead to name counsel (ORG-36)
VE-5

No confirmed security headers or abuse protection

Fix: HSTS, CSP and frame-ancestors headers; Vercel firewall (WAF) rules

Next phase: SOC 2 Type II and enterprise

App developerType IIPartialapps/web/next.config.ts sets headers only for /sw.js; no HSTS, CSP or frame-ancestors. 28 Oct: HSTS, same-origin framing (X-Frame-Options and CSP frame-ancestors), nosniff, a referrer policy and a permissions policy on every response (apps/web/next.config.ts). Later on 28 Oct: a full Content-Security-Policy sent REPORT-ONLY (own origin, Supabase, Google Fonts for a tenant that names one, Mermaid by its exact CDN path for the hub, vercel.live for previews; object-src 'none', base-uri and form-action self). Walked on a local production build as the VP across 17 screens with the browser's violation reports read: one finding (the hub's Mermaid) fixed, then none. Left: switch it from report-only to enforced after a week on the deployed demo, Vercel firewall rules
ID-5

No admin pages or SCIM: leavers are removed only by a script we run

Fix: Customer admin console; SSO per tenant (OneLogin, Okta, Entra, Google) with SCIM

Next phase: SOC 2 Type II and enterprise

App developerType IIDone30 Oct, ported from novvia and switched on per client (ID-2): SCIM 2.0 at /scim/v2 on its own narrow database role, the client admin console /admin/users (approve, levels and sites, suspend, admin as a separate permission, vendor access that lapses by itself, the append-only access log and its export), the HR roster import with access rules, and scripts/db/provision_client.sh. Tests: verify_identity.sql, verify_roster.sql (nightly), verify_scim.mts and rehearse_onboarding.mts (lab), four unit suites. Operating evidence begins when the first client's directory is connected; on the demo it stays off and users are linked by scripts/db/link_user.sh
ID-6

Our team's standing access to client data is not logged or time-limited

Fix: Log every support access; time-boxed elevation; disclose it in the contract

Next phase: SOC 2 Type II and enterprise

Data / platform engineerType IIPartialNot logged; the demo's "View as anyone" lets a demo login act as any person (20261023170000_demo_view_as.sql), which must stay out of client builds. 28 Oct: a support-access procedure and log (reason first, narrowest route, ends with the task, disclosed in the contract) in docs/compliance/information-security-policy.md, section 5a. View as anyone is now in the demo-only personas add-on (5b77b6d): a client database never has its functions (20261028150000 drops them where the add-on is not installed), and in a demo database it also refuses when the add-on is switched off. 30 Oct, ported from novvia (ID-5): time-boxed elevation is in the tooling for a client database. A client admin grants one of our logins access for at most 30 days with a reason (admin_grant_vendor_access), it lapses by itself (app.user_account.valid_until), and the grant, its use and its end are on the append-only access log (verify_identity.sql 21-22). Off until a client is provisioned (ID-2). Left: our own standing access to the database itself (the owner connection) is not time-boxed or logged by the app
ST-3

Uploads not checked

Fix: Allowed file types and size limits; strip location data from photos

Next phase: SOC 2 Type II and enterprise

App developerType IIDoneEvery bucket has a 25 MB limit and an allowed file-type list. 28 Oct: GPS coordinates are removed from JPEG photos before every upload of an incident photo, a DQ document, an outside-training certificate or a sign-in sheet, losslessly and keeping Orientation (packages/core/src/photo-location.ts, packages/db/src/photo-location.ts, test photo-location.test.mts). HEIC, PNG and WebP pass through; a phone browser hands over JPEG
LM-5

Logs and error reports may capture personal data

Fix: Scrub CDL, medical and injury details from logs and error tracking

Next phase: SOC 2 Type II and enterprise

App developerType IIPartialReviewed 28 Oct: the app has eight console calls, all error messages or status lines, none writing a record's content; no error-tracking service is installed. The rule for it is in docs/compliance/data-classification-and-retention.md (Confidential data never in logs). Left: scrubbing in the error tracker the day one is added
NP-1

Supabase's API layer (PostgREST) checks only the sign-in token: it can reach every table in `public`, leaves every permission to RLS, and has no per-user rate limit on data

Fix: Narrow what the API can reach and check every request inside Supabase: an `api` schema holding only the functions and views the screens use, the tables in a schema the API does not expose (closes DB-1), and a pre-request check (rate limit per user, refuse a disabled login); RLS stays as the wall behind it. A separate API server only if a client integration needs one

Next phase: SOC 2 Type II and enterprise

App developerType IINot started29 Oct, reworded: SCOPE is not without an API tier. Every screen goes through PostgREST, which checks the token and runs the request as authenticated, so the database's own rules decide everything. SOC 2 asks for that wall to be proven, not doubled, and it is: verify_rls.sql, verify_raw_writes.sql and verify_anon.sql run against a fresh database every night. Writes already go through SECURITY DEFINER functions, so the api schema is mostly about reads. The pre-request check is a PostgREST setting (db-pre-request); that it can be set on Supabase's hosted project is to confirm before relying on it. A separate API server (Next.js route handlers or Edge Functions) would mean rewriting every read and the offline outbox, so it is kept for a client integration such as SCIM or an export API
NP-2

Customers can't see who changed their records

Fix: Customer-facing audit-log export, built on the audit trail

Next phase: SOC 2 Type II and enterprise

Data / platform engineerType IIDoneAn incident's own audit trail can be exported from the incident record. 29 Oct, customer-wide: every change is kept (app.row_change, DB-4), and the VP's Reports screen has a Change log (/reports?type=change-log): when, the record by its business key, what it is, Updated / Deleted / Granted, by whom, and the fields before and after, filtered by period and by kind (login access among them), with the same Download CSV as every report. No uuid is shown; on a database without the migration the report says it is not set up yet. Checked in the browser as the VP on a fresh local database
NP-3

Reporting would run against production

Fix: Read replica; COFI change feed that includes deletes

Next phase: SOC 2 Type II and enterprise

Data / platform engineerType IINot started/cofi and the reports read the production database
NP-4

Every client shares one production project

Fix: Dedicated tenant projects as a premium option; scripted tenant setup

Next phase: SOC 2 Type II and enterprise

Data / platform engineerType IIPartialThe model is one project per client, not one shared project (docs/internal/release-and-deploy-plan.md, VE-1), so the premium option is the default, and the deploy workflows already deploy by client (<client>-staging, <client>-production). Not scripted: creating the Supabase and Vercel projects and setting up a client database (a setup script was written in the set-aside lucent-axis/novvia repo, to port)
NP-5

Logs and alerts spread across several tools

Fix: Central logging (SIEM) with one alert view, kept 12+ months

Next phase: SOC 2 Type II and enterprise

Data / platform engineerType IINot startedNo central logging
NP-6

No contractual uptime commitment

Fix: Uptime SLA, on-call rota, yearly DR drill

Next phase: SOC 2 Type II and enterprise

Compliance ownerType IIOrg taskRota and drill in docs/compliance/bc-dr-plan.md; the SLA is a contract term in the counsel brief (docs/compliance/vendors-and-counsel.md). Owner: Kyle, Dan
NP-7

Buyers ask for security evidence one request at a time

Fix: Trust page with the Type II report and pen-test letter; ISO 27001 only if a buyer requires it

Next phase: SOC 2 Type II and enterprise

Compliance ownerType IIOrg taskOnce the Type II report and pen-test letter exist; most platforms host one (docs/compliance/vendors-and-counsel.md). Owner: Kyle
TS-1

No code of conduct and no named security roles

Fix: A code of conduct and the roles (security, platform and app owner) in the information security policy, signed on joining and yearly

Kyle decides · Sign-off: Approve the code of conduct and the named security roles

Found against the criteria

Compliance ownerBefore Type IPartialDrafted 28 Oct: docs/compliance/information-security-policy.md, sections 2 and 3. Left: Kyle's approval and everyone's signature (ORG-14)
TS-2

Nothing shows management overseeing security

Fix: A quarterly security review by both owners, with minutes: missed controls and their fixes, open risks, incidents, system changes

Kyle decides · Sign-off: Hold the first quarterly security review with Dan

Found against the criteria

Compliance ownerBefore Type IPartialIn docs/compliance/control-calendar.md. Left: the first meeting, when Kyle is back
TS-3

No published way to report a vulnerability or reach us about security

Fix: A security contact address and `/.well-known/security.txt`; commitments in the contract and DPA

Found against the criteria

Compliance ownerBefore customer dataNot startedNeeds a company security address, which needs a company domain or mailbox (OW-4). Owner: Kyle
TS-4

No calendar of recurring controls and no evidence log; without a platform nothing shows a control ran

Fix: A control calendar (monthly, quarterly, yearly, on an event) and dated evidence files

Kyle decides · Sign-off: Sign October's monthly review

Found against the criteria

Compliance ownerBefore Type IPartialdocs/compliance/control-calendar.md and docs/compliance/evidence/ (28 Oct); first entries 27 and 28 Oct. First monthly review prepared 28 Oct (evidence/2026/2026-10-28-monthly-review-october.md: PRs #1 to #8, 0 open alerts), awaiting Kyle's sign-off
TS-5

The risk register had no fraud or insider risks and no trigger for a new review

Fix: Fraud and insider risks in the register; reviewed again on a new client, subprocessor or add-on

Kyle decides · Sign-off: Review the risk register

Found against the criteria

Compliance ownerBefore Type IPartialR13 to R15 and the triggers added 28 Oct (docs/compliance/risk-register.md). Left: Kyle's review
TS-6

No rule for disposing of data or cleaning a leaver's laptop

Fix: Disposal at contract end, laptops cleaned at leaving, exports deleted

Found against the criteria

Compliance ownerBefore Type IPartialDrafted 28 Oct: docs/compliance/data-classification-and-retention.md; the leaving checklist already asks for the written confirmation
TS-7

Malware protection not stated

Fix: Laptop OS protection on (BYOD policy); uploads typed, size-limited, never executed, location stripped

Found against the criteria

App developerBefore Type IPartialinformation-security-policy.md section 8, byod-policy.md rule 5, ST-3 done 28 Oct. Left: the policies signed
TS-8

Physical security not addressed

Fix: Name Supabase (on AWS) and Vercel as carved-out subservice organisations in the system description; read their SOC 2 reports yearly

Found against the criteria

Compliance ownerBefore Type IPartialStated in docs/compliance/system-inventory.md and the criteria matrix (28 Oct). Left: the reports read (VN-1)
TS-9

Changes made in a dashboard leave no record

Fix: A dashboard change log, filled the same day

Found against the criteria

Data / platform engineerBefore Type IPartialThe table in docs/compliance/change-management-policy.md (28 Oct), started with Kyle's Step 0 changes and the Dependabot switch
TS-10

No way to raise a concern that goes around the person it is about

Fix: A named route in the information security policy: concerns go to Kyle, or to the Business lead when they concern Kyle; no retaliation; each one logged

Found against the criteria

Compliance ownerBefore Type INot startedPoint of focus "Provides Separate Communication Lines" (29 Oct)
TS-11

Segregation of duties is not stated: one developer writes, merges, migrates and deploys

Fix: Say so in the change-management policy and the risk register, with the compensating controls: required CI checks, Kyle's monthly review of merged PRs and the dashboard log, and a second person on any High-risk change once there is one

Kyle decides · Scope: Accept one developer writing, merging and deploying, with Kyle's monthly review of merged PRs as the compensating control?

Found against the criteria

Data / platform engineerBefore Type IPartialPoint of focus "Addresses Segregation of Duties". 0 approvals on a PR is a decision (Dan, 28 Oct), so the auditor will look for this paragraph. 2 Nov: written in docs/compliance/change-management-policy.md (Segregation of duties): each thing one person can do alone, the control that catches it and where it shows; nobody reviews their own work (Dan reviews the dashboard changes Kyle makes); one approval required once a second developer joins. R16 added to the risk register, accepted while there is one developer. Left: Kyle's decision (Kyle decides, above), which is also his acceptance of R16 and approval of the policy (docs/compliance/approvals.md); then Done
TS-12

The access review lists people, not machine credentials

Fix: Add the non-human accounts to the quarterly access review: GitHub Actions secrets, GitHub Apps and Dependabot, Vercel and Supabase tokens and keys, the AWS role behind Bedrock; each with an owner, its scope and when it was last rotated. Starts from VE-2's secrets inventory

Found against the criteria

Data / platform engineerBefore Type IDoneThe 2022 points of focus name system, service and vendor accounts as well as people (29 Oct). 29 Oct: scripts/compliance/access_review.sh now lists every machine credential it can read, by name and date, never a value: GitHub repo, environment and Dependabot secrets and deploy keys (none on either repo), org secrets and installed GitHub Apps (owners only, so the script says so rather than reading as none), every Vercel project's variables with who added them (27 on scope-web, 23 of them from the Supabase integration), the team's integrations and the reviewer's own tokens; Supabase keys and AWS are read where they live. The review table and its rules (an owner by name, narrowest scope, rotated yearly and on any departure, personal tokens expire, unused means removed) are in docs/compliance/onboarding-offboarding.md. Its first run found a personal Vercel token that never expires; the first full pass is the next quarterly review (ORG-20)
TS-13

No written baseline for the security settings of GitHub, Vercel and Supabase, so drift cannot be seen

Fix: One baseline file: the settings each account must have (the main ruleset, MFA, SSL, network restrictions, password rules, preview protection, log retention), compared against the dashboards in the monthly review

Kyle decides · Owner switch: Approve the security-settings baseline, and switch off "members can create public repositories" on the GitHub org (GH-6); switch on the Vercel team's sensitive-variable policy (VC-6, VE-2)

Found against the criteria

Data / platform engineerBefore Type IPartialGitHub's ruleset is already a file. Points of focus "Uses Defined Configuration Standards" and "Creates Baseline Configuration of IT Technology" (29 Oct). 29 Oct: the baseline is written, docs/compliance/security-baseline.md (25 settings, each with why and who can change it), and scripts/compliance/baseline_check.py reads 19 of them back from GitHub, Vercel and Supabase; the other six are read by hand. A monthly control in docs/compliance/control-calendar.md; first run saved, docs/compliance/evidence/2026/2026-10-29-security-baseline.md: 8 differ, 7 of them already open items. Left: Kyle's approval of the baseline, and GH-6 (members can create public repos) switched off, an Org Owner's switch
TS-14

Every backup lives inside the same Supabase project

Fix: A scheduled, encrypted copy of the database and the Storage buckets to a place Supabase cannot reach (a Lucent Axis AWS bucket with object lock), restore-tested with ORG-23. A deleted project, a taken-over Supabase account or ransomware would otherwise take the backups with it

Kyle decides · Paid service: Where an off-site copy of the database lives, outside the Supabase project (for example a bucket in a Lucent Axis AWS account)

Found against the criteria

Data / platform engineerBefore customer dataNot startedPoint of focus "Addresses Offsite Storage", and the 2022 addition on threats to data recoverability (29 Oct). Independent of the PITR decision (AV-2), which shortens the gap but keeps the copy in the same place
TS-15

No exit plan for a critical vendor

Fix: One paragraph per critical vendor (Supabase, Vercel, GitHub, AWS) in the subprocessor list: how the data comes out, what would replace it, how long it would take

Found against the criteria

Compliance ownerType IINot startedPoint of focus "Implements Procedures for Terminating Vendor and Business Partner Relationships" (29 Oct)
TS-16

The PR template does not ask how a change is undone, or whether it could take the app down

Fix: Two lines in the template's Risk section: the rollback, and the availability impact (a migration that locks `inspection` or `route_day`, a change to the offline outbox)

Found against the criteria

App developerBefore Type IDone2022 point of focus on resilience requirements in change management, since Availability is in scope (29 Oct). 29 Oct: two lines in the Risk section of .github/pull_request_template.md, how the change is undone and what it could take down

To do outside the code

The roadmap's month-by-month tasks and open questions: accounts, settings, documents, contracts and exercises. To be worked through in their own session.

DoneIDTaskEvidence
ORG-01

Move SCOPE onto Lucent Axis-owned GitHub, Vercel, Supabase and other accounts before any other change (Step 0)

Compliance ownerNowPartialBoundary (28 Oct): only Lucent Axis accounts are audited. Done: GitHub repo (OW-1), Vercel team (OW-2), no personal domain (OW-4). Done 28 Oct: the Supabase project confirmed in the Lucent Axis organization and brought current (OW-3), the inventory (OW-7). Left: two owners and a vault (OW-6), Bedrock's AWS account (OW-5), the vault half of OW-8. Owner: Kyle
ORG-02

Rotate the Supabase anon and service-role keys, purge the demo password from git history, make the repo private

App developerNowPartialBoundary (28 Oct): lucent-axis/scope is private, gitleaks finds nothing in its history, and scope-web's keys were created by its own Supabase project, never under a personal account (OW-8). Left: the demo password in the bundle (SC-1) and the database password into the vault (DB-7). The personal demo's keys are outside the boundary. Owner: Dan
ORG-03

Turn on secret scanning and push protection in the GitHub org

Compliance ownerNowDoneDecided (Dan, 28 Oct): the free route. gitleaks scans the whole history in the required secrets CI job on every PR, and before every commit on each laptop through the pre-commit hooks, which are on main and installed by default since 28 Oct (349251f). The paid GitHub Secret Protection is the upgrade if a second committer joins or the platform bundles it. Weakness and reasoning: docs/compliance/README.md, Free first. Owner: Dan
ORG-04

Get quotes from 2-3 compliance platforms (Vanta, Drata, Secureframe or Sprinto) and pick one, with its partner auditor

Kyle decides · Paid service: Which compliance platform (Vanta, Drata, Secureframe), or a Type I run ourselves with a CPA firm hired directly?

Compliance ownerMonth 1Not startedQuote request and comparison table: docs/compliance/vendors-and-counsel.md. Owner: Kyle and Dan. Free first (docs/compliance/README.md) records the fallback: a Type I run by ourselves with a CPA firm hired directly is defensible, a Type II without a platform likely is not, so the platform is signed before the Type II window (ORG-30). No quote request has been sent
ORG-05

Connect GitHub, Supabase, Vercel and Google Workspace to the platform

Compliance ownerMonth 1Not startedAfter ORG-04 and Step 0: connect company accounts only (docs/compliance/vendors-and-counsel.md). Owner: Dan
ORG-06

Enforce MFA on every account that touches SCOPE: Google, GitHub, Supabase, Vercel, domain registrar

Kyle decides · Owner switch: Require two-factor sign-in on the GitHub org and 2-step verification in Google Workspace

Compliance ownerMonth 1PartialPer-account settings in docs/compliance/this-week.md. Owners: Kyle (GitHub org), Dan (Vercel, Supabase), Workspace admin (Google). 28 Oct: Vercel team done (enforced 27 Oct by Kyle). GitHub org: Kyle (the setting is visible to owners only). Supabase org: Dan. Google Workspace: Kyle or the Workspace admin. Read in the dashboard 28 Oct: both members of the Lucent Axis Supabase organization have MFA on; enforcement is the Owner's switch (ID-1)
ORG-07

Protect main: pull request required, no direct pushes, status checks must pass

Compliance ownerMonth 1DoneApplied 28 Oct (ruleset id 26059181, SC-5), proven by PRs #2 and #3 and a refused direct push. Shipping is scripts/git/ship.sh: the personal repo as before, origin HEAD:personal-main, a standing PR merged when the checks pass; build on personal/main. The monthly review of merged PRs is in the control calendar. Owner: Dan. 0 approvals by decision (Dan, 28 Oct); the compensating control is Kyle's monthly review of merged PRs
ORG-08

Sign contractor agreements with confidentiality and security clauses for every contractor

Compliance owner, Business leadMonth 1Not startedClauses for counsel listed in docs/compliance/vendors-and-counsel.md. Owner: Kyle or Business lead
ORG-09

Adopt and edit policy templates down to what the team will actually do

Kyle decides · Sign-off: Approve the policy set, removing anything we will not actually do

Compliance ownerMonth 1PartialDraft: docs/compliance/policies.md (the policy set, what each must say, which are drafted). Owner: Kyle. 28 Oct: written ourselves under Free first: information-security-policy.md (with acceptable use, access, encryption, secure development, malware), change-management-policy.md, data-classification-and-retention.md, beside the BYOD, incident response and BC/DR drafts. Awaiting Kyle's approval. 28 Oct: every draft carries a sign-off line, and the approvals are recorded in one place, docs/compliance/approvals.md
ORG-10

Write the system description: what SCOPE is, its boundary, data flows, subprocessors

Kyle decides · Sign-off: Review the system description

Compliance owner, Data / platform engineerMonth 1PartialStarts from docs/compliance/system-inventory.md (boundary, services, data flows) and docs/compliance/subprocessors.md. Owner: Kyle, with Dan. 28 Oct: drafted in the order of the description criteria (DC 200), with commitments, components, boundaries, carve-outs, CSOCs and CUECs: docs/compliance/system-description.md; the Step 0 facts (the Lucent Axis GitHub org, Vercel team and Supabase organization, plans, region, members) are in, read 28 Oct. Left: Kyle's review, and the client sign-in added to the components once it is built (ID-2)
ORG-11

Stand up client-one production and staging, separate from demo, and load realistic synthetic data

Kyle decides · Paid service: When to create each client's own staging and production projects (each a paid Supabase and Vercel project)

Data / platform engineerMonth 1PartialBoundary (28 Oct): no client production or staging project exists in the Lucent Axis accounts yet (VE-1); each is a paid project, so creating them waits for the plan decision with AV-2. What is ready for the day they are made: the environments are decided (docs/internal/release-and-deploy-plan.md: per client a staging project on synthetic data and a production project); the deploy workflows deploy to <client>-staging then <client>-production (SC-6). 30 Oct: the client database setup script (scripts/db/provision_client.sh: refuses a demo, imports the HR roster, switches SSO on), its rehearsal on a synthetic 300-person roster (scripts/db/rehearse_onboarding.mts, passed in this repository) and the hosted settings runbook (docs/client/provisioning.md) are ported from lucent-axis/novvia; the data request for Novvia stayed there. A new client database also needs the three reference catalogs, which scope loads with the sample data (docs/decisions.md, 2026-10-30). Owner: Dan
ORG-12

Confirm in writing with the auditor that the synthetic-data period counts toward the window

Compliance ownerMonth 1Not startedWording for the auditor in docs/compliance/vendors-and-counsel.md. Owner: Kyle, once the auditor is chosen
ORG-13

Background checks for everyone with production access

Kyle decides · Paid service: Which background-check service: pay per check (Checkr or Certn), or the platform's partner?

Compliance ownerMonth 2Not startedIn the joining checklist, docs/compliance/onboarding-offboarding.md; run through a pay-per-check service (Checkr or Certn), the platform's partner as the alternative (Free first, 28 Oct). Owner: Kyle
ORG-14

Security awareness training and policy acceptance for all staff and contractors

Kyle decides · Sign-off: Everyone reads and signs the policies, once they are approved

EveryoneMonth 2PartialIn the joining checklist (docs/compliance/onboarding-offboarding.md) and the policy set (docs/compliance/policies.md); delivered as our own training (docs/compliance/security-training.md), the platform's course as the alternative. Owner: everyone, tracked by Kyle. 28 Oct, free first: our own training in docs/compliance/security-training.md (accounts, phishing, client data, laptops, reporting) with a signature record. Left: everyone reads and signs it with the policies. 28 Oct: the signature record for everyone is section 2 of docs/compliance/approvals.md
ORG-15

Install the platform's device agent on every personal laptop; confirm disk encryption, screen lock and OS updates

EveryoneMonth 2Not startedRules the agent checks: docs/compliance/byod-policy.md. Owner: everyone, after ORG-04. 28 Oct, Free first: a quarterly device check at the access review instead, Google Workspace endpoint verification where our edition reports it, else a dated screenshot per laptop (disk encryption, screen lock, OS version), in byod-policy.md and the control calendar; the platform's agent is the upgrade if a platform is chosen
ORG-16

Write the BYOD policy, since all work happens on personal laptops

Kyle decides · Sign-off: Approve the BYOD and laptop policy

Compliance ownerMonth 2PartialDrafted 27 Oct: docs/compliance/byod-policy.md (disk encryption, screen lock, updates, no client data on the laptop, what happens on leaving). Left: Kyle's approval and everyone's signature with the other policies (ORG-09, ORG-14). Owner: Kyle
ORG-17

First risk assessment and risk register

Compliance owner, Data / platform engineerMonth 2PartialDraft: docs/compliance/risk-register.md (sixteen risks, each tied to readiness items). Owner: Kyle, Dan. 28 Oct: fraud and insider risks (R13, R14) and the free-tools risk (R15) added; review triggers written in. 2 Nov: R16, no segregation of duties (TS-11)
ORG-18

Vendor reviews: collect SOC 2 reports from Supabase, Vercel, GitHub, Google

Compliance ownerMonth 2Not startedWhich report from whom, and where: docs/compliance/subprocessors.md. Owner: Kyle
ORG-19

Onboarding and offboarding checklists, with access removed within 24 hours of departure

Kyle decides · Sign-off: Approve the joining and leaving checklists

Compliance ownerMonth 2PartialDrafted 27 Oct: docs/compliance/onboarding-offboarding.md (joining before any access, leaving within 24 hours, each a checklist kept per person as the evidence). Left: Kyle's approval, and the first real use of each. Owner: Kyle
ORG-20

First quarterly access review; limit business users to app-level admin, no direct database access

Compliance ownerMonth 2PartialTemplate in docs/compliance/onboarding-offboarding.md. Owner: Kyle, Dan. 28 Oct: scripts/compliance/access_review.sh produces the member lists; the first saved to docs/compliance/evidence/2026/2026-10-28-access-review.md. The first full review is the two owners filling the table. Re-run 28 Oct against the Lucent Axis accounts only, with the Supabase members read in its dashboard and the company database's sign-ins; ready for the two owners to fill the table and sign. 30 Oct: a client's business users now have an app-level admin and nothing below it: /admin/users (ID-5) approves, changes and removes access, and its access log export is the client's own review evidence; switched on per client
ORG-21

Logging and alerting: Supabase and Vercel logs retained, alerts on errors, auth failures and admin actions

Kyle decides · Paid service: The same log-drain decision (LM-1)

Data / platform engineer, App developerMonth 3PartialLM-1, LM-2. Running today on the free route: the monthly review of every app login (scripts/compliance/app_access.sql) and of Supabase's auth logs, in docs/compliance/control-calendar.md. 29 Oct: admin actions are logged in the database, every change to every row and every access change (app.row_change, DB-4, LM-2). Left: logs kept 12 months (a drain on each Pro plan, LM-1) and real-time alerts. Owner: Dan
ORG-22

Uptime monitoring and a public status page

Kyle decides · Outside service: The same monitor and status page (AV-4)

App developerMonth 3PartialAV-4. The endpoint for a monitor to call is built (/api/health on lucent-axis/scope, 28 Oct). Left: the monitor and status page (UptimeRobot or Better Stack free tier). Owner: Dan
ORG-23

Backup and restore test with documented RPO/RTO; Supabase point-in-time recovery on

Kyle decides · Paid service: The same PITR decision (AV-2); the first timed restore follows it

Data / platform engineerMonth 3PartialAV-1, AV-2. Read 28 Oct: the company project takes a daily backup (Pro), PITR is off, so RPO is up to 24 hours today. The restore test record and targets are written (docs/compliance/bc-dr-plan.md). Left: the PITR decision, and the first timed restore into a new project. Owner: Dan
ORG-24

Disaster recovery and business continuity plan, and a tabletop exercise

Compliance owner, Data / platform engineerMonth 3PartialAV-3. Draft: docs/compliance/bc-dr-plan.md. Owner: Kyle, Dan. 28 Oct: a ready-to-run DR tabletop script with a record form, docs/compliance/tabletop-exercises.md. Left: running it
ORG-25

Incident response plan and a tabletop exercise

Compliance ownerMonth 3PartialLM-3. Draft: docs/compliance/incident-response-plan.md. Owner: Kyle. 28 Oct: a ready-to-run IR tabletop script with a record form, docs/compliance/tabletop-exercises.md. Left: running it
ORG-26

Dependency scanning (Dependabot) with a patch SLA, critical fixes within 7 days

App developerMonth 3Done28 Oct: alerts on, dependabot.yml on main, the first Dependabot update merged through the required checks (PR #1), and the rule (criticals in 7 days, highs in 30, or a dated entry in the risk register) written into information-security-policy.md section 7, awaiting Kyle's approval with the rest of the policies (ORG-09). The monthly review in control-calendar.md is where the rule is shown to be kept. Owner: Dan
ORG-27

Third-party pen test of the SCOPE app; fix highs and criticals, keep the summary letter

Kyle decides · Paid service: Which pen-test firm, and its date

Compliance ownerMonth 3Not startedLM-4. Scope: docs/compliance/vendors-and-counsel.md. Owner: Kyle
ORG-28

Readiness check inside the compliance platform; close failing tests

Compliance ownerMonth 4Not startedAfter everything above, in the chosen platform. Owner: Kyle
ORG-29

Type I audit fieldwork with the auditor

Compliance ownerMonth 4Not startedBooked once ORG-04 is signed (docs/compliance/vendors-and-counsel.md). Owner: Kyle. 28 Oct: the auditor is the platform's partner, or a CPA firm hired directly (Free first)
ORG-30

Type II observation window starts the day after the Type I date, 3-6 months

Compliance ownerMonth 4Not startedCalendar date set once the Type I date is known (docs/compliance/vendors-and-counsel.md). Owner: Kyle
ORG-31

Does Lucent Axis already carry cyber insurance, and at what cover?

Compliance ownerOpen questionNot startedQuestion for the broker in docs/compliance/vendors-and-counsel.md. Owner: Business lead
ORG-32

Which Supabase and Vercel plans and regions is SCOPE on, and do they include point-in-time recovery, log retention and SAML SSO?

Data / platform engineerOpen questionPartialRead 28 Oct. Vercel (API): the Lucent Axis team is on Pro (trial), scope-web's functions in iad1 (Washington, D.C.). Supabase (dashboard): Lucent Axis organization on Pro, project on Nano compute in us-east-1, daily backups, PITR off (AV-2). From the vendors' pricing pages the same day, to confirm on each billing page: Vercel Pro keeps runtime logs 1 day (30 with Observability Plus) and offers drains; SAML SSO for the Vercel team is an Enterprise feature; Supabase Pro keeps logs 7 days, offers a drain as a paid add-on, and SSO for its dashboard is on Team. SAML for our own team is not required (MFA plus the access review, Free first). Left: those three facts confirmed in the dashboards. Owner: Dan
ORG-33

Will the Novvia pilot run on the shared Supabase project or its own, given tenant_id is not built?

Data / platform engineerOpen questionDoneDecided (Dan, 28 Oct): Novvia gets its own Supabase projects (staging and production) in the Lucent Axis organization and its own Vercel project, set up by cloning lucent-axis/scope (docs/internal/release-and-deploy-plan.md), never the shared project. So the pilot does not wait for tenant_id (DB-3). Owner: Dan, Kyle
ORG-34

Does Novvia accept a Type I report plus a bridge letter for go-live, with Type II to follow?

Kyle decides · Scope: Ask Novvia whether a Type I report plus a bridge letter is enough for go-live

Compliance ownerOpen questionNot startedOwner: Kyle, to ask Novvia. 28 Oct: the question is written, ready to send, in docs/compliance/vendors-and-counsel.md (Novvia: is a Type I enough to go live?); not sent
ORG-35

Should COFI join the audit boundary at Type II, or stay out until a COFI client asks?

Kyle decides · Scope: COFI in the audit boundary at Type II, or out until a COFI client asks?

Compliance ownerOpen questionNot startedDefault in docs/compliance/system-inventory.md: out, until a COFI client asks. Owner: Dan, Kyle. 28 Oct: what changes if it comes in is written in docs/compliance/vendors-and-counsel.md (COFI in the boundary); the decision is on the sign-off pack, docs/compliance/approvals.md
ORG-36

Who outside the team (counsel) drafts the DPA and the contractor security clauses?

Kyle decides · Outside service: Who counsel is, to draft the DPA, the contractor clauses and the availability terms

Compliance ownerOpen questionNot startedBrief for counsel (DPA, contractor clauses, availability terms) in docs/compliance/vendors-and-counsel.md. Owner: Kyle or Business lead to name counsel
Back to top