auth/permit_offer_notifications.ts

Permit offer WebSocket notification specs, builders, and the narrow NotificationSender interface that decouples offer/revoke send sites from BackendWebsocketTransport.

Six RemoteNotificationActionSpecs cover the consentful-permits lifecycle events the server pushes to affected accounts:

- permit_offer_received → recipient's sockets when an offer is created - permit_offer_retracted → recipient's sockets when a grantor retracts - permit_offer_accepted → grantor's sockets when the recipient accepts - permit_offer_declined → grantor's sockets when the recipient declines - permit_offer_supersede → grantor's sockets when a sibling accept, a revoke of the resulting permit, or destruction of the parent scope row obsoletes their pending offer - permit_revoke → revokee's sockets when one of their active permits is revoked (companion to the permit_revoke audit event)

Payloads are flat and normalized — PermitOfferJson for the offer-lifecycle notifications (decline reason rides on offer.decline_reason, not a sibling field), and {permit_id, role, scope_id, reason?} for permit_revoke. The revokee/grantor/recipient account id travels via the send target (the NotificationSender.send_to_account argument), not in the payload.

The specs surface as EventSpecs via create_action_event_spec — callers append PERMIT_OFFER_NOTIFICATION_SPECS to their event_specs on create_app_server so the surface reflects them and DEV-mode broadcast validation catches payload drift.

Declarations
#

26 declarations

view source

build_permit_offer_accepted_notification
#

auth/permit_offer_notifications.ts view source

(params: { offer: { id: string & $brand<"Uuid">; from_actor_id: string & $brand<"Uuid">; to_account_id: string & $brand<"Uuid">; role: string; scope_id: (string & $brand<...>) | null; ... 8 more ...; resulting_permit_id: (string & $brand<...>) | null; }; }): { ...; }

params

type { offer: { id: string & $brand<"Uuid">; from_actor_id: string & $brand<"Uuid">; to_account_id: string & $brand<"Uuid">; role: string; scope_id: (string & $brand<...>) | null; ... 8 more ...; resulting_permit_id: (string & $brand<...>) | null; }; }

returns

{ [x: string]: unknown; jsonrpc: "2.0"; method: string; params?: { [x: string]: unknown; } | undefined; }

build_permit_offer_declined_notification
#

auth/permit_offer_notifications.ts view source

(params: { offer: { id: string & $brand<"Uuid">; from_actor_id: string & $brand<"Uuid">; to_account_id: string & $brand<"Uuid">; role: string; scope_id: (string & $brand<...>) | null; ... 8 more ...; resulting_permit_id: (string & $brand<...>) | null; }; }): { ...; }

params

type { offer: { id: string & $brand<"Uuid">; from_actor_id: string & $brand<"Uuid">; to_account_id: string & $brand<"Uuid">; role: string; scope_id: (string & $brand<...>) | null; ... 8 more ...; resulting_permit_id: (string & $brand<...>) | null; }; }

returns

{ [x: string]: unknown; jsonrpc: "2.0"; method: string; params?: { [x: string]: unknown; } | undefined; }

build_permit_offer_received_notification
#

auth/permit_offer_notifications.ts view source

(params: { offer: { id: string & $brand<"Uuid">; from_actor_id: string & $brand<"Uuid">; to_account_id: string & $brand<"Uuid">; role: string; scope_id: (string & $brand<...>) | null; ... 8 more ...; resulting_permit_id: (string & $brand<...>) | null; }; }): { ...; }

params

type { offer: { id: string & $brand<"Uuid">; from_actor_id: string & $brand<"Uuid">; to_account_id: string & $brand<"Uuid">; role: string; scope_id: (string & $brand<...>) | null; ... 8 more ...; resulting_permit_id: (string & $brand<...>) | null; }; }

returns

{ [x: string]: unknown; jsonrpc: "2.0"; method: string; params?: { [x: string]: unknown; } | undefined; }

build_permit_offer_retracted_notification
#

auth/permit_offer_notifications.ts view source

(params: { offer: { id: string & $brand<"Uuid">; from_actor_id: string & $brand<"Uuid">; to_account_id: string & $brand<"Uuid">; role: string; scope_id: (string & $brand<...>) | null; ... 8 more ...; resulting_permit_id: (string & $brand<...>) | null; }; }): { ...; }

params

type { offer: { id: string & $brand<"Uuid">; from_actor_id: string & $brand<"Uuid">; to_account_id: string & $brand<"Uuid">; role: string; scope_id: (string & $brand<...>) | null; ... 8 more ...; resulting_permit_id: (string & $brand<...>) | null; }; }

