Authoring this site

How dontpanic.fm is served, and how to replace it.

This page is safe to hand to an AI assistant — there are no secrets on it. It describes how dontpanic.fm is hosted and what you need to know to rewrite it without breaking anything.

What is actually serving this

Two copies of Caddy and a plain static file server. There is no PHP, no Node, no database, no framework and no build step. The HTML you see is the HTML on disk.

Practically: you edit files on the Mac Mini, and the Pi publishes them. You never touch the Pi.

Where the files are

/Users/Shared/dontpanic.fm/
├── index.html      the front page — players are built from streams.json
├── style.css       placeholder styling, replace freely
├── streams.json    the station list
├── on-air.html     how to broadcast
├── authoring.html  this page
└── CLAUDE.md       working notes

Saving a file publishes it. There is no deploy step and no cache to clear — the moment you write index.html, that is what the public sees. A broken page is broken in public. Keep a copy of anything you are about to replace: cp index.html index.bak.html

Getting in

ssh matt@macmini.tail32e87c.ts.net
cd /Users/Shared/dontpanic.fm

Or work locally on your own Mac and push the whole folder up:

rsync -av ./ matt@macmini.tail32e87c.ts.net:/Users/Shared/dontpanic.fm/

Both need Tailscale running. The folder belongs to you — no sudo is required for anything on this page.

Four rules that will bite you

These are not style preferences. Each one produces a failure that looks like a server fault but is not.

1. Links need the .html extension

The file server does no extension guessing. /on-air.html works; /on-air returns 404. Write <a href="/on-air.html">, never href="/on-air".

2. Files must stay world-readable

Caddy runs as david, but this folder belongs to matt. It can only serve what it can read. Files need 644 and directories 755. An editor or script that writes 600 produces a 403 Forbidden that looks like the server is broken. If a new file will not load, check this first:

chmod 644 newfile.html          # or, for everything:
chmod -R u=rwX,go=rX /Users/Shared/dontpanic.fm

3. /listen/ belongs to the radio

Anything under /listen/ is proxied to Icecast, not read from disk. Do not create a file or folder called listen — it would be unreachable. Use those URLs, do not shadow them:

4. streams.json drives the players

index.html fetches streams.json at page load and builds one player per entry, then polls the status URL every fifteen seconds to mark each station on air or offline. Adding a station is a data edit:

{ "name": "Late Show", "mount": "late",
  "url": "/listen/late", "description": "What it is." }

If you rewrite index.html from scratch you may drop this mechanism entirely — nothing else depends on it. But if you keep the players, keep the offline check: an <audio> element pointed at a mount with no source fails silently, and a silent player reads as a broken website.

What you are free to change

index.html and style.css are placeholders written to be thrown away. Replace the markup, the layout, the typography, the colours, the whole approach. Add as many pages, images, fonts and scripts as you like. Nothing in the hosting setup cares what the HTML looks like.

The only fixed points are the four rules above and the fact that / serves index.html.

Putting Aether on the page

Aether Desktop is Tauri v2 — a Rust shell around a webview, with the audio running as a Web Audio graph and a dsp-wasm/ crate already in the workspace. Web Audio is a browser API, so the audio engine should run in an ordinary browser tab with the Rust shell's jobs stubbed out. That makes the shortest path to showing it off hosting the front end you already have, rather than porting anything.

The wasm target is a user-level install, no sudo:

rustup target add wasm32-unknown-unknown

Three things worth considering, roughly in order of how native they are here:

Leave these alone

Checking your work

curl -sS -o /dev/null -w '%{http_code}\n' https://dontpanic.fm/
curl -sS -o /dev/null -w '%{http_code}\n' https://dontpanic.fm/yourpage.html

200 is good. 403 means permissions — rule 2. 404 means the filename or the extension is wrong — rule 1.