Quarterly Access Review Procedure
What this review actually is today
Stratify is a single-engineer team (register/facts.md). This review is, honestly, currently a self-check: Toby reviewing Toby's own access, because Toby is the only human with any technical access to review. That is stated here plainly rather than dressed up as independent oversight it isn't. The review still has value even at team-of-one scale — it's the periodic forcing function to notice an application role that was granted for a one-off task and never removed, or a partner API key with broader scopes than it needs — but its real value as a control starts once a second person holds any elevated role, at which point this becomes an actual review of someone else's access rather than an audit of one's own memory.
Scope
Quarterly (every 3 months, aligned to the calendar quarter), the ISMS Owner reviews:
- `subscriber_roles` assignments — every row in the table, cross-checked against who should
currently hold admin, founder, or compliance (apps/web/lib/auth/roles.ts:4). For each elevated role held, confirm there is still a stated reason someone holds it.
- Service-role key usage and exposure — the Supabase service-role API key is the structural
bypass for RLS (register/facts.md, "Database & access control"; getSupabaseService() used throughout apps/web/app/(admin)/). Confirm:
- Where the key currently lives (production env vars on the Hetzner host, local Keychain
entries) and that the list of places it lives hasn't silently grown.
- No commit, log, or Sentry/PostHog payload contains the key in plaintext (spot-check via
git log -p grep and a manual look at recent error payloads).
- The key has not been rotated on a schedule that's drifted from what
docs/secrets.md's
rotation table implies it should be (event-driven rotation — leaver, suspected compromise — not a fixed calendar cadence today; note that as a gap if a fixed cadence is ever adopted).
- Partner API keys (
partner_api_keystable) — count of active (non-revoked) keys per
partner, cross-checked against partners.status (sandbox/live/suspended). Flag any live-status partner with a key issued but no completed Phase A KYB checklist (apps/web/lib/admin/partner-kyb.ts, PARTNER_PHASE_A_ITEMS) — that combination shouldn't be possible given canPartnerGoLive()'s gate, but the review exists partly to catch a gate that was bypassed or a status changed outside the normal action path.
- No unexpected elevated access. Confirm: no GitHub collaborator beyond the current expected
set, no Tailscale-network member beyond the current expected set, no active Keychain-shared secret for someone who has since left (see PRO-01 leaver checklist) — cross-referencing GitHub settings, tailscale status, and PRO-01's evidence log for the quarter.
Steps
- Pull current state. Query
subscriber_roles,partner_api_keys(revoked_at is null),
and partners via the service-role key (this review is one of the legitimate reasons to use it read-only). List GitHub collaborators and Tailscale network members from their respective consoles.
- Compare against expectation. For each row/entry, write down: who/what it is, what access
it grants, and whether that's still justified. "Justified" for a team of one today typically means "this is Toby's own access, still needed" — say so rather than inventing false findings.
- Flag and act on anomalies. Anything unexpected (a role that should have been revoked under
PRO-01, a partner key active past a suspension, a stale GitHub collaborator) gets actioned immediately, not just noted — this review is not a report-only exercise.
- Write the review record. Produce a dated markdown entry (see Evidence below) stating: date
of review, who performed it, what was reviewed, what was found, and what (if anything) was changed as a result.
- Sign and date. The ISMS Owner signs (name + date) the review record. Since there is no
second person to countersign today, that limitation is stated in the record itself rather than silently presented as a two-person review.
Evidence
Output format: one dated markdown file per quarter under docs/compliance/isms/evidence/, e.g. evidence/2026-Q3-access-review.md, containing:
# Access Review — <Quarter> <Year>
Date performed: <date>
Performed by: ISMS Owner (Toby)
## subscriber_roles reviewed
<table or list: user, role, justification, action if any>
## Service-role key
<location(s) confirmed, exposure spot-check result>
## partner_api_keys reviewed
<count active, any anomalies against KYB/status gate>
## Unexpected access check
<GitHub collaborators, Tailscale members, cross-ref against PRO-01 log — findings>
## Findings and actions taken
<list, or "none">
Signed: Tobias Temmen, ISMS Owner — <date>This is a proposed lightweight format, not a fixed template enforced by tooling — adjust it as the review matures, but keep every quarter's record immutable once written (git history is the version control, per register/schema.md's "why YAML + git" rationale).
Review
This procedure itself is reviewed whenever the team grows past one person with elevated access — at that point "self-check" language throughout this document should be revisited and the review should genuinely become a check on someone else's access, not just Toby's own. Otherwise reviewed annually.