You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
How is the site deployed, and what is the provisioning checklist? #16
Decide the deploy shape and the ordered list of accounts to create later:
Cloudflare project shape from the hosting research, build command, output directory, preview versus production, secrets.
The provisioning checklist: Cloudflare account, domain purchase and DNS, Turnstile site, email path setup, Google Workspace mailbox, PostHog project. For each: what it produces (keys, IDs, records) and which env variable it fills.
What can be verified before accounts exist, and what only after.
Produce the deploy plan and checklist as the resolution. A wizard skill run may follow when the accounts are created.
One Worker with Static Assets. Prerendered pages are served as assets without invoking the Worker; only POST /api/assessment reaches it. No second Worker, no Pages project, no Wrangler environments — those exist to isolate multiple deployments and there is one site.
assets.directory is never hand-written — the Vite plugin fills it. Other assets.* keys (html_handling, not_found_handling) may be set in wrangler.jsonc and are carried through.
Ignore Cloudflare's "bare wrangler deploy auto-detects TanStack Start" shortcut: it generates config pointing at .output/server/index.mjs and .output/public, which are Nitro paths, not the Vite-plugin layout used here.
Caching: Static Assets default to public, max-age=0, must-revalidate + ETag. Add public/_headers making Vite's hashed /assets/* immutable; leave HTML on the revalidating default. _headers never applies to Worker responses, so the POST handler sets its own (Cache-Control: no-store, per #12).
Preview vs production
#15 put CI in GitHub Actions, so deployment is Actions-driven rather than Workers Builds:
Trigger
Command
Result
pull request
wrangler versions upload
Preview URL, production untouched
push to main
wrangler deploy
version created, 100% traffic
Versions and deployments are decoupled, so rollback is a deployment change rather than a rebuild.
Preview URLs are indexable, and #14 ships the site index, follow. The mitigation is already in place: #14's canonical tag points at VITE_SITE_URL, so preview builds must be built with the production VITE_SITE_URL, marking every preview as a duplicate of production. Setting workers_dev: false would remove the risk entirely but also disables Preview URLs, since they depend on it — not worth losing previews for a residual risk that canonical already covers.
Provisioning checklist
Ordered by dependency. Each step names what it produces and which variable it fills.
1 · Cloudflare account
Free plan. → CLOUDFLARE_ACCOUNT_ID (GitHub secret). Create a scoped API token with Workers Scripts: Edit → CLOUDFLARE_API_TOKEN (GitHub secret).
2 · Domain and DNS
Purchase, point nameservers at Cloudflare, wait for the zone to go active. → VITE_SITE_URL, and the domain hard-coded into public/robots.txt and public/sitemap.xml (#14 — these cannot be written before this step).
3 · Google Workspace mailbox
The address that receives submissions. Apex MX stays pointed at Google throughout — nothing in this checklist changes it. → ASSESSMENT_TO, and VITE_CONTACT_EMAIL for #12's failure-panel mailto:.
4 · Email Routing on a subdomain ⚠️
Enable Email Routing on forms.<domain>, not the apex. Cloudflare writes the subdomain's MX, SPF and DKIM records. Enabling it on the apex would replace Google's MX and break the client's mail.
Test this step before relying on the free lane. The email research could not confirm from any primary source that a routing subdomain is an acceptable from domain for free verified-destination sends. If it is rejected, the fallback is Workers Paid (USD 5/month) plus Email Sending onboarding — a cost change, not a redesign. Confirm here rather than discovering it at launch.
5 · Verified destination address
Add the step-3 mailbox as a verified destination; Cloudflare emails a verification link. Sends to verified destinations are free on every plan and count against no quota. Declare the EMAILsend_email binding in wrangler.jsonc with destination_address set to it. Sender: assessment@forms.<domain>.
6 · Turnstile widget
Create an invisible widget (#13… #12). Add the production hostname; Cloudflare recommends production sitekeys not allow localhost. → VITE_TURNSTILE_SITEKEY (build-time) and TURNSTILE_SECRET via wrangler secret put. Set secrets.required in wrangler.jsonc so a deploy missing it fails loudly.
7 · PostHog project
Cloud US. → VITE_POSTHOG_PROJECT_TOKEN. Flip three project settings: enable Cookieless server hash mode, enable Discard client IP data, leave Record user sessions off.
8 · WAF rate-limiting rule
A rate-limiting rule on POST /api/assessment (#12 deliberately kept this out of code to keep the Worker stateless). Produces no variable — it exists only in the dashboard, so it is invisible to the repo and easy to forget on a rebuild. Worth a note in the runbook.
9 · Custom domain on the Worker
Attach <domain> (and www if wanted) to the Worker.
10 · Assets and launch
public/og.png (1200×630), favicon.ico, apple-touch-icon.png; robots.txt and sitemap.xml written with the real domain; submit the sitemap in Google Search Console.
11 · Copy gate
pnpm placeholders must exit clean before the production deploy — the hard gate from #15's deploy workflow, protecting #14's decision to ship indexable.
What can be verified before any account exists
Everything except the network itself. This is the map's standing constraint discharged.
Verified locally
How
Full page, prerendered
vite dev, then vite preview on the production build
Form POST end to end
wrangler dev runs the Worker in workerd via Miniflare, no login
Activity
Abuubkar commentedon Sep 5, 2026
Resolved by grilling, on top of the Cloudflare SSG research and the email research.
Deploy shape
One Worker with Static Assets. Prerendered pages are served as assets without invoking the Worker; only
POST /api/assessmentreaches it. No second Worker, no Pages project, no Wrangler environments — those exist to isolate multiple deployments and there is one site.assets.directoryis never hand-written — the Vite plugin fills it. Otherassets.*keys (html_handling,not_found_handling) may be set inwrangler.jsoncand are carried through.Ignore Cloudflare's "bare
wrangler deployauto-detects TanStack Start" shortcut: it generates config pointing at.output/server/index.mjsand.output/public, which are Nitro paths, not the Vite-plugin layout used here.Caching: Static Assets default to
public, max-age=0, must-revalidate+ ETag. Addpublic/_headersmaking Vite's hashed/assets/*immutable; leave HTML on the revalidating default._headersnever applies to Worker responses, so the POST handler sets its own (Cache-Control: no-store, per #12).Preview vs production
#15 put CI in GitHub Actions, so deployment is Actions-driven rather than Workers Builds:
wrangler versions uploadmainwrangler deployVersions and deployments are decoupled, so rollback is a deployment change rather than a rebuild.
Preview URLs are indexable, and #14 ships the site
index, follow. The mitigation is already in place: #14's canonical tag points atVITE_SITE_URL, so preview builds must be built with the productionVITE_SITE_URL, marking every preview as a duplicate of production. Settingworkers_dev: falsewould remove the risk entirely but also disables Preview URLs, since they depend on it — not worth losing previews for a residual risk that canonical already covers.Provisioning checklist
Ordered by dependency. Each step names what it produces and which variable it fills.
1 · Cloudflare account
Free plan. →
CLOUDFLARE_ACCOUNT_ID(GitHub secret). Create a scoped API token with Workers Scripts: Edit →CLOUDFLARE_API_TOKEN(GitHub secret).2 · Domain and DNS
Purchase, point nameservers at Cloudflare, wait for the zone to go active. →
VITE_SITE_URL, and the domain hard-coded intopublic/robots.txtandpublic/sitemap.xml(#14 — these cannot be written before this step).3 · Google Workspace mailbox
The address that receives submissions. Apex MX stays pointed at Google throughout — nothing in this checklist changes it. →
ASSESSMENT_TO, andVITE_CONTACT_EMAILfor #12's failure-panelmailto:.4 · Email Routing on a subdomain⚠️
Enable Email Routing on
forms.<domain>, not the apex. Cloudflare writes the subdomain's MX, SPF and DKIM records. Enabling it on the apex would replace Google's MX and break the client's mail.5 · Verified destination address
Add the step-3 mailbox as a verified destination; Cloudflare emails a verification link. Sends to verified destinations are free on every plan and count against no quota. Declare the
EMAILsend_emailbinding inwrangler.jsoncwithdestination_addressset to it. Sender:assessment@forms.<domain>.6 · Turnstile widget
Create an invisible widget (#13… #12). Add the production hostname; Cloudflare recommends production sitekeys not allow
localhost. →VITE_TURNSTILE_SITEKEY(build-time) andTURNSTILE_SECRETviawrangler secret put. Setsecrets.requiredinwrangler.jsoncso a deploy missing it fails loudly.7 · PostHog project
Cloud US. →
VITE_POSTHOG_PROJECT_TOKEN. Flip three project settings: enable Cookieless server hash mode, enable Discard client IP data, leave Record user sessions off.8 · WAF rate-limiting rule
A rate-limiting rule on
POST /api/assessment(#12 deliberately kept this out of code to keep the Worker stateless). Produces no variable — it exists only in the dashboard, so it is invisible to the repo and easy to forget on a rebuild. Worth a note in the runbook.9 · Custom domain on the Worker
Attach
<domain>(andwwwif wanted) to the Worker.10 · Assets and launch
public/og.png(1200×630),favicon.ico,apple-touch-icon.png;robots.txtandsitemap.xmlwritten with the real domain; submit the sitemap in Google Search Console.11 · Copy gate
pnpm placeholdersmust exit clean before the production deploy — the hard gate from #15's deploy workflow, protecting #14's decision to ship indexable.What can be verified before any account exists
Everything except the network itself. This is the map's standing constraint discharged.
vite dev, thenvite previewon the production buildwrangler devruns the Worker in workerd via Miniflare, no login1x…AApasses,2x…AAfails,3x…AAreturns "token already spent"EMAILbinding simulated; message written to console and local filesVITE_POSTHOG_PROJECT_TOKENvp check,vp buildpnpm placeholdersVariable map
CLOUDFLARE_ACCOUNT_ID/CLOUDFLARE_API_TOKENVITE_SITE_URLASSESSMENT_TOVITE_CONTACT_EMAILEMAILVITE_TURNSTILE_SITEKEYTURNSTILE_SECRETVITE_POSTHOG_PROJECT_TOKENKnown risks carried into production
assessment_failed{reason:"delivery"}is the tripwire.A
wizardskill run is the natural follow-up once someone sits down to do steps 1–10.Abuubkar commentedon Sep 5, 2026
Resolved. Deploy shape, ordered provisioning checklist, and the before/after-accounts verification split in the resolution comment.