Elyra
Elyra The coding agent e The native code editor Elyra Grove Native local development environment Askr The real server for Laravel & PHP Elyra Framework Rust + Svelte 5 framework for desktop apps Elyra Conductor Local project conductor Elyra SQL Server MySQL-compatible SQL server in Rust Elyra SQL Client Native desktop SQL workbench Elyra SQL Anywhere Replication-ready SQL engine
Release notes
Changelog
Elyra

Wire format

The low-level IPC contract. For the app-facing API see frontend runtime, commands, and events. Index: docs/README.md.

The bridge is MessagePack over the elyra://localhost custom protocol, called from the frontend with fetch. Same origin for both the app and IPC, so there is no CORS, no preflight, and no JSON anywhere in the hot path.

Endpoints

Path Purpose
elyra://localhost/ and /index.html the frontend (embedded assets, or the fallback page)
elyra://localhost/__cmd/<name> command invocation (POST)
elyra://localhost/__events event stream long-poll (GET)

Request — arguments (compact)

The request body is a compact MessagePack array of the call arguments:

invoke("add", 2, 3)  ->  encode([2, 3])  ->  msgpack array [2, 3]

Rust decodes it into the command's argument tuple, e.g. (i64, i64). Compact (positional) encoding is used here because the tuple shape is fixed by the function signature — field names would be dead weight.

Zero-argument commands ignore the body entirely, which avoids the ()nil vs [] → empty-array mismatch between serde and the JS encoder.

Response — results (named)

Results are encoded with rmp_serde::to_vec_named:

SystemInfo { os, arch, commands }  ->  msgpack map { "os": ..., "arch": ..., "commands": [...] }

Named encoding means structs decode to plain JS objects and survive field reordering between Rust and TypeScript versions — the property that makes the contract robust as the app evolves. Scalars (String, i64, …) encode identically either way.

Events (M1) — Rust → frontend push

The frontend keeps one request open against /__events. The shell holds it until the next event batch is ready, then responds; the frontend immediately reconnects. No evaluate_script, no base64 — a continuous binary stream over the same custom protocol. @elyra/runtime's channel() multiplexes all named channels over this single connection.

The response body is a MessagePack array of [channel, value] pairs:

[ ["tick", 1], ["tick", 2], ["cursor", { "x": 10, "y": 20 }] ]

Each value is to_vec_named-encoded and appended to the batch verbatim (the batch is framed with low-level rmp), so there is no double-encode and no bin wrapper — one decode on the JS side.

Batching

Emits accumulate in a queue and flush together, so N state changes cost one IPC round, not N. With the default zero window the natural response→reconnect gap coalesces bursts; App::batch_window(..) adds an explicit coalescing delay for frame-level batching of sustained streams. After ~20s idle the poll returns an empty batch as a keep-alive.

Status

The x-elyra-status response header is ok or error. On error the body is a UTF-8 message (HTTP 500). The frontend runtime turns that into a CommandError.

Decision summary

  • Arguments: compact to_vec (positional array) — shape fixed by signature.
  • Results: to_vec_named (maps) — resilient to field reordering.
  • JS side uses @msgpack/msgpack, which matches rmp-serde byte-for-byte.

Async, never blocking

The custom-protocol handler is wry's asynchronous variant: every request is spawned onto the tokio runtime and responded to from there, so the UI thread never blocks on a command or a long-poll. (M0's block_on is gone.)