Kernel protocol
Each tab’s Rust-managed Python subprocess communicates over stdin and stdout. Every message is one JSON object followed by a newline.
Transport
Section titled “Transport”| Property | Value |
|---|---|
| Encoding | UTF-8 JSON text |
| Framing | One JSON object per line |
| Request direction | Rust writes to child stdin |
| Response direction | Python writes to child stdout |
| Concurrency | One I/O loop per tab, one request processed at a time |
| Queue | Tokio MPSC channel with capacity 64 per tab |
| Correlation | Request and response share id |
The process’s original stdin is reserved for protocol messages. During cell execution, user code receives an empty text stream, so interactive input is not supported and cannot consume a later protocol request.
Request schema
Section titled “Request schema”{ "id": "cell-uuid", "type": "execute", "code": "x = 6\nx * 7"}Rust models code as optional and omits it when absent. The server defaults missing code to an empty string.
Request types
Section titled “Request types”type |
code |
Server behavior | Used by released Rust path? |
|---|---|---|---|
execute |
Python source | Parse and run source in the persistent namespace. | Yes |
restart |
Ignored | Replace the globals dictionary with builtins only. | No; Rust restarts by killing and spawning the process. |
ping |
Ignored | Return pong in stdout. |
Used for the five-second kernel startup handshake. |
| Any other string | Ignored | Return an error naming the unknown request type. | No |
Response schema
Section titled “Response schema”{ "id": "cell-uuid", "type": "result", "stdout": "42\n", "stderr": "", "error": null, "output_type": "text"}| Field | Type | Meaning |
|---|---|---|
id |
String | Echoes the request ID. For malformed JSON it is an empty string. |
type |
String | Always result in the current server. |
stdout |
String | Captured standard output plus printed final-expression representation. |
stderr |
String | Captured standard error. |
error |
String or null | Traceback, interrupt string, protocol error, or null. |
output_type |
String | Always text in the current server. |
Execution semantics
Section titled “Execution semantics”For an execute request:
- Parse the source with
ast.parse(..., mode="exec"). - Remove a final bare expression from the module body, when present.
- Execute preceding statements in the process-global dictionary.
- Evaluate the final expression separately.
- Store a non-
Noneresult as_and print itsreprinto captured stdout. - Catch
KeyboardInterruptand otherBaseExceptionvalues. - Restore the process’s original stdin, stdout, and stderr objects.
- Serialize one result line and flush it.
The globals dictionary begins as:
{"__builtins__": __builtins__, "__name__": "__main__"}Error responses
Section titled “Error responses”Malformed request JSON
Section titled “Malformed request JSON”{ "id": "", "type": "result", "stdout": "", "stderr": "", "error": "Invalid JSON: <decoder details>", "output_type": "text"}Unknown type
Section titled “Unknown type”{ "id": "request-id", "type": "result", "stdout": "", "stderr": "", "error": "Unknown request type: example", "output_type": "text"}Python exception
Section titled “Python exception”error contains traceback.format_exc() output. Stdout and stderr emitted before the exception remain available in their fields.
Interrupt
Section titled “Interrupt”The Python server reports:
KeyboardInterrupt: Execution interruptedThe Rust backend also schedules SIGKILL two seconds after sending SIGINT, so the process may terminate after this response.
Rust-side transport failures
Section titled “Rust-side transport failures”The command can reject with messages including:
No kernel for tab NKernel is not running. Restart to continue.Kernel has no request channelFailed to queue execution requestResponse channel closedstdin write failed: ...stdin flush failed: ...Invalid response JSON: ...Kernel process terminated unexpectedlystdout read error: ...
These errors are converted into a frontend error output when they occur during cell execution.
