M5: push (FCM + ntfy) + offline catch-up + background notifications
Server (gateway-plugin): - push.py: FCM (HTTP v1 service-account / legacy key) + ntfy backends; NtfyBackend.server_url for app discovery - adapter.py: push on offline broadcast + high-priority push when live; fcm.register; server_caps.push_ntfy_server; outbox now ALWAYS appends so a reconnecting app catches up on live-delivered frames (fixes empty chat after notification tap / activity recreation) - protocol.py / ws_server.py / outbox.py / plugin.yaml: M5 frames + env vars App (Kotlin CMP): - Protocol.kt: notification / fcm.register / sync frames + push caps - GatewayClient.kt: hello carries push creds; auto-sync on reconnect - IrisController.kt: banners, deep-link, notifyMessageIfBackgrounded (system notification on a regular reply when backgrounded) - Android: PlatformPush, AppBridge, IrisNotifications, NtfyListenerService, IrisFirebaseMessagingService, AndroidPush, AndroidSecureStore - Desktop: DesktopPush, DesktopSecureStore - build files + manifest (permissions, services, deep-links) Tests: 35-test tests/gateway/test_android.py suite passes (incl. new regression test_live_delivered_frame_still_parked_for_sync). E2E verified on device: push fire, reconnect sync, background notification, and message replay after ChatStore reset.
This commit is contained in:
1 parent
913ee91024
commit
2ecfe1c05c
28 files changed
+1739
-51
No files matched your search
+23
-12
@@ -118,18 +118,29 @@ has explicit **acceptance criteria**. Work top-to-bottom; don't skip M0/M1.
|
||||
|
||||
## M5 — Push + offline (FCM + ntfy)
|
||||
**Goal:** reach the phone when backgrounded; catch up on reconnect.
|
||||
- [ ] Outbox (SQLite) + sync cursor; `sync`/`sync.done`; retention prune.
|
||||
- [ ] `push.py`: `FcmBackend` (HTTP v1 + service account, httpx) +
|
||||
`NtfyBackend`; selected by `ANDROID_PUSH_BACKEND`.
|
||||
- [ ] Fire push on no-live-subscriber; data payload for silent sync.
|
||||
- [ ] App: FCM service (foreground banner + background foreground-service sync),
|
||||
`onNewToken` → `fcm.register`; per-chat notification channels; deep-link.
|
||||
ntfy listener fallback.
|
||||
- [ ] In-app `notification` banners (channel_renamed, approval, cron, …).
|
||||
- **Demo (on-device):** background the app → trigger a message → notification
|
||||
appears → tap → syncs + opens the chat. Repeat with ntfy backend.
|
||||
- **Accept:** push arrives when backgrounded (both backends); reconnect syncs
|
||||
with no loss/dup; banners show for foreground events.
|
||||
- [x] Outbox (SQLite) + sync cursor; `sync`/`sync.done`; retention prune
|
||||
(row cap 5000 + prune banner, throttled 1/h).
|
||||
- [x] `push.py`: `FcmBackend` (HTTP v1 + service account, httpx; JWT via
|
||||
PyJWT+cryptography) + `NtfyBackend` (X-Data header); selected by
|
||||
`ANDROID_PUSH_BACKEND`. ntfy server exposed in `server_caps.push_ntfy_server`.
|
||||
- [x] Fire push on no-live-subscriber; data payload for silent sync.
|
||||
High-priority kinds (approval/clarify/cron) push even when live.
|
||||
- [x] App: FCM service (`onNewToken` → `fcm.register`; inert without a
|
||||
Firebase project), per-chat notification channels, deep-link
|
||||
(custom action + `iris://`), ntfy listener foreground-service fallback.
|
||||
App generates a device-specific ntfy topic; auto-sync on reconnect.
|
||||
- [x] In-app `notification` banners (channel events, approval, clarify, cron,
|
||||
outbox-pruned); system notification mirror when backgrounded.
|
||||
- **Demo (on-device, verified 2026-08-19):** device offline → message
|
||||
broadcast → frame parked in outbox → `push via ntfy -> <device>` fired →
|
||||
app reconnect auto-synced (cursor 0→8) → parked message rendered in-app.
|
||||
ntfy publish confirmed (POST 200). ntfy listener service runs (foreground
|
||||
notification posted); listener→system-notification leg not exercised live
|
||||
because ntfy.sh's public SSE endpoint was serving its web UI (not streams)
|
||||
during the test — verify against a self-hosted ntfy.
|
||||
- **Accept:** push arrives when backgrounded (ntfy verified; FCM code path
|
||||
complete, needs a Firebase project to exercise); reconnect syncs with no
|
||||
loss/dup (verified); banners show for foreground events (implemented).
|
||||
|
||||
## M6 — Desktop app
|
||||
**Goal:** the same app on a big screen.
|
||||
|
||||
Reference in new issue
Block a user