Repository navigation
feat(notifications): encrypt rich alerts from paired servers - #42
Merged
NicholasZolton merged 1 commit intoOct 10, 2026
Merged
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
Shared FCM collapsing, overlapping-source aggregation, and duplicated preference controls can lose or misrepresent notifications.
3 open findings
What changed in this PR
Adds end-to-end encrypted rich Android notifications for directly paired servers while sharing T3 Connect’s alert policy and native presentation.
Changes:
- Encrypts server payloads using P-256 ECDH, HKDF, and AES-GCM.
- Adds native decryption, persistent keys, multi-environment activity aggregation, and settings.
- Shares notification policy code and expands migration, interoperability, and Android tests.
| File | Description |
|---|---|
wiki/paired-notifications.md |
Updates the compatibility decision. |
wiki/log.md |
Records the policy change. |
packages/shared/src/agentActivityPolicy.test.ts |
Tests shared alert policy. |
packages/shared/src/agentActivityPayloads.ts |
Generalizes notification payload helpers. |
packages/shared/src/agentActivityAndroid.ts |
Shares Android payload generation. |
packages/shared/src/agentActivityAlerts.ts |
Adds explicit transition return types. |
packages/shared/src/agentActivityAggregate.ts |
Adjusts shared aggregate typing and sorting. |
packages/shared/package.json |
Exports shared activity modules. |
packages/contracts/src/environmentHttp.ts |
Adds encryption keys, preferences, and capability status. |
infra/relay/src/agentActivity/FcmDeliveries.ts |
Uses shared Android notification policy. |
infra/relay/src/agentActivity/FcmDeliveries.test.ts |
Updates shared-policy tests. |
infra/relay/src/agentActivity/ApnsDeliveryQueue.ts |
Uses generalized payload sanitization. |
infra/relay/src/agentActivity/ApnsDeliveries.ts |
Uses shared alert and payload helpers. |
infra/relay/src/agentActivity/AgentActivityPublisher.ts |
Uses shared aggregation helpers. |
infra/relay/scripts/android-push-watch.ts |
Uses shared Android payload utilities. |
docs/user/mobile-notifications.md |
Documents encrypted rich notifications. |
docs/operations/android-notifications.md |
Documents encryption and rollout operations. |
apps/server/src/notifications/runtime.ts |
Wires notification encryption. |
apps/server/src/notifications/PairedNotifications.ts |
Produces encrypted rich activity deliveries. |
apps/server/src/notifications/PairedNotifications.test.ts |
Tests encrypted server delivery behavior. |
apps/server/src/notifications/notificationTestUtils.ts |
Adds independent encryption test utilities. |
apps/server/src/notifications/NotificationStore.ts |
Migrates persisted notification state. |
apps/server/src/notifications/NotificationEncryption.ts |
Implements payload encryption. |
apps/server/src/notifications/NotificationEncryption.test.ts |
Tests confidentiality and interoperability. |
apps/mobile/src/features/settings/PairedNotificationSettings.tsx |
Adds paired activity settings. |
apps/mobile/src/features/agent-awareness/registrationPayload.ts |
Shares awareness preference construction. |
apps/mobile/src/features/agent-awareness/pairedNotifications.ts |
Registers keys and encrypted capability. |
apps/mobile/src/features/agent-awareness/pairedNotifications.test.ts |
Tests encrypted mobile registration. |
apps/mobile/src/features/agent-awareness/androidNotifications.ts |
Extends the native module bridge. |
apps/mobile/modules/t3-agent-notifications/android/src/test/resources/paired-notification-v1.json |
Adds an encryption fixture. |
apps/mobile/modules/t3-agent-notifications/android/src/test/java/expo/modules/t3agentnotifications/PairedNotificationCryptoTest.kt |
Tests native decryption. |
apps/mobile/modules/t3-agent-notifications/android/src/test/java/expo/modules/t3agentnotifications/PairedAgentNotificationsTest.kt |
Tests paired presentation and aggregation. |
apps/mobile/modules/t3-agent-notifications/android/src/test/java/expo/modules/t3agentnotifications/AgentNotificationsTest.kt |
Refactors shared notification tests. |
apps/mobile/modules/t3-agent-notifications/android/src/test/java/expo/modules/t3agentnotifications/AgentNotificationFixture.kt |
Extracts Android test setup. |
apps/mobile/modules/t3-agent-notifications/android/src/main/java/expo/modules/t3agentnotifications/T3AgentNotificationsModule.kt |
Exposes key and configuration APIs. |
apps/mobile/modules/t3-agent-notifications/android/src/main/java/expo/modules/t3agentnotifications/PairedNotificationKeys.kt |
Manages wrapped registration keys. |
apps/mobile/modules/t3-agent-notifications/android/src/main/java/expo/modules/t3agentnotifications/PairedNotificationCrypto.kt |
Decrypts authenticated envelopes. |
apps/mobile/modules/t3-agent-notifications/android/src/main/java/expo/modules/t3agentnotifications/AgentNotifications.kt |
Integrates paired native presentation. |
apps/mobile/modules/t3-agent-notifications/android/src/main/java/expo/modules/t3agentnotifications/AgentActivitySnapshots.kt |
Merges activity sources locally. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+58
to
+62
| ordered | ||
| .flatMap(::activityRows) | ||
| .sortedBy { priority(ActivityPhase.forStatus(it.status)) } | ||
| .take(5) | ||
| val activeCount = sources.sumOf { |
Comment on lines
+147
to
+148
| onValueChange={(enabled) => { | ||
| void save({ liveActivitiesEnabled: enabled }).catch(() => { |
| : entry, | ||
| ), | ||
| }; | ||
| alert: alert !== null, |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
Paired Android notifications currently hide useful thread titles and status behind generic alerts. Rich notifications should work without T3 Connect, but their content must not reach Google in plaintext.
This reuses Connect's notification policy and Android presentation for direct paired delivery. Servers encrypt each payload for the phone's registration key; Android decrypts natively, including after process exit, and merges activity from independently paired environments. Existing session authorization, opt-out, retry deduplication, ordering, and foreground-thread suppression remain in place.
Test plan
git diff --check.Rollout note
Update both sending servers and the Android binary. Enabled registrations refresh automatically when the app opens; updated servers refuse keyless registrations and never fall back to rich plaintext. Preserve the Android package, private signing identity, app data, and existing Firebase configuration.
UI evidence
Before/after screenshots omitted with Nicholas's approval because agent device access is disabled. Physical-phone delivery previously verified only the generic implementation, not this encrypted upgrade.
brave-tulip-laurel