Repository navigation
Conversation
| // ParserService starts the Runme parser service if true otherwise it doesn't start the parser service. | ||
| ParserService bool `json:"parserService" yaml:"parserService"` | ||
|
|
||
| // Jupyter configures Runme-managed kernels. Runme always exposes the direct |
There was a problem hiding this comment.
Should we just allow the client to send the start command?
## Summary
- migrate Runme Web from the Jupyter Server registry/proxy API to
runner-owned `/v1/jupyter/kernels` routes
- attach the current OIDC ID token to kernel lifecycle requests and
authenticated channels WebSockets
- let the client send a Jupyter kernelspec-style `argv` when starting a
kernel, including a virtual-environment Python path
- validate kernel commands in the start UI as JSON argv with exactly one
`{connection_file}` placeholder
- list and select kernels by `(runner name, kernel ID)`, while
preserving optional browser-local display names
- add a **Kernels** column to Notebook Runner Status and a runner-scoped
management tab with refresh, start, restart, and stop controls
- document kernel identity, client-supplied commands, `RunnerUserRole`
authorization, lifecycle behavior, direct routes, and current
JSON-channels limitations
## Why
The previous web integration assumed a separately managed Jupyter Server
and proxy routes. Runme now owns kernel processes and bridges browser
WebSockets directly to kernel ZeroMQ channels, so the web app uses the
direct lifecycle API and authenticates those requests itself.
Kernel commands belong to the client rather than runner configuration. A
user can select a virtual environment by changing the executable in the
launch argv, for example `/workspace/.venv/bin/python`, without
restarting or reconfiguring the Runme server.
## Server dependency
Depends on draft server PR
[runmedev/runme#1280](runmedev/runme#1280),
which replaces the Jupyter Server proxy with the direct kernel API and
authorizes client-supplied launches with `RunnerUserRole`.
## User impact
- Jupyter cells can select live kernels from configured runners without
a Jupyter Server registry.
- Users can launch `ipykernel` from a specific Python virtual
environment on the runner host.
- Multiple kernels are distinguished by runner and kernel ID; optional
display names improve readability.
- Users can manage kernels from Notebook Runner Status and share one
kernel across cells when state should persist.
## Validation
- `runme run build test`
- focused Vitest coverage for the direct Jupyter manager and
kernel-management UI
- `pnpm -C app run check:documentation`
- live authenticated browser verification from the original
direct-kernel rollout: list, start, restart, execute against, and stop
runner-owned kernels
---------
Signed-off-by: Jeremy lewi <jeremy@lewi.us>
d9e1aa3 to
6f66c8f
Compare
Signed-off-by: Jeremy Lewi <jeremy@lewi.us>
Signed-off-by: Jeremy lewi <jeremy@lewi.us>
Signed-off-by: Jeremy lewi <jeremy@lewi.us>
Signed-off-by: Jeremy lewi <jeremy@lewi.us>
Signed-off-by: Jeremy lewi <jeremy@lewi.us>
Signed-off-by: Jeremy lewi <jeremy@lewi.us>
c67dba5 to
7c28608
Compare
Signed-off-by: Jeremy lewi <jeremy@lewi.us>
7c28608 to
5920278
Compare
|
The Jupyter kernel API runs commands chosen by the client and is always on. It needs a switch, off by default and dependent on runnerService, so operators choose whether to allow it. Mind adding a |
Signed-off-by: Jeremy lewi <jeremy@lewi.us>
|
@sourishkrout Done in 194b88c. I added |
Summary
/v1/jupyter/kernelslifecycle and channels routesargvused to start a kernel, including a Python virtual-environment executable{connection_file}placeholder, and launch without a shellRunnerUserRolev1.kernel.websocket.jupyter.orgbinary subprotocolWhy
The old model depended on a separately managed Jupyter Server, token discovery, and an HTTP/WebSocket proxy. Runme now owns the kernel process and bridges the browser channels WebSocket directly to the kernel's loopback ZeroMQ sockets, so the server registry and proxy are unnecessary.
Kernel commands are selected by the client instead of being fixed in Runme server configuration. This lets a user start
ipykernelfrom a particular Python or virtual environment without reconfiguring or restarting the runner.Design document
The architecture, protocol decisions, threat model, rollout constraints, and implementation goal are documented in the direct WebSocket-to-kernel Runme notebook.
Related work and issues
API
The legacy
/v1/jupyter/servers/...routes are removed.The start request contains a display name and the complete argument vector:
{ "name": "project-venv", "argv": [ "/workspace/.venv/bin/python", "-m", "ipykernel_launcher", "-f", "{connection_file}" ] }argv[0]is resolved on the runner host. Runme validates the argument count and size, rejects NUL bytes, requires exactly one{connection_file}placeholder outside the executable, and invokes the process directly without shell expansion. Restart reuses the original launch specification.Decisions that deserve careful review
These are the API, security, and compatibility choices that will become expensive to change after clients depend on them:
RunnerUserRolepermits the caller to supply an arbitrary executable and arguments that run as the runner's OS user. This is intentional; it must not be exposed to a weaker role without introducing a separate policy or allowlist.argv; Runme owns connectivity. The client selects the environment and kernel command. Runme selects ports, creates the HMAC key and connection file, controls its path and permissions, and only substitutes{connection_file}.exec.Command. Shell strings, interpolation, pipelines, and environment-variable expansion are not part of the API.kernel_id, allowing multiple kernels with the same name.Reviewer guide
pkg/agent/server/jupyter_kernels.go: public HTTP/WebSocket API shape, strict JSON decoding, lifecycle dispatch, status mapping, upgrade rules, and rejection of the binary subprotocol.pkg/agent/server/server.go: route registration and theRunnerUserRoleauthorization boundary.pkg/agent/jupyter/kernel_manager.go: launch-spec validation, connection-file ownership, direct process execution, state transitions, restart semantics, and cleanup.pkg/agent/jupyter/bridge.go: long-lived WebSocket-to-ZeroMQ bridge behavior, reconnects, backpressure, heartbeat handling, and close reasons.pkg/agent/jupyter/channel_json.go: JSON channels compatibility boundary and explicit binary-buffer rejection.pkg/agent/jupyter/message.go: Jupyter multipart framing limits and HMAC signing/verification.pkg/agent/jupyter/connection.goandsocket.go: connection-file schema, loopback endpoint construction, and the narrow ZeroMQ socket abstraction.pkg/agent/jupyter/process_unix.goandprocess_windows.go: process-group isolation, interrupt, termination, and platform-specific guarantees.pkg/agent/jupyter/metrics.go: OpenTelemetry lifecycle and bridge instruments; reviewers should check attribute cardinality and whether each signal has an operational consumer.Validation
SHELL=/bin/bash ./runme run checkCurrent limitations