Self-hosted remotes
Connect a Letta Code remote runtime to your own WebSocket endpoint.
Self-hosted remotes let you run the Remote client API without the hosted environment router. Your service accepts a WebSocket from a Letta Code runtime, sends commands over that socket, and renders the events it receives back.
For most products, use the Letta Agent SDK with backend: "cloud". The SDK can manage Constellation sandboxes and remote connections without requiring you to build a router. Use self-hosted remotes only when you need to own the transport yourself: embedded desktop routing, LAN-only control, private infrastructure, or SDK/runtime development.
Direct mode
Section titled “Direct mode”In direct mode, your app is the WebSocket server and the Letta Code runtime connects to it.
Letta Code runtime ── WebSocket ──> your appimport { WebSocketServer, WebSocket } from "ws";
const token = process.env.REMOTE_TOKEN ?? "dev-token";const server = new WebSocketServer({ host: "127.0.0.1", port: 8284 });
let runtime: WebSocket | null = null;
server.on("connection", (socket, request) => { if (request.headers.authorization !== `Bearer ${token}`) { socket.close(1008, "unauthorized"); return; }
runtime = socket;
socket.on("message", (raw) => { const event = JSON.parse(raw.toString()); handleRuntimeEvent(event); });
socket.on("close", () => { if (runtime === socket) runtime = null; });});
function send(command: unknown) { if (!runtime || runtime.readyState !== WebSocket.OPEN) { throw new Error("Remote runtime is not connected"); } runtime.send(JSON.stringify(command));}Start the runtime with your endpoint:
LETTA_BASE_URL=http://localhost:8284 \IGNORE_SELF_HOSTED_LISTENER_ERROR=1 \letta remote --env-name local-dev --backend localThen send the same v2 frames used by the hosted Remote client API:
send({ type: "input", runtime: { agent_id: "agent-...", conversation_id: "conv-...", }, payload: { kind: "create_message", messages: [{ role: "user", content: "Inspect this repo." }], },});For shared command semantics, use the App Server protocol lifecycle. For hosted transport details, use the Remote client API reference.
Heartbeats and reconnects
Section titled “Heartbeats and reconnects”For a single direct socket, keep transport behavior simple:
- send
pingperiodically and expectpong - mark the runtime offline when the socket closes
- send
syncafter reconnecting
You can skip hosted seq/ack handling in direct mode unless you add replay or multiple subscribers.
When to add a broker
Section titled “When to add a broker”Add a broker only if direct mode is not enough.
Letta Code runtime <── runtime WS ──> broker <── client WS ──> custom clientA broker is useful when you need multiple clients, multiple runtimes, runtime discovery, replay buffers, or internet routing. If you do not need those, direct mode is easier to operate and reason about.
Security
Section titled “Security”Bind local development servers to 127.0.0.1, require a bearer token, and avoid exposing file, terminal, memory, or secret commands to untrusted clients. For LAN or internet deployments, use wss://, authenticate both sides, and validate browser origins if browsers can connect.
Skip to content