# WebSocket Protocol Real-time bidirectional event bus. Single connection per client. ### Connection ``` ws://{host}{BASE_PATH}/ws?token={access_token} ``` **Lifecycle:** - Server sends `ping` every 54 seconds - Client must respond with `pong` within 60 seconds or disconnection - Max message size: 4 KB - Write deadline: 10 seconds ### Event Envelope All messages are JSON: ```json { "type": "event.type", "payload": { ... } } ``` ### Room Subscription Clients subscribe to rooms for scoped event delivery: ```json { "type": "subscribe", "payload": { "room": "channel:uuid" } } { "type": "unsubscribe", "payload": { "room": "channel:uuid" } } ``` ### Event Routing Table | Prefix | Direction | Description | |--------|-----------|-------------| | `channel.created` | → client | New channel created | | `channel.updated` | → client | Channel metadata changed | | `channel.deleted` | → client | Channel removed | | `message.created` | → client | New message in subscribed channel | | `message.updated` | → client | Message content changed | | `message.deleted` | → client | Message removed | | `cursor.updated` | → client | Branch cursor moved | | `typing.start` | ↔ both | Participant started typing (payload includes `participant_id`, `participant_type`) | | `typing.stop` | ↔ both | Typing ended | | `participant.joined` | → client | Participant added to channel | | `participant.left` | → client | Participant removed from channel | | `participant.updated` | → client | Participant role changed | | `presence.update` | → client | Participant online/idle/offline status change | | `model.changed` | → client | Channel model roster changed | | `persona.updated` | → client | Persona metadata changed | | `kb.updated` | → client | Knowledge base changed | | `kb.document.status` | → client | Document processing status update | | `note.created` | → client | Note created | | `note.updated` | → client | Note content changed | | `note.deleted` | → client | Note removed | | `notification.new` | → client | New notification | | `notification.read` | → client | Notification marked read | | `memory.extracted` | → client | New memory extracted | | `memory.status` | → client | Memory approved/rejected | | `role.fallback` | → client | Role fallback triggered | | `user.mentioned` | → client | User was @mentioned in a channel | | `workflow.assigned` | → client | Workflow stage assigned to team/user | | `workflow.claimed` | → client | Workflow assignment claimed | | `workflow.advanced` | → client | Workflow stage advanced | | `workflow.completed` | → client | Workflow instance completed | | `workspace.updated` | → client | Workspace state changed | | `project.updated` | → client | Project metadata changed | | `extension.updated` | → client | Extension config changed | | `settings.updated` | → client | Global settings changed | | `user.updated` | → client | User profile changed | | `file.created` | → client | File uploaded or generated (§17b.4) | | `file.deleted` | → client | File removed | Direction: `→ client` = server-to-client only, `← client` = client-to-server only, `↔ both` = bidirectional. --- ## 19. Appendix: Enums ### Channel Types `direct` (single-user), `group` (multi-user/multi-model), `workflow` (staged) ### Participant Types `user`, `persona`, `session` ### Participant Roles `owner`, `member`, `observer` ### Presence Statuses `online`, `idle`, `offline` ### Message Roles `user`, `assistant`, `system`, `tool` ### Scopes `personal`, `team`, `global` ### Visibility (model catalog) `enabled`, `disabled`, `team` ### User Roles `admin`, `user` ### Team Member Roles `admin`, `member` ### Provider Types `openai`, `anthropic`, `openrouter`, `venice` (extensible via registry) ### Model Types `chat`, `embedding`, `image` ### Memory Scopes `user`, `persona` ### Memory Statuses `pending`, `approved`, `rejected` ### Workspace Owner Types `user`, `project` ### Workspace Statuses `active`, `archived` ### Index Statuses `pending`, `indexing`, `ready`, `error` ### KB Document Statuses `pending`, `chunking`, `embedding`, `ready`, `error` ### Provider Health Statuses `healthy`, `degraded`, `down` ### Routing Policy Types `provider_prefer`, `team_route`, `cost_limit`, `model_alias` ### Extension Tiers `browser` (implemented), `starlark` (future), `sidecar` (future) ### Grant Types `team_only`, `global`, `groups` ### Resource Grant Scopes `persona`, `kb` ### Notification Types `role.fallback`, `kb.ready`, `kb.error`, `grant.changed`, `memory.extracted`, `user.mentioned`, `workflow.assigned`, `workflow.claimed`, `task.completed`, `task.failed`, `task.budget_exceeded` See [enums.md](enums.md#notification-types) for full descriptions. ### Git Auth Types `https_pat`, `https_basic`, `ssh_key` ### Policies | Key | Default | Description | |-----|---------|-------------| | `allow_registration` | `"true"` | Allow new user self-registration | | `allow_user_byok` | `"true"` | Allow users to add personal API keys | | `allow_user_personas` | `"true"` | Allow users to create personal Personas | | `allow_team_providers` | `"true"` | Allow team admins to configure team providers | | `kb_direct_access` | `"true"` | Show KB picker in channel (false = strict enterprise mode) | | `require_email` | `"false"` | Require email on registration | | `default_system_prompt` | `""` | Injected into all conversations (admin override) | --- ## Page Routes (Non-API) Server-rendered Go template surfaces. Not REST endpoints — these return HTML pages. | Route | Surface | Description | |-------|---------|-------------| | `/login` | Login | Standalone login/register page | | `/` | Chat | Main chat interface | | `/chat/:chatID` | Chat | Chat with specific channel loaded | | `/editor` | Editor | Workspace file editor | | `/editor/:wsId` | Editor | Editor with specific workspace | | `/notes` | Notes | Notes interface | | `/notes/:noteId` | Notes | Notes with specific note loaded | | `/admin` | Admin | Platform administration | | `/admin/:section` | Admin | Admin with specific section | | `/settings` | Settings | User settings | | `/settings/:section` | Settings | Settings with specific section | All page routes (except `/login`) require authentication via `AuthOrRedirect` middleware (reads `sb_token` cookie). Admin routes additionally require `RequireAdminPage()`. **Dynamic surface routing** for admin-installed extension surfaces is not yet implemented. The current five surfaces are hardcoded in Go route registration. Future: extension manifests declare surface routes, the page engine dynamically registers them with appropriate data loaders. See EXTENSIONS.md for the design direction.