# Minimal single-tenant SandboxTemplate for DeepSeek Harness Web. # # This is the "just one DSH Web" quick-start template. For the full # multi-tenant platform (warm pool, per-user claims, gateway, idle-suspend), # see examples/dsh-agents-as-a-service/. # # Claims reference this template directly (spec.sandboxTemplateRef): there is # no warm pool and no per-user platform here. See the README for the full # quickstart; in short the prerequisites are: # - Namespace dsh-demo. # - Secret dsh-provider-keys in dsh-demo with key DEEPSEEK_API_KEY # (injected via envFrom; the pod starts without it, only model calls fail). # - Optional: Secret dsh-provider-keys-extra with additional provider keys # (OPENROUTER_API_KEY, GEMINI_API_KEY, NVIDIA_API_KEY, ...). It is # referenced with `optional: true`, so it may be absent. # The agent-sandbox controller (v1.0.8, API v1beta1) must be installed. apiVersion: extensions.agents.x-k8s.io/v1beta1 kind: SandboxTemplate metadata: name: dsh-web-template namespace: dsh-demo spec: service: true # headless Service per Sandbox -> stable DNS (status.serviceFQDN) # networkPolicyManagement is omitted, so the controller uses its Managed # secure default (ingress only from the sandbox-router, public egress). This # quickstart reaches the sandbox through `kubectl port-forward`, which is # local to the pod. The multi-tenant example documents opt-in policies in # examples/dsh-agents-as-a-service/docs/optional-hardening.md. # # OPTIONAL: recreate a Failed sandbox Pod instead of retaining it # (agent-sandbox v1.0.8+). Documented with the multi-tenant example. # failedPodPolicy: Recreate podTemplate: spec: # OPTIONAL: run under gVisor. gVisor breaks `kubectl port-forward`, so # this quickstart's only access path will not work; use the multi-tenant # gateway example or a Service/NodePort instead. # runtimeClassName: gvisor # # OPTIONAL: pin to a dedicated node pool. Replace the placeholder with a # node label; add a matching toleration if the pool is tainted. # nodeSelector: # dsh.example.com/node-pool: dsh securityContext: runAsNonRoot: true runAsUser: 1000 # Matches the UID of the 'node' user runAsGroup: 1000 fsGroup: 1000 # DSH's credentials plugin refuses to start when .credentials.yaml has # any group/other permission bit (it creates the file 0600). The # default kubelet fsGroup policy ("Always") rewrites it to 0660 on every # pod start, so a recreated or resumed sandbox crash-loops; OnRootMismatch # avoids that walk once the volume root already carries the ownership. # Rationale in full at # examples/dsh-agents-as-a-service/manifests/10-sandboxtemplate.yaml. fsGroupChangePolicy: OnRootMismatch containers: - name: dsh-web image: ghcr.io/sredevopsorg/deepseek-kubernetes-sandbox:dsh-v0.2.1-rc.5 imagePullPolicy: IfNotPresent ports: - name: web containerPort: 3080 envFrom: - secretRef: name: dsh-provider-keys # Secret with key DEEPSEEK_API_KEY # Optional extra provider keys; an absent Secret is safe. - secretRef: name: dsh-provider-keys-extra optional: true env: # Point the OS home at the persisted agent workspace. DSH's # directory picker (the folder explorer used to choose a workspace) # lists $HOME when it opens and has no start-path setting, so with # the image default (/home/node, not on a volume) it would open on a # throwaway directory. /workspace is the PVC-backed WORKDIR, so the # picker opens on the tree that survives suspend/resume, and `~` in # agent commands resolves there. Trade-off: tools that keep caches # or config under $HOME (npm/pnpm, uv, git, gh, DSH's global # ~/.agents instructions and skills) write dotfiles onto the PVC # inside the agent workspace. DSH_HOME is explicit # (/home/node/.dsh), so sessions, config and credentials are # unaffected. Rationale in full at # examples/dsh-agents-as-a-service/manifests/10-sandboxtemplate.yaml. - name: HOME value: /workspace # Empty => DSH derives links from the request Host. Keep it empty # when fronting the sandbox with a proxy/gateway. - name: DSH_PUBLIC_URL value: "" # DSH Host/Origin-fences /api. Empty trusts only localhost, which # is right for `kubectl port-forward`. Set this to the public host # (e.g. dsh.example.com) if you front DSH with a proxy or Ingress, # so requests forwarded with the browser's Host are accepted. The # full example documents both exposure variants in # examples/dsh-agents-as-a-service/manifests/exposure/README.md. - name: DSH_TRUSTED_HOSTS value: "" # DSH's own bash file sandbox (bubblewrap, else Landlock) cannot be # enforced in this hardened pod - bubblewrap is installed but is # unsetuid and needs an unprivileged user namespace - and fails # closed with SANDBOX_UNAVAILABLE. The pod is the boundary here; # keep the deployment override in sync with the multi-tenant example # and see its README troubleshooting row to restore a confined mode. - name: DSH_PERMISSION_MODE value: danger-full-access volumeMounts: - name: dsh-home mountPath: /home/node/.dsh # DSH_HOME # / is behind DSH's launch-token cookie (401 without it); use an # unauthenticated path for probes. The startup probe gives a cold # start (plugin activation on a small node, or the first resume of a # suspended sandbox) up to 120s before liveness can restart it. startupProbe: httpGet: path: /favicon.svg port: 3080 periodSeconds: 5 failureThreshold: 24 readinessProbe: httpGet: path: /favicon.svg port: 3080 initialDelaySeconds: 5 periodSeconds: 5 livenessProbe: httpGet: path: /favicon.svg port: 3080 initialDelaySeconds: 15 periodSeconds: 15 securityContext: allowPrivilegeEscalation: false capabilities: drop: ["ALL"] seccompProfile: type: RuntimeDefault volumeClaimTemplates: - metadata: name: dsh-home spec: accessModes: ["ReadWriteOnce"] resources: requests: storage: 5Gi # storageClassName omitted => cluster default