Notifications

Preferences

How VitNode decides which notifications each user receives in the notification list, as push and by email - installation policy, mandatory types, user choices and time zone.

Users choose, per notification type, whether it shows in their notification list, whether it comes as push, and how it reaches them by email. Core builds the preferences page at /settings/notifications from the registered types, so a new plugin's types appear there with no migration and no UI code.

The settings page

/settings/notifications has two parts:

  • Where notifications reach you - push and email for the whole account. Push asks the browser for permission on this device. The Email switch turns notification email off for every type the member can change, after a confirmation, and on again - back to exactly what they had, or to the installation defaults after a reload.
  • What you get notified about - one accordion row per type, grouped by category. The bell, phone and mail icons show at a glance what is on. Open a row to flip each channel and pick the email frequency: right away, the daily digest or the weekly digest.

Every change saves instantly and says so with a toast.

Push is coming

Push choices are stored and respected by the policy today, but VitNode does not deliver push yet. Members can set them up now, so nothing changes for them when delivery lands.

How core decides

One function decides what a user receives, in this order:

  1. Installation policy disabled the type → nothing. Mandatory types ignore this.
  2. Mandatory type → always in-app. Email follows the installation default.
  3. The user's own choice for the type - skipped when the type is locked for members (Member can edit off in the AdminCP).
  4. The installation default for the type, set in the AdminCP.
  5. The type's own defaults.

When the AdminCP sets the notification list to Disabled for a type, it never lands in the list, whatever the user picked - email can still go out. Locked types show a "Set by the admin" badge in the member's settings, and the API refuses changes to them with 400.

The notification list, push and email are resolved separately. A user can take a type by email only, in the list only, any mix or nothing at all. Push is on by default wherever the installation allows it.

When email is available

A type can email only when all three allow it:

  • the type declares email;
  • an email adapter is configured;
  • the type's policy in the AdminCP does not set email to Disabled.

There is no installation-wide email switch: to stop a kind of email, disable email on that type.

When any of them says no, the user sees no email choice for that type and gets none.

Example

forum.topic_reply defaults to { inApp: true, email: "daily" }. An administrator changed its email default to weekly.

UserIn-appEmail
Never opened preferencesyesweekly - installation default
Chose immediateyesimmediate - their choice
Turned in-app offnoweekly - email is independent
Any user, after email is Disabledyesnone

What users can change

PUT /api/@vitnode/core/notifications/preferences accepts a partial update:

{
  "types": {
    "forum.topic_reply": { "inApp": true, "push": false, "email": "weekly" }
  }
}

The route answers 400 for an unknown type, a change to a mandatory or locked type, a channel the installation switched off for that type (list, push or email), or an email mode the type cannot send. Choices are stored per user and merged, so saving one type never resets another.

Digests

Daily digests go out at 08:00 and weekly digests on Monday at 08:00, in the member's time zone. Nobody picks the hour or the day - one predictable schedule keeps digests simple. See Email and digests.

Time zone

The time zone belongs to the account, not to notifications: it is the timeZone column on core_users, and members change it under Region on /settings. It takes any IANA name, like Europe/Warsaw, or null to follow the time zone of their language, then UTC. Use this device's time zone fills it in from the browser.

PUT /api/@vitnode/core/users/me/time-zone
Content-Type: application/json

{ "timeZone": "Europe/Warsaw" }

An unknown name answers 400. A save emits user.updated and refreshes the session, so c.get("user")?.timeZone sees the new value on the next request.

Mandatory types and account email

A type with mandatory: true is for notices users must not miss in their inbox, like "your account role changed". It is always delivered in-app and shows as fixed on the preferences page. Its email follows the installation default, as long as email is available.

Account email is not a notification

Password resets, email verification and sign-in codes stay on c.get("email").send(). They never pass through notification preferences, type policies or digests, so a user who turned every notification off still receives them.