Add push preferences to relay connect frames

This commit is contained in:
courtmanr@gmail.com
2026-08-11 15:21:07 +01:00
parent 44af57b8fc
commit 4ab549787a
3 changed files with 39 additions and 4 deletions
@@ -19,6 +19,10 @@ Own the desktop and mobile relay runtimes, their persisted relay state
boundaries, the server-owned mobile relay capability boundary, and the
canonical reconnect, encryption, protocol, proxy, and relay-trust behavior
for Pulse instance bridging.
Mobile CONNECT frames may carry explicit push preferences for critical,
warning, approval, and fix-result notifications. The relay protocol preserves
each boolean so server-side delivery can enforce the paired device's choices;
legacy clients omit the entire preferences object and remain wire-compatible.
## Canonical Files
+12 -4
View File
@@ -149,10 +149,18 @@ type RegisterAckPayload struct {
// ConnectPayload is sent by the app in CONNECT frames.
type ConnectPayload struct {
InstanceID string `json:"instance_id"`
AuthToken string `json:"auth_token"`
DeviceToken string `json:"device_token,omitempty"` // push notification device token (mobile apps only)
Platform string `json:"platform,omitempty"` // "ios" or "android" (mobile apps only)
InstanceID string `json:"instance_id"`
AuthToken string `json:"auth_token"`
DeviceToken string `json:"device_token,omitempty"` // push notification device token (mobile apps only)
Platform string `json:"platform,omitempty"` // "ios" or "android" (mobile apps only)
NotificationPreferences *PushNotificationPreferences `json:"notification_preferences,omitempty"` // server-enforced delivery preferences
}
type PushNotificationPreferences struct {
CriticalEnabled bool `json:"critical_enabled"`
WarningEnabled bool `json:"warning_enabled"`
ApprovalEnabled bool `json:"approval_enabled"`
FixResultEnabled bool `json:"fix_result_enabled"`
}
// ConnectAckPayload is sent by the relay in CONNECT_ACK frames.
+23
View File
@@ -122,6 +122,12 @@ func TestControlFrameRoundTrip(t *testing.T) {
AuthToken: "auth-token-xyz",
DeviceToken: "device-token-123",
Platform: "ios",
NotificationPreferences: &PushNotificationPreferences{
CriticalEnabled: true,
WarningEnabled: false,
ApprovalEnabled: true,
FixResultEnabled: false,
},
}
frame, err := NewControlFrame(FrameConnect, 0, orig)
if err != nil {
@@ -155,6 +161,23 @@ func TestControlFrameRoundTrip(t *testing.T) {
if got.Platform != orig.Platform {
t.Errorf("Platform: got %q, want %q", got.Platform, orig.Platform)
}
if got.NotificationPreferences == nil {
t.Fatal("NotificationPreferences was omitted from CONNECT round trip")
}
if *got.NotificationPreferences != *orig.NotificationPreferences {
t.Errorf("NotificationPreferences: got %#v, want %#v", got.NotificationPreferences, orig.NotificationPreferences)
}
legacyFrame, err := NewControlFrame(FrameConnect, 0, ConnectPayload{
InstanceID: "relay_legacy",
AuthToken: "legacy-auth",
})
if err != nil {
t.Fatalf("NewControlFrame(legacy CONNECT) error = %v", err)
}
if bytes.Contains(legacyFrame.Payload, []byte("notification_preferences")) {
t.Fatalf("legacy CONNECT unexpectedly serialized notification preferences: %s", legacyFrame.Payload)
}
})
t.Run("channel open payload", func(t *testing.T) {