Stratify
Engineering

Cloudflare Access

docs/runbooks/cloudflare-access.md

Source updated 03. Aug. 2026

Cloudflare Access — open Stratify preview

Nothing gates `app.stratifyinvest.com` at the edge today. Stratify used to sit on ventures.wait-what.co, which had a Cloudflare Zero Trust Access application (email + one-time PIN) in front of it. Moving to its own host left that application behind: /ops, /pilot and the rest are reachable at the edge and rely solely on Supabase sign-in and role checks.

This runbook is how you put the gate back, or deliberately open parts of it. That layer is not in this repo; change it in the Cloudflare dashboard or via API.

API token (same account as Loki)

If Access for loki.vet already works in the dashboard, you are probably on the right Cloudflare login (e.g. Apple relay account). The blocker is usually the API token, not the account.

ENV_CLOUDFLARE_API_TOKEN today can only see zone loki.vet and returns Authentication error on Access APIs — typical when the token was created with zone-only DNS permissions and no Zero Trust scopes.

Create a new token (or edit) with at least:

PermissionScope
Access: Apps and PoliciesEdit
Access: Organizations, Identity Providers, and GroupsEdit (if you use email allowlists)
Zone → DNSEdit (optional, for DNS)
Zone → ZoneRead (or Edit)

Include zones: All zones (or explicitly stratifyinvest.com + loki.vet). Account resources: All accounts or your relay account id 95d458dfe0515c28c201b2d91346ec8a.

security add-generic-password -a "$USER" -s "ENV_CLOUDFLARE_API_TOKEN" -w "NEW_TOKEN"
export CLOUDFLARE_ZONE_ID=1db8bc7c3ef60758f8a642828b4ffd8a   # stratifyinvest.com
./scripts/cf-access-handbook-bypass.sh

Zone-scoped tokens often cannot call /accounts/{id}/access/*; the script uses /zones/{zone_id}/access/apps and creates a separate self-hosted app for app.stratifyinvest.com/handbook with an inline Bypass → Everyone policy — more specific than a host-wide application, so it wins.

Same checklist as Loki pre-launch: loki/docs/PRE_LAUNCH_HARDENING_HANDOFF.md (Cloudflare Access setup).

Cursor Cloudflare plugin (MCP)

Docs search works without a special token. Automating Access still needs the token scopes above; MCP OAuth alone lists the same account but does not replace Access API permissions.

  1. MCP auth: Optional — for Workers/D1/R2 on the relay account.
  2. API token (automation): As in the table above. Then run:
./scripts/cf-access-handbook-bypass.sh

Docs MCP query that matches this runbook: *“Bypass public endpoint Access application path”* (see common policies).

Why the handbook looks unstyled (white page)

Handbook HTML can load with HTTP 200 while CSS/JS under /_next/static/* still returns 302 → Cloudflare Access login. Browsers then render structure without Tailwind → white background, default fonts.

Check:

curl -sI "https://app.stratifyinvest.com/_next/static/chunks/webpack.js" | head -3
# Bad: 302 + location: cloudflareaccess.com
# Good: 200 from origin

Fix (pick one):

  1. Cloudflare (recommended): Bypass Access for /handbook, /handbook/*, and /_next/* (see script below).
  2. Inline fallback: Handbook layout injects critical dark-theme CSS if the main stylesheet is blocked.

Open handbook for cofounders (recommended minimum)

  1. Cloudflare Zero TrustAccessApplications
  2. Open the application that protects app.stratifyinvest.com (create one first if there is none)
  3. Either:
  • Bypass rule: Add policy *Bypass* for paths /handbook, /handbook/*, **and /_next/***, or
  • Remove gate: Delete the application or set policy to *Everyone* / disable Access for that hostname

After change, verify:

curl -sI "https://app.stratifyinvest.com/handbook" | head -5

Expect HTTP/2 200 (or 308/404 from the Hetzner origin if the handbook build is not deployed yet), not 302 to cloudflareaccess.com. A 404 after bypass means Access is open — ship the handbook routes next.

Remove Access for the whole preview

Same as above, but remove or bypass the entire app.stratifyinvest.com application — which is the state today. Ops routes (/ops, /pilot, …) are then world-*reachable*, protected only by app-level auth. Keep Access on those paths if you want an edge gate and only the handbook public.

App-level access (this repo)

  • `/handbook` — public in Next.js (no role check)
  • `/ops` and admin — still require Supabase sign-in + admin / founder / compliance roles