# Realtime chat: three primitives, each for its job

Chat needs three different realtime behaviors, and agents cram them all into one. Messages are database state (postgres_changes). Typing indicators are ephemeral (broadcast). "Who is online" is presence. Use each primitive for what it is.

## Checkable procedure

1. Create the messages table with RLS: users can read messages in rooms they belong to, insert only as themselves (WITH CHECK on sender id). Add the table to the `supabase_realtime` publication.
2. Subscribe to postgres_changes filtered by room for message history and live inserts. This gives ordering and persistence for free.
3. Send typing indicators with broadcast on the same channel, not as database rows. Broadcasts are ephemeral: no storage, no history to clean up, no RLS needed on a table.
4. Track presence for the online roster: each client tracks its user id and metadata, and the channel syncs the roster. Handle the sync event to render the member list.
5. Gate the channel by room membership in your RLS policies and verify the JWT is attached to the realtime client. A chat channel without authorization leaks the conversation to anyone who guesses the topic.

## Ordering constraints

Schema and policies first, then the subscription code, then presence. Test with two real clients in two browsers; one client cannot reveal ordering or presence bugs.

## Verification

Two users in a room: messages appear in order on both, typing shows and clears, the roster shows both, and a third user outside the room receives nothing.