Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
3.6 KiB
Deploying to benstull.art — Cloudflare Pages + R2
Publishes the simulator as a fully-static site: the frontend on Cloudflare
Pages (benstull.art, app at /human-experience-simulator, apex redirects to
it) and the video/audio on R2 behind static.benstull.art. No origin server.
Design: docs/superpowers/specs/2026-06-30-cloudflare-static-publish-design.md.
Plan: docs/superpowers/plans/2026-06-30-cloudflare-static-publish.md.
This project is exempt from the flotilla/PPE/§9 pipeline (operator-set) — it deploys directly to Cloudflare. Safety/hygiene rules still hold.
Prerequisites (operator gestures — need Cloudflare auth)
wrangler is not installed in the dev box; the deploy runs from a machine with
Cloudflare credentials.
npm i -g wrangler
wrangler login
benstull.art is already a Cloudflare zone (no nameserver migration).
1. Build the artifacts
From the repo root (run as a MODULE — tools/http.py shadows stdlib http if run
as a script):
.venv/bin/python -m tools.build_static
Produces:
dist/— Pages output:dist/human-experience-simulator/(frontend + bakedclips.json/ring.json/media-versions.json+ generatedconfig.js) and a rootdist/_redirects(apex → app path, and no-slash → slashed).dist-media/— R2 upload tree, ~2.3 GB / 604 files, manifest-referenced only (clip bases + 154 transition morphs + 5 per-scale audio). No master/mezzanine sources.
2. R2 — bucket, CORS, custom domain, media
wrangler r2 bucket create hef-media
wrangler r2 bucket cors put hef-media --rules deploy/cloudflare/r2-cors.json
wrangler r2 bucket sync ./dist-media r2://hef-media
Then in the dashboard (R2 → hef-media → Settings):
- Custom domain: add
static.benstull.art(this is themediaBasebaked intoconfig.js). - CORS is load-bearing — the WebGL dream shader taints without it. The policy
in
r2-cors.jsonallowshttps://benstull.art+Range(video scrubbing). - Caching: the media URLs are content-hash-versioned (
?v=<hash>), so set the objectsCache-Control: public, max-age=31536000, immutable(a re-baked clip gets a new hash → new URL → cache busts cleanly).
If you change the media base, rebuild with --media-base https://<host>/ so
config.js matches.
3. Pages — deploy + domain + redirect
wrangler pages project create human-experience-simulator
wrangler pages deploy dist --project-name human-experience-simulator
Then in the dashboard (Pages → project → Custom domains):
- Bind
benstull.artto the project. - The app serves at
benstull.art/human-experience-simulator/; the generateddist/_redirectssends/and the no-slash form to it. (Alternatively, a zone-level Redirect Rulebenstull.art/→/human-experience-simulatoris the authoritative equivalent if you prefer not to ship_redirects.)
4. Verify before/after going live
# CORS + Range on the media origin (after step 2)
curl -sI -H "Origin: https://benstull.art" -H "Range: bytes=0-1" \
https://static.benstull.art/<some>/base.mp4 | grep -iE "access-control-allow-origin|content-range|accept-ranges"
Open https://benstull.art/ → should redirect to
https://benstull.art/human-experience-simulator/. Turn the Feel knob to max
and confirm the dream effect renders (no SecurityError in the console — that
would mean CORS is misconfigured and the GL texture is tainted). The local
static-build E2E (simulator/e2e/tests/static-build.spec.ts) exercises this same
cross-origin path before you ship.