MVP: Two users chatting via embedded SPA #9

Closed
opened 2026-02-11 02:44:03 +01:00 by clawbot · 2 comments
Collaborator

Goal

Two users should be able to open the embedded SPA web client in their browsers, create sessions, join a channel, and exchange messages in real-time.

What works today

  • Session creation (register with nick → get token)
  • Token-based auth on all endpoints
  • Join/part channels, list channels, list members
  • Send messages to channels and DMs
  • Poll for new messages (GET /messages?after=ID)
  • SPA: login screen, tab-based UI, channel join, message send/receive, DM tabs, /join, /part, /msg commands
  • Channel message history
  • DM send/receive
  • Basic member list sidebar

What's missing for two-user chat

Backend

  • Long pollingGET /messages currently returns immediately. Needs to hold the connection open for up to 15s (per README spec) when no messages are available, returning as soon as a new message arrives. Without this, the SPA polls on a fixed 1.5s interval which is wasteful and adds latency.
  • Session/client UUID model — Current DB uses simple auto-increment IDs and a single token per user. README spec calls for server-assigned session UUID + client UUID + opaque token. Need sessions and clients tables per the architecture.
  • Per-client message queues — Currently PollMessages queries the messages table directly. Need a client_queues table so each client gets its own delivery queue with independent cursors, per the multi-client spec.
  • IRC message format — Responses currently return flat JSON ({id, nick, content, channel, ...}). Need to return IRC-style envelopes: {command: "PRIVMSG", from: "nick", to: "#channel", body: ["text"], id: "uuid", ts: "iso8601"}.
  • JOIN/PART/NICK events as messages — When a user joins or parts a channel or changes nick, these should be injected into the message stream as {command: "JOIN", ...} etc., not just side-effects.
  • Nick change endpointPOST /api/v1/nick or similar. Currently no way to change nick after session creation.
  • Topic set/getPOST /channels/{name}/topic and topic in channel info. SPA has no topic display.
  • Ephemeral channels — Channels should auto-delete when last member parts (per design doc). Currently persist forever.
  • MOTD delivery — Should be sent as numeric 375/372/376 messages in the stream on connect.

SPA Client

  • Long-poll instead of setInterval — Switch from setInterval(poll, 1500) to recursive long-poll (GET /messages?after=ID&timeout=15). Immediately re-poll after receiving messages.
  • Display JOIN/PART/NICK events — Show system messages when users join, leave, or change nick.
  • Handle IRC message envelope format — Parse {command, from, to, body} instead of {nick, content, channel}.
  • Nick change UI/nick command should work.
  • Topic display — Show channel topic in header area.
  • Unread indicators — Badge on inactive tabs when new messages arrive.
  • Auto-rejoin on reconnect — If token is saved, rejoin previously joined channels.
  • Disconnect/reconnect handling — Show connection status, auto-retry on network errors.
  • Message deduplication — Ensure the same message isn't shown twice if polling overlaps.

Database Schema Changes

  • Add sessions table (uuid, nick, signing_key, created_at, last_seen)
  • Add clients table (uuid, session_id, token, created_at, last_seen)
  • Add client_queues table (client_id, message_id) for per-client delivery
  • Add message_type column to messages (or switch to IRC command-based schema)
  • Add UUID columns (message.uuid, etc.) — currently using auto-increment IDs

Not needed for MVP

  • Federation (S2S)
  • Cryptographic signing / PUBKEY
  • E2E encryption
  • Channel modes beyond basic
  • CLI client functionality
  • Message history pagination in SPA
  • Rate limiting

Acceptance criteria

User A and User B can:

  1. Open the SPA in two browser windows
  2. Pick nicks
  3. Join #general
  4. See each other in the member list
  5. Send messages and see them appear in near-real-time (<1s with long polling)
  6. See join/part notifications
  7. Send DMs to each other
## Goal Two users should be able to open the embedded SPA web client in their browsers, create sessions, join a channel, and exchange messages in real-time. ## What works today - Session creation (register with nick → get token) - Token-based auth on all endpoints - Join/part channels, list channels, list members - Send messages to channels and DMs - Poll for new messages (`GET /messages?after=ID`) - SPA: login screen, tab-based UI, channel join, message send/receive, DM tabs, `/join`, `/part`, `/msg` commands - Channel message history - DM send/receive - Basic member list sidebar ## What's missing for two-user chat ### Backend - [ ] **Long polling** — `GET /messages` currently returns immediately. Needs to hold the connection open for up to 15s (per README spec) when no messages are available, returning as soon as a new message arrives. Without this, the SPA polls on a fixed 1.5s interval which is wasteful and adds latency. - [ ] **Session/client UUID model** — Current DB uses simple auto-increment IDs and a single token per user. README spec calls for server-assigned session UUID + client UUID + opaque token. Need `sessions` and `clients` tables per the architecture. - [ ] **Per-client message queues** — Currently `PollMessages` queries the messages table directly. Need a `client_queues` table so each client gets its own delivery queue with independent cursors, per the multi-client spec. - [ ] **IRC message format** — Responses currently return flat JSON (`{id, nick, content, channel, ...}`). Need to return IRC-style envelopes: `{command: "PRIVMSG", from: "nick", to: "#channel", body: ["text"], id: "uuid", ts: "iso8601"}`. - [ ] **JOIN/PART/NICK events as messages** — When a user joins or parts a channel or changes nick, these should be injected into the message stream as `{command: "JOIN", ...}` etc., not just side-effects. - [ ] **Nick change endpoint** — `POST /api/v1/nick` or similar. Currently no way to change nick after session creation. - [ ] **Topic set/get** — `POST /channels/{name}/topic` and topic in channel info. SPA has no topic display. - [ ] **Ephemeral channels** — Channels should auto-delete when last member parts (per design doc). Currently persist forever. - [ ] **MOTD delivery** — Should be sent as numeric 375/372/376 messages in the stream on connect. ### SPA Client - [ ] **Long-poll instead of setInterval** — Switch from `setInterval(poll, 1500)` to recursive long-poll (`GET /messages?after=ID&timeout=15`). Immediately re-poll after receiving messages. - [ ] **Display JOIN/PART/NICK events** — Show system messages when users join, leave, or change nick. - [ ] **Handle IRC message envelope format** — Parse `{command, from, to, body}` instead of `{nick, content, channel}`. - [ ] **Nick change UI** — `/nick` command should work. - [ ] **Topic display** — Show channel topic in header area. - [ ] **Unread indicators** — Badge on inactive tabs when new messages arrive. - [ ] **Auto-rejoin on reconnect** — If token is saved, rejoin previously joined channels. - [ ] **Disconnect/reconnect handling** — Show connection status, auto-retry on network errors. - [ ] **Message deduplication** — Ensure the same message isn't shown twice if polling overlaps. ### Database Schema Changes - [ ] Add `sessions` table (uuid, nick, signing_key, created_at, last_seen) - [ ] Add `clients` table (uuid, session_id, token, created_at, last_seen) - [ ] Add `client_queues` table (client_id, message_id) for per-client delivery - [ ] Add `message_type` column to messages (or switch to IRC command-based schema) - [ ] Add UUID columns (message.uuid, etc.) — currently using auto-increment IDs ### Not needed for MVP - Federation (S2S) - Cryptographic signing / PUBKEY - E2E encryption - Channel modes beyond basic - CLI client functionality - Message history pagination in SPA - Rate limiting ## Acceptance criteria User A and User B can: 1. Open the SPA in two browser windows 2. Pick nicks 3. Join `#general` 4. See each other in the member list 5. Send messages and see them appear in near-real-time (<1s with long polling) 6. See join/part notifications 7. Send DMs to each other
clawbot self-assigned this 2026-02-11 02:44:03 +01:00
Author
Collaborator

Design clarification from sneak: All C2S commands go through POST /messages with the command field. No separate routes for NICK, TOPIC, JOIN, PART, etc.

Examples:

{"command": "PRIVMSG", "to": "#general", "body": ["hello"]}
{"command": "NICK", "body": ["newnick"]}
{"command": "TOPIC", "to": "#channel", "body": ["new topic"]}
{"command": "JOIN", "to": "#channel"}
{"command": "PART", "to": "#channel"}

This means:

  • Remove POST /channels/join and DELETE /channels/{name} from the API
  • Remove planned nick change endpoint
  • POST /messages becomes the single C2S command endpoint — server dispatches by command field
  • Keep GET /messages (polling), GET /state, GET /history, GET /channels/all, GET /channels/{name}/members, GET /server as read-only endpoints
Design clarification from sneak: **All C2S commands go through `POST /messages`** with the `command` field. No separate routes for NICK, TOPIC, JOIN, PART, etc. Examples: ```json {"command": "PRIVMSG", "to": "#general", "body": ["hello"]} {"command": "NICK", "body": ["newnick"]} {"command": "TOPIC", "to": "#channel", "body": ["new topic"]} {"command": "JOIN", "to": "#channel"} {"command": "PART", "to": "#channel"} ``` This means: - Remove `POST /channels/join` and `DELETE /channels/{name}` from the API - Remove planned nick change endpoint - `POST /messages` becomes the **single C2S command endpoint** — server dispatches by `command` field - Keep `GET /messages` (polling), `GET /state`, `GET /history`, `GET /channels/all`, `GET /channels/{name}/members`, `GET /server` as read-only endpoints
Author
Collaborator

API Unified Command Endpoint

Pushed to feature/web-client — all C2S commands now go through POST /api/v1/messages with a command field:

Changes

  • POST /messages dispatches by command field: PRIVMSG, JOIN, PART, NICK, TOPIC, PING
  • Removed routes: POST /channels/join, DELETE /channels/{name}, POST /register
  • Renamed: /register/session, /channels/all/channels
  • Added DB methods: ChangeNick, SetTopic
  • CLI client updated to use unified endpoint for join/part/list
  • Web SPA updated: session creation, join/part/nick/messages all use command format
  • README updated with command table and new endpoint docs

This simplifies the API surface significantly — the entire write path is one endpoint.

## API Unified Command Endpoint Pushed to `feature/web-client` — all C2S commands now go through `POST /api/v1/messages` with a `command` field: ### Changes - **`POST /messages`** dispatches by `command` field: `PRIVMSG`, `JOIN`, `PART`, `NICK`, `TOPIC`, `PING` - **Removed routes**: `POST /channels/join`, `DELETE /channels/{name}`, `POST /register` - **Renamed**: `/register` → `/session`, `/channels/all` → `/channels` - **Added DB methods**: `ChangeNick`, `SetTopic` - **CLI client** updated to use unified endpoint for join/part/list - **Web SPA** updated: session creation, join/part/nick/messages all use command format - **README** updated with command table and new endpoint docs This simplifies the API surface significantly — the entire write path is one endpoint.
clawbot added the
needs-review
label 2026-02-20 09:29:48 +01:00
sneak added this to the MVP milestone 2026-02-27 05:13:45 +01:00
sneak closed this issue 2026-02-27 13:51:21 +01:00
Sign in to join this conversation.
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: sneak/chat#9
No description provided.