Skip to content

feat: register native APNs/FCM device token on push.native (P2 push, slice 3a — mobile) - #8

Open
saucam wants to merge 1 commit into
mainfrom
feat/p2-push-native
Open

feat: register native APNs/FCM device token on push.native (P2 push, slice 3a — mobile)#8
saucam wants to merge 1 commit into
mainfrom
feat/p2-push-native

Conversation

@saucam

@saucam saucam commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

What

Slice 3a — mobile half. Completes self-hosted native push: when the daemon advertises push.native (its native/relay transport sends via APNs/FCM directly — see codeoid #261), the client registers this device's native APNs/FCM token instead of an Expo token.

Change

  • push-protocol.ts: PUSH_NATIVE_CAPABILITY = "push.native".
  • push.ts registerForPush: picks the token type by what the daemon advertises —
    • push.nativegetDevicePushTokenAsync() (raw APNs/FCM token; no EAS projectId needed),
    • pushgetExpoPushTokenAsync() (unchanged, slice 2),
    • neither → register nothing (no push transport configured).

Backward-compatible: a daemon on Expo still advertises push; a daemon on native/relay advertises push.native. The registration wire message (push.register { token, platform }) is unchanged — platform already tells the daemon/relay whether to send via APNs or FCM.

Verification

tsc + eslint clean; expo export --platform web bundles 1020 modules. Device e2e (real APNs/FCM delivery + tap) needs an EAS dev build + a daemon running transport: native with your .p8 + Firebase service account — the one step that can't run headlessly.

Follow-ups

  • Post-release: drop the local push-protocol.ts shim and import push.* + capabilities from @codeoid/protocol once a release ships them (both slices used the shim to decouple from the unreleased protocol).
  • Standalone relay service (src/relay in codeoid) when the multi-user/hosted path is needed — mobile needs no further change (it already registers native tokens for relay too).

🤖 Generated with Claude Code

…slice 3a — mobile)

Completes slice 3a's mobile half. When the daemon advertises push.native
(transport native/relay — it sends via APNs/FCM directly), the client now
registers this device's NATIVE push token (getDevicePushTokenAsync) instead
of the Expo token. Expo transport (capability "push") is unchanged.

- push-protocol.ts: PUSH_NATIVE_CAPABILITY = "push.native".
- push.ts registerForPush: pick the token type by the advertised capability —
  native (raw APNs/FCM token, no EAS projectId) for push.native, Expo for
  push; register nothing if neither is advertised. Backward-compatible.

Verified: tsc + eslint clean; expo export (web) bundles 1020 modules. Device
e2e (real APNs/FCM delivery) needs a dev build + a daemon running transport
native with your .p8 + Firebase creds.

Signed-off-by: Yash Datta <saucam@gmail.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@highflame-oracle highflame-oracle Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔮 Oracle Review

🎯 Start Here

src/lib/push-protocol.ts (~20 min) — Security changes in push-protocol.ts


📋 PR Summary

What this PR does: Completes the mobile half of self-hosted native push by allowing clients to register raw APNs/FCM tokens when the daemon advertises the push.native capability.

Key changes:

  • Added PUSH_NATIVE_CAPABILITY constant to the protocol shim.
  • Implemented conditional token registration in registerForPush to select native tokens for push.native or Expo tokens for standard push.
  • Maintained backward compatibility with existing Expo-based daemon configurations.

Areas affected: Push notification registration logic, Protocol definitions

Testing notes: Full end-to-end verification requires an EAS dev build and a daemon running with native transport configuration.


🔍 Code Review

This is a well-structured implementation that cleanly integrates native push support without disrupting existing Expo workflows. The capability-driven approach effectively abstracts the token selection logic, making the code easy to maintain and extend.

What's good:

  • ✨ The capability-based routing logic is a strong pattern that ensures clear separation between protocol detection and implementation.
  • ✨ Smart use of the protocol shim to decouple from the unreleased @codeoid/protocol package.

Review Stats: suggestion:1


Generated by Oracle - Highflame's AI Code Reviewer

Comment thread src/lib/push.ts
);
let token: string;
if (wantsNative) {
// Raw APNs (iOS) / FCM (Android) device token — the self-hosted daemon or

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion

Defensive type coercion may be unnecessary

Expo's getDevicePushTokenAsync() returns Promise<DevicePushToken> where data is typed as string. The defensive type check typeof device.data === 'string' ? device.data : String(device.data) is reasonable but unnecessary given the type contract. However, it doesn't cause harm and provides runtime safety if the API contract changes.

Suggested fix:

Suggested change
// Raw APNs (iOS) / FCM (Android) device token — the self-hosted daemon or
Consider simplifying to `token = device.data;` if you're confident in Expo's type contract, or keep as-is for defense in depth.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant