Anatomy of a CAR-TER Server
A "server" here is any program that joins a MeshSocket channel and drives a layout. This page is the language-agnostic checklist; then pick Building a Server in Python (with the client library) or Building a Server in Any Language (Raw Protocol) (any language, raw frames).
The five responsibilities
Every integration does some subset of these:
- Connect & identify. Open the WebSocket, send an identify with your
channel, a sending-capablerole, andcan_broadcast: true(pluscan_route: trueif you'll send routed requests). Wait forwelcome. - Push state (server → device). Broadcast
broadcast_requestframes tagged with amsg_type; controls with a matchingfilterpick them up via theirvaluePath. See Recipe: Telemetry Dashboard. - Handle inputs (device → server). Listen for
broadcastframes and demux onmsg_typeto react to button/toggle/slider actions. See Recipe: Interactive Controls. - Answer requests. For
route_msgrequests (e.g. content for a tapped graph node), register a handler keyed on the requesttypeand return a value. See Recipe: Graph + Request/Reply Content. - Shape the UI (optional). Feed
dynamicTabs/dynamic groups and respond topollGroups. See Recipe: Server-Driven (Dynamic) UI.
The standard loop
flowchart TD
A([connect]) --> B["identify(channel, role, can_broadcast=true)"]
B --> C[wait for welcome]
C --> D[register handlers]
D --> H1["on 'broadcast' → demux payload.msg_type → react to actions"]
D --> H2["on routed type → handle request, return reply (optional)"]
D --> P[start a producer]
P --> L["every N seconds (or on change):<br/>broadcast_request {msg_type, …data}"]
L -->|repeat| L
That's it. The relay handles transport, fan-out, and routing; your code is just "produce data, consume actions."
Design conventions that save pain
- Pick a
msg_typenamespace. Tag every broadcast with amsg_typeand have controls filter on it. Without it, every control sees every frame. Treat your set ofmsg_types +valuePaths as your API and write it down. - Echo authoritative state. When you handle an action that changes something, broadcast the new state back rather than letting the control assume success. The UI then reflects reality (and every viewer stays in sync).
- Send native types. A gauge wants the number
73, not"73". JSON numbers and bools flow through cleanly; don't stringify. (The{{value}}rules in Data Flow — sync & actions preserve native types for exactly this reason.) - Be a
viewer-friendly producer. Dashboards should connect asviewer(listen-only); your server is the one that broadcasts. Keep the asymmetry clear. - Coalesce high-rate streams. The device already coalesces UI updates to ~60fps, but don't blast thousands of frames/sec — a few Hz per metric is plenty and keeps the relay happy. The Connect+ gateway also rate-limits.
Choosing how to build it
- Building a Server in Python — there is a maintained MeshSocket Python client. Fewest lines;
emit/on/requestwrap the frames for you. Best if Python is an option. - Building a Server in Any Language (Raw Protocol) — the frame shapes are simple JSON; any language with a WebSocket client (Node, Go, Rust, C#, browser JS, …) can implement them in a few dozen lines. Use this for non-Python stacks.
For the verbs and frame shapes either way, keep Message Reference handy.