You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
-`apps/push/src/` is split by concern: `http.ts` (Bun API), `ownership.ts` (signed challenge verification), `storage.ts` (SQLite persistence for subscriptions/pubkeys/challenges/seen outer event ids), `relayWatcher.ts` (relay subscription for outer `kind: 1059` events with catch-up vs live delivery gating), and `push.ts` (Web Push delivery + invalid subscription cleanup, including stale subscriptions tied to an old VAPID keypair)
82
-
- Push service proof events use `kind: 27235` with short-lived per-pubkey challenge nonces; the server never decrypts NIP-17 payloads and only emits generic notifications for outer `kind: 1059` events tagged `["linky","push"]`, so sender self-copies / reactions / edits can sync over relays without triggering push
82
+
- Push service proof events use `kind: 27235` with short-lived per-pubkey challenge nonces; `/subscribe` and `/unsubscribe` both require valid proofs per affected pubkey, full unsubscribe only happens when the last proven pubkey is removed, the server never decrypts NIP-17 payloads, and it only emits generic notifications for outer `kind: 1059` events tagged `["linky","push"]`, so sender self-copies / reactions / edits can sync over relays without triggering push
83
83
84
84
## Code Conventions
85
85
@@ -109,15 +109,15 @@ IMPORTANT: Always run `bun run check-code` after making changes. It runs typeche
109
109
- PWA service worker is built from `apps/web-app/src/sw.ts` via Vite PWA `injectManifest`; changes there affect both prod and dev SW behavior
110
110
- Dev mode now keeps the registered PWA service worker alive for push testing; use `#advanced/push-debug` to inspect persistent client/SW push logs and manually reset service workers/caches when needed
111
111
- Push registration now validates the live `PushSubscription.options.applicationServerKey` against the current server VAPID public key and forces a re-subscribe on mismatch; open clients also re-register when the service worker emits `pushsubscriptionchange`
112
-
- Push registration persists a stable browser `installationId` plus the last server-registered endpoint in localStorage; subscribe calls also request cleanup of legacy subscriptions without an installation id for the same pubkey, the push server replaces stale endpoints for the same installation, and the client best-effort unregisters the previous endpoint when the browser rotates/replaces the current subscription, preventing duplicate generic notifications from old endpoints
112
+
- Push registration persists a stable browser `installationId` plus the last server-registered endpoint in localStorage; subscribe calls also request cleanup of legacy subscriptions without an installation id for the same pubkey, the push server replaces stale endpoints for the same installation, and the client best-effort unregisters the previous endpoint with a signed unsubscribe proof when the browser rotates/replaces the current subscription, preventing duplicate generic notifications from old endpoints
113
113
- Cashu payments now publish actual token chat messages to the recipient without the outer push marker and emit one separate notify-only wrapped event per payment (`kind: 24133`, `["linky","payment_notice"]`) as the sole push trigger; receiver inbox sync never stores that notice in chat history, but the service worker and open-app notification paths render it as `You received money` / `Přijali jste peníze`
114
114
- The PWA service worker mirrors the active `nsec` into IndexedDB on app startup/login, clears it on logout, and for closed-app push delivery it fetches the outer `kind: 1059` event from relays, decrypts it locally in the service worker, uses `You received money` / `Přijali jste peníze` copy for Cashu token messages and notify-only payment notice events, and still shows a generic fallback notification when decrypt/validation fails; any open Linky window client still suppresses the service-worker notification in favor of in-app notification logic, while inbox sync keeps actual Cashu token chat messages silent in notification surfaces and uses the notify-only payment event for the user-visible payment alert
115
115
- Chat retention is enforced in `useMessagesDomain` (latest 500 messages/contact, 3000 global; reactions capped to 5000 and orphaned reactions are pruned)
116
116
- Wallet top-up receive quotes are cached in owner-scoped localStorage until claimed/expired, so dismissing the QR screen does not drop a pending receive
117
117
- Push service env is documented in `apps/push/.env.example`; `PUSH_VAPID_SUBJECT`, `PUSH_VAPID_PUBLIC_KEY`, and `PUSH_VAPID_PRIVATE_KEY` must be set before `apps/push` starts
118
118
-`apps/push` CORS allowlist is configured via `PUSH_CORS_ORIGIN`; it accepts `*` or a comma-separated list of allowed web app origins
119
119
-`apps/push` relay watcher defaults now match the web app chat publish relays (`wss://relay.damus.io`, `wss://nos.lol`, `wss://relay.0xchat.com`, `wss://shu01.shugur.net`) unless overridden via `PUSH_DEFAULT_RELAYS`
120
-
-`apps/push` relay watcher uses a 3-day catch-up `since` window to accommodate NIP-59 randomized outer `created_at`, persists seen outer event ids in SQLite, suppresses notification delivery until EOSE switches the watcher into live mode, periodically refreshes the subscription to reset reconnect `since` drift, and prunes old seen-event rows on the server cleanup interval
120
+
-`apps/push` relay watcher uses a 3-day catch-up `since` window to accommodate NIP-59 randomized outer `created_at`, persists seen outer event ids in SQLite, keeps a size-bounded in-memory seen-id cache for O(1) hot-path dedupe checks, suppresses notification delivery until EOSE switches the watcher into live mode, periodically refreshes the subscription to reset reconnect `since` drift, and prunes both cache-expired ids and old SQLite seen-event rows on the server cleanup interval
121
121
- Container publishing is handled by `.github/workflows/push-image.yml`, which builds `apps/push/Dockerfile` and publishes the image to GHCR
Copy file name to clipboardExpand all lines: apps/push/README.md
+3-9Lines changed: 3 additions & 9 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -79,15 +79,7 @@ Every pubkey listed in `recipientPubkeys` needs its own proof.
79
79
80
80
### `POST /unsubscribe`
81
81
82
-
Remove an entire stored subscription by endpoint:
83
-
84
-
```json
85
-
{
86
-
"endpoint": "https://example.push/service"
87
-
}
88
-
```
89
-
90
-
Or remove only selected pubkeys from a subscription with ownership proofs:
82
+
Remove selected pubkeys from a subscription with ownership proofs. If you remove the subscription's last remaining pubkey, the whole subscription row is deleted:
91
83
92
84
```json
93
85
{
@@ -114,6 +106,8 @@ Or remove only selected pubkeys from a subscription with ownership proofs:
114
106
}
115
107
```
116
108
109
+
Every pubkey listed in `recipientPubkeys` needs its own unsubscribe proof. Full subscription removal requires proving ownership for the subscription's current pubkeys.
0 commit comments