returns

{ [x: string]: unknown; jsonrpc: "2.0"; method: string; params?: { [x: string]: unknown; } | undefined; }

build_permit_offer_supersede_notification
#

auth/permit_offer_notifications.ts view source

(params: { offer: { id: string & $brand<"Uuid">; from_actor_id: string & $brand<"Uuid">; to_account_id: string & $brand<"Uuid">; role: string; scope_id: (string & $brand<...>) | null; ... 8 more ...; resulting_permit_id: (string & $brand<...>) | null; }; reason: "sibling_accepted" | ... 1 more ... | "scope_destroyed"; cause_id: string & $brand<...>; }): { ...; }

params

type { offer: { id: string & $brand<"Uuid">; from_actor_id: string & $brand<"Uuid">; to_account_id: string & $brand<"Uuid">; role: string; scope_id: (string & $brand<...>) | null; ... 8 more ...; resulting_permit_id: (string & $brand<...>) | null; }; reason: "sibling_accepted" | ... 1 more ... | "scope_destroyed"; cause_...

returns

{ [x: string]: unknown; jsonrpc: "2.0"; method: string; params?: { [x: string]: unknown; } | undefined; }

build_permit_revoke_notification
#

auth/permit_offer_notifications.ts view source

(params: { permit_id: string & $brand<"Uuid">; role: string; scope_id: (string & $brand<"Uuid">) | null; reason: string | null; }): { [x: string]: unknown; jsonrpc: "2.0"; method: string; params?: { ...; } | undefined; }

params

type { permit_id: string & $brand<"Uuid">; role: string; scope_id: (string & $brand<"Uuid">) | null; reason: string | null; }

returns

{ [x: string]: unknown; jsonrpc: "2.0"; method: string; params?: { [x: string]: unknown; } | undefined; }

NotificationSender
#

auth/permit_offer_notifications.ts view source

NotificationSender

Narrow structural capability for sending a JSON-RPC notification to every socket bound to an account.

BackendWebsocketTransport satisfies this interface — its send_to_account(account_id, message) signature accepts the broader JsonrpcMessageFromServerToClient type, which is contravariantly compatible with JsonrpcNotification here. The interface stays local so handlers don't couple to the concrete transport, and tests can inject a capturing stub with no WS machinery.

Returns the number of sockets the notification was sent to — callers typically ignore it (used by telemetry / tests).

send_to_account

type (account_id: Uuid, message: JsonrpcNotification) => number

PERMIT_OFFER_ACCEPTED_NOTIFICATION_METHOD
#

permit_offer_accepted_notification_spec
#

auth/permit_offer_notifications.ts view source

{ method: string; kind: "remote_notification"; initiator: "backend"; auth: null; side_effects: true; input: ZodObject<{ offer: ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; from_actor_id: $ZodBranded<...>; ... 11 more ...; resulting_permit_id: ZodNullable<...>; }, $strict>; }, $strict>; output: ZodVoid; async...

PERMIT_OFFER_DECLINED_NOTIFICATION_METHOD
#

permit_offer_declined_notification_spec
#

auth/permit_offer_notifications.ts view source

{ method: string; kind: "remote_notification"; initiator: "backend"; auth: null; side_effects: true; input: ZodObject<{ offer: ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; from_actor_id: $ZodBranded<...>; ... 11 more ...; resulting_permit_id: ZodNullable<...>; }, $strict>; }, $strict>; output: ZodVoid; async...

PERMIT_OFFER_NOTIFICATION_SPECS
#

PERMIT_OFFER_RECEIVED_NOTIFICATION_METHOD
#

permit_offer_received_notification_spec
#

auth/permit_offer_notifications.ts view source

{ method: string; kind: "remote_notification"; initiator: "backend"; auth: null; side_effects: true; input: ZodObject<{ offer: ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; from_actor_id: $ZodBranded<...>; ... 11 more ...; resulting_permit_id: ZodNullable<...>; }, $strict>; }, $strict>; output: ZodVoid; async...

PERMIT_OFFER_RETRACTED_NOTIFICATION_METHOD
#

permit_offer_retracted_notification_spec
#

auth/permit_offer_notifications.ts view source

{ method: string; kind: "remote_notification"; initiator: "backend"; auth: null; side_effects: true; input: ZodObject<{ offer: ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; from_actor_id: $ZodBranded<...>; ... 11 more ...; resulting_permit_id: ZodNullable<...>; }, $strict>; }, $strict>; output: ZodVoid; async...

PERMIT_OFFER_SUPERSEDE_NOTIFICATION_METHOD
#

permit_offer_supersede_notification_spec
#

auth/permit_offer_notifications.ts view source

{ method: string; kind: "remote_notification"; initiator: "backend"; auth: null; side_effects: true; input: ZodObject<{ offer: ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; from_actor_id: $ZodBranded<...>; ... 11 more ...; resulting_permit_id: ZodNullable<...>; }, $strict>; reason: ZodEnum<...>; cause_id: $Zo...

PERMIT_REVOKE_NOTIFICATION_METHOD
#

permit_revoke_notification_spec
#

auth/permit_offer_notifications.ts view source

{ method: string; kind: "remote_notification"; initiator: "backend"; auth: null; side_effects: true; input: ZodObject<{ permit_id: $ZodBranded<ZodUUID, "Uuid", "out">; role: ZodString; scope_id: ZodNullable<...>; reason: ZodNullable<...>; }, $strict>; output: ZodVoid; async: true; description: string; }

PermitOfferAcceptedParams
#

auth/permit_offer_notifications.ts view source

ZodObject<{ offer: ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; from_actor_id: $ZodBranded<ZodUUID, "Uuid", "out">; to_account_id: $ZodBranded<ZodUUID, "Uuid", "out">; ... 10 more ...; resulting_permit_id: ZodNullable<...>; }, $strict>; }, $strict>

Params for permit_offer_accepted — recipient accepted the offer.

PermitOfferDeclinedParams
#

auth/permit_offer_notifications.ts view source

ZodObject<{ offer: ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; from_actor_id: $ZodBranded<ZodUUID, "Uuid", "out">; to_account_id: $ZodBranded<ZodUUID, "Uuid", "out">; ... 10 more ...; resulting_permit_id: ZodNullable<...>; }, $strict>; }, $strict>

