This week: Step 0, MFA, scanning, branch protection
Every step here is a dashboard click or an account move. Each says who can do it; nobody should do one without saying so first.
Where it stands (28 Oct)
Read from GitHub, Vercel and the Supabase dashboard on 28 Oct. Kyle had already started Step 0
on the evening of 27 Oct: a Lucent Axis Vercel team, a project in it built from
lucent-axis/scope, and a Lucent Axis Supabase organization connected to it through Vercel's
Supabase integration.
Dan's roles today: Member on the Vercel team, Developer in the Supabase organization, member with repo admin on GitHub. So requiring MFA on the Supabase organization, enforcing SSL, network restrictions and the password rules are Kyle's (Owner), or Dan's once Kyle makes him Owner or Administrator.
| Item | Outcome | Waiting on |
|---|---|---|
| ORG-06 MFA | Vercel team: done, MFA enforced by Kyle on 27 Oct (team setting mfa.enforced, read 28 Oct). Supabase org: read 28 Oct, both members have MFA on; "Require MFA to access organization" is off and only the Owner can switch it on (ID-1). GitHub org: Dan cannot see the setting (owner only). Google Workspace: not checked |
Kyle (GitHub org, Supabase org switch, Google) |
| OW-6 two owners | GitHub org: Kyle is still the only owner. Vercel team: Kyle Owner, Dan Member. Supabase org: Kyle Owner, Dan Developer. No vault yet: Bitwarden's free organization is the choice under Free first, and Dan can create it now and invite Kyle | Kyle: a second owner on each. Dan: the vault |
| OW-2 Vercel team | Done. Lucent Axis (lucent-axis), created by Kyle 27 Oct, Pro plan (trial), MFA enforced, Vercel Authentication on every deployment except custom domains. Project scope-web, root apps/web, from lucent-axis/scope main, functions in iad1, live with Production env vars and the demo on the company database since 28 Oct |
Kyle: billing before the trial ends |
| OW-3 Supabase org | Done, read 28 Oct. Project scope-dev (ref zvxabodonzynkxmcpbji) is the only one in the Lucent Axis organization, Pro plan, Nano compute, us-east-1, linked to lucent-axis/scope; members Kyle (Owner) and Dan (Developer). It was created with PITR off: daily backups only, so RPO is up to 24 hours today (AV-2). Dan brought the database current on 28 Oct and every verify_*.sql passes against it |
Kyle: the Owner switches (ID-1, DB-7, ID-7); AV-2 decides PITR |
| ORG-26 Dependabot alerts | Done 28 Oct: switched on for lucent-axis/scope (Claude ran gh api -X PUT repos/lucent-axis/scope/vulnerability-alerts, approved by Dan); 0 open alerts at first read. dependabot.yml is on main from the CI session |
Nothing. The 7-day patch rule is in the policy set |
| DB-7 database door | Read 28 Oct: SSL is not enforced and the database accepts connections from any address. Dan's Developer role cannot change either | Kyle (Owner), or Dan once made Owner or Administrator; and the vault |
| ORG-32 / VN-4 plans and regions | Vercel: all three SCOPE projects run functions in iad1 (Washington, D.C., US); Lucent Axis team on Pro (trial). Supabase: Pro, us-east-1, daily backups, PITR off. Log retention and SAML read from the vendors' pricing pages, to confirm on each billing page (system-inventory.md) |
Dan: confirm those three facts on the billing pages |
| ORG-07 branch ruleset | Done 28 Oct: ruleset main (id 26059181) from .github/rulesets/main.json, pull request required with 0 approvals (Dan, 28 Oct), the five required checks, no bypass |
Kyle: the monthly review of merged PRs |
| ORG-33 Novvia's projects | Decided (Dan, 28 Oct): Novvia gets its own Supabase and Vercel projects, cloned from lucent-axis/scope. lucent-axis/novvia is set aside |
Nothing |
| ORG-35 COFI in the boundary | Default: out, until a COFI client asks | Dan and Kyle's decision |
Dan's clicks, in order
Dependabot alerts (ORG-26): done 28 Oct, kept for the record. github.com/lucent-axis/scope > Settings > Advanced
Security (called Code security on older layouts; left sidebar, "Security" section) > Dependabot alerts > Enable. Leave Dependabot
security updates and version updates off (the dependabot.yml is code, SC-7). Or, in a
terminal: gh api -X PUT repos/lucent-axis/scope/vulnerability-alerts. Done when the
Security tab lists Dependabot alerts.
Supabase read (OW-3, ORG-32, VN-4, ID-1): done 28 Oct, kept for the record. 5 minutes, changes nothing.
- supabase.com/dashboard, sign in. Top-left organization switcher: note every organization
listed and which one holds the project connected to
scope-web(its project ref is the subdomain in Vercel >scope-web> Settings > Environment Variables >SUPABASE_URL, which shows the host without revealing a key). - In that organization: Settings > General: the plan. Settings > Team: every member and role. Settings > Security (or Team, depending on the plan): whether MFA is required.
- In the project: Project Settings > General: the region (must be a
us-region). Database > Backups: daily backups and whether Point in Time Recovery is on. Project Settings > Log drains (or Logs): the retention. - Write each answer into the table in system-inventory.md, or paste it to Claude to write in. No keys or passwords: none of these screens need one copied.
MFA on the Supabase org (ORG-06, ID-1). Kyle's switch (Owner), or Dan's once made Owner or Administrator. In the organization, Settings > Security > Require MFA. Both members already have MFA on (read 28 Oct), so the setting can be switched on now.
Password rules on the NEW project (ID-7). Kyle's (Owner), or Dan's once made Owner or
Administrator. Authentication > Providers > Email
(or Authentication > Policies, depending on the dashboard version): minimum password length 12,
requirement "letters and digits", and leaked password protection on (Pro plan). The same
rules are in supabase/config.toml for local databases. The demo personas' passwords are set by
gen_demo_auth.sql and are not checked by it; give the company copy's personas a password of 12
or more anyway.
The database door (DB-7), once the vault exists. Section 5 below; the switches are Kyle's
(Owner), or Dan's once made Owner or Administrator.
One addition from 28 Oct: the Vercel integration has already copied the new project's database
password, JWT secret and service-role key into scope-web's Preview and Development variables.
The app uses none of them (it runs on the publishable key and RLS, VE-2), so after rotating the
password, delete POSTGRES_*, SUPABASE_JWT_SECRET, SUPABASE_SERVICE_ROLE_KEY and
SUPABASE_SECRET_KEY from the project, or the vault is not the only place the password lives.
Waiting on others
- Kyle, in the Lucent Axis Supabase organization (Owner only; or make Dan an Administrator so he can): require MFA (ID-1), enforce SSL and set network restrictions (DB-7), and on the project's Email sign-in settings set 12 characters, letters and digits, and leaked-password protection (ID-7). Dan's Developer role can read these and change none of them (tried 28 Oct).
- Kyle: second owner on the GitHub org and the Vercel team (OW-6); GitHub org "require two-factor" (ORG-06); the compliance platform and its auditor (ORG-04); whether Novvia accepts Type I plus a bridge letter (ORG-34); Vercel billing before the Pro trial ends.
- Counsel or the Business lead: naming counsel (ORG-36), contractor agreements (ORG-08), cyber insurance (ORG-31), a company domain or not (OW-4).
- Dan and Kyle: COFI in or out of the boundary (ORG-35). ORG-33 is decided (Dan, 28 Oct):
Novvia gets its own projects, cloned from
lucent-axis/scope. - Dan: the vault, Bitwarden's free organization (OW-6, DB-7, OW-8), created now and Kyle invited.
The rule: the personal demo does not change (Dan, 27 Oct)
The audit boundary (Dan, 28 Oct): everything in a Lucent Axis account, and nothing else. Dan's personal GitHub repo, Vercel projects and Supabase project are Paul's demo on synthetic data and are not tested against the requirements. So nothing below is done to them for the audit's sake.
Paul is demoing to clients on the personal setup, so until Lucent Axis has its own Vercel and Supabase running, nothing that the personal demo depends on changes:
- the Vercel project
scope-safety-pocon Dan's personal account, its settings and env vars - its Supabase project, its keys, its database password and the demo persona passwords
- the personal repo
daniel-sgalia/scope-safety-pocand the routine that pushes main to it
Step 0 is therefore done by building a copy under Lucent Axis, never by moving or
reconfiguring the original: new Vercel project, new Supabase project, new keys, built from
lucent-axis/scope. The personal demo is retired only after the company copy has been proven and
Paul has switched to it, and then it is paused, not rotated in place.
Each step below says whether it is safe now (it cannot reach the personal demo) or waits (for the company copy, or for Paul's switch).
Order: build the company copy (1), MFA on each account as it lands (2), fresh secrets come with
the new projects (3). Branch protection (4) applies to lucent-axis/scope now and leaves the
personal repo alone; the database door (5) applies to the new project.
1. Step 0: accounts
GitHub (OW-1, OW-6)
| Step | Who | When | Done when |
|---|---|---|---|
Make a second owner of lucent-axis (Dan, or another Lucent Axis person) |
Kyle | Safe now | Org > People shows two Owners |
| Download the org owners' recovery codes into the company vault | Kyle and the second owner | Safe now, once there is a vault | Each owner's codes stored in the vault, not on a laptop |
Archive the personal repo and drop it as a remote (lucent-axis/scope, renamed from lucent-axis/demo, has been origin since 28 Oct) |
Dan | Waits for Paul's switch | git remote -v shows lucent-axis only. OW-1 then moves to Done |
Vault. OW-6, DB-7 and OW-8 all need a company password vault with shared folders. Under Free first (README.md) it is Bitwarden's free organization, enough for the two owners; Dan can create it now and invite Kyle. Bitwarden Teams or 1Password Business once a third person needs shared secrets.
Vercel (OW-2)
Today: scope-safety-poc (Paul's demo) and scope-novvia are on Dan's personal Vercel account
(danielsgalia-6070's projects), next to unrelated personal projects. Neither is transferred
or changed.
28 Oct: the first two rows are done by Kyle except the second owner (team lucent-axis, Pro
trial, Kyle Owner, Dan Member), and the third is done: project scope-web builds from
lucent-axis/scope and has run with Production environment variables since 28 Oct. See "Where
it stands" above.
| Step | Who | When | Done when |
|---|---|---|---|
| Create a Lucent Axis Vercel team on company billing (Pro; log drains and deployment protection on previews need it) | Dan, with a company card | Safe now | Team exists, billed to Lucent Axis |
| Invite Kyle as Owner, so the team has two owners | Dan | Safe now | Two owners |
Create a NEW project in the team, built from lucent-axis/scope, with its own env vars pointing at the new Supabase project |
Dan | After the Supabase copy | It builds, and the demo runs on it end to end |
| Paul switches his demos to the new address | Paul, Dan | When the copy is proven | Paul confirms |
Pause scope-safety-poc on the personal account |
Dan | After the switch | Personal project paused |
Novvia: its own new project in the team, cloned from lucent-axis/scope (ORG-33; lucent-axis/novvia is set aside) |
Dan | Dan's call |
Supabase (OW-3)
| Step | Who | When | Done when |
|---|---|---|---|
| Create a Lucent Axis Supabase organization on company billing | Dan, with a company card | Safe now | Org exists |
| Invite Kyle as Owner | Dan | Safe now | Two owners |
| Create a NEW project in it (US region; created by Kyle 27 Oct with PITR off, AV-2) and build it from the migrations, then copy the demo's data across with a dump and restore. The personal project is only read, never changed | Dan | Safe now: reading the personal database does not change it | Every scripts/db/verify*.sql passes against the new project |
| Write down: plan, region, PITR, backup retention, log retention of the NEW project | Dan | With the step above | The blanks in system-inventory.md filled (ORG-32, VN-4, AV-2) |
| Pause the personal Supabase project | Dan | After Paul's switch | Paused |
Client-one production and staging (ORG-11) are created in this org too, never moved in later.
Domain (OW-4)
scope-web answers at scope.lucent-axis.com since 4 Nov (DNS at Cloudflare, a DNS-only CNAME to
Vercel), and that address opens on the hub. scope-web-mauve.vercel.app still works and still opens
on the persona picker. staging.scope.lucent-axis.com is attached in Vercel, but its environment
has no branch and no variables yet. SCOPE Training is still on *.vercel.app.
Everything else (OW-5)
The full list is system-inventory.md. The code uses no email or error-tracking service. The one other account with a key is Anthropic (Spotter in the demo): the company copy gets a key from a Lucent Axis Anthropic organization; the personal demo keeps its key until it is paused. AWS Bedrock (client builds) is created in a Lucent Axis AWS account from the start.
2. MFA everywhere (ORG-06, VE-4, ID-1)
| Account | Setting | Who |
|---|---|---|
GitHub org lucent-axis |
Settings > Authentication security > Require two-factor authentication. Members without 2FA are removed, so check first | Kyle (org owner) |
| Vercel team | Team Settings > Security: require MFA for members. Done 27 Oct | Kyle |
| Supabase org | Organization settings > Security: require MFA. Both members have it on (read 28 Oct) | Kyle (Owner), or Dan once made Owner or Administrator |
| Google Workspace | Admin console > Security > 2-Step Verification > Enforce | Workspace admin |
| Registrar, Anthropic, AWS, the vault | Each account's own security page | Whoever owns it |
Evidence for the auditor: one screenshot per account of the enforced setting, dated.
3. Fresh secrets (OW-8, ORG-02, closes SC-1's rotation)
The new projects come with new keys, so the company copy starts with nothing created under a personal account. The personal demo's keys and passwords are NOT rotated while Paul demos on it; at the switch it is paused, which retires them. The list below is what the company copy must have generated fresh, and stored only in the vault and the new Vercel project.
| Secret | Where it is used | Regenerate in |
|---|---|---|
Supabase publishable key (NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY) |
Every build, the browser | Supabase > API keys, then each Vercel environment |
| Supabase secret / service-role key | Not used by the app or the scripts (checked 27 Oct); may still exist in someone's .env.local |
Supabase > API keys |
| Supabase JWT signing key | Every session | New with the new project |
Database password (SUPABASE_DB_URL in .env.local) |
Scripts run by hand | Supabase > Database; store only in the vault |
NEXT_PUBLIC_DEMO_PASSWORD |
The persona picker; shipped to the browser by design | A new password for the company copy's personas (gen_demo_auth.sql against the NEW project), set in the new Vercel project. Purging it from the bundle is code (SC-1) |
ANTHROPIC_API_KEY |
Spotter, demo only | Anthropic console, in the Lucent Axis org |
| Vercel and Supabase personal access tokens on laptops | CLI | Revoke the old ones; new ones under the company accounts |
gitleaks in CI finds nothing in the git history, so there is no history to purge today.
4. Protect main on lucent-axis/scope only (ORG-07, SC-5)
Decided (Dan, 28 Oct): the company repo follows the rules now; the personal repo does not
change. Checked 28 Oct: Paul's demo (scope-safety-poc) is deployed from Dan's personal side,
so a lock on lucent-axis/scope cannot reach the live demo. (Later on 28 Oct: lucent-axis/scope
main does deploy, to scope-web in the Lucent Axis Vercel team, which Kyle created on 27 Oct.
That is the company copy, not Paul's demo; its Vercel status on each commit is not a check to
require until its builds pass.) Dan is admin on the repo and can set this.
Superseded 28 Oct by the pre-commit/CI session: the ruleset is now a file,
.github/rulesets/main.json (no admin bypass; required checks check, secrets, pre-commit,
build, data; emergency = an owner sets it to Disabled), applied by Dan, with
scripts/git/ship.sh for the push routine. The steps below are the first draft, kept for the
record.
Settings > Rules > Rulesets > New ruleset > New branch ruleset:
- Name
main, enforcement Active - Target branches: Include default branch
- Bypass list: Organization admin, set to For pull requests only. An owner can merge a pull request whose checks fail in an emergency, but nobody can push to main directly, so every change still leaves a PR. Today only Kyle is an org owner; Dan gets the path once OW-6 makes him the second owner
- Restrict deletions; block force pushes
- Require a pull request before merging: 0 approvals (Dan, 28 Oct: mostly one developer, so nobody waits on a second person). The author merges their own PR once the checks pass
- Require status checks to pass:
checkandsecrets, source GitHub Actions (the two jobs in.github/workflows/ci.yml, which run on pull requests too). Leave "require branches to be up to date" off
Repo settings alongside it:
- Merge commits only (squash and rebase off). The personal repo's commits arrive unchanged; a squash would rewrite them and the next sync would conflict
- Dependabot alerts on (free on Team; alerts only, no automatic PRs)
Emergency rule, for the policy: a bypassed merge gets a comment on the PR within one working day saying why.
What an auditor will say about 0 approvals. SOC 2 change management (CC8.1) usually expects a second person to look at a change. With one developer, the usual answer is a compensating control instead of a gate: every change is a PR with green checks (enforced), and Kyle reviews the list of merged PRs once a month and records it (a short note or tick per month). That is written into the change-management policy; if the auditor wants pre-merge review later, the setting is one number.
The push routine once the ruleset is on
Until the ruleset is switched on, main is pushed to both remotes as today: git push personal main and git push origin main. Once it is on, the second of those is refused, so:
The personal repo is untouched: work is still committed to main and pushed with git push personal main exactly as today, and Paul's demo deploys as it always has. Only the second push
changes:
git push origin main:personal-main(originislucent-axis/scope; a branch, not main)- One standing pull request,
personal-mainintomain, picks the new commits up; CI runs on it - When the checks are green, Dan merges it himself with a merge commit
- Repeat: each push adds to the same PR until it is merged, then the next push opens a new one
Kyle's own changes, when there are any, go the same way the other round: a branch and a PR on
lucent-axis/scope, merged once green, and after the merge Dan pulls lucent-axis/scope main into the personal repo so
the two do not drift.
When Paul has switched to the company copy (OW-1), the personal repo is archived and the standing PR becomes ordinary branch-per-change work.
lucent-axis/novvia is set aside (28 Oct); Novvia's clone of lucent-axis/scope carries the same
ruleset file.
5. Lock the database door (DB-7)
OW-3 is done; this waits for the vault and for an Owner or Administrator.
| Step | Where | Who |
|---|---|---|
| Enforce SSL on connections | Supabase > Database > SSL configuration | Kyle (Owner), or Dan once made Owner or Administrator |
| Network restrictions: allow only the addresses that need a direct connection (none of the app's traffic needs one: it goes through the API) | Supabase > Database > Network restrictions | Kyle (Owner), or Dan once made Owner or Administrator |
Rotate the database password; keep it in the vault only, never in chat or a file outside .env.local |
Supabase > Database | Kyle (Owner), or Dan once made Owner or Administrator |
Secret scanning and push protection (ORG-03, SC-3)
Decided (Dan, 28 Oct): option 2, the free route, under the free-first rule in README.md. Option 1 is the upgrade, not the plan.
On GitHub Team, secret scanning and push protection for private repos come with the paid GitHub Secret Protection add-on, charged per active committer (two today). Options:
- Buy it for the org and switch both on for all repos (Kyle, Org > Code security > Configurations). This is what the roadmap asks for and what an auditor recognises.
- Do not buy it; rely on gitleaks in CI (already running over the whole history on every push) and add a gitleaks pre-commit hook. Cheaper, but it catches a secret after the push, not before.
Dependabot alerts are free on Team and can be switched on now (Repo > Settings > Code security).