Stratify
Legal and compliance

Quarterly Access Review Procedure

docs/compliance/isms/procedures/PRO-02-access-review.md

Source updated 03. Aug. 2026

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:

  1. `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.

  1. 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).

  1. Partner API keys (partner_api_keys table) — 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.

  1. 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

  1. 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.

  1. 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.

  1. 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.

  1. 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.

  1. 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.