Campus notifications
Campus notifications are account-scoped, durable user events. An app or Campus service publishes one logical record. CampusOS stores the record and enforces access; the system-supplied campus-notifications app owns the inbox interface.
app or Campus event
-> notifications.publish(topic grant)
-> durable CampusOS inbox
-> optional active-shell mirror or encrypted Web Push of the same notification IDThe trusted shell owns the global unread badge and a bounded Flash surface for newly unread high and critical records. Flashes appear at the top right on desktop and between trusted header and app content on mobile. Once a Flash is painted, the shell marks its durable record read so the same event is not highlighted by both the Flash and the unread badge. The painted Flash remains mounted for its close, action, expiry, or trusted lifecycle rule. Its optional app-supplied presentation is declarative data rendered by the shell; an app cannot inject markup, script, an iframe, or privileged Campus controls into it.
The bell and the mobile Notifications tab open campus-notifications through the same installation, launch-descriptor, isolated-origin, and iframe path as every other app. On desktop that iframe occupies the shared secondary app host, so the current app remains visible beside it. Its presentation is anchored to the trusted notification bell, is not movable, and does not inherit Personal Agent's drag handle or floating-panel chrome. On mobile it occupies the full selected Notifications tab between trusted chrome; the underlying app iframe remains retained rather than being destroyed. The Notifications frontend uses CampusSDK to list permitted records, mark materially visible rows read (including persistent alerts), mark all records read, request device-notification permission, and activate a selected record.
CampusOS retains the parts that cannot depend on that frontend being open:
- durable account-scoped storage and permission enforcement;
- the shell's global unread projection;
- browser/native notification mirroring and Service Worker click routing;
- receiving and projecting events while another app is active;
- the trusted host for MCP, payment, and passkey approval surfaces.
Activation dispatch is a CampusSDK contract, not app-specific shell knowledge. The Notifications app calls app.notifications().activate(id); the SDK sends a source-bound, grant-checked launcher request to CampusOS. CampusOS re-resolves the durable record before opening an app or hosting a trusted approval surface. This prevents an iframe from inventing a privileged target while keeping approval protocol and activation behavior reusable by SDK apps.
New notifications are mirrored by the active shell only when:
deliveryisinbox-and-system;- Account notifications are enabled;
- the user granted browser notification permission from CampusOS chrome.
The durable inbox does not depend on system notification permission. For delivery while no Campus window is open, the user explicitly enables Web Push on a device with requestSystemPermission({ webPush: true }) or enableWebPush(). The trusted shell registers a Push API subscription for its Service Worker and sends only the subscription endpoint, P-256 public key, and authentication secret to CampusOS over the authenticated same-origin session. The private subscription key remains browser-held.
An item published with delivery: 'inbox-and-web-push' enters a durable CampusOS push outbox. CampusOS encrypts a bounded single-record payload with RFC 8291 aes128gcm, authenticates to the browser push service with an RFC 8292 VAPID key, retries transient failures, removes expired subscriptions on 404/410, and keeps the inbox record even when device delivery is unavailable. The dedicated push worker drains this outbox independently of application and Runtime lifecycle reconciliation. Time-bounded Flash presentations cap the push service's retention TTL, and a record that was already read is not sent later. The Service Worker displays the notification and routes clicks back to the same durable ID. Account notification settings remain authoritative for both active-shell and Web Push delivery.
Incoming calls.incoming records use this path with critical/high urgency. Subscribed devices can therefore alert while every Campus window is closed; the durable inbox and active-shell call Flash remain the fallback when the current device has not enabled Web Push. Opening Calls checks the local subscription and, when needed, exposes a trusted-shell opt-in action; browser permission is still requested only by the user's click.
Web Push clicks normally open the durable record in Notifications. The trusted shell special-cases the system-owned calls.incoming topic: it resolves the same durable ID and opens Calls directly at the ringing call's Answer/Decline screen, without auto-answering or granting media access.
Creating a record publishes notifications:created. Marking one or more records read publishes notifications:read after the durable transaction commits. The shell and Notifications app refresh their authoritative snapshots for either event, keeping the trusted unread badge and inbox rows in sync even when the mutation came through an application service capability.
Publication
Browser apps use app.notifications().publish(input) and backends use the same POST /v1/services/notifications service with their installation credential. Publication requires notifications.publish for the exact topic. Reading and mutation use snapshot(), markRead(id), and markAllRead() and require notifications.read for the visible topics. Device settings and activation use systemPermission(), requestSystemPermission(), enableWebPush(), disableWebPush(), and activate(id) on the same SDK object.
| Field | Meaning |
|---|---|
topic | Permission-scoped event family, up to 128 safe characters |
title | Short user-visible title, 1–200 characters |
body | Optional user-visible detail, up to 2,000 characters |
data | App-owned structured context |
priority | low, normal, high, or critical; presentation only |
presentation | Optional declarative Flash layout for a high or critical record |
delivery | inbox, inbox-and-system, or inbox-and-web-push |
dedupeKey | Optional stable retry key, scoped to account and source app |
Titles and bodies may be visible on a device lock screen. Put private payloads in app-owned storage and use opaque IDs in data. A dedupeKey returns the original notification on retry, so event producers should bind it to a durable payment proof, message, or job completion ID.
An important notification can customize its Flash without owning the trusted surface:
await app.notifications().publish({
topic: 'builds',
title: 'Build finished',
body: 'Version 42 is ready.',
priority: 'high',
presentation: {
mode: 'flash',
tone: 'success',
eyebrow: 'Release ready',
expiresInSeconds: 90,
actions: [{
id: 'open',
label: 'Open',
type: 'open-app',
style: 'primary',
target: '?build=42',
}],
},
})layout is standard or call; tone is neutral, accent, success, warning, or critical; and one Flash may expose up to three actions. open-app targets must be same-app query strings beginning with ?, while dismiss has no target. The shell marks the record read before opening the publishing installation, so neither a Flash nor its action can navigate to another app or an external origin. expiresInSeconds removes stale attention by marking the durable record read.
Incoming Campus Mail is the first built-in producer: message storage and its high-priority mail.incoming notification commit atomically without waking the recipient app runtime.
The owner's Personal Agent can also discover the trusted campus.schedule-notification MCP tool. A request such as “send me a push notification in two minutes” becomes a durable one-shot schedule with a server-relative delaySeconds: 120; when due, CampusOS re-checks the Agent installation's notifications.publish(agent.reminders) grant and creates the requested delivery record. An explicit browser/Web Push request uses delivery: 'inbox-and-web-push'; other reminders retain inbox-and-system. After the scheduling tool returns, its managed Agent extension asks the trusted shell to verify the current browser's subscription. If it is missing, Campus presents an Enable Web Push action; only the owner's click invokes the browser permission request and creates the subscription. This path does not wake the Agent workload, and the one-shot job is visible and cancellable in Cron.