Notifications and sending songs to friends

Reactions to your songs, new followers, and songs sent to you are now
notifications: a bell on the feed (app and website) with an unread
count that updates live, and a list that marks them read. "Send ✉" on
a song sends it to people who follow you, with an optional note.
Settings picks which kinds to be notified about.

Push delivery uses no third-party accounts:
- Android: UnifiedPush through a distributor app (ntfy), pointed at
  our own ntfy server (docker-compose.ntfy.yml). Works the same in
  the F-Droid build; no Firebase.
- Browsers: standard Web Push from the existing service worker.
Both use Web Push encryption with a VAPID key pair the server makes
and keeps in the database. Push addresses are checked like any other
user-supplied URL. Setup is in docs/PUSH.md; the privacy page covers
the new data.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-14 22:14:34 -04:00
co-authored by Claude Opus 5.5
parent 3b39910f13
commit f112a3515c
55 changed files with 3783 additions and 22 deletions
+45
View File
@@ -0,0 +1,45 @@
# Notifications
musicshare tells people about reactions to their songs, new followers, and songs sent to them. Each user picks
which kinds in Settings. Everything always shows under the bell on the feed; push notifications are extra.
Nothing goes through Google's Firebase or another push company's account:
- **Phones (Android):** [UnifiedPush](https://unifiedpush.org). A "distributor" app on the phone keeps one
connection open to a push server, and apps receive their notifications through it. We run the push server
ourselves ([ntfy](https://ntfy.sh), `docker-compose.ntfy.yml`). People install the ntfy app (free, open source,
on F-Droid and Play) and point it at our server.
- **Browsers:** standard Web Push. The server signs messages with its own key pair (VAPID), so there's no account
with anyone, but each browser maker runs its own delivery service (Chrome uses Google's, Firefox uses Mozilla's).
That's built into browsers and can't be swapped.
- **iPhone:** Apple only allows its own push service. Not set up.
Messages are end-to-end encrypted (RFC 8291) in every case: the push server and browser relays only see ciphertext.
## Server setup
1. Pick a subdomain for the push server, e.g. `push.binarygnome.com`, and point DNS at the server.
2. Set `NTFY_DOMAIN` in `deploy/production.env`.
3. Start it with the other services. Add `-f docker-compose.ntfy.yml` to the usual command:
```bash
docker compose -f docker-compose.yml -f docker-compose.prod.yml -f docker-compose.behind-proxy.yml \
-f docker-compose.ntfy.yml --env-file deploy/production.env up -d
```
4. In the reverse proxy (NPMplus), add a proxy host for that subdomain:
- Forward to `http://127.0.0.1:3739`.
- Turn on websocket support and HTTPS.
5. Check it: `curl https://push.binarygnome.com/v1/health` should say `{"healthy":true}`.
The server's Web Push keys are generated the first time they're needed and kept in the `server_keys` table. Don't
delete that row: every phone and browser would have to turn notifications on again. To manage the keys yourself,
set `VAPID_PUBLIC_KEY` and `VAPID_PRIVATE_KEY` (make a pair with `npx web-push generate-vapid-keys`).
## On a phone
1. Install **ntfy** ([F-Droid](https://f-droid.org/packages/io.heckel.ntfy/) or Play).
2. In ntfy, open **Settings > Default server** and enter `https://push.binarygnome.com`. Without this step it
uses the public ntfy.sh server, which also works, but then the push server isn't ours.
3. In musicshare, go to **Settings > Notifications > Turn on for this phone**.
ntfy shows one quiet, permanent notification so Android keeps its connection alive. People can hide it by
long-pressing it and turning off that notification category.