mirror of
https://github.com/J3vb/OwnCord.git
synced 2026-09-03 03:50:00 +03:00
docs: update specs for LiveKit migration (Phase 4)
- CLAUDE.md: update key features (LiveKit voice/video), remove WebRTC track ID rules, update project structure - PROTOCOL.md: replace voice_offer/answer/ice with voice_token, remove soundboard_play, note client-side speaker detection - CHATSERVER.md: replace Pion SFU with LiveKit companion process, update architecture, security, and library references
This commit is contained in:
@@ -0,0 +1,230 @@
|
||||
# ChatServer — Self-Hosted Windows Chat Platform
|
||||
|
||||
Native Windows desktop client + self-hosted server.
|
||||
Two executables: `chatserver.exe` (server) and
|
||||
`OwnCord.exe` (Tauri v2 client). Server operator runs
|
||||
the server, friends install the client.
|
||||
|
||||
## Tech Stack
|
||||
|
||||
### Server (`chatserver.exe`)
|
||||
|
||||
- **Go** — Single exe, no dependencies. Embeds admin web UI via `go embed`.
|
||||
- **SQLite** — Single `.db` file. WAL mode. Zero config.
|
||||
- **LiveKit** — Voice/video media server. Runs as a companion
|
||||
process alongside `chatserver.exe`. Token-based auth.
|
||||
- **Admin panel** — Web-based, served at `/admin`.
|
||||
Browser access, not part of the client.
|
||||
|
||||
### Client (`OwnCord.exe`)
|
||||
|
||||
**Tauri v2** (Rust backend + TypeScript/HTML/CSS frontend).
|
||||
See LANGUAGE-REVIEW.md for the evaluation that led to this
|
||||
choice, and CLIENT-ARCHITECTURE.md for the full design.
|
||||
|
||||
- Tauri v2 desktop app using system WebView2 (NOT Electron)
|
||||
- ~10-15 MB install size, ~30-50 MB RAM idle
|
||||
- TypeScript frontend with CSS from HTML mockups
|
||||
- WebSocket client for real-time chat (browser `WebSocket` API)
|
||||
- LiveKit for voice/video (LiveKit JS SDK in webview)
|
||||
- Global keyboard hooks via `tauri-plugin-global-shortcut`
|
||||
- System tray via Tauri's built-in tray support
|
||||
- Windows toast notifications via `tauri-plugin-notification`
|
||||
- Windows Credential Manager via `windows-rs` Rust crate
|
||||
- NSIS installer via Tauri bundler
|
||||
|
||||
## Architecture
|
||||
|
||||
```text
|
||||
SERVER (chatserver.exe) — runs on the host machine
|
||||
├── REST API (Go net/http)
|
||||
├── WebSocket Hub (real-time messages, presence, typing)
|
||||
├── LiveKit Companion Process (voice/video media)
|
||||
├── SQLite Database (data/chatserver.db)
|
||||
├── File Storage (data/uploads/)
|
||||
├── Admin Web UI (embedded, browser-based, /admin)
|
||||
└── config.yaml
|
||||
|
||||
CLIENT (OwnCord.exe) — installed by each friend
|
||||
├── Native Windows UI
|
||||
├── WebSocket Client (chat connection)
|
||||
├── LiveKit Client (voice/video via livekitSession.ts)
|
||||
├── Audio Engine (device management, noise suppression)
|
||||
├── Local Settings (connection profiles, keybinds, audio config)
|
||||
└── System Tray Integration
|
||||
```
|
||||
|
||||
### How It Works
|
||||
|
||||
1. Server operator runs `chatserver.exe` on their PC/home server
|
||||
2. Friends download and install `OwnCord.exe`
|
||||
3. Client connects to the server via IP/domain + port
|
||||
4. All chat, voice, video, and file transfers go through the server
|
||||
5. Admin manages the server through a browser at `https://server-ip:port/admin`
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Protocol & Server Core (2–3 weeks)
|
||||
|
||||
- [ ] Define client-server protocol over WebSocket
|
||||
(JSON messages with type/payload structure)
|
||||
- [ ] Message types: auth, chat, typing, presence,
|
||||
channel_update, voice_signal, file_transfer
|
||||
- [ ] Server: Go project with `go embed` for admin
|
||||
panel static files only
|
||||
- [ ] SQLite setup with migrations on startup (users,
|
||||
channels, messages, sessions, roles, invites)
|
||||
- [ ] config.yaml generation on first run (port, name,
|
||||
max upload size, voice quality, TLS mode)
|
||||
- [ ] Server systray icon (getlantern/systray) — minimize to tray, status
|
||||
indicator, open admin panel, quit
|
||||
- [ ] Windows Firewall handling on first launch
|
||||
- [ ] Optional: register as Windows Service for headless operation
|
||||
|
||||
## Phase 2: Auth & Security (2–3 weeks)
|
||||
|
||||
- [ ] Invite-only registration — server generates
|
||||
invite codes, client has "Redeem Invite" flow
|
||||
- [ ] bcrypt (cost 12+) passwords, server-side session tokens (256-bit random)
|
||||
- [ ] Client stores auth token securely via Windows Credential Manager / DPAPI
|
||||
- [ ] Login rate limiting: 5 attempts/min/IP, lockout after 10 failures
|
||||
- [ ] Optional TOTP 2FA (`pquerna/otp`) — QR code
|
||||
during setup, prompts on login
|
||||
- [ ] Roles: Owner, Admin, Moderator, Member + custom roles with bitfield permissions
|
||||
- [ ] Per-channel permission overrides, enforced server-side on every action
|
||||
- [ ] TLS modes: self-signed (default), Let's Encrypt,
|
||||
manual cert, off (Tailscale)
|
||||
- [ ] Client: certificate pinning or trust-on-first-use (TOFU) for self-signed certs
|
||||
|
||||
## Phase 3: Client App — Core UI (3–4 weeks)
|
||||
|
||||
- [ ] Connection dialog: server address, port, login/register, invite code entry
|
||||
- [ ] Save server profiles (connect to multiple
|
||||
servers like TeamSpeak)
|
||||
- [ ] Main window layout: server list → channel list → message area → member list
|
||||
- [ ] Channel tree view with categories, text channels, voice channels
|
||||
- [ ] Message rendering: markdown, code blocks, timestamps, avatars, replies, reactions
|
||||
- [ ] Message input: multi-line, markdown preview, emoji picker, file drag-and-drop
|
||||
- [ ] Unread indicators, @mention badges per channel
|
||||
- [ ] System tray: minimize to tray, notification popups, badge count
|
||||
- [ ] Keyboard shortcuts: Ctrl+K quick switcher,
|
||||
Escape to close panels, customizable PTT key
|
||||
- [ ] Settings: account, appearance (light/dark),
|
||||
notifications, audio devices, keybinds
|
||||
|
||||
## Phase 4: Real-Time Chat Features (2–3 weeks)
|
||||
|
||||
- [ ] WebSocket client with auto-reconnect, exponential
|
||||
backoff, message replay on reconnect
|
||||
- [ ] Send/receive messages in real-time, append to scrollback
|
||||
- [ ] Message history: paginated from server on channel switch, scroll-to-load-more
|
||||
- [ ] Threads, replies (inline preview), reactions (emoji), edit, delete
|
||||
- [ ] Typing indicators ("X is typing..." below input)
|
||||
- [ ] Online/offline/idle/DnD presence with status icons in member list
|
||||
- [ ] File uploads: drag-and-drop or clipboard paste,
|
||||
progress bar, inline image previews
|
||||
- [ ] Client-side file validation before upload (size check, warn on large files)
|
||||
- [ ] Search: query server FTS5 endpoint, display results with jump-to-message
|
||||
- [ ] Windows toast notifications with action buttons (reply, mark read)
|
||||
- [ ] Notification sounds (configurable, per-channel mute/override)
|
||||
|
||||
## Phase 5: Voice & Video (3–5 weeks)
|
||||
|
||||
- [ ] LiveKit integration in native client for voice/video
|
||||
(client connects to LiveKit directly using token from server)
|
||||
- [ ] Audio device selection: input/output dropdowns in settings, live preview
|
||||
- [ ] Voice channels: click to join/leave, show connected users with speaking indicators
|
||||
- [ ] Voice controls: mute (button + keybind), deafen, per-user volume sliders
|
||||
- [ ] Push-to-talk: configurable global hotkey that works in fullscreen games
|
||||
- [ ] Voice activity detection with configurable sensitivity
|
||||
- [ ] Noise suppression (RNNoise or equivalent, bundled with client)
|
||||
- [ ] Server-side: LiveKit companion process with token-based auth
|
||||
and webhook sync for voice state updates
|
||||
- [ ] Voice quality: low (32kbps) / medium (64kbps) / high (128kbps Opus)
|
||||
- [ ] Screen sharing via LiveKit screen share track
|
||||
- [ ] Video calls: camera capture, displayed in voice channel panel
|
||||
|
||||
## Phase 6: Admin Panel — Web-Based (1–2 weeks)
|
||||
|
||||
- [ ] Served by server at `/admin`, browser-only access
|
||||
- [ ] Auth: admin credentials, session-based
|
||||
- [ ] Dashboard: connected users, message count, disk usage, CPU/RAM, uptime
|
||||
- [ ] User management: list all, edit roles, ban/unban, reset password, force disconnect
|
||||
- [ ] Channel management: create, rename, reorder, set permissions, archive
|
||||
- [ ] Invite management: generate, view active, set expiry/use limit, revoke
|
||||
- [ ] Server settings: name, icon, MOTD, max upload size, voice quality, TLS config
|
||||
- [ ] Moderation: kick, ban, temp ban, slow mode, mute, word filter, audit log
|
||||
- [ ] Backup: trigger manual backup, configure
|
||||
schedule, view/restore from admin panel
|
||||
- [ ] Built with simple HTML/CSS/JS embedded in the server binary
|
||||
|
||||
## Phase 7: Distribution & Updates (1–2 weeks)
|
||||
|
||||
- [ ] **Server:** GitHub Actions builds
|
||||
`chatserver.exe` (amd64), SHA256, GitHub Release
|
||||
- [ ] **Client:** Tauri bundler (NSIS) installer —
|
||||
Program Files, Start Menu, auto-start, protocol
|
||||
handler for `chatserver://` invite links
|
||||
- [ ] Client auto-update: check GitHub releases on
|
||||
launch, prompt to download + install
|
||||
- [ ] Server update: admin panel shows available update, one-click download + restart
|
||||
- [ ] Docs: Quick Start, Port Forwarding, Tailscale,
|
||||
Client install guide
|
||||
- [ ] Security hardening checklist for server operators
|
||||
- [ ] SECURITY.md, README.md, CONTRIBUTING.md
|
||||
|
||||
---
|
||||
|
||||
## Windows-Specific Details
|
||||
|
||||
### Client (Tauri v2)
|
||||
|
||||
- **Installer:** Tauri bundler (NSIS, ~10-15 MB).
|
||||
Registers `chatserver://` protocol handler.
|
||||
- **Auto-start:** Registry key
|
||||
`HKCU\Software\Microsoft\Windows\CurrentVersion\Run`.
|
||||
- **Credentials:** Auth tokens stored in Windows
|
||||
Credential Manager via `windows-rs` Rust crate.
|
||||
- **Push-to-talk:** Global hotkey via
|
||||
`tauri-plugin-global-shortcut`.
|
||||
- **Audio:** LiveKit SDK via `livekitSession.ts`.
|
||||
- **Screen capture:** LiveKit screen share track.
|
||||
- **Notifications:** `tauri-plugin-notification`
|
||||
(Windows toast).
|
||||
- **Tray:** Tauri built-in system tray with badge.
|
||||
- See CLIENT-ARCHITECTURE.md for full design.
|
||||
|
||||
### Server
|
||||
|
||||
- **Firewall:** Prompt on first run. Installer can pre-register firewall rule.
|
||||
- **SmartScreen:** Unsigned exe shows warning. Code signing cert resolves this.
|
||||
- **Data path:** `data/` next to exe. Installer version uses `%APPDATA%/ChatServer/`.
|
||||
- **Logs:** `data/logs/` with daily rotation, viewable from admin panel.
|
||||
- **Service mode:** `chatserver.exe --service install` to register as Windows Service.
|
||||
|
||||
## Security Priorities
|
||||
|
||||
**Critical:** Invite-only registration, bcrypt auth,
|
||||
TLS (self-signed minimum), file upload validation
|
||||
(magic bytes, block executables), input sanitization
|
||||
server-side, credential storage via DPAPI, backups.
|
||||
|
||||
**High:** Rate limiting, TOTP 2FA, role permissions,
|
||||
WebSocket auth, LiveKit token auth, cert pinning/TOFU,
|
||||
update integrity (SHA256).
|
||||
|
||||
## Server Libraries (Go)
|
||||
|
||||
| Purpose | Library |
|
||||
| --- | --- |
|
||||
| HTTP/routing | `net/http` + `chi` |
|
||||
| WebSocket | `nhooyr.io/websocket` |
|
||||
| LiveKit | `livekit/server-sdk-go` (token generation, webhook validation) |
|
||||
| SQLite | `modernc.org/sqlite` (pure Go) |
|
||||
| Auth | `golang.org/x/crypto/bcrypt` |
|
||||
| TOTP | `pquerna/otp` |
|
||||
| Sanitization | `bluemonday` |
|
||||
| TLS | `golang.org/x/crypto/acme/autocert` |
|
||||
| Systray | `getlantern/systray` |
|
||||
| Config | `koanf` |
|
||||
| Logging | `log/slog` |
|
||||
@@ -0,0 +1,475 @@
|
||||
# WebSocket Protocol Spec
|
||||
|
||||
All client-server communication (except file uploads and
|
||||
admin panel) happens over a single WebSocket connection.
|
||||
Messages are JSON with a `type` and `payload`.
|
||||
|
||||
## Message Format
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "message_type",
|
||||
"id": "unique-request-id",
|
||||
"payload": { }
|
||||
}
|
||||
```
|
||||
|
||||
- `type` — string, required. Determines how payload is interpreted.
|
||||
- `id` — string, optional. Client-generated UUID for request/response correlation.
|
||||
- `payload` — object, required. Contents vary by type.
|
||||
|
||||
Server responses to client requests include the same `id` for correlation.
|
||||
|
||||
---
|
||||
|
||||
## Authentication
|
||||
|
||||
### Client → Server
|
||||
|
||||
```json
|
||||
{ "type": "auth", "payload": { "token": "session-token-here" } }
|
||||
```
|
||||
|
||||
### Server → Client (success)
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "auth_ok",
|
||||
"payload": {
|
||||
"user": {
|
||||
"id": 1, "username": "alex",
|
||||
"avatar": "uuid.png", "role": "admin"
|
||||
},
|
||||
"server_name": "My Server",
|
||||
"motd": "Welcome!"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Server → Client (failure)
|
||||
|
||||
```json
|
||||
{ "type": "auth_error", "payload": { "message": "Invalid or expired token" } }
|
||||
```
|
||||
|
||||
Connection is closed by server after auth_error.
|
||||
|
||||
---
|
||||
|
||||
## Chat Messages
|
||||
|
||||
### Send Message (Client → Server)
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "chat_send",
|
||||
"id": "req-uuid",
|
||||
"payload": {
|
||||
"channel_id": 5,
|
||||
"content": "Hello everyone!",
|
||||
"reply_to": null,
|
||||
"attachments": ["upload-uuid-1"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Message Broadcast (Server → Client)
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "chat_message",
|
||||
"payload": {
|
||||
"id": 1042, "channel_id": 5,
|
||||
"user": {
|
||||
"id": 1, "username": "alex",
|
||||
"avatar": "uuid.png"
|
||||
},
|
||||
"content": "Hello everyone!",
|
||||
"reply_to": null,
|
||||
"attachments": [{
|
||||
"id": "upload-uuid-1",
|
||||
"filename": "photo.jpg",
|
||||
"size": 204800,
|
||||
"mime": "image/jpeg",
|
||||
"url": "/files/upload-uuid-1"
|
||||
}],
|
||||
"timestamp": "2026-03-14T10:30:00Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Send Ack (Server → Client)
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "chat_send_ok",
|
||||
"id": "req-uuid",
|
||||
"payload": {
|
||||
"message_id": 1042,
|
||||
"timestamp": "2026-03-14T10:30:00Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Edit Message (Client → Server)
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "chat_edit",
|
||||
"id": "req-uuid",
|
||||
"payload": {
|
||||
"message_id": 1042,
|
||||
"content": "Hello everyone! (edited)"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Edit Broadcast (Server → Client)
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "chat_edited",
|
||||
"payload": {
|
||||
"message_id": 1042,
|
||||
"channel_id": 5,
|
||||
"content": "Hello everyone! (edited)",
|
||||
"edited_at": "2026-03-14T10:31:00Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Delete Message (Client → Server)
|
||||
|
||||
```json
|
||||
{ "type": "chat_delete", "id": "req-uuid", "payload": { "message_id": 1042 } }
|
||||
```
|
||||
|
||||
### Delete Broadcast (Server → Client)
|
||||
|
||||
```json
|
||||
{ "type": "chat_deleted", "payload": { "message_id": 1042, "channel_id": 5 } }
|
||||
```
|
||||
|
||||
### Reaction Add/Remove (Client → Server)
|
||||
|
||||
```json
|
||||
{ "type": "reaction_add", "payload": { "message_id": 1042, "emoji": "👍" } }
|
||||
{ "type": "reaction_remove", "payload": { "message_id": 1042, "emoji": "👍" } }
|
||||
```
|
||||
|
||||
### Reaction Broadcast (Server → Client)
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "reaction_update",
|
||||
"payload": {
|
||||
"message_id": 1042,
|
||||
"channel_id": 5,
|
||||
"emoji": "👍",
|
||||
"user_id": 1,
|
||||
"action": "add"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Typing Indicators
|
||||
|
||||
### Client → Server (throttle to 1 per 3 seconds)
|
||||
|
||||
```json
|
||||
{ "type": "typing_start", "payload": { "channel_id": 5 } }
|
||||
```
|
||||
|
||||
### Server → Client (broadcast to channel members)
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "typing",
|
||||
"payload": {
|
||||
"channel_id": 5,
|
||||
"user_id": 1,
|
||||
"username": "alex"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Client-side: show indicator for 5 seconds, reset on new typing event from same user.
|
||||
|
||||
---
|
||||
|
||||
## Presence
|
||||
|
||||
### Presence Client → Server
|
||||
|
||||
```json
|
||||
{ "type": "presence_update", "payload": { "status": "online" } }
|
||||
```
|
||||
|
||||
Status values: `online`, `idle`, `dnd`, `offline`
|
||||
|
||||
### Presence Server → Client (broadcast)
|
||||
|
||||
```json
|
||||
{ "type": "presence", "payload": { "user_id": 1, "status": "online" } }
|
||||
```
|
||||
|
||||
Server auto-sets `idle` after 10 minutes of no WebSocket activity.
|
||||
|
||||
---
|
||||
|
||||
## Channel Updates
|
||||
|
||||
### Server → Client (on channel created/edited/deleted/reordered)
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "channel_create",
|
||||
"payload": {
|
||||
"id": 8, "name": "gaming",
|
||||
"type": "text",
|
||||
"category": "Hangout", "position": 3
|
||||
}
|
||||
}
|
||||
{
|
||||
"type": "channel_update",
|
||||
"payload": {
|
||||
"id": 8, "name": "gaming-talk",
|
||||
"position": 4
|
||||
}
|
||||
}
|
||||
{ "type": "channel_delete", "payload": { "id": 8 } }
|
||||
```
|
||||
|
||||
Channel types: `text`, `voice`, `announcement`
|
||||
|
||||
---
|
||||
|
||||
## Voice Signaling
|
||||
|
||||
### Join Voice Channel (Client → Server)
|
||||
|
||||
```json
|
||||
{ "type": "voice_join", "payload": { "channel_id": 10 } }
|
||||
```
|
||||
|
||||
### Server → Client (voice state updates, broadcast to channel)
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "voice_state",
|
||||
"payload": {
|
||||
"channel_id": 10, "user_id": 1,
|
||||
"username": "alex",
|
||||
"muted": false, "deafened": false,
|
||||
"speaking": false,
|
||||
"camera": false, "screenshare": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Voice User Left (Server → Client)
|
||||
|
||||
```json
|
||||
{ "type": "voice_leave", "payload": { "channel_id": 10, "user_id": 1 } }
|
||||
```
|
||||
|
||||
### voice_token (Server → Client)
|
||||
|
||||
Sent after a successful `voice_join`. Contains the LiveKit access token
|
||||
and server URL for the client to connect directly to LiveKit.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "voice_token",
|
||||
"payload": {
|
||||
"channel_id": 10,
|
||||
"token": "eyJhbGciOi...",
|
||||
"url": "ws://localhost:7880"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `channel_id` (number) — the voice channel joined
|
||||
- `token` (string) — LiveKit JWT access token
|
||||
- `url` (string) — LiveKit WebSocket URL (e.g. `ws://localhost:7880`)
|
||||
|
||||
### Voice Config (Server → Client, sent after voice_join acceptance)
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "voice_config",
|
||||
"payload": {
|
||||
"channel_id": 10, "quality": "medium", "bitrate": 64000,
|
||||
"max_users": 50
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Client uses `bitrate` to configure the Opus encoder. Other fields are
|
||||
informational for UI. (`threshold_mode` and `top_speakers` fields have
|
||||
been removed — LiveKit handles audio mixing and speaker selection
|
||||
internally.)
|
||||
|
||||
### Voice Control (Client → Server)
|
||||
|
||||
```json
|
||||
{ "type": "voice_mute", "payload": { "muted": true } }
|
||||
{ "type": "voice_deafen", "payload": { "deafened": true } }
|
||||
```
|
||||
|
||||
### Voice Camera / Screenshare (Client → Server)
|
||||
|
||||
```json
|
||||
{ "type": "voice_camera", "payload": { "enabled": true } }
|
||||
{ "type": "voice_screenshare", "payload": { "enabled": true } }
|
||||
```
|
||||
|
||||
Requires `USE_VIDEO` (bit 11) or `SHARE_SCREEN` (bit 12) permission.
|
||||
Rate limit: 2/sec per user.
|
||||
|
||||
**Note:** Active speaker detection (`voice_speakers`) is no longer a
|
||||
server→client WebSocket message. Speaker detection is handled client-side
|
||||
via LiveKit SDK events (`ParticipantEvent.IsSpeakingChanged`).
|
||||
|
||||
---
|
||||
|
||||
## Member Updates
|
||||
|
||||
### Server → Client
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "member_join",
|
||||
"payload": {
|
||||
"user": {
|
||||
"id": 5, "username": "newuser",
|
||||
"avatar": null, "role": "member"
|
||||
}
|
||||
}
|
||||
}
|
||||
{ "type": "member_leave", "payload": { "user_id": 5 } }
|
||||
{ "type": "member_update", "payload": { "user_id": 5, "role": "moderator" } }
|
||||
{ "type": "member_ban", "payload": { "user_id": 5 } }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Server Restart
|
||||
|
||||
### Restart Server → Client
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "server_restart",
|
||||
"payload": {
|
||||
"reason": "update",
|
||||
"delay_seconds": 5
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `reason` (string): Why the server is restarting. Currently only `"update"`.
|
||||
- `delay_seconds` (integer): How many seconds until the server shuts down.
|
||||
|
||||
Client behavior: Display a banner ("Server restarting..."),
|
||||
then auto-reconnect after the delay expires.
|
||||
|
||||
---
|
||||
|
||||
## Initial State (sent after auth_ok)
|
||||
|
||||
### Ready Server → Client
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "ready",
|
||||
"payload": {
|
||||
"channels": [
|
||||
{
|
||||
"id": 1, "name": "general",
|
||||
"type": "text", "category": "Main",
|
||||
"position": 0, "unread_count": 3,
|
||||
"last_message_id": 1040
|
||||
},
|
||||
{
|
||||
"id": 10, "name": "voice-chat",
|
||||
"type": "voice", "category": "Main",
|
||||
"position": 1
|
||||
}
|
||||
],
|
||||
"members": [
|
||||
{
|
||||
"id": 1, "username": "alex",
|
||||
"avatar": "uuid.png",
|
||||
"role": "admin", "status": "online"
|
||||
},
|
||||
{
|
||||
"id": 2, "username": "jordan",
|
||||
"avatar": null,
|
||||
"role": "member", "status": "idle"
|
||||
}
|
||||
],
|
||||
"voice_states": [
|
||||
{ "channel_id": 10, "user_id": 2, "muted": false, "deafened": false }
|
||||
],
|
||||
"roles": [
|
||||
{
|
||||
"id": 1, "name": "Owner",
|
||||
"color": "#E74C3C",
|
||||
"permissions": 2147483647
|
||||
},
|
||||
{
|
||||
"id": 2, "name": "Admin",
|
||||
"color": "#F39C12",
|
||||
"permissions": 1073741823
|
||||
},
|
||||
{ "id": 3, "name": "Member", "color": null, "permissions": 1049601 }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Message History (REST, not WebSocket)
|
||||
|
||||
Fetched via REST API, not WebSocket, to keep the WS connection lean.
|
||||
|
||||
```text
|
||||
GET /api/channels/{id}/messages?before={msg_id}&limit=50
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Format
|
||||
|
||||
Any request that fails returns:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "error",
|
||||
"id": "original-req-uuid",
|
||||
"payload": {
|
||||
"code": "FORBIDDEN",
|
||||
"message": "No permission to post here"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Error codes: `FORBIDDEN`, `NOT_FOUND`, `RATE_LIMITED`, `INVALID_INPUT`,
|
||||
`SERVER_ERROR`, `CHANNEL_FULL`, `VOICE_ERROR`
|
||||
|
||||
---
|
||||
|
||||
## Rate Limits
|
||||
|
||||
- Chat messages: 10/sec per user
|
||||
- Typing events: 1/3sec per user per channel
|
||||
- Presence updates: 1/10sec per user
|
||||
- Reactions: 5/sec per user
|
||||
- Voice signaling: 20/sec per user
|
||||
- Voice camera/screenshare: 2/sec per user
|
||||
|
||||
Server sends `rate_limited` error with `retry_after` in seconds.
|
||||
Reference in New Issue
Block a user