Files
bellsystems-cp/docs/melody-binary-serving.md
bonamin 72f7e00990 feat: serve melody .bsm binaries over plain HTTP instead of Firebase Storage
ESP32 devices can't spare the 40KB+ RAM a TLS client needs, so Firebase
Storage's HTTPS-only download URLs were blocking melody downloads. Binaries
are now written to local disk (./data/melody_binaries) and served through a
new unauthenticated /api/melodies/download/{pid} route, exposed publicly on
a separate melodies.bellsystems.net vhost (plain HTTP, no TLS) so the main
console domain can stay HTTPS-only with no exceptions. Preview audio still
uses Firebase Storage since it's only ever fetched by the HTTPS admin UI.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-06 09:41:24 +03:00

3.9 KiB

Melody Binary Serving (Plain HTTP)

Why this exists

ESP32 devices download .bsm melody files directly, using a URL supplied through the Android app. The download previously pointed at a Firebase Storage public URL (HTTPS only). ESP32's TLS client needs 40KB+ of RAM to open an HTTPS connection, which these devices can't reliably spare. To fix this, melody .bsm binaries are now stored and served by our own backend over plain HTTP, instead of Firebase Storage.

This does not affect the audio preview file (information.previewURL) — that's only ever fetched by the browser admin UI, which is already HTTPS, so it stays on Firebase Storage unchanged.

What changed

  • Storage: .bsm binaries are written to ./data/melody_binaries/{melody_uid}.bsm on the host (mounted into the backend container at /app/storage/melody_binaries, same pattern as ./data/firmware and ./data/built_melodies).
  • Upload path: SelectArchetypeModal / BuildOnTheFlyModal still call POST /api/melodies/{melodyId}/upload/binary exactly as before. The backend (backend/melodies/service.py::save_binary_for_melody) now writes the bytes to local disk instead of uploading to Firebase Storage, and returns a URL like http://melodies.bellsystems.net/download/{pid}.
  • Stored URL: the melody's url field (Firestore + SQLite) now holds that plain-HTTP URL instead of a Firebase public URL. No schema change — url was already a plain string field.
  • Download route: GET /api/melodies/download/{pid} (backend/melodies/router.py) is unauthenticated (devices have no login token) and resolves pid → the melody's {uid}.bsm file on disk, same pattern as the existing firmware download route (backend/firmware/router.py::download_firmware).
  • PID uniqueness: since pid is now the public lookup key for the download route, backend/melodies/service.py::_check_pid_unique rejects creating/renaming a melody to a PID already used by another melody (409 Conflict).
  • Cleanup: deleting a melody or its binary file now also deletes the local .bsm file (service.delete_local_binary).

Infrastructure (outside this repo)

The plain-HTTP requirement is handled by keeping melodies.bellsystems.net on a separate hostname from console.bellsystems.net, so the console's NPM proxy host can stay HTTPS-only with no per-path exceptions.

Required, one-time setup outside this repository:

  1. DNS: add an A/CNAME record for melodies.bellsystems.net pointing at the same host as console.bellsystems.net.
  2. NPM (Nginx Proxy Manager) proxy host: create a new proxy host for melodies.bellsystems.net forwarding to the nginx container's exposed port (90 per docker-compose.yml). Do not force SSL / do not enable the "Force SSL" redirect on this proxy host — it must remain reachable over plain HTTP.

The in-repo nginx/nginx.conf already has a dedicated server_name melodies.bellsystems.net block that maps /download/{pid} to the backend's /api/melodies/download/{pid} route, so no further nginx changes are needed once the NPM proxy host exists.

Verification

  1. Build/select an archetype for a melody → confirm a file appears at ./data/melody_binaries/{uid}.bsm and the melody's url becomes http://melodies.bellsystems.net/download/{pid}.
  2. curl http://localhost:8000/api/melodies/download/{pid} (direct to backend, bypassing nginx) → returns the .bsm bytes, no auth header needed.
  3. curl http://localhost:90/api/melodies/download/{pid} (through nginx on the console server block) → same result.
  4. Once NPM is configured, curl http://melodies.bellsystems.net/download/{pid} → same result, over plain HTTP.
  5. Delete the melody → confirm the local .bsm file is removed.
  6. Upload a preview audio file → confirm it still lands in Firebase Storage and previewURL still populates (regression check).