WebSocket events
Every client holds one WebSocket at GET /api/ws (wss://api.novaterra.world/api/ws in production), authenticated by the same session cookie as the REST API. This is how the desktop tells you what you need to know at the moment you need to know it: widgets subscribe by topic and the server pushes typed events.
Both directions are discriminated unions on type, so a client can switch exhaustively:
import { ServerEvent } from '@novaterra/contracts';
socket.addEventListener('message', (raw) => { const event = ServerEvent.parse(JSON.parse(raw.data)); if (event.type === 'signal.created') showSignal(event.signal);});Server → client
Pushed by the API. Anything that changes while you are looking at the world arrives here.
type | Payload |
|---|---|
hello | beingId: IdserverTime: ISODate |
pong | serverTime: ISODate |
signal.created | signal: Signal |
signal.updated | signal: Signal |
message.delta | threadId: IdmessageId: Iddelta: string |
message.created | message: Message |
message.tool | threadId: IdmessageId: IdtoolCall: Json |
task.updated | task: Task |
project.updated | project: Project |
trace | event: TraceEvent |
memory.created | memory: Memory |
widget.suggest | widgetType: stringreason: stringconfig?: Json |
presence.update | beingId: Idstatus: 'online' | 'away' | 'offline'doing?: string |
wallet.updated | wallet: Wallet |
twin.activity | kind: stringsummary: stringat: ISODate |
square.post | post: Message |
room.updated | threadId: IdmeetingId: Id | nullfloorBeingId: Id | nullpresentBeingIds: Id[]session: Json | null |
ambient | mood: stringhint?: string |
Client → server
Sent by the browser to declare interest and presence.
type | Payload |
|---|---|
subscribe | topics: string[] |
presence | status: 'online' | 'away'doing?: string |
ping | — |