Skip to content
Pools

Images and runner

A SandboxPool template names the image, the command, optional runnerPath, and optional attachCommand. Callers cannot override those. Pool shape: Sandbox pools.

Durable execution

Kobe uses the same supervisor for wait-mode and detached commands. A raw Kubernetes exec cannot apply a working directory without a shell, cannot always recover the exact exit code, and dies with its connection. A pool therefore offers the durable execution API only if its image ships kobe-runner and the template says where:

template:
  defaultContainer: workspace
  runnerPath: /kobe-runner

Copy the binary into your own agent image. Pin a release tag that matches the operator (v0.47.0 here):

COPY --from=zondax/kobe-runner:v0.47.0 /kobe-runner /kobe-runner
RUN install -d -o 65532 -g 65532 -m 0700 /var/run/kobe/executions
USER 65532:65532

Replace 65532:65532 with the UID and GID your workload uses. The directory must exist and be writable by that identity; ordinary non-root users cannot create new directories directly under /var/run. The runner is a static binary with no dependencies on the base image.

That spool is not an isolation boundary. The runner and workload use the same container identity, so the workload can remove or replace its files. Kobe never restores spawn authority from a missing spool and never uses a runner report as proof that target capacity is safe to reuse. An execution that crossed startedAt therefore holds one active slot until that exact Sandbox target is destroyed.

Without runnerPath, POST /v1/sandbox-leases/{id}/executions returns 501 Not Implemented in both modes. Selecting wait mode must not silently remove working-directory, exact-exit-code, process-group cancellation, or reconnectability.

Output from every durable execution is retained inside the container until the Sandbox is torn down and can be read back through GET /v1/sandbox-leases/{id}/executions/{execution}/logs. Kobe retains at most 1 MiB of stdout and 1 MiB of stderr per execution, and accepts at most 256 idempotency keys over one Sandbox lifetime. Requests beyond the limit return 409 Conflict with reason execution_limit_exhausted: retrying cannot succeed; request a new lease.

Cancelling an execution asks the runner to terminate its whole process group. Lease release first closes new access, then asks every recorded runner to cancel. Runner state remains tenant-controlled, so Kobe retains each started execution record and slot, destroys the exact Sandbox target, and retires them only after target absence is proven.

Caller commands: executable resource commands.

attachCommand

A bare kobe attach joins the container's own process. For an image whose job is to host sessions that is the wrong landing place. Declare what attach should run instead:

template:
  defaultContainer: agent
  attachCommand: ["zellij", "attach", "--create", "kobe"]

Now kobe attach dev lands in a multiplexer session that outlives the connection. The caller's own -- argv still wins, and a pool that declares nothing keeps attaching to the container process.

An image that ships kobe-runner gets the same without a multiplexer:

template:
  defaultContainer: agent
  runnerPath: /kobe-runner
  attachCommand: ["/kobe-runner", "session", "attach", "--name", "main"]

A bare kobe attach dev then resumes one shell session and redraws its screen. Callers who also want automatic reconnects use kobe attach dev --session main.

The command comes from the pool, never the caller. Declaring it also changes which subresource attach calls, from pods/attach to pods/exec, so the grant it needs is the exec verb either way.

Sandbox images

zondax/kobe-sandbox includes kobe-runner, mise, tmux, Git, GitHub CLI (gh), OpenSSH, JSON/search/network diagnostics, OpenSSL headers, and a C toolchain. It also ships kobe-sshd, so an SSH client can reach the sandbox through kobe ssh-proxy without any port being declared. The workload runs as 65532:65532, with HOME=/home/agent and a writable /home/agent/work. Clone projects there and run mise install; the image trusts mise configurations under that workspace and puts tool shims on PATH for direct kobe exec commands.

The image also ships Node 24 with the Codex and Claude Code CLIs, on PATH for kobe exec and for SSH login shells. No credentials are baked in: each lease signs in its own user. The CLIs track their latest release at image build time.

The same image carries a desktop: the Tauri 2 Linux system prerequisites, software rendering, Xvfb, Openbox, noVNC, Firefox, and a visible terminal. Callers: Desktop. Firefox is registered for HTTP and HTTPS links, so OAuth flows opened by desktop applications stay inside the noVNC desktop. There is no separate GUI image. Nothing starts the desktop until you ask, so a headless lease pays only the image size. Project tools beyond Node still come from the project's mise.toml. Pin a release tag or digest in your pool.

To use the desktop from a pool, declare its port in the template (alongside your resource requests and limits):

template:
  defaultContainer: workspace
  runnerPath: /kobe-runner
  attachCommand: ["tmux", "new-session", "-A", "-s", "agent"]
  containers:
    - name: workspace
      image: zondax/kobe-sandbox:v0.47.0
  exposedPorts:
    - name: desktop
      container: workspace
      port: 6080

The desktop is opt-in. Start it with the lease-lived manager:

kobe exec gui -- kobe-desktop start
kobe exec gui -- kobe-desktop status

kobe-desktop start is idempotent and runs the desktop supervisor in a dedicated tmux server, so it outlives the one-hour runner-execution limit. Use kobe-desktop logs, kobe-desktop terminal, or kobe-desktop browser to inspect the session. kobe-desktop stop stops only that managed desktop.

Here gui is your lease ID or alias. In another terminal, forward its declared port:

kobe port-forward gui 6080:6080

Open http://127.0.0.1:6080/vnc.html?autoconnect=1&resize=scale&path=websockify. This is the whole virtual 1440×900 desktop. It starts with a terminal and a blue background; right-click the desktop for Terminal, Browser, and Desktop status. Run your project's normal Linux desktop development command in another execution, using --cwd /home/agent/work/your-project. Each execution already inherits DISPLAY=:99, the X11 authority file, and the session D-Bus address.

kobe-desktop-up remains the foreground low-level supervisor for diagnostics. Starting it directly through kobe exec is limited to that execution's one-hour timeout; prefer kobe-desktop start for an interactive workspace.

Both noVNC (6080) and its VNC backend (5900) listen only on the Sandbox's loopback interface; X11 uses a Unix socket with a per-session cookie. Access comes through Kobe's authorized port forward. Authorize port 6080 with an explicit port declaration or a permitted development port range. This does not require host networking, privilege escalation, or container capabilities.

kobe-desktop-up stays in the foreground and supervises every desktop component. A component exit fails the execution and stops its siblings; cancelling its runner execution or releasing the lease stops the desktop. When started in tmux, use Ctrl-c in that pane to stop it. Only one desktop can run in a Sandbox.

Build locally with docker buildx bake sandbox --load (set PLATFORM=linux/arm64 on an ARM host).

Available for:
Apple macOS logomacOSMicrosoft Windows logoWindowsLinux logoLinux
Download Kunobi