Connect SDK
Notifications and timers
Declare private criteria at the collection authority, deliver opaque wake-up signals, and schedule generation-fenced one-shot timers.Notification evaluation runs at the local connector or hosted provider. Connect receives an opaque signal, grant, criterion, and cursor. Static presentation comes from the manifest. Matching record paths, frontmatter, bodies, and runtime event payloads remain at the collection authority.
Declare criteria in the application manifest
{
"notifications": {
"criteria": [{
"id": "task.changed",
"event": {
"id": "mdbase.record.modified",
"version": "1.0.0"
},
"if": {
"$expr": "\"task\" in event.data.types"
},
"debounce": "5s",
"minimum_interval": "1m",
"presentation": {
"title": "Tasks changed",
"body": "Open Worklog to see the latest changes.",
"tag": "task-changes"
}
}]
}
}Criteria use canonical Runtime 0.2 event contracts and CEL over CloudEvent data. Presentation is static manifest data, so private record content cannot be interpolated into a push. The approved grant stores an exact snapshot of each selected criterion.
Register browser push from a user action
const worker = await navigator.serviceWorker.register(
"/service-worker.js"
);
await connection.registerNotifications({
serviceWorker: worker,
criteria: ["task.changed"]
});import { showMdbasePushNotification } from "@mdbase/connect";
self.addEventListener("push", (event) => {
event.waitUntil(
showMdbasePushNotification(
self.registration,
event.data?.json()
)
);
});Treat the signal as a prompt to refresh current authorized state. Re-registering atomically replaces the selected criteria for the installation. Call unregisterNotifications(worker) when the user opts out.
Reconcile one-shot timers at the authority
await connection.reconcileTimers({
namespace: "task-reminders",
criterion_id: "task.reminder",
timers: [{
id: "task-123:reminder-1",
fire_at: "2026-07-25T10:00:00Z",
data: { kind: "task_reminder" }
}]
});Reconciliation is atomic. Unchanged timers keep their generation and fired state, changed timers receive a new generation, new timers are scheduled, and omitted active timers are cancelled. IDs and optional private data remain at the collection authority. Signals carry the timer criterion and cursor metadata.
Use signed webhooks for developer-managed delivery
Distributed applications should normally keep Apple and Firebase sender credentials in their own infrastructure. Declare one HTTPS webhook and verify the signature over the exact raw body before parsing it.
import { verifyNotificationWebhook } from "@mdbase/connect-webhooks";
const event = verifyNotificationWebhook({
body: rawRequestBody,
headers: request.headers,
keys: cachedConnectSigningKeys
});
if (await deliveries.claim(event.delivery_id)) {
await sendPush(event.connection_id, event.notification);
}Cache keys from /v1/notifications/webhook-signing-keys, enforce the built-in five-minute replay window, and store delivery_id before doing work because Connect retries until the endpoint succeeds.
Delivery choices change the trust boundary
| Mode | Best fit | Additional trust |
|---|---|---|
| Web Push | Browser and installed PWA | Browser push service and Connect delivery storage |
| Managed FCM | Personal or single-owner native application | Connect sender identity can address the application's Firebase project |
| Signed webhook | Broadly distributed application | Developer backend holds its own platform sender credentials |