Kernel Communication Protocol

Message protocol between the browser process and the kernel process. Applies equally to pipe and WebSocket transports. See kernel-architecture for the overall design.

Framing

All messages are newline-delimited JSON (one JSON object per line, terminated by \n). No length prefix is needed — JSON objects cannot contain unescaped newlines, so splitting on \n is unambiguous.

For WebSocket: each WebSocket text frame carries exactly one JSON message (no newline needed, but including one is harmless).

For pipe: the kernel reads from stdin line-by-line; the browser reads from the kernel's stdout line-by-line.

Message Structure

Every message is a JSON object. The kind field identifies the message class:

// Request (browser → kernel)
{"kind": "request", "id": "<uuid>", "method": "<name>", "params": {...}}

// Response (kernel → browser, to a request)
{"kind": "response", "id": "<same uuid>", "result": {...}}
{"kind": "response", "id": "<same uuid>", "error": {"code": -32000, "message": "..."}}

// Event (kernel → browser, unsolicited)
{"kind": "event", "event": "<name>", "data": {...}}

Request IDs are UUIDs generated by the browser. The kernel echoes the same ID in the response. Events have no ID.

Request Methods (Browser → Kernel)

kernel.info

Handshake. Returns kernel capabilities.

// Request
{"kind": "request", "id": "1", "method": "kernel.info", "params": {}}
// Response
{"kind": "response", "id": "1", "result": {"version": 1, "language": "python", "python_version": "3.12"}}

kernel.auth

Optional authentication (WebSocket mode only). Send immediately after the ready event.

{"kind": "request", "id": "2", "method": "kernel.auth", "params": {"token": "<secret>"}}
// Response
{"kind": "response", "id": "2", "result": {"ok": true}}

execute.cell

Execute a cell. Returns immediately with {"status": "started"} — output arrives as events. Only one cell may execute at a time; sends an error if busy.

{"kind": "request", "id": "3", "method": "execute.cell",
 "params": {"cell_id": "stage4", "source": "print(1+1)",
            "inputs": {"var_name": "value"}}}  
// Response (immediate)
{"kind": "response", "id": "3", "result": {"status": "started"}}

execute.interrupt

Interrupt the currently running cell. Returns after raising KeyboardInterrupt into the executor thread.

{"kind": "request", "id": "4", "method": "execute.interrupt", "params": {}}
{"kind": "response", "id": "4", "result": {"ok": true}}

kernel.reset

Clear the namespace. Blocks until any running cell finishes or is interrupted.

{"kind": "request", "id": "5", "method": "kernel.reset", "params": {}}
{"kind": "response", "id": "5", "result": {"ok": true}}

execute.input_reply

Reply to an input_request event. Must be sent while the cell is paused waiting for input.

{"kind": "request", "id": "6", "method": "execute.input_reply",
 "params": {"value": "user typed this"}}

Events (Kernel → Browser)

kernel.ready

Sent once on connection. First message from the kernel.

{"kind": "event", "event": "kernel.ready", "data": {"version": 1}}

stream

A chunk of stdout or stderr from the running cell. May be sent many times per cell execution.

{"kind": "event", "event": "stream",
 "data": {"cell_id": "stage4", "name": "stdout", "text": "Retry 1...\n"}}

display_data

A show() or plot() item produced during cell execution. The browser appends it to the cell's output panel immediately.

{"kind": "event", "event": "display_data",
 "data": {"cell_id": "stage4", "item": {"kind": "plot", "title": "...", "series": [...]}}}

The item field has the same structure as the current show_items list: {"kind": "plot", ...}, {"kind": "html", "content": "..."}, a JSONML list, or a string.

input_request

The cell called input(prompt). The kernel pauses until execute.input_reply is received.

{"kind": "event", "event": "input_request",
 "data": {"cell_id": "stage4", "prompt": "Enter value: "}}

execute.done

Cell execution finished (success or error). The browser marks the cell Done/Error and re-enables the Run button.

// Success
{"kind": "event", "event": "execute.done",
 "data": {"cell_id": "stage4", "status": "ok"}}

// Error
{"kind": "event", "event": "execute.done",
 "data": {"cell_id": "stage4", "status": "error",
          "error": "Traceback (most recent call last):\n  ..."}}

State Machine

The kernel is always in one of three states:

Error Codes

Compatibility With Existing Control Socket

The existing control-api JSON-RPC socket on the browser process is unchanged in structure. The only behavioural change is that sheet.cell.run and sheet.run_all become non-blocking: they return {"status": "started"} immediately and fire-and-forget to the kernel. A new method sheet.cell.await blocks (with timeout) until execute.done is received for a given cell.

WebSocket Authentication

For WebSocket mode, a shared secret token is passed as a command-line argument to the kernel process and configured in the browser. The browser sends kernel.auth immediately after the ready event. Unauthenticated requests are rejected with -32002. Token rotation is not in scope for v1.

Example: Full Cell Execution (Pipe Mode)

Browser                        Kernel
  |                              |
  |<-- {event: kernel.ready} ----|
  |                              |
  |--> {method: execute.cell} -->|
  |<-- {result: started} --------|
  |                              |
  |<-- {event: stream, "Retry 1"}|
  |<-- {event: stream, "Retry 2"}|
  |<-- {event: display_data, df}--|
  |<-- {event: execute.done, ok}-|
  |                              |
version 1  ·  created 2026-05-31