From 4d9599feae5b014b8383184dff96299fbb1d7897 Mon Sep 17 00:00:00 2001 From: Zac Wang Date: Wed, 7 Oct 2026 06:44:08 +0000 Subject: [PATCH] feat(sandboxes): add DeepSeek Harness (dsh) sandbox image Installs @deepseek-ai/dsh on the community base, matching the gemini/codex pattern: a plain CLI layered on openshell-base with its own policy.yaml, no gateway. dsh's headless profile (one-shot task in, final answer out, no server, no open port) is the intended entrypoint for eval/scripted use, the same shape as the other CLI-driven sandboxes here. Co-Authored-By: Claude Sonnet 5 Signed-off-by: Zac Wang --- sandboxes/dsh/Dockerfile | 32 ++++++ sandboxes/dsh/README.md | 51 +++++++++ sandboxes/dsh/policy.yaml | 210 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 293 insertions(+) create mode 100644 sandboxes/dsh/Dockerfile create mode 100644 sandboxes/dsh/README.md create mode 100644 sandboxes/dsh/policy.yaml diff --git a/sandboxes/dsh/Dockerfile b/sandboxes/dsh/Dockerfile new file mode 100644 index 0000000..61bef79 --- /dev/null +++ b/sandboxes/dsh/Dockerfile @@ -0,0 +1,32 @@ +# syntax=docker/dockerfile:1.4 + +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +# DeepSeek Harness (dsh) sandbox image for OpenShell +# +# Builds on the community base sandbox and adds DeepSeek Harness's headless +# profile: a one-shot CLI runner (no GUI, no server, no open port), the +# shape this repo's other sandboxes use for a CLI-driven coding agent. +# Build: docker build -t openshell-dsh --build-arg BASE_IMAGE=openshell-base . +# Run: openshell sandbox create --from dsh + +ARG BASE_IMAGE=ghcr.io/nvidia/openshell-community/sandboxes/base:latest +FROM ${BASE_IMAGE} + +USER root + +# Install DeepSeek Harness CLI (pinned for reproducibility). Developer +# preview: expect breaking changes between releases. +RUN npm install -g @deepseek-ai/dsh@0.2.0-rc.2 + +# Copy sandbox policy +COPY policy.yaml /etc/openshell/policy.yaml + +# Harness home (DSH_HOME, default ~/.dsh): profiles, credentials, session log. +RUN mkdir -p /sandbox/.dsh && \ + chown -R sandbox:sandbox /sandbox/.dsh + +USER sandbox + +ENTRYPOINT ["/bin/bash"] diff --git a/sandboxes/dsh/README.md b/sandboxes/dsh/README.md new file mode 100644 index 0000000..6a75aee --- /dev/null +++ b/sandboxes/dsh/README.md @@ -0,0 +1,51 @@ +# DeepSeek Harness Sandbox + +OpenShell sandbox image pre-configured with [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (`dsh`), an open-source, plugin-architected agent harness from DeepSeek AI. + +> [!NOTE] +> DeepSeek Harness is in developer preview. Expect compatibility-breaking changes between releases. + +## What's Included + +- **DeepSeek Harness CLI** (`@deepseek-ai/dsh@0.2.0-rc.2`) — `dsh`, run via its `headless` profile +- Everything from the [base sandbox](../base/README.md) + +## Build + +```bash +docker build -t openshell-dsh . +``` + +To build against a specific base image: + +```bash +docker build -t openshell-dsh --build-arg BASE_IMAGE=ghcr.io/nvidia/openshell-community/sandboxes/base:latest . +``` + +## Usage + +### Create a sandbox + +```bash +openshell sandbox create --from dsh +``` + +### Run a one-shot task + +`dsh`'s `headless` profile runs a single task from the command line, prints the final answer, and exits — no GUI, no server, no open port. This is the profile to drive from an eval harness or a script: + +```bash +dsh --profile headless "run the tests" +``` + +Pipe a task in instead of passing it as an argument: + +```bash +{ echo "Summarize these changes:"; git diff --stat; } | dsh --profile headless +``` + +Pass `--json` for a newline-delimited JSON event stream (`session`, `status`, `text`, `thinking`, `tool_call`, `tool_result`, `final`) instead of plain text on stdout, and `--session-id ` to continue a previous conversation. Exit code 0 means the task completed; 1 means it aborted or errored. + +## Configuration + +DeepSeek Harness stores profiles, credentials, and the session log under its Harness home, `~/.dsh` by default (override with `$DSH_HOME`). See the [DeepSeek Harness docs](https://deepseek-harness.github.io/deepseek-harness/) for provider and plugin configuration. diff --git a/sandboxes/dsh/policy.yaml b/sandboxes/dsh/policy.yaml new file mode 100644 index 0000000..b218edf --- /dev/null +++ b/sandboxes/dsh/policy.yaml @@ -0,0 +1,210 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +version: 1 + +# --- Sandbox setup configuration (queried once at startup) --- + +filesystem_policy: + include_workdir: true + read_only: + - /usr + - /lib + - /proc + - /dev/urandom + - /app + - /etc + - /var/log + read_write: + - /sandbox + - /tmp + - /dev/null + +landlock: + compatibility: best_effort + +process: + run_as_user: sandbox + run_as_group: sandbox + +# --- Network policies (queried per-CONNECT request) --- +# +# Each named policy maps a set of allowed (binary, endpoint) pairs. +# Binary identity is resolved via /proc/net/tcp inode lookup + /proc/{pid}/exe. +# Ancestors (/proc/{pid}/status PPid walk) and cmdline paths are also matched. +# SHA256 integrity is enforced in Rust via trust-on-first-use, not here. + +network_policies: + claude_code: + name: claude-code + endpoints: + - { host: api.anthropic.com, port: 443, protocol: rest, enforcement: enforce, access: full, tls: terminate } + - { host: statsig.anthropic.com, port: 443 } + - { host: sentry.io, port: 443 } + - { host: raw.githubusercontent.com, port: 443 } + - { host: platform.claude.com, port: 443 } + binaries: + - { path: /usr/local/bin/claude } + - { path: /usr/bin/node } + + github_ssh_over_https: + name: github-ssh-over-https + endpoints: + - host: github.com + port: 443 + protocol: rest + tls: terminate + enforcement: enforce + rules: + # Git Smart HTTP read-only: allow clone, fetch, pull + # Discovery (query string is included in path matching) + - allow: + method: GET + path: "/**/info/refs*" + # Data transfer for reads (all repos) + - allow: + method: POST + path: "/**/git-upload-pack" + # Data transfer for writes + # - allow: + # method: POST + # path: "/**/git-receive-pack" + binaries: + - { path: /usr/bin/git } + + nvidia_inference: + name: nvidia-inference + endpoints: + - { host: integrate.api.nvidia.com, port: 443 } + binaries: + - { path: /usr/bin/curl } + - { path: /bin/bash } + - { path: /usr/local/bin/opencode } + + # --- GitHub REST API (read-only) --- + github_rest_api: + name: github-rest-api + endpoints: + - host: api.github.com + port: 443 + protocol: rest + tls: terminate + enforcement: enforce + access: read-only + binaries: + - { path: /usr/local/bin/claude } + - { path: /usr/bin/gh } + + # --- GitHub Copilot API --- + # Endpoints from https://docs.github.com/en/copilot/reference/copilot-allowlist-reference + copilot: + name: copilot + endpoints: + # Auth and user management + - { host: github.com, port: 443 } + - { host: api.github.com, port: 443 } + # Copilot API (subscription-tier routing) + - { host: api.githubcopilot.com, port: 443, protocol: rest, enforcement: enforce, access: read-write } + - { host: api.individual.githubcopilot.com, port: 443, protocol: rest, enforcement: enforce, access: read-write } + - { host: api.business.githubcopilot.com, port: 443, protocol: rest, enforcement: enforce, access: read-write } + - { host: api.enterprise.githubcopilot.com, port: 443, protocol: rest, enforcement: enforce, access: read-write } + # Model proxy + - { host: copilot-proxy.githubusercontent.com, port: 443, protocol: rest, enforcement: enforce, access: read-write } + # Telemetry, feature flags, updates + - { host: origin-tracker.githubusercontent.com, port: 443 } + - { host: telemetry.enterprise.githubcopilot.com, port: 443 } + - { host: default.exp-tas.com, port: 443 } + - { host: release-assets.githubusercontent.com, port: 443 } + binaries: + - { path: /usr/bin/copilot } + - { path: "/usr/lib/node_modules/@github/copilot/node_modules/@github/**/copilot" } + + pypi: + name: pypi + endpoints: + - { host: pypi.org, port: 443 } + - { host: files.pythonhosted.org, port: 443 } + # uv python install downloads from python-build-standalone on GitHub + - { host: github.com, port: 443 } + - { host: objects.githubusercontent.com, port: 443 } + # uv resolves python-build-standalone release metadata via the GitHub API + - { host: api.github.com, port: 443 } + - { host: downloads.python.org, port: 443 } + binaries: + - { path: /sandbox/.venv/bin/python } + - { path: /sandbox/.venv/bin/python3 } + - { path: /sandbox/.venv/bin/pip } + - { path: /app/.venv/bin/python } + - { path: /app/.venv/bin/python3 } + - { path: /app/.venv/bin/pip } + - { path: /usr/local/bin/uv } + # Managed Python installations from uv python install + - { path: "/sandbox/.uv/python/**" } + + vscode: + name: vscode + endpoints: + - { host: update.code.visualstudio.com, port: 443 } + # NOTE: OPA host matching uses exact equality, not glob — list hosts explicitly. + - { host: az764295.vo.msecnd.net, port: 443 } + - { host: vscode.download.prss.microsoft.com, port: 443 } + - { host: marketplace.visualstudio.com, port: 443 } + - { host: gallerycdn.vsassets.io, port: 443 } + binaries: + - { path: /usr/bin/curl } + - { path: /usr/bin/wget } + - { path: "/sandbox/.vscode-server/**" } + - { path: "/sandbox/.vscode-remote-containers/**" } + + cursor: + name: cursor + endpoints: + - { host: cursor.blob.core.windows.net, port: 443 } + # NOTE: OPA host matching uses exact equality, not glob — list hosts explicitly. + - { host: api2.cursor.sh, port: 443 } + - { host: repo.cursor.sh, port: 443 } + - { host: download.cursor.sh, port: 443 } + - { host: cursor.download.prss.microsoft.com, port: 443 } + binaries: + - { path: /usr/bin/curl } + - { path: /usr/bin/wget } + - { path: "/sandbox/.cursor-server/**" } + + opencode: + name: opencode + endpoints: + - host: registry.npmjs.org + port: 443 + - host: opencode.ai + port: 443 + - host: integrate.api.nvidia.com + port: 443 + binaries: + - path: /usr/lib/node_modules/opencode-ai/bin/.opencode + - path: /usr/bin/node + - path: /usr/local/bin/opencode + + codex: + name: codex + endpoints: + - { host: api.openai.com, port: 443 } + - { host: auth.openai.com, port: 443 } + - { host: chatgpt.com, port: 443 } + binaries: + - { path: /usr/bin/codex } + - { path: /usr/bin/node } + - { path: "/usr/lib/node_modules/@openai/**" } + + deepseek_harness: + name: deepseek-harness + endpoints: + # Default model provider (dsh defaults to deepseek-v4-flash). Add + # entries here for any other provider a profile is configured to use. + - { host: api.deepseek.com, port: 443, protocol: rest, enforcement: enforce, access: full, tls: terminate } + # npm registry: `dsh plugin install` fetches plugins at runtime, not + # only at image build time. + - { host: registry.npmjs.org, port: 443 } + binaries: + - { path: /usr/bin/dsh } + - { path: "/usr/lib/node_modules/@deepseek-ai/dsh/**" } + - { path: /usr/bin/node }