Council role: Operator (run-it-in-prod pressure test) Topic: Music Site and Blog — soundfactory.com / teropotila.com migration off Angular/AWS to Cloudflare Date: 2026-06-20
R2 has no egress fees — that headline is real and load-bearing for the near-$0 goal. But Class-B operations (GET requests) are $0.36/million after the 10M free-tier ops/month. A composer site is one viral sync placement away from 50M GET requests in 48 hours. At Tero's current traffic that is probably theoretical, but the failure mode has no natural circuit breaker. Workers Static Assets sits in front of the static deploy and caches aggressively, but R2-served audio files on a custom subdomain (e.g., media.soundfactory.com) bypass that cache entirely unless you explicitly put a Worker or Cache Rule in front of the bucket. If you ship R2 naked and a film gets licensed and goes press-viral, the ops bill materializes before anyone wakes up.
Ship a Cache Rule or a thin Caching Worker in front of every R2 audio/video path on day one, not as a follow-up task.
R2 ops-volume alerting does not exist out of the box. Cloudflare's R2 dashboard shows usage but there is no native budget alert or webhook trigger on ops counts. You need to either (a) wire a scheduled Worker that queries the R2 Analytics API and posts to a Slack channel or email if ops-this-month exceeds a threshold, or (b) accept that you find out at invoice time. For a near-$0 site this is the one bill-surprise vector that matters.
Static-asset cache purge is invisible. When Tero updates a portfolio page or blog post, the deploy pipeline must bust the cache on affected routes or visitors get stale HTML for hours. new.cybercussion.com's deploy pipeline has cache-busting logic (import-map stamping) — carry that pattern forward explicitly. Without it you will get "I pushed the fix but the site still shows the old version" incidents at the worst moment (e.g., the day a film opens).
Contact-form Worker errors are silent by default. The Resend-backed contact Worker has no dead-letter queue. If Resend's free tier (100 emails/day) hits its cap or the API key rotates, form submissions are dropped with no record. Add a D1 write (or KV append) on every form POST so you have a local copy independent of Resend's delivery status. This is one table and ten lines of code — build it into the initial Worker, not as a follow-up.
DNS cutover (VF5) has an invisible failure window. Moving nameservers to a new CF account means the old AWS Angular site goes dark the moment NS propagation completes globally. If anything in the new site is broken — a misconfigured wrangler.toml asset path, a missing 301 for an old Angular route, a CORS header missing on R2 — the SEO equity and any live bookings traffic hits a wall.
The cutover runbook must include:
- New site verified live on a staging subdomain under the new CF account before NS flip
- 301 map for every Angular route that has inbound links (pull from Search Console / Ahrefs on the old domain first)
- A rollback NS plan — lower old AWS TTL to 60s at least 48h before cutover so a rollback propagates fast
- Post-cutover smoke: hit every 301 source, every R2 audio URL, the contact form, and confirm Search Console picks up the sitemap within 48h
If the council recommends Emdash, name what "v0.1.0 in production on a client's live revenue site" means operationally: no community-sourced incident responses, no Stack Overflow answers, no plugin ecosystem for auth/search/image-resize. When Tero's blog breaks at midnight before a film festival, the on-call surface is the Emdash GitHub issues and Cloudflare Discord.
Plain Astro SSG has five years of production deployment history, a large community, and solves F1 (real HTML per route) natively. The operator recommendation is plain Astro unless Tero explicitly opts into bleeding-edge risk. Emdash is a watch-this-space once it exits beta.
R2-served progressive MP4 is operationally simpler but gives you no adaptive bitrate, no seek-without-full-download on slow connections, and no play/completion analytics. Cloudflare Stream solves all three but adds a metered line item (~$5/1000 minutes stored + delivery).
The operator concern: R2-only video will generate "video won't load on mobile" support complaints that look like hosting bugs but are actually an unresolved product decision. Make the R2-vs-Stream call explicitly before launch, based on Tero's actual video catalog (sizes, lengths, whether HD showreels are a primary client-facing asset). If showreels are short clips under 2 minutes, R2 progressive is fine. If Tero has 10-minute HD film scores, Stream is the right call and the cost is predictable and small.
| Item | Why it can't wait |
|---|---|
| Cache Rule / Caching Worker in front of R2 media paths | Only bill-surprise vector with no natural ceiling |
| D1/KV write on every contact-form POST | Silent Resend failure = lost client inquiry, no recovery |
| Staging subdomain smoke before NS cutover | Rolling back a broken cutover costs SEO equity and trust |
| 301 map from all Angular routes with inbound links | SEO equity loss is permanent if links 404 on launch day |
Everything else — llms.txt, JSON-LD depth, Emdash vs Astro, Stream vs R2 — has a recoverable failure path. These four do not.