Stop notifications after removing a host (#3176)

* fix(notifications): stop pushes after host removal

Push tokens were persisted indefinitely with no relationship to app pairing state. Give registrations a 48-hour renewable lease so offline unpairing converges, and revoke immediately when the host is reachable.

* fix(notifications): make token revocation bounded and durable
This commit is contained in:
Mohamed Boudra
2026-08-10 23:33:31 +02:00
committed by GitHub
parent 29ae25d7aa
commit 6aa9f92156
33 changed files with 702 additions and 227 deletions
+19
View File
@@ -2409,6 +2409,19 @@ export const RegisterPushTokenMessageSchema = z.object({
token: z.string(),
});
export const PushUnregisterRequestSchema = z.object({
type: z.literal("push.unregister.request"),
token: z.string(),
requestId: z.string(),
});
export const PushUnregisterResponseSchema = z.object({
type: z.literal("push.unregister.response"),
payload: z.object({
requestId: z.string(),
}),
});
// ============================================================================
// Terminal Messages
// ============================================================================
@@ -2753,6 +2766,7 @@ export const SessionInboundMessageSchema = z.discriminatedUnion("type", [
PingMessageSchema,
ListCommandsRequestSchema,
RegisterPushTokenMessageSchema,
PushUnregisterRequestSchema,
ListTerminalsRequestSchema,
SubscribeTerminalsRequestSchema,
UnsubscribeTerminalsRequestSchema,
@@ -2971,6 +2985,8 @@ export const ServerInfoStatusPayloadSchema = z
daemonStatusRpc: z.boolean().optional(),
// COMPAT(relayConfig): added in v0.2.6, remove gate after 2027-01-31.
relayConfig: z.boolean().optional(),
// COMPAT(pushTokenRevocation): added in v0.3.2, remove gate after 2027-02-10.
pushTokenRevocation: z.boolean().optional(),
// COMPAT(terminalRestoreModes): added in v0.1.81, remove gate after 2026-11-23.
"terminal-restore-modes": z.boolean().optional(),
// COMPAT(terminalInputModeReplay): added in v0.2.6, remove gate after 2027-02-02.
@@ -5575,6 +5591,7 @@ export const SessionOutboundMessageSchema = z.discriminatedUnion("type", [
DictationStreamErrorMessageSchema,
StatusMessageSchema,
PongMessageSchema,
PushUnregisterResponseSchema,
RpcErrorMessageSchema,
ArtifactMessageSchema,
AgentUpdateMessageSchema,
@@ -6143,6 +6160,8 @@ export type ClientHeartbeatMessage = z.infer<typeof ClientHeartbeatMessageSchema
export type ListCommandsRequest = z.infer<typeof ListCommandsRequestSchema>;
export type ListCommandsResponse = z.infer<typeof ListCommandsResponseSchema>;
export type RegisterPushTokenMessage = z.infer<typeof RegisterPushTokenMessageSchema>;
export type PushUnregisterRequest = z.infer<typeof PushUnregisterRequestSchema>;
export type PushUnregisterResponse = z.infer<typeof PushUnregisterResponseSchema>;
// Terminal message types
export type ListTerminalsRequest = z.infer<typeof ListTerminalsRequestSchema>;