Per-subject notification inbox for Convex with fan-out delivery, read/unread state, reactive queries, and sandboxed tables requiring no auth coupling.
npm install @vllnt/convex-notificationsNotifications Inbox is a per-subject directed inbox for Convex: `deliver` fans out one notification per recipient, every notification arrives unread, and `list`/`unreadCount`/`get` are reactive queries that stream changes to connected clients without extra subscriptions. Server-sourced timestamps prevent caller-supplied times, a daily cron purges read notifications past retention in bounded batches (unread are never purged), and the typed `Notifications<TPayload>` client with `payloadValidator` narrows the stored payload at the boundary. Notification data lives in the component's own sandboxed tables — no blast radius into your schema — the host owns auth and resolves identity, and the component runs identically on Convex Cloud and self-hosted convex-backend.
The @vllnt/convex-notifications component provides inbox.list() and inbox.unreadCount() methods that work as reactive Convex queries. You re-export them from your host app and call them with useQuery in React. Both are scoped to a subjectRef so each user's inbox is isolated.
inbox.deliver(ctx, recipients, type, payload?) accepts a single subjectRef or an array and writes one notification row per recipient in a single mutation. It returns a notificationIds array so you can track each delivery individually.
The @vllnt/convex-notifications component exposes inbox.markRead(ctx, notificationId) for individual notifications and inbox.markAllRead(ctx, subjectRef) for bulk marking. markAllRead is bounded and self-rescheduling so it handles large inboxes without hitting Convex mutation limits.
The component ships with a built-in bounded purge via inbox.purge() and registers a daily cron that sweeps read notifications past the retention window in batches. Unread notifications are never purged automatically.
@vllnt/convex-notifications is auth-agnostic. The host application resolves identity and decides which subjectRef a caller may read from or deliver to. The component accepts any opaque string as a subjectRef and never inspects or stores user session data directly.
Every list and unreadCount call in @vllnt/convex-notifications is keyed to a single subjectRef and cannot span another subject's inbox. Tables are sandboxed and reachable only through the component's exported functions, so host or sibling tables are never touched.
Yes. The Notifications class is generic: new Notifications<TPayload>(components.notifications, { payloadValidator }) lets you type and validate the payload at the component boundary using a Convex validator. The stored payload stays opaque to the component itself and is returned as-is in NotificationView results.
@vllnt/convex-notifications is mount-safe. Each named app.use mount in convex.config.ts creates an isolated sandbox, so multiple instances do not share tables or interfere with each other.
@vllnt/convex-notifications is backend-only and has no ./react entry point. Inbox lists, unread counts, and individual notifications are fetched through standard useQuery calls against query functions you re-export from your host app using the component's list, unreadCount, and get methods.