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:

  1. Connect & identify. Open the WebSocket, send an identify with your channel, a sending-capable role, and can_broadcast: true (plus can_route: true if you'll send routed requests). Wait for welcome.
  2. Push state (server → device). Broadcast broadcast_request frames tagged with a msg_type; controls with a matching filter pick them up via their valuePath. See Recipe: Telemetry Dashboard.
  3. Handle inputs (device → server). Listen for broadcast frames and demux on msg_type to react to button/toggle/slider actions. See Recipe: Interactive Controls.
  4. Answer requests. For route_msg requests (e.g. content for a tapped graph node), register a handler keyed on the request type and return a value. See Recipe: Graph + Request/Reply Content.
  5. Shape the UI (optional). Feed dynamicTabs/dynamic groups and respond to pollGroups. 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_type namespace. Tag every broadcast with a msg_type and have controls filter on it. Without it, every control sees every frame. Treat your set of msg_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 as viewer (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/request wrap 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.