Backend & architecture
Understand UI, Host, and Backend boundaries, then connect a native process using Wire Protocol v1.
Verified September 11, 2026
On this page
When a Backend is useful#
A UI-only Curio already has access to registered Host services through the Web SDK. Add a Backend for your own native computation, language runtime, long-running work, or a service shared with other Curios.
Curio UI → Web SDK → Host service bus → host.* built-in service
→ @self (this Curio’s Backend)
→ named service (provider Backend)
Host owns window and process lifecycle, connection identity, routing, cancellation, and contract validation. The Web SDK transports calls and events. Your Backend implements your methods and cleans up its work when canceled or shut down. A Backend-only Curio can provide services without a UI.
Configure the process#
Merge this fragment into a manifest for dev.example.text-tools. The development command assumes you have implemented backend/index.mjs; the production entry must be built separately:
{
"backend": {
"entry": "backend/text-tools",
"lifecycle": "on-demand",
"transport": "unix",
"startWithUi": true
},
"development": {
"backend": {
"mode": "managed",
"command": ["node", "backend/index.mjs"]
}
}
}
Preserve development.ui if your project already has a UI server. Managed mode launches the command for you. External mode lets you attach your own process using .trove/dev-session.json and the session-aware connection logic you implement.
Node is only an example development runtime. Host does not bundle Node or Python. A distributable Backend currently requires a self-contained native executable for the target platform. The CLI only scaffolds Web and static templates; there is no supported
--template nodeor--template rust.
Connect using Wire Protocol v1#
The Backend connects to the Unix Domain Socket in TROVE_SOCKET_PATH and authenticates with TROVE_SESSION_TOKEN. Treat that token and the development session file as credentials; do not commit or log them.
Each message is UTF-8 JSON preceded by a 4-byte unsigned big-endian payload length. It is not newline-delimited JSON, HTTP, or WebSocket. A receiver must buffer fragmented frames and parse multiple frames in a single read. The current frame limit is 8 MiB; follow the limits negotiated by Host.
- Connect to the socket and send
hellowith the session token and supported protocol range. - Receive
welcome; verify the selected protocol and inspectsession,features, andlimits. - Send
readyonly when your service can handle requests. - Process requests, responses, subscriptions, and cancellation by ID.
- On
shutdown, stop work, sendgoodbye, and close the socket.
{
"v": 1,
"type": "hello",
"client": "backend",
"sessionToken": "REPLACE_WITH_HOST_SESSION_TOKEN",
"protocol": { "min": 1, "max": 1 },
"features": ["call", "cancel"],
"implementation": { "name": "my-backend", "version": "0.1.0", "language": "rust" }
}
This illustrates a message payload before framing, not a reusable token. The Wire message JSON Schema documents individual message shapes. Schema validity does not establish correct handshake ordering or runtime behavior.
Implement private requests#
The UI can call trove.self.call("uppercase", { text: "hello" }) once your Backend implements it. Host routes it as @self. A successful response repeats the incoming request ID:
{
"v": 1,
"type": "response",
"id": "INCOMING_REQUEST_ID",
"result": { "text": "HELLO" }
}
Return exactly one of result or error. Error payloads contain a stable code and human-readable message, with optional data, retryable, and traceId. Requests can run concurrently and complete out of order; correlate by ID rather than arrival order. On cancel, cancel the matching operation where possible.
For reverse calls to Host, preserve the Host-provided request context when continuing the same call chain. Do not fabricate caller identity. A named public service additionally needs a Contract and manifest declaration.
Verify the protocol and lifecycle#
trove protocol test --command node -- backend/index.mjs
Or run trove protocol test to use your project’s configured Backend command. The test creates an isolated temporary session and checks the handshake, fragmented frames, concurrent responses, structured errors, and clean shutdown. The probe sends eight concurrent requests to @self.__trove_conformance_unknown_method__; return one structured error per request with its matching ID. It also requires a successful process exit after shutdown, all within 20 seconds.
The protocol probe is an interoperability check, not a test of your business logic. Also verify a real UI call, cancellation during work, closing and reopening an on-demand Curio, and an installed package with no development runtime dependency.