בס״ד

PWA Private Deploy — Cloudflare Access on a custom domain

docs/HOW_TO/pwa-private-deploy.md · last changed (pre-VM history) · rendered from GitHub master

PWA Private Deploy — Cloudflare Access on a custom domain

Status: option 3 from 2026-05-08 conversation. Long-game replacement for the
Netlify-drop quick fix. ~1.5 hr total setup, then permanent until you change
the domain or reset.

What this gets you

A real bookmarkable URL like ops.hookstreetcapital.com that:
- Loads on phone, iPad mini, desktop, anywhere
- Requires you to be signed into your Google account to access
- Locks down to ONLY sam@hookstreetcapital.com — no one else can reach it even with the URL
- Embedded API key still works as second factor
- No CORS / file:// drama
- Survives device changes and OneDrive syncs

The architecture: Cloudflare Pages serves the HTML → Cloudflare Access in front gates it on Google login → only emails you whitelist can pass through → the page then talks to the Apps Script PWA endpoint as before.

Prerequisites

Sequence

1. Move domain DNS to Cloudflare (~15 min, one-time)

Skip this step if hookstreetcapital.com is already on Cloudflare DNS.

a. Sign in to Cloudflare. Add Site → enter hookstreetcapital.com.
b. Cloudflare scans existing DNS records; review they all came across.
c. Cloudflare gives 2 nameservers. Update them at your registrar (Namecheap/GoDaddy/wherever the domain is).
d. Wait 5-30 min for DNS propagation. Verify with nslookup hookstreetcapital.com — answers should match Cloudflare's.

2. Create Cloudflare Pages project (~10 min)

a. Cloudflare dashboard → Pages → Create a project → Direct upload (no Git for now).
b. Project name: hookstreet-ops (or whatever).
c. Drag the outputs/pwa-deploy/ folder into the upload box.
d. Pages deploys it. You get a hookstreet-ops.pages.dev URL.

3. Add custom domain (~5 min)

a. Cloudflare Pages → your project → Custom domains → Set up a custom domain.
b. Enter ops.hookstreetcapital.com (or whatever subdomain).
c. Cloudflare auto-creates the CNAME record since DNS is already with them.
d. Wait 1-2 min. SSL cert provisions automatically.
e. Visit ops.hookstreetcapital.com — should load the dashboard.

4. Add Cloudflare Access (~20 min — the security layer)

a. Cloudflare dashboard → Zero Trust → Access → Applications → Add an application → Self-hosted.
b. Application name: Hook Street Ops.
c. Domain: ops.hookstreetcapital.com.
d. Identity providers: enable Google. Cloudflare walks you through OAuth setup with Google. Takes ~5 min.
e. Policy: Add policy → name "Sam only" → Action: Allow → Include: Emails → enter sam@hookstreetcapital.com. Optionally also include ztreitel@gmail.com for backup.
f. Save.

5. Test

a. From phone, fresh browser session: visit ops.hookstreetcapital.com.
b. Cloudflare Access intercepts → asks you to sign in with Google.
c. You sign in as sam@hookstreetcapital.com.
d. Cloudflare verifies you're on the allow-list, lets you through.
e. The page loads, fetches obligations from PWA endpoint, displays them.
f. Bookmark the URL. Future visits within session window (default 24h) skip the sign-in.

Update flow (when the HTML changes)

Two options:
- Manual: re-drag the updated folder into Cloudflare Pages → Direct upload
- Auto (recommended): switch the project to Git mode, point at a private GitHub repo containing the HTML, push to deploy

Git mode is the doctrine-aligned move because it ties dashboard updates to the same workflow as everything else.

Decommission / kill switch

If the URL ever leaks: Cloudflare → Pages → your project → Delete.
If just the access list changes: Zero Trust → Access → Applications → edit policy.

Cost

Free tier covers this entirely:
- DNS on Cloudflare: free
- Pages: free (100K req/mo, way more than personal use)
- Access: free for up to 50 users
- Domain renewal: whatever you already pay for hookstreetcapital.com

Source

Sam ask 2026-05-08 noon, voice memo: "the best way so that the information is available only through password or login." Doctrine-aligned permanent replacement for the Netlify-drop quick fix that uses an unprotected URL.

Source trail · docs/HOW_TO/pwa-private-deploy.md @ master · rendered 2026-07-02 7:23 PM EDT by scripts/build-docs.py · the .md in the repo is the truth; this page is the phone-readable view