Repository navigation
feat(assistant): start the code sandbox's OpenShell gateway from Settings - #495
Merged
Merged
Conversation
…ersation The assistant gets run_python, run_bash, sandbox_files and import_sandbox_file once an admin turns on Settings > Code sandbox (sandbox.enabled, off by default) and points Lens at an NVIDIA OpenShell gateway. Each conversation gets its own sandbox (lens-chat-<id>) with none of Lens's data or keys, no egress unless sandbox.network allows the package registries or public addresses, and its files kept between runs. Attachments go in by id; a file comes out only through an import_files approval. With the network on, a run after outside text asks first. Idle sandboxes are stopped and unused ones deleted by the routines loop, and deleting a conversation deletes its sandbox. OpenShell's Python client is the `sandbox` extra, fetched as a component when the sandbox is turned on; tests run it against a fake gRPC gateway. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HXPWXcSCQb9hWPr7xkZu7B
The last check asked for 2026-10-17's note after the call without a date had made today's note, so on that day it found the note the deleted root had left behind. It now asks for a day no earlier call makes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HXPWXcSCQb9hWPr7xkZu7B
…-r960xc-c7-sandbox Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HXPWXcSCQb9hWPr7xkZu7B
# Conflicts: # threat-model.yaml
…ings The Docker Compose install gets a sandbox profile: openshell-init makes the gateway's certificates once, openshell-key copies the client's to a volume only Lens reads, and openshell-gateway runs NVIDIA OpenShell 0.1.4 with the Docker driver (no capabilities, read-only, telemetry off). Lens signs in to it over mutual TLS. Settings > Code sandbox > Run the gateway on this machine asks the upgrade agent to start it (action=services, profile=sandbox) and turns the sandbox on at openshell-gateway:17670, after a preflight (the Compose install, a 64-bit machine, Landlock 3). Off deletes every sandbox first. The same over GET/PUT /api/v1/admin/sandbox and the MCP tools sandbox_gateway and set_sandbox_gateway. An agent that knows only monitoring says to start it by hand, with the line to run. The network setting web becomes hosts: OpenShell resolves only names its policy lists, so the sandbox reaches the package registries and the names an admin lists in sandbox.hosts, at public addresses only. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HXPWXcSCQb9hWPr7xkZu7B
…sandbox' into claude/project-thread-r960xc-c7-gateway
…network Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HXPWXcSCQb9hWPr7xkZu7B
# Conflicts: # docs/assistant.md # fastapi_backend/app/domain/ai_tools.py # fastapi_backend/app/domain/components.py # fastapi_backend/app/domain/settings.py # fastapi_backend/pyproject.toml # fastapi_backend/uv.lock # threat-model.md # threat-model.yaml
…sandbox' into claude/project-thread-r960xc-c7-gateway # Conflicts: # fastapi_backend/app/domain/ai_tools.py # threat-model.yaml
…-r960xc-c7-gateway # Conflicts: # CHANGELOG.md # docs/assistant.md # docs/configuration.md # fastapi_backend/app/domain/ai_tools.py # fastapi_backend/app/domain/sandbox.py # fastapi_backend/app/domain/settings.py # fastapi_backend/app/domain/store.py # fastapi_backend/tests/api/test_sandbox.py # fastapi_backend/tests/fake_openshell.py # nextjs-frontend/components/settings/bodies.tsx # nextjs-frontend/components/settings/model.ts # threat-model.json # threat-model.md # threat-model.yaml
…-r960xc-c7-gateway # Conflicts: # threat-model.yaml
…-r960xc-c7-gateway # Conflicts: # CHANGELOG.md # fastapi_backend/app/api/mcp_tools.py
…-r960xc-c7-gateway # Conflicts: # threat-model.yaml
Merged
19 of 22 tasks
…-r960xc-c7-gateway # Conflicts: # threat-model.yaml
Ported from #512 so this PR's upgrade check passes while that lands: the entity-types step re-types weekday Terms as Dates, which the default entity list leaves out, so the check read them as gone. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HXPWXcSCQb9hWPr7xkZu7B
Owner
Author
|
"Upgrade from the last release" failed on 91274b4, but the cause isn't this PR. After the upgrade, "Monday" and "Friday" were missing from the entity list. #395's #512 fixes the check, and I ported its change in 563a4c6, which also merges main. With it, Generated by Claude Code |
17 of 23 tasks
adeelahmad
pushed a commit
that referenced
this pull request
Oct 18, 2026
…ay and clipper sign-in as built Since the last README refresh, #474, #484, #510, #511, #497, #495, #406, #516, #517 and #491 merged. The README's built list now says a conversation exports as Markdown, PDF or JSON and its answers, questions and code copy; a Tools chip picks an answer's tools, limit and namespaces; sandbox runs open under the answer with downloads, and Settings starts the gateway; deep research plans, runs and returns a cited report; ready-made MCP servers (the browser, GitHub) and the optional containers start from the web app; and the web clipper signs in or takes an API token. Each has its checks in docs/requirements.md. In progress drops the merged PRs and lists the open feature PRs; the Planned proof item keeps only the Raspberry Pi timing (#481 and #482 are closed); the docs index links requirements and checks, and published feeds. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDx7PPUauyjMn1LZs8JAsS
adeelahmad
added a commit
that referenced
this pull request
Oct 18, 2026
…way (#544) sandbox.py has asked the upgrade agent for the sandbox profile since #495, but the agent only knew mcp, browser and monitoring, so Settings fell back to the line to run by hand. The profile's three containers (openshell-init, openshell-key, openshell-gateway) join services_of, with a test, and the docs say which agent knows it. Claude-Session: https://claude.ai/code/session_01HXPWXcSCQb9hWPr7xkZu7B Co-authored-by: Adeel Ahmad <noreply@anthropic.com>
adeelahmad
pushed a commit
that referenced
this pull request
Oct 18, 2026
…ay and clipper sign-in as built Since the last README refresh, #474, #484, #510, #511, #497, #495, #406, #516, #517, #491, #400 and #506 merged. The README's built list now says the assistant answers questions about Lens itself; a conversation exports as Markdown, PDF or JSON and its answers, questions and code copy; a Tools chip picks an answer's tools, limit and namespaces; sandbox runs open under the answer with downloads, and Settings starts the gateway; deep research plans, runs and returns a cited report; ready-made MCP servers, the MCP servers page and the optional containers start from the web app; and the web clipper signs in or takes an API token. Each has its checks in docs/requirements.md. In progress lists only open PRs; the Nebula mesh (#546, merged, partly built) moves to Planned with what's next; the Planned proof item keeps only the Raspberry Pi timing; the docs index links requirements and checks, and published feeds. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDx7PPUauyjMn1LZs8JAsS
adeelahmad
added a commit
that referenced
this pull request
Oct 18, 2026
…ay and clipper sign-in as built (#536) Since the last README refresh, #474, #484, #510, #511, #497, #495, #406, #516, #517, #491, #400 and #506 merged. The README's built list now says the assistant answers questions about Lens itself; a conversation exports as Markdown, PDF or JSON and its answers, questions and code copy; a Tools chip picks an answer's tools, limit and namespaces; sandbox runs open under the answer with downloads, and Settings starts the gateway; deep research plans, runs and returns a cited report; ready-made MCP servers, the MCP servers page and the optional containers start from the web app; and the web clipper signs in or takes an API token. Each has its checks in docs/requirements.md. In progress lists only open PRs; the Nebula mesh (#546, merged, partly built) moves to Planned with what's next; the Planned proof item keeps only the Raspberry Pi timing; the docs index links requirements and checks, and published feeds. Claude-Session: https://claude.ai/code/session_01HDx7PPUauyjMn1LZs8JAsS Co-authored-by: Adeel Ahmad <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Before: the code sandbox needed an NVIDIA OpenShell gateway the admin ran and wired up themselves. Its
webnetwork couldn't work either, because OpenShell resolves only the names its policy lists.After: Settings → Code sandbox has Run the gateway on this machine. The switch has the upgrade agent start OpenShell's gateway next to Lens (the new Compose profile
sandbox) and turns the sandbox on atopenshell-gateway:17670. Lens signs in to the gateway over mutual TLS. It first checks that the machine can run the gateway (the Docker Compose install, a 64-bit machine, Landlock 3 in the kernel) and says why when it can't. Turning it off deletes every sandbox before the gateway stops. Agents get the same throughGET/PUT /api/v1/admin/sandboxand the MCP toolssandbox_gatewayandset_sandbox_gateway. The network settingwebbecomeshosts: the package registries plus the names an admin lists insandbox.hosts, held to public addresses.How:
openshell-initmakes the gateway's certificates once, in the host's/var/lib/openshell. That folder has the same path inside and out, because the gateway has Docker mount files from it into the sandboxes.openshell-keycopies the client certificate to thesandbox-keyvolume, owned by Lens's user with mode 0400.openshell-gatewayruns OpenShell 0.1.4 with the Docker driver, configured bysandbox/gateway.toml. It runs as root for the Docker socket, withcap_drop: ALL,no-new-privileges, a read-only image and telemetry off.sandboxnetwork, mountsandbox-keyread-only and getLENS_SANDBOX_TLS_DIR.sandbox.turn()writes the services request in C6's format (action=services,profile=sandbox,turn=on|off).gateway_view()reads the agent's status and probes the gateway with an authenticated call. A plain health call answers anyone, so it wouldn't show that Lens is let in.I checked this design against a real OpenShell 0.1.4 gateway in Docker. Lens's client connected as uid 1000 over mTLS, and a caller without the certificate was refused. Sandboxes kept running with the gateway hardened. With the network off, nothing got out. A name that resolves to a private address was refused. One thing I couldn't check is real HTTPS to PyPI from a sandbox, because this environment intercepts TLS.
This is PR 2 of 3 for the code sandbox, after #473 (merged). Until C6's host-agent change ships, the agent answers a
profile=sandboxrequest with "monitoring is on or off". Settings then says the agent is older than this Lens and shows the line to run by hand. PR 3 (#497) adds the conversation step and the file chip.Motivation and Context
Adeel asked for standard Python and bash tools for the chat app on OpenShell (chat gaps, module C7). This PR makes it start with one switch instead of a hand-run gateway.
Screenshots (if appropriate):
n/a (a switch, banners and a hosts list in the existing Settings section).
Steps to reproduce (if appropriate):
Types of changes
!in the title)Checklist:
make hookschecks it on commit).docs/accordingly.## UnreleasedinCHANGELOG.md.Done questions (AGENTS.md)
sandboxprofile is C6's PR, and the conversation step is PR 3 (feat(chat): show the code sandbox's runs in the conversation and download the files it makes #497).GET/PUT /api/v1/admin/sandboxand the MCP toolssandbox_gatewayandset_sandbox_gateway(admins, with the write scope to change)..env. Without an upgrade agent, Settings shows the one line to run.sandbox.hostsshows only with thehostsnetwork.sandbox.hostsis in Settings with a default of none. The gateway address is still editable. The port (17670) is fixed for now and listed as refine later.ci/validate_threat_model.pypasses.sandbox.hostshas a default.sandbox.gateway's default moves to 17670. A storedwebnetwork (from feat(assistant): run Python and bash in an OpenShell sandbox per conversation #473, unreleased) reads ashosts, so no upgrade step is needed.tests/api/test_sandbox.py(the switch end to end, an old agent, preflight, mTLS, the hosts policy) and__tests__/sandbox-gateway.test.ts.docs/assistant.md(The gateway),docs/configuration.mdanddocs/mcp.mdare updated. The OpenAPI files come frommake openapi.docs/assistant.md#the-gateway: asking before a run reaches an unlisted host, the port as a setting, macOS and microsandbox, Podman, GPUs, and a real OpenShell run in CI. The C6 hand-off is in the project notes.Local CI (
ci/local-ci.sh 91274b4b --jobs backend,frontend,pre-commit,title): FastAPI pass (1089s), Next.js pass (327s), pre-commit pass (92s), PR title pass.🤖 Generated with Claude Code
https://claude.ai/code/session_01HXPWXcSCQb9hWPr7xkZu7B