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
{{ message }}
Repository navigation
Console: register the mobile shell's native push token after sign-in, unregister it on sign-out #29042
The mobile shell (objectstack-ai/mobile#144) exposes ObjectStackNative.getPushToken(). It resolves to { token, vendor: 'apns' | 'fcm' }, or null on a simulator, when permission is denied, or when no token is available. The first call shows the OS permission prompt, so the Console decides when that happens. The shell holds no API credential, so the registration must come from the Console under its own session.
Unregister the token on sign-out and on account switch.
Re-register when the token changes; compare with the last registered value kept per session.
Choose the moment of the permission prompt deliberately: after the first meaningful screen, not during sign-in. Do nothing when the call resolves null.
Acceptance
With a fake bridge, sign-in registers the token, sign-out unregisters it, and null leads to no request.
An end-to-end check on a device build receives a push and opens the payload's route. This is recorded as a manual run if no device lab exists.
Shell side of this card's ask 3 (token rotation) and the duplicate-device fix, from objectstack-ai/mobile#163 → PR objectstack-ai/mobile#167. The seat has accepted the PR; it awaits the maintainer's merge.
What the shell now offers the Console. Everything is feature-detected, so the Console can ship before an updated shell is installed.
window.ObjectStackNative.installId:string | null, synchronous. It is a random UUID that persists across WebView storage loss. It is exposed only to pages on the configured server's origin; every other page (a trusted IdP page included) reads null.
Consumer step: send deviceId = installId on registration only when it is a string. Otherwise keep today's behaviour.
objectstack-native:pushTokenChanged: a window event with detail: { token, vendor }, announced as has('pushTokenChanged').
The shell holds a rotated token until the Console posts ready, so the listener must be mounted by then. useNativeHost already mounts its listeners in that commit.
Unlock rule for the pm:blocked state on this card is unchanged. This comment only records the shell half. The source of truth for the bridge contract is mobile's docs/HYBRID-SHELL.md once objectstack-ai/mobile#167 merges.
Seam for this card's unregister half · 2026-11-02T20:56Z
PR #29565 (#29458) merged at 2026-11-02T20:55Z. The Console now answers the shell's objectstack-native:connectionRemoved event inside useNativeHostRouting (@object-ui/mobile). Today it calls event.waitUntil() with a promise that resolves at once, because no Console path registers a device yet.
When this card registers the push token, it also owes the matching unregister. Pass onConnectionRemoved: () => <unregister this device under the Console's own session> to the Console's useNativeHostRouting call (NativeShellRouting). The shell is held until that promise settles, for at most 10 s. A rejected promise releases the shell, which then records the token as left registered. No other wiring is needed.
Carried here from the contract review of PR #29767 (record 6595157663, PASS), which adds an optional origin to RegisterDeviceRequest (#29642, ruling B 6593872191). This card is the Console producer for it.
When the Console fills origin from window.location.origin, send it only when window.location.protocol is http: or https:. In a sandboxed iframe, or an about:blank / data: context, window.location.origin answers the opaque "null". The spec grammar refuses that value, and the refusal is a 400 on the whole POST /api/v1/notifications/devices body, so the device would not register at all. Leaving origin out in that case keeps registration working.
Activity
objectstack-fleet commentedon Nov 2, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: p2 ·
domain:ui·pm:blocked· 2026-11-02T06:36Z⛔ Not a claim.
domain:ui, p2. ItsBlocked-by:lines, @object-ui/mobile: shell mode — detectwindow.ObjectStackNativeand adapt the Console when the mobile shell hosts it #29039 (shell detection) and Push channel: build the device registry routes (/notifications/devices) and the APNs + FCM transport that ADR-0012 marks "MUST ship M1" #29041 (the device registry), are both open, so it waits inpm:blockedand the unlock scan releases it when both close.Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
repo:mobile#1 · os-justin · session_01QSeKYnW1Xf6HDSWz1eGAqv
Shell side of this card's ask 3 (token rotation) and the duplicate-device fix, from objectstack-ai/mobile#163 → PR objectstack-ai/mobile#167. The seat has accepted the PR; it awaits the maintainer's merge.
What the shell now offers the Console. Everything is feature-detected, so the Console can ship before an updated shell is installed.
window.ObjectStackNative.installId:string | null, synchronous. It is a random UUID that persists across WebView storage loss. It is exposed only to pages on the configured server's origin; every other page (a trusted IdP page included) readsnull.deviceId = installIdon registration only when it is a string. Otherwise keep today's behaviour.objectstack-native:pushTokenChanged: a window event withdetail: { token, vendor }, announced ashas('pushTokenChanged').ready, so the listener must be mounted by then.useNativeHostalready mounts its listeners in that commit.ObjectStackShell/<appVersion>user-agent token: now a documented contract of the shell. It is not a capability signal and not a security signal.Unlock rule for the
pm:blockedstate on this card is unchanged. This comment only records the shell half. The source of truth for the bridge contract is mobile'sdocs/HYBRID-SHELL.mdonce objectstack-ai/mobile#167 merges.objectstack-fleet commentedon Nov 2, 2026
domain:ui#5 · zhuangjianguo · session_01QsR2jQjvWkZ7is6GXbC6DB
Seam for this card's unregister half · 2026-11-02T20:56Z
PR #29565 (#29458) merged at 2026-11-02T20:55Z. The Console now answers the shell's
objectstack-native:connectionRemovedevent insideuseNativeHostRouting(@object-ui/mobile). Today it callsevent.waitUntil()with a promise that resolves at once, because no Console path registers a device yet.When this card registers the push token, it also owes the matching unregister. Pass
onConnectionRemoved: () => <unregister this device under the Console's own session>to the Console'suseNativeHostRoutingcall (NativeShellRouting). The shell is held until that promise settles, for at most 10 s. A rejected promise releases the shell, which then records the token as left registered. No other wiring is needed.objectstack-fleet commentedon Nov 3, 2026
domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K
Carried here from the contract review of PR #29767 (record 6595157663, PASS), which adds an optional
origintoRegisterDeviceRequest(#29642, ruling B 6593872191). This card is the Console producer for it.When the Console fills
originfromwindow.location.origin, send it only whenwindow.location.protocolishttp:orhttps:. In a sandboxed iframe, or anabout:blank/data:context,window.location.originanswers the opaque"null". The spec grammar refuses that value, and the refusal is a 400 on the wholePOST /api/v1/notifications/devicesbody, so the device would not register at all. Leavingoriginout in that case keeps registration working.