# deepseek-kubernetes-sandbox **DeepSeek Harness Web, packaged as a container.** This repository's primary artifact is a slim, non-root, multi-arch image of [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) Web (`dsh web`), ready for Docker and Kubernetes. Everything else exists to support that image or to demonstrate an implementation on top of it. The flagship implementation is [`examples/dsh-agents-as-a-service/`](examples/dsh-agents-as-a-service/): the "agents-as-a-service" platform pattern with one DSH Web instance per user, provisioned from a warm pool on [kubernetes-sigs/agent-sandbox](https://github.com/kubernetes-sigs/agent-sandbox), suspended when idle, and fronted by a small browser-usable gateway. ## Quick start ```bash docker run -d --name dsh-web \ -p 3080:3080 \ -v dsh-home:/home/node/.dsh \ -e DEEPSEEK_API_KEY \ ghcr.io/sredevopsorg/deepseek-kubernetes-sandbox:dsh-v0.2.1-rc.5 ``` DSH Web is **not usable at a bare `/`**. Without its launch-token cookie it answers `401`, and the token is printed only in the startup logs: ```bash docker logs dsh-web 2>&1 | grep -m1 '?token=' # dsh web: http://localhost:3080/?token=... ``` Open the printed `?token=...` URL once: DSH sets its auth cookie and the UI loads normally afterwards. Hitting `/` directly (or after the cookie expires) returns `401` by design. ### Host port and public URL If `3080` is taken locally, map another port and set the public URL so the quoted token URL matches: ```bash docker run -d --name dsh-web -p 18081:3080 \ -e DSH_PUBLIC_URL=http://localhost:18081 \ -v dsh-home:/home/node/.dsh \ -e DEEPSEEK_API_KEY \ ghcr.io/sredevopsorg/deepseek-kubernetes-sandbox:dsh-v0.2.1-rc.5 # then open http://localhost:18081/?token=... ``` ### Hosts, probes and the 401 - **Probes must not hit `/`.** It returns `401` without the launch token, so a readiness/liveness probe on `/` crash-loops the pod. Probe `/favicon.svg` instead: it is unauthenticated. The shipped manifests use it. - **Host allow-listing.** DSH rejects unknown `Host` headers. `localhost` and `127.0.0.1` are always trusted; add any other public hostname to `DSH_TRUSTED_HOSTS` (comma or space separated). A gateway or Ingress that forwards the browser's `Host` needs its hostname listed there. - **State lives in `/home/node/.dsh`.** Mount a volume (Docker) or PVC (Kubernetes) to keep conversations, sessions and config across restarts. ### Runtime environment | Variable | Default | Purpose | | --- | --- | --- | | `DEEPSEEK_API_KEY` | empty | Provider credential consumed by DSH. Inject it; never bake it into the image or commit it. | | `DSH_PORT` | `3080` | Port DSH Web listens on inside the container. | | `DSH_HOST` | auto-detected pod/container IP | Bind address. Leave empty in Kubernetes. | | `DSH_PUBLIC_URL` | `http://localhost:${DSH_PORT}` | Public base URL used in generated links and the launch-token URL in the logs. | | `DSH_TRUSTED_HOSTS` | empty | Extra trusted `Host`/`Origin` entries, comma/space separated. | | `DSH_HOME` | `/home/node/.dsh` | DSH state directory (mount a volume here). | DSH also resolves the standard per-provider environment names — `OPENROUTER_API_KEY`, `GEMINI_API_KEY`, `NVIDIA_API_KEY`, `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GROQ_API_KEY`, `XAI_API_KEY`, `HF_TOKEN`, and others — through its packaged provider catalog. Inject them the same way; a key saved in DSH's credential store wins over the environment. See [provider credentials](docs/main-image.md#provider-credentials). Full contract, build args, probes, volumes and signals: [docs/main-image.md](docs/main-image.md). ## Published images Published to GitHub Container Registry. The main image is the repository image; example components are nested packages below it: ``` ghcr.io/sredevopsorg/deepseek-kubernetes-sandbox # DSH Web (main image) ghcr.io/sredevopsorg/deepseek-kubernetes-sandbox/dsh-agents-as-a-service/gateway # example gateway ``` | Tag | Contents | Meaning | | --- | --- | --- | | `dsh-v0.2.1-rc.5` | DSH Web, port `3080` | **Current release identity.** Immutable: once published it never moves. | | `latest` | DSH Web | Most recent release; same digest as the current identity. | | `main` | DSH Web | Development channel rebuilt when image-definition files change on `main`. Not a release; not for production. | The example gateway is published under its nested path with the same release identity, e.g. `ghcr.io/sredevopsorg/deepseek-kubernetes-sandbox/dsh-agents-as-a-service/gateway:dsh-v0.2.1-rc.5`. Release identities, channels and rollback rules are explained in [docs/versioning.md](docs/versioning.md). Production deployments should pin a digest: ```bash docker buildx imagetools inspect ghcr.io/sredevopsorg/deepseek-kubernetes-sandbox:dsh-v0.2.1-rc.5 # image: ghcr.io/sredevopsorg/deepseek-kubernetes-sandbox@sha256: ``` ## Build from source ```bash make image # build the main DSH Web image (clones and builds upstream DSH) make run # build it, then run it interactively (needs DEEPSEEK_API_KEY) ``` The pins (`DSH_VERSION`, `RELEASE_REVISION`, `PNPM_VERSION`) come from [versions.env](versions.env) — never hard-code them (`make check` verifies). [`build.sh`](build.sh) is a deprecated shim that forwards to `make`; use the Makefile targets. ## Examples Self-contained implementations that consume the image. Index and contract: [`examples/README.md`](examples/README.md) and [docs/examples.md](docs/examples.md). | Example | What it demonstrates | | --- | --- | | [`examples/dsh-agents-as-a-service/`](examples/dsh-agents-as-a-service/) | Full multi-tenant platform: SandboxTemplate with per-user PVCs, warm pool, one claim per user, suspend-when-idle, wake-on-return, browser gateway with signup + `HttpOnly` sessions + HTTP/WebSocket proxy, Ingress or Gateway API + GitHub SSO exposure, and opt-in gVisor/NetworkPolicy/provider-key hardening and failed-Pod recovery. Start with the [walkthrough](examples/dsh-agents-as-a-service/README.md) and the [architecture](examples/dsh-agents-as-a-service/docs/architecture.md). | | [`examples/minimal-single-tenant/`](examples/minimal-single-tenant/) | The smallest useful setup: one `SandboxTemplate` and one `SandboxClaim`, reached through `kubectl port-forward` + the launch-token URL. Manifest: [`manifests/10-sandboxtemplate.yaml`](examples/minimal-single-tenant/manifests/10-sandboxtemplate.yaml). | The gateway is a component of the multi-tenant example and has its own [README](examples/dsh-agents-as-a-service/gateway/README.md) (API, configuration, auth/SSO, limitations). The example also documents opt-in gVisor, `nodeSelector`, NetworkPolicy and extra provider-key hardening in [optional hardening](examples/dsh-agents-as-a-service/docs/optional-hardening.md). ## Agent skills DSH discovers project skills from `.dsh/skills//SKILL.md`. The repository ships [`dsh-agent-sandbox-platform`](.dsh/skills/dsh-agent-sandbox-platform/SKILL.md), which covers operating and extending the platform, the optional [agent-sandbox MCP server](https://github.com/kubernetes-sigs/agent-sandbox/blob/main/clients/integrations/mcp-server/README.md), and a read-only cluster preflight. Runbooks, the MCP reference, the extension guide and troubleshooting notes live beside `SKILL.md`. ## Repository layout | Path | Purpose | | --- | --- | | [`Dockerfile`](Dockerfile) | Primary artifact: the multi-stage DSH Web image (port `3080`). | | [`versions.env`](versions.env) | Single source of truth for the DSH ref, repository revision, pnpm/control-plane versions and image names. | | [`Makefile`](Makefile), [`build.sh`](build.sh) | Canonical local entrypoint; `build.sh` is a deprecated shim. | | [`docs/`](docs/README.md) | Repository docs: image contract, example contract, gitflow, versioning. | | [`examples/`](examples/README.md) | One self-contained implementation per directory. | | [`examples/dsh-agents-as-a-service/`](examples/dsh-agents-as-a-service/) | Multi-tenant platform example: `manifests/`, `gateway/`, `docs/`, `scripts/`. | | [`examples/minimal-single-tenant/`](examples/minimal-single-tenant/) | Single-sandbox quickstart. | | [`scripts/`](scripts/) | Repository checks: versions, example layout, manifest YAML, version bump. | | [`.dsh/skills/`](.dsh/skills/) | Project-local DSH skills, auto-discovered from the git root. | | [`.github/`](.github/) | CI, release automation, dependency updates, PR/issue templates. | | [`.env.sample`](.env.sample) | Reference environment names for local runs; nothing reads it automatically. Never commit a real key. | ## Development The Makefile is the canonical entrypoint. No cluster is needed for `check`, `test`, `lint`, `gateway` or `image`: | Target | What it does | | --- | --- | | `make check` | Version pins, example layout and manifest YAML checks. Run before every commit. | | `make test` | Gateway unit tests (`pytest`). | | `make lint` | `ruff` + `shellcheck` (+ `yamllint`/`actionlint` when installed). | | `make image` | Build the main DSH Web image. | | `make gateway` | Build the example gateway image locally (`dsh-aaas-gateway:dev`). | | `make e2e` | Full kind walkthrough of the multi-tenant example (needs a cluster with agent-sandbox). | | `make changelog` | Regenerate `CHANGELOG.md` with git-cliff (release only). | | `make set-version` | Bump pins in `versions.env`; e.g. `make set-version DSH_VERSION= RELEASE_REVISION=rN`. | Local setup and the PR workflow: [CONTRIBUTING.md](CONTRIBUTING.md); branches, required checks and releases: [docs/gitflow.md](docs/gitflow.md). ## Versioning and releases The release identity is the upstream DSH ref plus an optional repository revision: `DSH_VERSION` and `RELEASE_REVISION` in [versions.env](versions.env). The current identity is **`dsh-v0.2.1-rc.5`** — the first release at this upstream DSH ref, so it carries no repository revision. Tags are immutable; a broken release is fixed by publishing `-r(N+1)`, never by re-tagging. `CHANGELOG.md` is generated by [git-cliff](https://git-cliff.org) from Conventional Commits and is never edited by hand. See [`CHANGELOG.md`](CHANGELOG.md), [docs/versioning.md](docs/versioning.md) and [cliff.toml](cliff.toml). ## Contributing - [CONTRIBUTING.md](CONTRIBUTING.md) — local setup, PR workflow, what a change needs. - [AGENTS.md](AGENTS.md) — repository conventions for AI coding agents (and humans) and the CI commands. - [SECURITY.md](SECURITY.md) — private vulnerability reporting and project threat notes. - [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) — community standards. - [docs/gitflow.md](docs/gitflow.md) — branches, protected `main`, required checks, release process. Do not commit secrets; secret-shaped YAML lives in `*.yaml.example` files, and version pins change only through `versions.env` / `make set-version`. ## License [MIT](LICENSE) © Nicolás Georger.