Lux Docs

Build on Lux without guessing.

Lux Cloud + OSS core

Product docs for the Lux runtime: tables, cache, vectors, realtime, time series, auth, HTTP, SDK, CLI, and self-hosting.

Push

Send native push notifications to iOS and the web. Lux talks to Apple (APNs) and the W3C Web Push standard (VAPID) directly — no OneSignal, no Firebase SDK. Push is its own standalone scope: it works with or without Lux Auth.

The model

A device is registered against an opaque subject_id — any string you choose. It may be a Lux Auth user id, but it doesn't have to be: if you keep your own auth (e.g. Supabase), use your user id as the subject. You send by that same id, and Lux fans the notification out to every device registered under it.

Registration and sending on another subject's behalf require a secret key — your server is the trust boundary. A signed-in user with a Lux Auth session can self-register their own device (subject = auth.uid()) with a publishable key.

1. Configure APNs

In the dashboard, open your project → Push and paste your Apple credentials: the Team ID, an APNs Auth Key (.p8) and its Key ID, and your app's bundle id as the topic. Use the sandbox environment for development builds. The .p8 is stored encrypted and pushed down to your project.

2. Register a device

The device gets its push token from the OS, then hands it to your server, which registers it in Lux under your subject id:

Register (secret key)
import { createClient } from '@luxdb/sdk';

// Server-side, with your project secret key.
const db = createClient(process.env.LUX_URL, process.env.LUX_SECRET_KEY);

// Register a device for one of your users (any id you like).
await db.push.registerFor(user.id, {
  token: apnsHexToken,   // the device token from the OS
  platform: 'ios',
});

If you use Lux Auth, the client can register its own device directly with the publishable key — the subject is taken from the session, never asserted by the client:

Self-register (session)
// Client-side, signed in with a Lux Auth session.
await db.push.register({ token: apnsHexToken, platform: 'ios' });

On iOS the token comes from the standard registration callback — no SDK required to obtain it:

iOS: get the token
func application(_ application: UIApplication,
    didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
  let hex = deviceToken.map { String(format: "%02x", $0) }.joined()
  // POST `hex` to your server, which calls db.push.registerFor(...)
}

3. Send

Send to one subject, or many at once. Delivery is asynchronous and at-least-once; dead tokens are pruned automatically.

Send (secret key)
// One subject → all their devices.
await db.push.send(user.id, {
  title: 'Alice mentioned you',
  subtitle: 'Acme · #engineering',
  body: 'Can you take a look at the deploy?',
  thread_id: 'workspace:acme:channel:eng',  // lock-screen grouping
  image: 'https://cdn.example.com/thumb.png',
  data: { type: 'mention', channel_id: 'eng' },
});

// Many subjects in one call.
await db.push.send([alice.id, bob.id], { title: 'Standup', body: 'Starting now' });

Notification fields

title and body render the alert. The rest map to APNs: subtitle, thread_id (grouping), category (action buttons), sound, badge, image (attaches a thumbnail via a notification-service-extension), and content_available for silent / background delivery. data is an arbitrary string map delivered to the client for routing.

Managing devices

Devices
// List a subject's active devices (secret key).
const { data: devices } = await db.push.devices(user.id);

// Remove a device by id.
await db.push.unregister(deviceId);

Web Push (browsers & desktop)

The same db.push surface delivers to browsers and desktop over the W3C Web Push standard — real VAPID encryption (RFC 8291), no vendor account. In the dashboard, open PushWeb Push and click Enable Web Push: Lux generates a VAPID keypair, holds the private key, and gives you the public key to use as the browser's applicationServerKey.

In the browser, subscribe and register the subscription as the device token. The SDK helper handles the permission prompt, the service-worker subscription, and registration in one call:

Web Push: subscribe & register
// Browser, signed in with a Lux Auth session (self-register).
const publicKey = await db.push.getVapidPublicKey();
await db.push.subscribeWebPush({ vapidPublicKey: publicKey });

// Or register a subscription for a subject with your secret key (server-side).
await db.push.registerFor(user.id, {
  token: JSON.stringify(subscription),  // the PushSubscription from the browser
  platform: 'web',
});

Your service worker receives the decrypted payload and shows the notification — the same title, body, and data fields you send to iOS:

Service worker
// sw.js
self.addEventListener('push', (event) => {
  const n = event.data.json();
  event.waitUntil(
    self.registration.showNotification(n.title, { body: n.body, data: n.data })
  );
});

Sending is identical to iOS: address a subject_id and Lux fans out to that subject's devices across every platform. Dead subscriptions (a browser that unsubscribed, HTTP 410) are pruned automatically.