Params for permit_offer_declined. The decline reason (if any) rides along inside offer.decline_reason — the DB stamps it on the offer row during decline, so a sibling reason field would just duplicate it.

PermitOfferReceivedParams
#

auth/permit_offer_notifications.ts view source

ZodObject<{ offer: ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; from_actor_id: $ZodBranded<ZodUUID, "Uuid", "out">; to_account_id: $ZodBranded<ZodUUID, "Uuid", "out">; ... 10 more ...; resulting_permit_id: ZodNullable<...>; }, $strict>; }, $strict>

Params for permit_offer_received — offer delivered to its recipient.

PermitOfferRetractedParams
#

auth/permit_offer_notifications.ts view source

ZodObject<{ offer: ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; from_actor_id: $ZodBranded<ZodUUID, "Uuid", "out">; to_account_id: $ZodBranded<ZodUUID, "Uuid", "out">; ... 10 more ...; resulting_permit_id: ZodNullable<...>; }, $strict>; }, $strict>

Params for permit_offer_retracted — grantor-side retraction.

PermitOfferSupersedeParams
#

auth/permit_offer_notifications.ts view source

ZodObject<{ offer: ZodObject<{ id: $ZodBranded<ZodUUID, "Uuid", "out">; from_actor_id: $ZodBranded<ZodUUID, "Uuid", "out">; to_account_id: $ZodBranded<ZodUUID, "Uuid", "out">; ... 10 more ...; resulting_permit_id: ZodNullable<...>; }, $strict>; reason: ZodEnum<...>; cause_id: $ZodBranded<...>; }, $strict>

Params for permit_offer_supersede. Fires to the grantor's sockets when their pending offer is obsoleted — either by a sibling accept (reason: 'sibling_accepted'), by revoke of the resulting permit (reason: 'permit_revoked'), or by deletion of the parent scope row the offer was bound to (reason: 'scope_destroyed'). cause_id points at the accepted offer id, the revoked permit id, or the destroyed scope row id respectively.

PermitRevokeParams
#

auth/permit_offer_notifications.ts view source

ZodObject<{ permit_id: $ZodBranded<ZodUUID, "Uuid", "out">; role: ZodString; scope_id: ZodNullable<$ZodBranded<ZodUUID, "Uuid", "out">>; reason: ZodNullable<...>; }, $strict>

Params for permit_revoke. Delivered to the revokee's sockets when one of their active permits is revoked. Flat wire shape — revoked_by is admin-UI-visible but deliberately omitted here (the revokee doesn't need to learn the admin's identity). Target account is implicit in the send target.

Depends on
#

Imported by
#