Home
Softono
swyxdotio

swyxdotio

Open source MIT JavaScript
411
Stars
49
Forks
41
Issues
14
Watchers
1 week
Last Commit

About swyxdotio

This is the repo for swyx's blog - Blog content is created in github issues, then posted on swyx.io as blog pages! Comment/watch to follow along my blog within GitHub

Platforms

Web Self-hosted

Languages

JavaScript

swyx's personal site

swyx's personal site, using:

  • SvelteKit 2 + Svelte 5
  • Tailwind 3 + Tailwind Typography
  • marked + shiki for markdown rendering (replaced mdsvex/remark)
  • Cloudflare Workers with Static Assets (hybrid: prerendered static pages + on-demand SSR posts, edge-cached)
  • GitHub Issues as CMS

If you want to make a site based on this, see https://github.com/swyxio/swyxkit for a cleaner starter template

Architecture / rendering

  • Static (prerendered at build): /about, /portfolio, /subscribe.
  • Dynamic + edge-cached: /, /ideas, /[slug], /rss.xml, /sitemap.xml, and /api/*. Rendered on demand on Cloudflare Workers and stored in the Cache API via src/hooks.server.js until s-maxage expires, so new posts appear without a rebuild and serving is O(1).
  • Durable content manifest: the Worker reads the parsed GitHub Issues CMS data from the CONTENT_MANIFEST KV namespace. GitHub is only queried to bootstrap an empty namespace or refresh it after a webhook, so ordinary cache misses do not depend on GitHub availability.
  • Compact ideas list: /api/listContent.json omits article bodies for the default /ideas, RSS, and sitemap paths. Full-body search remains available through /api/searchContent.json, which the browser downloads only after a reader uses the search box.
  • Instant publishing: a GitHub Issues webhook hits /api/revalidate, which verifies the signature, refreshes the KV manifest, and rolls a KV-backed cache generation. Cache keys also include the Worker version, so both publishes and deploys bypass older edge entries.

Environment variables (Cloudflare Workers)

What each variable does

Variable Required? What it does
GH_TOKEN Yes A GitHub Personal Access Token used to authenticate calls to the GitHub Issues API (the CMS). Without it, requests are unauthenticated and capped at 60/hr, which the site blows through quickly and starts failing. With it, the limit is 5000/hr. Read at runtime via $env/dynamic/private (Cloudflare platform.env) and at build time for the prerendered pages — so it must be set in both the runtime secrets and the build environment.
GH_WEBHOOK_SECRET Recommended A shared secret used to verify (HMAC SHA‑256) that incoming requests to /api/revalidate actually came from your GitHub webhook. This enables fast publishing: editing an Issue refreshes the KV manifest and rolls the cache generation instead of waiting for the s-maxage TTL. If unset, /api/revalidate returns 500 and you fall back to TTL-based freshness.

Where to get the values

  • GH_TOKEN — GitHub → Settings → Developer settings → Personal access tokens. A classic token with public_repo (or repo for private) scope is sufficient since it only reads Issues.
  • GH_WEBHOOK_SECRET — generate any strong random string, e.g. openssl rand -hex 32. You'll paste the same value into the GitHub webhook config (below).

Set them on Cloudflare Workers

Option A — Dashboard: Cloudflare dashboard → Workers & Pages → your Worker → Settings → Variables and Secrets. Add each runtime value and encrypt secrets. GH_TOKEN must also be present in the Git-connected build environment because prerendered pages read it during builds.

Option B — Wrangler CLI:

# runtime secrets (encrypted)
npx wrangler secret put GH_TOKEN
npx wrangler secret put GH_WEBHOOK_SECRET

Each command prompts for the value. List them with npx wrangler secret list. wrangler.toml declares GH_TOKEN as required, so deployments fail clearly instead of publishing a Worker without CMS authentication. It also declares the CONTENT_MANIFEST KV binding. Wrangler creates that namespace on the first deployment and the Worker fills it on the first uncached content request.

Local dev reads the same names from a gitignored .env file (see .env.example).

Wire up the GitHub webhook (for instant publishing)

In your content repo: Settings → Webhooks → Add webhook:

  • Payload URL: https://swyxdotio.swyxio.workers.dev/api/revalidate
  • Content type: application/json
  • Secret: the same value as GH_WEBHOOK_SECRET
  • Events: "Let me select individual events" → check Issues only

On each Issue create/edit, the endpoint verifies the signature, refreshes the durable content manifest, derives the affected slug, and rolls the cache generation for the relevant pages (/, /ideas, /{slug}, /rss.xml, /sitemap.xml, and the list/api endpoints).

Commands

  • npm run dev — local dev server
  • npm run build — production build (Cloudflare adapter)
  • npm run preview — preview the build with wrangler dev
  • node tests/markdown.test.mjs — markdown renderer regression checks
  • npm test — Playwright e2e (requires GH content)

Live URL

See https://swyx.io