Architecture

CAR-TER is a three-actor system. Understanding who owns what makes the rest of the protocol obvious.

The three actors

1. The device (CAR-TER app)

An iPhone or iPad running CAR-TER. It loads a layout (a JSON document) and renders it as a live UI. It is a thin client: it knows how to draw controls, extract values from messages, and emit actions — but it contains no business logic. Everything dynamic comes from the network.

The device joins a relay channel with a role (commonly viewer for a dashboard, controller for a remote, or editor during live authoring).

2. The relay (MeshSocket server)

A small, generic WebSocket message router. It does not understand layouts, controls, or telemetry — it only moves frames between clients on the same channel. Two routing primitives matter:

  • Broadcast — one client sends broadcast_request; the relay re-emits it as a broadcast frame to every other member of the channel (subject to scope). This is fan-out: one server, many viewers.
  • Routed RPC — one client sends route_msg targeting another by id/name; the relay forwards it and returns the target's reply. This is point-to-point.

You have two ways to run a relay; see Connection & Pairing:

  • Self-hosted — run the open-source MeshSocket relay on your LAN (ws://…). Zero auth, zero cost, fits hobby/home use.
  • Connect+ gateway — the hosted, authenticated, slot-metered service (wss://…) that wraps the same relay with a token gateway.

3. Your server

The program you write. It joins the same channel and supplies everything the layout needs: it broadcasts the data controls display and handles the actions controls emit. It can be a 30-line script or a full service. Language is up to you — see Building a Server in Python for the supported client library, or Building a Server in Any Language (Raw Protocol) to implement the frames directly.

How a session comes together

sequenceDiagram participant S as Your server participant R as MeshSocket relay participant P as CAR-TER app (phone) S->>R: identify (role hub, can_broadcast, can_route, channel home) P->>R: identify (role viewer, channel home) Note over S,P: Both are now on channel "home" S->>R: broadcast_request {msg_type metrics, cpu 73} R-->>P: broadcast → gauge updates P->>R: button tap → broadcast_request {msg_type command} R-->>S: broadcast → server reacts

Nothing about the layout is known to the relay — the relay just sees frames on a channel. The meaning lives in the conventions your layout and server agree on (event names, msg_type discriminators, valuePaths). Those conventions are the subject of Data Flow — sync & actions.

Channels and roles

  • Channel — a namespace. Members of a channel see each other's broadcasts and can route to each other. Use one channel per "scene" (e.g. home, studio, rack-3). On the Connect+ gateway, channels are additionally namespaced per account, so your home never collides with someone else's.
  • Role — a label (viewer, controller, hub, editor, node, …) that also drives capability defaults at the relay (who may broadcast, route, or monitor the member list). You can set capabilities explicitly; see The MeshSocket Protocol.

Where layouts come from

The device can get a layout four ways — all produce the same runtime behavior:

  1. Bundled / on-disk JSON loaded in the app.
  2. The web editor (editor/) — author visually, push to a paired device.
  3. The MCP server (carter-mcp) — have an LLM build and push layouts live; see Live Editing & the MCP Authoring Loop.
  4. Hand-written JSON following the Layout Schema.

As an integrator you mostly care about the connection block of the layout and the events its controls use — the rest is presentation.

Next: The MeshSocket Protocol.