Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

Multi-Process & IPC

Kael supports an Electron-style multi-process architecture: the UI runs in the main process while heavy or untrusted work runs in supervised child processes that communicate over typed IPC. Transport is platform-native — Unix domain sockets on macOS/Linux, named pipes on Windows — and the framework handles framing, request/response correlation, progress streaming, and crash reporting for you.

Process model

Every process has a class describing its role:

#![allow(unused)]
fn main() {
use kael::ProcessClass;

ProcessClass::Ui;        // the main UI process
ProcessClass::Worker;    // background compute
ProcessClass::Media;     // media decode/playback
ProcessClass::Extension; // sandboxed plugins (see Plugins & Extensions)
}

A child process is described by a ProcessInfo, built fluently:

#![allow(unused)]
fn main() {
use kael::{ProcessId, ProcessInfo};

let info = ProcessInfo::worker(ProcessId(0), "thumbnailer")
    .executable("/path/to/worker-binary")
    .arg("--quiet")
    .env("RUST_LOG", "warn");
}

Constructors exist for each role: ProcessInfo::worker, ProcessInfo::media, and ProcessInfo::extension.

Spawning a worker (host side)

WorkerHost owns the socket directory and supervises spawned children. request sends a typed payload and blocks for the response; fire_and_forget sends without waiting; health_check pings the child.

#![allow(unused)]
fn main() {
use kael::{ProcessClass, ProcessId, ProcessInfo, WorkerHost};

let mut host = WorkerHost::with_temp_dir();
let info = ProcessInfo::worker(ProcessId(0), "thumbnailer")
    .executable(worker_binary_path);

let worker = host.spawn_worker(ProcessClass::Worker, info)?;

worker.health_check()?; // round-trip ping

let response: serde_json::Value = worker.request(serde_json::json!({
    "op": "echo",
    "message": "hello from host",
}))?;
assert_eq!(response["message"], "hello from host");
}

The worker child

The child binary connects back to the host with WorkerClient::connect_from_env (it reads the GPUI_WORKER_SOCKET / GPUI_WORKER_PIPE environment variable the host sets) and serves requests with run. The handler receives a WorkerRequest and a progress callback for streaming intermediate updates, and returns a WorkerResponse or WorkerError.

use anyhow::Result;
use kael::{WorkerClient, WorkerProgress, WorkerRequest, WorkerResponse};

fn main() -> Result<()> {
    let client = WorkerClient::connect_from_env()?;
    client.run(|request, progress| match request {
        WorkerRequest::Ping => Ok(WorkerResponse::Pong),
        WorkerRequest::Execute { payload } => {
            progress(WorkerProgress::Update(serde_json::json!({ "step": 1 })));
            // ... do work ...
            Ok(WorkerResponse::Result(payload))
        }
    })
}

The message types:

TypeVariants
WorkerRequestPing, Execute { payload: serde_json::Value }
WorkerResponsePong, Result(serde_json::Value)
WorkerProgressUpdate(serde_json::Value)
WorkerErrorExecution(String), Cancelled

Supervision & crash handling

Register an event callback to observe lifecycle events. A child that crashes is reported as a SupervisorEvent::Exited rather than taking down the host:

#![allow(unused)]
fn main() {
use kael::SupervisorEvent;

host.on_event(|event| match event {
    SupervisorEvent::Exited { id, .. } => eprintln!("worker {id:?} exited"),
    _ => {}
});
}

Each WorkerHandle exposes id() to correlate it with supervisor events.

Extension processes

Extension children use the same transport but a richer RPC envelope (handshake, contribution discovery, command dispatch). They are managed by ExtensionHostRuntime rather than WorkerHost — see Plugins & Extensions.