# Realtime

Realtime in Nizhal is a **hint to re-pull** — not a data channel. The cursor pull is **authoritative**.

## Ping flow

```
mutator commit → RealtimeAdapter.publish(bucketKey) → WS frame → client → echo.pull()
```

- `publish` runs only from the server commit chokepoint
- Payload is the **bucket key only** — no row data on the wire (no leak surface)
- Client subscribes via `GET /sync/stream` (WebSocket) or a swappable transport

If a ping is missed (sleep, partition, DO idle), the next pull still converges. Set `pull.intervalMs` on `createNizhalClient` for a periodic fallback when realtime is unavailable.

## Adapters

| Adapter | Import | Use case |
|---------|--------|----------|
| `inProcessRealtime` | `@nizhal/server/adapters` | Default single-process pub/sub |
| `listenNotifyRealtime` | `@nizhal/server/adapters` | Multi-instance Postgres via `LISTEN/NOTIFY` |
| `cloudflareRealtime` | `@nizhal/server/adapters/cloudflare` | Workers + Durable Objects (PartyServer) |
| `cloudflareHttpRealtime` | `@nizhal/server/adapters/cloudflare` | HTTP long-poll fallback on Workers |

All implement `RealtimeAdapter`: `publish(bucket)` + `subscribe(onPing)`.

Default client transport uses PartySocket-compatible reconnect semantics against whichever adapter you deploy.

## Presence v2

`/sync/stream` supports `track` / `untrack` with heartbeat timeout. State and diffs are bucket-scoped — `presenceState`, `onPresence` on the client.

Presence is orthogonal to data sync: it does not replace pull for row convergence.

## React Native

Native WebSockets via `@nizhal/react-native` (`nitroWebSocketSource`) send `Authorization` on the upgrade header. NetInfo-based `reactNativeOnlineDetector` flushes the outbox on reconnect. See [React Native](/react-native/).

## cursorReset

If the client cursor is corrupt or far in the future, pull returns `cursorReset: true` and the collection re-bootstraps from cursor `0`.

## Next

- [How sync works](/concepts/how-sync-works/)
- [Realtime adapters](/server/realtime/)