diff --git a/examples/jupyter-sandbox/README.md b/examples/jupyter-sandbox/README.md new file mode 100644 index 0000000000..153e636fa0 --- /dev/null +++ b/examples/jupyter-sandbox/README.md @@ -0,0 +1,64 @@ +# Jupyter Sandbox + +Run a published Jupyter image in OpenShell, expose its server locally, and +execute a local notebook on the sandboxed kernel. + +Run these commands from `examples/jupyter-sandbox`. You need a local OpenShell +gateway, the `openshell` CLI, Cargo, and OpenSSL on the host. + +## 1. Install the notebook CLI + +Install the [Jupyter community notebook CLI](https://github.com/jupyter-ai-contrib/nb-cli): + +```shell +cargo install nb-cli --version 0.0.10 --locked +``` + +OpenShell pulls the published `quay.io/jupyter/base-notebook:2026-09-29` image +when it creates the sandbox. The image includes Jupyter Server and a Python +kernel, so no container build is needed. + +## 2. Launch the service + +```shell +JUPYTER_TOKEN="$(openssl rand -hex 32)" +openshell sandbox create \ + --name jupyter-demo \ + --from quay.io/jupyter/base-notebook:2026-09-29 \ + --policy policy.yaml \ + --expose 8888 \ + --detach --no-tty \ + -- jupyter server \ + --ServerApp.ip=127.0.0.1 \ + --ServerApp.port=8888 \ + --ServerApp.port_retries=0 \ + --ServerApp.open_browser=False \ + --ServerApp.root_dir=/home/jovyan \ + --ServerApp.terminals_enabled=False \ + --ServerApp.allow_remote_access=True \ + --IdentityProvider.token="$JUPYTER_TOKEN" +``` + +The CLI prints the service URL after the sandbox is ready. Jupyter starts as +the sandbox's main process and listens on its loopback port. Keep the token in +this shell for step 3. + +## 3. Execute the notebook on the remote kernel + +Use the service URL printed in step 2 as the `--gateway` value: + +```shell +nb execute demo.ipynb \ + --gateway 'http://default--jupyter-demo.openshell.localhost:/' \ + --gateway-token "$JUPYTER_TOKEN" +``` + +The command writes `285` into `demo.ipynb` on your computer. Its code +runs in a Jupyter kernel inside the sandbox. The `nb` CLI accepts the service +URL as a flag and authenticates its REST and WebSocket connections. + +When finished, delete the sandbox and its service: + +```shell +openshell sandbox delete jupyter-demo +``` diff --git a/examples/jupyter-sandbox/demo.ipynb b/examples/jupyter-sandbox/demo.ipynb new file mode 100644 index 0000000000..d22c824df7 --- /dev/null +++ b/examples/jupyter-sandbox/demo.ipynb @@ -0,0 +1,35 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "intro", + "metadata": {}, + "source": [ + "# Jupyter in an OpenShell sandbox\n", + "This notebook runs on the sandboxed Jupyter kernel." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "sum-of-squares", + "metadata": {}, + "outputs": [], + "source": [ + "print(sum(i * i for i in range(10)))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/jupyter-sandbox/policy.yaml b/examples/jupyter-sandbox/policy.yaml new file mode 100644 index 0000000000..3fb812a8b6 --- /dev/null +++ b/examples/jupyter-sandbox/policy.yaml @@ -0,0 +1,20 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +version: 1 + +filesystem_policy: + include_workdir: true + read_only: [/opt, /usr, /lib, /proc, /dev/urandom, /etc, /var/log] + read_write: [/home/jovyan, /tmp, /dev/null] + +landlock: + compatibility: best_effort + +process: + run_as_user: jovyan + run_as_group: users + +# The Jupyter service is exposed inbound through the gateway. The notebook +# does not need outbound network access for this demonstration. +network_policies: {}