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).