JupyterHub Spawner: per-user notebook servers as Sandstorm sandboxes
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 25/100
- Issue type
- Feature
- Clarity
- Mostly clear
- Activity status
- Quiet
- Domain
- backend-api-design
Research direction
Start with recipes/jupyter-kernel/ and the forkd-controller sandbox endpoints named in the issue; map the existing recipe and API before deciding how the Spawner will start, poll, stop, persist state, and expose kernel ports. The kernel-readiness, resource-limit, and authentication questions are unresolved, so settle those design choices first. Done means the package installs and the stated JupyterHub integration test spawns five kernels that each execute a numpy expression.
Written by the indexing model from the issue text.
Description
Goal
A JupyterHub Spawner that runs each user's single-user notebook server as a Sandstorm sandbox. Hub admins would get per-user servers that cost almost nothing while idle and come back with their kernel state intact, instead of being culled.
Why this fits Sandstorm now
The case for this changed with v0.6. It is no longer about cold-start time. It is about idle time, which is what Sandstorm is built for.
- A JupyterHub user's server is idle most of the day. Today an admin either keeps it running and paying for memory, or lets
jupyterhub-idle-cullerkill it, and the user's variables are lost. - On Sandstorm an idle server drops through frozen (about 10 ms back) to snapshot (about 0.2 s back, no memory held), and the first request wakes it with the kernel's state as it was. That is the four tiers in the README, applied to notebooks.
What already exists (v0.6)
- Lifecycle:
POST /v1/sandboxeswithtemplate(orsnapshot_tagto fork a warmed image) andmemory_limit_mib. AlsoGET /v1/sandboxes/:id,DELETE /v1/sandboxes/:id, andPOST /v1/sandboxes/:id/tierto put one away explicitly. Calls carry the master's bearer token. The master places sandboxes across nodes, so multi-host is no longer out of scope. - Reaching the server: each sandbox has a
gateway_porton its node and a per-sandboxtoken. The gateway streams responses and splices WebSockets (#365), which is what notebook kernels use. It also wakes a cold sandbox when a request reaches it (ADR 7). Kernels' ZMQ ports never leave the sandbox: jupyter-server speaks HTTP and WebSocket to the browser, so no port forwarding beyond the gateway is needed.
The first thing to settle: the token on the browser's path
The gateway only accepts the sandbox token as the x-sandstorm-sandbox-token header. A browser going through JupyterHub's proxy (configurable-http-proxy) cannot add a per-user header, and CHP has no per-route header injection. So the proposal has to pick one of these before any Spawner code is written:
- A small proxy step on the hub side that adds the header per route (a CHP replacement such as Traefik with a per-route header middleware).
- A second credential the gateway accepts, such as a cookie or a query parameter scoped to one sandbox, designed against RFC 16.5.
- Something else, with the reasons.
Whichever it is must keep the property the gateway exists for: no request reaches a sandbox without that sandbox's own token.
Scope
- In scope: a
sandstorm-jupyter-spawnerpackage (insdk/here or its own repo) subclassingjupyterhub.spawner.Spawner:start()creates the sandbox and returns the URL the hub's proxy should route to.poll()reads the sandbox. A frozen or snapshot tier counts as running, because it wakes on the next request.stop()deletes it. An idle-culler hook should move it to snapshot instead of deleting it.get_state/load_statekeep the sandbox id and token across hub restarts.
- A template for the single-user server (jupyter-server and ipykernel, with the SciPy stack pre-imported and warmed), built for v0.6 templates. The old
recipes/jupyter-kernelis a v0.5 rootfs. - Out of scope: a custom Jupyter UI, replacing JupyterHub, and CPU or IO quotas. The master takes a memory ceiling today and nothing else, so CPU limits are a separate feature.
Acceptance criteria
c.JupyterHub.spawner_class = "sandstorm_jupyter_spawner.SandstormSpawner"with a master URL, token and template starts a working notebook server.- An integration test brings up a local JupyterHub against a one-node fleet and starts 5 users in parallel. Each runs a numpy cell, is moved to the snapshot tier, then runs a second cell that reads a variable from the first.
- The time from a click on a snapshot-tier user's notebook to the cell running is reported, next to the hot-tier time.
Still help wanted. If you want to pick it up, start with a short proposal for the token question above.
- Dominant language
- Rust
- Stars
- 2.9k
- Forks
- 227
- PR merge metrics
- No merged PRs in 30d
Getting set up
This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from deeplethe/forkd
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
-
backend:firecracker
Difficulty 5/5 Over a week Newbie friendliness 25/100
-
backend:firecracker bug
Difficulty 5/5 Over a week Newbie friendliness 28/100
-
backend:firecracker help wanted optimization
Difficulty 4/5 3-5 days Newbie friendliness 38/100
-
backend:firecracker enhancement help wanted needs-verification
Difficulty 4/5 3-5 days Newbie friendliness 48/100
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 83/100
Maintainers usually reply within 1 day
-
priority middle
Difficulty 1/5 Under an hour Newbie friendliness 62/100
KATO-Hiro/AtCoderClans#12940 ·
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 83/100
Maintainers usually reply within 1 day
-
o8 next debug build aborts on Glass to Dark appearance switch (nil identifier messaged before check)Openbug
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
hurttlocker/o8#5112 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 79/100
Maintainers usually reply within 1 day