Skip to content

feat(sandbox): OpenShell pilot policy + image for the engineer bot - #310

Open
regnull wants to merge 1 commit into
mainfrom
openshell-pilot
Open

regnull wants to merge 1 commit into
mainfrom
openshell-pilot

Conversation

@regnull

@regnull regnull commented Oct 23, 2026

Copy link
Copy Markdown
Owner

OpenShell pilot: sandbox policy for the @engineer bot

Problem

OpenBot bots (starting with @engineer) currently run with direct shell/file/network
tool access
on the host, so a confused or malicious model could read secrets or touch
things it shouldn't. NVIDIA/OpenShell (v0.1.5,
Apache-2.0) wraps each bot's execution in a kernel-level sandbox (Landlock filesystem +
per-binary network policy + process controls) governed by a declarative YAML policy, with
an OCSF audit trail of every allow/deny decision.

This PR lands the pilot artifacts: the first policy, the sandbox base image, and a
runbook. The pilot wraps the @engineer bot and runs in advisory mode
(enforcement: audit) — denials are logged and drafted into rule proposals instead of
hard-failing the bot.

What's in the box

File Purpose
policies/engineer.yaml Schema-verified policy for the engineer sandbox: workspace FS, git remotes, package registries
policies/engineer-base.Dockerfile Sandbox base image (ubuntu:24.04 + git/gh/curl/python3/pip/venv/rg/jq/patch, non-root ubuntu user)
policies/README.md Quickstart + hard-won v0.1.5 operational details

Policy excerpt (network rules; all enforcement: audit for the pilot):

network_policies:
  github:
    endpoints:
      - { host: github.com,   port: 443, protocol: rest, enforcement: audit, access: full }
      - { host: api.github.com, port: 443, protocol: rest, enforcement: audit, access: full }
      - { host: objects.githubusercontent.com, port: 443, protocol: rest, enforcement: audit, access: read-only }
    binaries:
      - path: /usr/lib/git-core/git-remote-http   # git fetch/push exec this, not `git`
      - path: /usr/bin/gh
      - path: /usr/bin/curl
      - path: /sandbox/.local/bin/uv
  pypi:
    endpoints:
      - { host: pypi.org, port: 443, protocol: rest, enforcement: audit, access: read-only }
      - { host: files.pythonhosted.org, port: 443, protocol: rest, enforcement: audit, access: read-only }
    binaries:
      - path: /usr/bin/python3.12   # pip3 is a script; the interpreter makes the calls
      - path: /sandbox/.local/bin/uv

What was verified (live, against the running sandbox)

  • git ls-remote https://github.com/... and gh work through the policy (after fixing
    the binary-path gotcha below).
  • pypi.org/pypi/ruff/json reachable via python3.12; python3.12 -m pip download
    fetched a wheel to /tmp (read-write path) through the policy.
  • Default-deny holds: curl https://example.com → connection refused, and the refusal
    shows up as NET:OPEN DENIED in openshell logs engineer --source sandbox.
  • Policy hot-reload works: five policy revisions applied live via
    openshell policy set engineer --policy ... --wait without restarting the sandbox.
  • E2E: full backend suite inside the sandbox — uv sync --frozen, ruff, pytest →
    811 passed / 30 failed / 30 deselected (21:42). Sample failures pass on the host
    (e.g. test_migration_creates_the_todo_list_table), so they're environmental skew, not
    policy denials: uv pulled Python 3.13 (repo targets 3.12) and the image lacked
    patch. Both are fixed in the committed image/tooling recipe; audit log showed 36
    denials, all default-deny behaving as intended.

Gotchas that cost us time (documented in policies/README.md)

  1. Network binaries must be kernel-resolved real paths, never symlinks — git
    fetch/push actually execs /usr/lib/git-core/git-remote-http (git-remote-https is a
    symlink to it); pip3 is a script that runs as /usr/bin/python3.12. A symlink path
    fails silently as "connection refused"; the sandbox log carries a SYMLINK HINT.
  2. Default image lacks git/gh/python/curl and apt is unusable inside (non-root +
    default-deny network) → tools must be baked into a custom image.
  3. uv downloads CPython builds from github.com/astral-sh/... (via
    releases.astral.sh), so those hosts must be allowed for the uv binary even when
    pypi itself is allowed.
  4. openshell sandbox create attaches to the main process — keep the sandbox alive
    with a sleep-style main process and launch detached (e.g. nohup).

Out of scope / next steps

  • Flip individual rules from enforcement: audit to enforce after a few PR cycles of
    observed traffic (openshell rule get engineer --status pending lists drafted rules;
    nothing was auto-approved in the pilot).
  • Pin uv-managed Python to 3.12 in the image so in-sandbox pytest matches the host.
  • Wire run_shell execution to route through openshell sandbox exec (bot-side change).
  • Roll out to @qa/@reviewer policies and move real tokens out of bot reach
    (OpenShell credential injection) if friction stays low.

First pilot of NVIDIA/OpenShell sandboxing for OpenBot bots (advisory
mode, enforcement=audit): declarative policy covering the workspace,
git remotes (github/api.github/objects.githubusercontent), package
registries (pypi.org/files.pythonhosted.org) and uv's CPython release
host; ubuntu:24.04 base image (default image lacks git/gh/python/curl
and apt is unusable inside); README quickstart with the v0.1.5 gotchas
(real-binary network paths, uv->github releases dependency,
hot-reload, detached create).

Verified live: git->github, gh, pypi API + pip download, default-deny
(conn refused), policy hot-reload x5, full backend suite in-sandbox
(811 passed / 30 failed — environmental: uv Python 3.13 vs repo 3.12,
missing patch binary; sample failures pass on host).

@regnull regnull left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OpenBot - `@reviewer`

Reviewed the diff (3 new files under policies/, no runtime wiring). Ready to merge.

  • Correctness: policy YAML matches the probe results reported by @engineer (github/gh/pypi/astral allowed, default-deny confirmed via example.com); all rules are enforcement: audit so nothing can hard-fail the pilot.
  • Filesystem policy is conservative (ro: /usr,/bin,/lib,/etc,/proc + rw /tmp), image is non-root, patch included after the in-sandbox pytest run flagged it. No secrets committed. CI green.
  • Docs are honest about the uv/Python 3.13 drift and symlink-vs-real-path gotchas.

Nits (non-blocking, carry forward):

  1. README says "host baseline 841 passed" but the repo targets Python 3.12 while uv pulled 3.13 in-sandbox — worth a one-line note in the README next cycle (pin is already planned in the follow-up image).
  2. Dockerfile installs unpinned apt packages (fine for a pilot image, worth pinning when this becomes production tooling).
  3. github.com appears in both the github (access: full) and astral_releases (access: read-only) groups — reconcile before flipping anything to enforce so the effective policy isn't ambiguous.

Since #310 is pilot artifacts only and everything is audit-mode, I'm fine with this going in as-is.

@qa please test and merge PR #310.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant