A single-page interactive: one real tūī recording shown four ways at once — longitudinal air wave, voltage-over-time signal (both channels), digital samples, and frequency spectrum. Pure static: HTML + inline JS/CSS, with a 12-second tūī clip embedded as a base64 data URI. No backend, no database, no user input, no outbound calls from the page.
public/index.html the whole app (self-contained)
public/favicon.svg tūī mark (inline SVG, no raster fallback)
main.go go.mod optional Go static server (embeds public/)
deploy/cloudflared.config.yml.example Cloudflare Tunnel ingress
No third-party origins: fonts are system stacks, the audio is inlined, and the
CSP is default-src 'none' with connect-src 'none'. The page makes no
network requests of any kind once served.
It's one static file, so host it as static content:
- Cloudflare Pages — new project → upload
public/→ live on HTTPS. Free, nothing to patch, no tunnel. This is the cheapest way to meet "public, behind Cloudflare". - Any static host (Netlify, S3+CloudFront, a jail already running nginx) works
the same way — just serve
public/.
Skip the Go binary and the tunnel entirely if you go this route.
Use this if you want it served from your own box rather than Cloudflare's edge.
cd tui-exhibit
go build -o tui-exhibit # single binary, assets baked in
./tui-exhibit -addr 127.0.0.1:8080Cross-compiling from another machine to a FreeBSD host:
GOOS=freebsd GOARCH=amd64 go build -o tui-exhibitBind to loopback (default) when a tunnel fronts it — nothing needs a public
port. In a Bastille jail, either bind the jail's address and point the tunnel
at it, or run the binary and cloudflared in the same jail on 127.0.0.1.
Run it under your service manager. A minimal rc.d-style supervisor, daemon(8),
or a tiny service wrapper is enough; it's a long-running foreground process.
Install cloudflared (FreeBSD: pkg install cloudflared, or grab a release
binary). Then:
cloudflared tunnel login
cloudflared tunnel create tui-exhibit
cloudflared tunnel route dns tui-exhibit tui.example.com
cp deploy/cloudflared.config.yml.example ~/.cloudflared/config.yml
# edit: set the tunnel UUID, credentials path, and your hostname
cloudflared tunnel run tui-exhibitThe tunnel gives you public HTTPS with no inbound ports opened. Cloudflare handles TLS and edge compression (the page is ~230 KB because the audio is inlined; Brotli at the edge cuts it hard).
Optional: in the Cloudflare dashboard turn on Always Use HTTPS, and if you ever want to gate it, Cloudflare Access puts a login in front without any code change.
If you'd rather expose a port and let Caddy do auto-TLS:
tui.example.com {
root * /path/to/public
file_server
header {
Content-Security-Policy "default-src 'none'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; font-src 'self'; img-src 'self' data:; media-src 'self' data:; connect-src 'none'; base-uri 'none'; frame-ancestors 'none'"
X-Content-Type-Options nosniff
Referrer-Policy no-referrer
Permissions-Policy "geolocation=(), camera=(), microphone=()"
}
}
- No backend and no user input, so the attack surface is the static asset
itself.
connect-src 'none'in the CSP means the page can't make network requests even if modified. - GET and HEAD only.
http.FileServeranswers 200 to any method, including POST, DELETE and TRACE. Thesecuremiddleware rejects everything but GET and HEAD with a 405, so a public endpoint never echoes a body to TRACE or appears to accept a write. - Request limits are set for public exposure:
ReadTimeout15s,ReadHeaderTimeout5s,WriteTimeout30s,MaxHeaderBytes64 KiB. - The inline
<script>/<style>require'unsafe-inline'in the CSP. That's acceptable here because there is no injection surface; don't copy this CSP to a page that takes user input. Permissions-Policydenies geolocation, camera and microphone — the app uses none of them.
-
Fonts — done. The page originally pulled Fraunces/Inter from Google Fonts, which sent every visitor's IP to Google before a byte rendered. It now uses system font stacks (
--serif/--sans/--monoin:root), so there are no third-party origins and the CSP is tightened tostyle-src 'self' 'unsafe-inline'andfont-src 'self'. -
Recording licence. The embedded clip is xeno-canto XC378280 by Matthias Feuersenger, CC BY-NC-SA 4.0. Public display is fine as long as use stays non-commercial, the attribution stays on the page (it's in the footer), and any redistribution keeps the same licence. Swapping in your own recording removes the constraint.
Replace the base64 data URI. From a new mp3:
B64=$(base64 -w0 your-clip.mp3) # macOS/BSD: base64 -i your-clip.mp3 | tr -d '\n'
# then replace the value of `var audioB64="..."` in public/index.htmlKeep it short (10–15 s) so the page stays light and loops cleanly.