cp .env.example .env
pnpm installpnpm run devTo enable HMR:
pnpm run hmrpnpm run previewPreview runs the TypeScript server with the production asset configuration.
- Production deploys from
main. - Staging deploys from the
stagetag via:
pnpm run push:stageThe app keeps browsers conservative while serving shared content from Fastly:
| Response | Cache policy | Deploy purge |
|---|---|---|
| Rendered HTML | Browsers revalidate; Fastly caches for 5 minutes with 1 week stale-while-revalidate | documents |
Root public/ files |
Browsers and Fastly cache for 1 hour | static-assets |
Fingerprinted /assets/* |
Browsers and Fastly cache immutable URLs for 1 year | Never |
| Personalized, mutation, and error responses | private, no-store |
Not cached |
app/middleware/render.ts applies the document policy unless an action sets its own Cache-Control. app/router.ts tags root public/ files. The production workflow completes the Fly rollout, then purges the documents and static-assets surrogate keys twice to cover Fastly edge/shield propagation.
Surrogate keys are public cache tags, not credentials; Fastly normally removes them before responding to browsers. Purge requests are authorized with the GitHub Actions FASTLY_API_TOKEN secret. Fingerprinted assets are not purged so older documents and open tabs can continue loading their matching assets.
- Create a branch from the latest target branch.
- Push your branch and open a PR.
- Run
pnpm run validatebefore shipping a PR. - See
AGENTS.mdfor repo-specific rules (routes, assets, tests). Remix framework patterns live under.agents/skills/remix/.
app/— Remix 3 site:routes.ts(URL contract),router.ts(middleware and route wiring),actions/,ui/,data/, etc.server.ts(repo root) — Node HTTP server used in development and production.data/— Blog posts and author metadata (separate fromapp/data/, which holds app-layer server modules such as blog queries).
- The
/newsletterarchive renders issues from the privateremix-run/newsletterGitHub repository at runtime via a single tarball fetch. - Because the repo is currently private, set
NEWSLETTER_GITHUB_TOKEN(see.env.example) locally and in production to enable archive rendering. Without it,/newsletterreturns a 503 when no cached snapshot exists. - Issue directories are strict integers (
newsletter-<N>); markdown filenames carry the UTC publication date (YYYY-MM-DD-remix-newsletter-N.md). - Add
draft: trueto an issue's frontmatter to keep it out of the archive. Missing or falsedraftvalues are treated as published.
- Add a markdown file at
data/posts/{slug}.md. - Keep post author names aligned with
data/authors.yml. - Put post images under
public/blog-images/posts/{slug}/. - Put featured header images under
public/blog-images/headers/. - Use relative blog links like
[Title](post-slug)(not/blog/post-slug).