/

CrowdVision · source-available, not open source · © 2026 Nicolò Ghignatti

Notification Contracts

notification-schema holds the one message notification sends to a browser, and the Redis channel it travels on. The same bytes are published for the in-app bell and sent as the Web Push payload, so the bell and the pop-up can never disagree.


The message

flowchart LR
    N["notification\nbuilds Notification"] -->|"Redis notifications"| S["socket\nroutes on domainName"]
    N -->|"Web Push, same bytes"| W["service worker\ntitle · message · icon"]
    S -->|"socket.io notification"| B["frontend bell\ntype · message · timestamp\nhides metrics switched off"]
FieldTypeRule
idstringMillisecond clock at publish.
typeinfo | warning | dangerSeverity, closed set. Not the metric: that is a preference’s notificationType.
titlestringPop-up heading.
messagestringBody of both the bell entry and the pop-up.
timestampstringISO-8601 UTC with milliseconds.
domainNamestring, optionalAbsent means a system-wide broadcast; present means only that domain’s room.
metricalertable metric key, optionalSet on breaches — the same key as a preference’s notificationType. The bell shows the breach only if the account switched that metric on for the domain; push is filtered the same way server-side. Absent on manual alerts, which always show.
iconstring, optionalThe service worker shows an icon only when one is sent.

Why socket parses it

socket reads domainName to pick the room, then relays the bytes it received. It parses the whole Notification rather than reaching for one key, so a message the producer would never write is skipped instead of broadcast — a renamed domainName would otherwise send one tenant’s alert to every client.


Tests

The crate also hosts the schema check for fixtures/notification-preferences.json (tests/notification_preferences_schema.rs), the GET / POST /notification/preferences shape. It has no Rust type here — only notification parses it — and is bound by notification’s controllers.rs (every request applied, every rejection refused with its reason) and the frontend’s src/utils/notificationPreferences.spec.ts.