A live web control surface for a VPS-hosted Squeezebox / Lyrion Music Server (LMS): guests browse the library and queue songs from their phones, the owner runs the queue and playback — and any NFC sticker can be bound to an album so a single tap plays it on the speaker.
▶ Live: harmonizerlabs.cc/cloud-squeeze
The Tap console: every sticker is a re-pointable link to a song — the sticker never changes, only what it means.
Bind any NTAG NFC sticker to an album or song; tapping it opens a tiny page that plays it on the Squeezebox — no app install, no login for the tap itself. Stickers are re-pointable: the printed URL never changes, only what it resolves to, so one sticker can mean a different album next week. The console writes tags over Web NFC, prints QR label sheets, and counts taps per sticker.
Security is layered: an HMAC token in the URL fragment (never logged) gates the basic tag, and the hardened tier verifies NTAG 424 DNA "SUN" tags with RFC-4493 AES-CMAC and a monotonic counter, so a captured tap can't be replayed.
LMS is powerful but its built-in UI is dated and assumes one trusted operator on the LAN. Squeezebox Cloud puts a modern, multi-user front end on top and serves it publicly from a VPS: a React UI over a WebSocket-synced Express backend. It runs end-to-end with no music server attached — the LMS client falls back to in-memory state, so you can clone it and it just works. Requests are per-account (a signed-in listener's plays shape their shuffle), inputs are Zod-validated, and admin access is gated by an external SSO — while the public jukebox and the tap path stay open.
npm install
npm run dev # Vite client + Express server → http://127.0.0.1:5177Everything below is engineering detail.
| Area | Path |
|---|---|
| React UI (now-playing, queue, library) | src/ |
| Tap console (bind / write / QR / analytics) | src/tap/ |
| Tap resolver + HMAC / SUN verification | server/tap*.js (tapSun.js = AES-CMAC) |
| LMS client (+ in-memory fallback) | server/lmsClient.js |
| Recommender / listener taste | server/recommender.js, server/listenerTaste.js |
| Live state + WebSocket broadcast | server/state.js, server/app.js |
React 19 + Vite (TypeScript)
│ REST + WebSocket
▼
Express 5 API
├─ lmsClient.js → Lyrion/Squeezebox server (in-memory fallback)
├─ tap*.js → NFC tag resolve / HMAC + NTAG-424 SUN verify
├─ library.js → music-source scan & search
├─ recommender.js / listenerTaste.js / curation.js
└─ state.js → shared state + WebSocket broadcast
npm test # Vitest unit/integration (supertest)
npm run test:all # vitest + build + smokes + PlaywrightReact 19 · Vite · TypeScript · Express 5 · ws · Zod · Vitest · Playwright · Docker.
Deployed behind nginx on a VPS at /cloud-squeeze/; admin auth via an external SSO.
MIT — see LICENSE.

