mitos is the command-line interface for snapshot-fork sandboxes. It drives
the sandbox lifecycle (create, exec, file IO, fork, terminate, list) against a
Kubernetes cluster, and brings a local kind dev cluster up or down for a
one-command local-dev loop.
go build -o mitos ./cmd/mitos/mitos run <command> [--pool P] [--timeout N] create a sandbox, run the
command, terminate, exit with
the command's exit code
mitos sandbox create [--pool P] create a sandbox, print its id
mitos sandbox ls [-n namespace] [-A] list sandboxes
mitos sandbox exec <id> <command...> run a command in a sandbox
mitos sandbox fork <id> [--replicas N] fork a sandbox, print new ids
mitos sandbox terminate <id> destroy a sandbox
mitos ws create <name> create an empty workspace
mitos ws ls [-n namespace] list workspaces
mitos ws log <workspace> list revisions, newest first
mitos ws diff <workspace> <revision> content-hash diff vs the parent
mitos ws fork <src-ws> <revision> <dst-ws> branch a committed revision
mitos ws revert <workspace> <revision> set the head to a past revision
mitos ws rm <name> delete a workspace and revisions
mitos ws bind <id> <workspace> bind a sandbox to a workspace
mitos dev up [--skip-cluster-create] bring a local kind dev cluster
up with a mock control plane
mitos dev down delete the local kind dev cluster
mitos doctor [-n namespace] run the node + install preflight
and print remediation
mitos auth login --token <session> writes a credential profile to
~/.config/mitos/credentials.json (mode 0600, honoring MITOS_CONFIG_DIR).
The profile holds the session token, the resolved email, and the default org.
One login authenticates every agent-facing surface. The Python and TypeScript
SDKs and mitos-mcp resolve the bearer token with this precedence:
- an explicit argument (the SDK
api_key, themitos-mcp --tokenflag); - the
MITOS_API_KEYenvironment variable; - the
tokenfield of~/.config/mitos/credentials.json(honoringMITOS_CONFIG_DIR); - none, in which case the surface runs tokenless (the standalone
sandbox-serveraccepts that).
The credential file's token is sent verbatim as the Authorization: Bearer
value; the hosted gateway decides whether it is valid. If your deployment
requires a scoped API key minted with mitos auth keys create rather than the
raw login session, set that key as MITOS_API_KEY or pass it explicitly; it
overrides the file. The token VALUE is never logged or echoed in an error.
mitos doctor runs an install/node preflight and prints a report
with an actionable, LLM-legible remediation per failing check. It is meant to run
on a KVM worker node, or as an in-cluster Job, and exits non-zero if any check
fails so it composes in an install pipeline.
Checks:
kvm-device:/dev/kvmis present and a usable character device.kernel-module-nf_tables/-vhost_vsock/-tun: the required kernel modules are loaded.guest-kernel: the guest kernel image is staged where forkd boots from (default/var/lib/mitos/vmlinux).pki-secrets: the controller mintedmitos-ca,mitos-forkd-tls, andmitos-controller-tls.image-pull-secret: a pull secret is present (a WARN, not a fail: public images still pull, only private registries need it).psa-privileged: the install/pool namespace carriespod-security.kubernetes.io/enforce=privileged.
The cluster checks (PKI, pull secret, PSA) read object PRESENCE only, never a
Secret's contents, so a report can never leak a secret value. The node checks
are meaningful on a Linux KVM node; on a workstation they honestly report
"absent". Without a reachable kubeconfig the cluster checks are skipped and the
node checks still run. See docs/platforms/host-prerequisites.md for the
host/kernel checklist doctor enforces.
A Workspace is durable, forkable agent state independent of any single
sandbox. Its revisions form a DAG, and the verbs are git-shaped:
mitos ws create <name>creates an emptyWorkspace.mitos ws lslists workspaces with their head revision, revision count, and whether the head is resumable (paired with a memory snapshot).mitos ws log <workspace>lists the workspace's revisions newest first, each with its phase, resumable flag, and lineage (fromClaim:<n>,fromWorkspaceRevision:<n>, orroot).mitos ws diff <workspace> <revision>prints the path-level content-hash diff recorded for a revision (captured when a sandbox terminates with a{diff: true}output).mitos ws fork <src-ws> <revision> <dst-ws>branches a committed revision into an existing destination workspace. A fork is a content-addressed branch: the new revision shares the parent's content manifest, so no bytes are copied. Forking an uncommitted revision is refused with an LLM-legible error.mitos ws revert <workspace> <revision>sets a workspace head back to a past revision by creating a new tip that shares that revision's content. Revisions are immutable, so a revert is a new tip, never a history rewrite.mitos ws rm <name>deletes a workspace; its revisions are garbage-collected by owner reference.mitos ws bind <sandbox-id> <workspace>binds a running sandbox to a workspace. A sandbox binds one workspace for its lifetime; re-binding to a different workspace is refused.
Global flags --namespace/-n and --pool may appear before the subcommand.
mitos run exits with the executed command's exit code so it chains in shell
pipelines.
For every run and sandbox verb, mitos resolves a Kubernetes connection
from the standard kubeconfig (KUBECONFIG, --kubeconfig, or in-cluster). It
then:
sandbox createcreates aSandboxwithspec.source.poolRefreferencing the pool and waits for it to reach theReadyphase, then prints the sandbox name as the sandbox id.sandbox exec/ file IO reads the per-sandbox bearer token from the sandbox's Secret at request time and calls the sandbox's HTTP API. The token value is held in memory only for the request and is never logged; it is redacted from any error string.sandbox forkcreates aSandboxwithspec.source.fromSandboxand waits for the requested number of forks to beReady.sandbox lslistsSandboxes (a namespace with-n, all namespaces with-A, or the backend default otherwise).sandbox terminatedeletes theSandbox, which the controller reaps.
This is the same Sandbox path the controller and forkd implement; the CLI
is a thin client over the CRDs plus the token-scoped HTTP exec.
mitos dev up brings up a local kind cluster running a MOCK control plane so
the full claim path completes without KVM:
kind create cluster(tolerating an already-existing cluster; skipped with--skip-cluster-createto target a cluster you already stood up).kubectl apply -f deploy/crds/(the CRDs).kubectl apply -k deploy/dev/(the dev overlay).
The dev overlay (deploy/dev/) runs:
- the controller with
--mock --disable-pki-bootstrap, so it dials forkd over insecure gRPC (no control plane CA or TLS Secrets). - a forkd DaemonSet with
--mockand no TLS flags, using the no-KVM mock fork engine. It mounts no/dev/kvmand carries nomitos.run/kvmnodeSelector, so it schedules on the plain kind node. - a default
SandboxPoolnameddev-defaultwithspec.templateinline in thedefaultnamespace.
The controller discovers the mock forkd by its app.kubernetes.io/component: forkd pod label, builds the dev-default pool snapshot over insecure gRPC, and a
claim forks via the mock engine and reaches Ready:
mitos dev up
mitos sandbox create --pool dev-default # prints the sandbox id, Ready
mitos sandbox ls
mitos sandbox terminate <id>
mitos dev downThe dev manifests reference the mitos-controller:ci and mitos-forkd:ci
image tags with imagePullPolicy: IfNotPresent. Build and load them before
mitos dev up (CI does this automatically):
docker build -f Dockerfile.controller -t mitos-controller:ci .
docker build -f Dockerfile.forkd -t mitos-forkd:ci .
kind load docker-image mitos-controller:ci --name mitos-dev
kind load docker-image mitos-forkd:ci --name mitos-devThe dev cluster uses the mock fork engine, which has NO guest VM. A claim
reconciles to Ready and the control-plane dispatch works, but a real in-VM
exec is not exercised on the dev cluster. To run real sandboxes locally you need
a node with /dev/kvm and the production manifests (deploy/controller/ +
deploy/daemon/) with the mitos.run/kvm=true node label.
PROVEN in CI:
- command dispatch for
runand everysandboxverb; - the cluster
Sandboxclaim path with token-scoped exec; mitos dev uporchestration (CRDs + mock controller + mock forkd + pool);sandbox lsover the control plane;- on the dev mock cluster on kind:
sandbox createreachesReady,sandbox lsshows it, andsandbox terminateremoves it.
The mock-engine exec limitation above is the one gap: real in-VM exec is proven
by the KVM CI of the API, not by the kind dev smoke.
kubectl-mitos is a separate kubectl plugin for the OPERATOR persona: a
cluster admin who inspects and operates the sandbox objects already in the
cluster. Installed as kubectl-mitos on PATH, it is invoked as
kubectl mitos <verb> and reads the cluster connection from the standard
kubeconfig resolution.
go build -o /usr/local/bin/kubectl-mitos ./cmd/kubectl-mitos/kubectl mitos ls [-n ns] [-A] list Sandboxes
kubectl mitos ps [name] [-n ns] [-A] list fork Sandboxes (or one sandbox's forks)
kubectl mitos tree [--pool P] [-n ns] [-A] render the fork/lineage DAG
kubectl mitos top [-n ns] [-A] per-sandbox CoW-aware metering
kubectl mitos logs <sandbox> [-n ns] husk stub pod console for a claim
kubectl mitos exec <sandbox> [-n ns] -- cmd run a command in a sandbox
tree walks the lineage DAG: each Sandbox with spec.source.poolRef is a root,
and a Sandbox with spec.source.fromSandbox nests under whatever sandbox its
spec.source.fromSandbox names (a pool-ref sandbox OR another fork sandbox,
so a multi-level fork chain nests). Siblings sort by name; an orphan fork sandbox
whose source is out of scope is surfaced as its own root rather than dropped.
--pool <name> scopes to one pool via a transitive walk over the source refs.
top shows per-sandbox CoW-aware metering pulled from each node's forkd
GET /v1/metering endpoint (operational data on the same access class as
/metrics and /healthz). The columns are HONEST about what they mean:
UNIQUE-MEMis the marginal unique (private-dirty) memory a fork actually adds. It is NOTmemory.current.SHARED-MEMis the shared-once template attribution: the page set every fork of a template maps copy-on-write, counted once per template at the node level (seeinternal/metering).UNIQUE-DISKis the backing storage the sandbox alone owns.
A sandbox with no metering datum (no endpoint, an unreachable forkd, or no matching row) shows a dash in every metered cell, never a zero and never a fabricated value.
logs <sandbox> prints the husk stub pod console for the claim (the
mitos.run/husk pod labeled mitos.run/claim=<claim>) via the Kubernetes
pod-logs API, then a one-line guest-console note. On a mock or no-VMM control
plane (kind) there is no husk pod or no live guest, so the stub console is
reported absent and the guest console states it needs a running sandbox: the
guest serial/vsock console streams only from a live VMM, not from this
read-only operator path.
exec <sandbox> -- <cmd> runs a command in the sandbox over the forkd HTTP
sandbox API, authenticating with the per-sandbox bearer token read from the
claim's <claim>-sandbox-token Secret: the SAME gate the SDK uses, never
bypassing auth. The token value is held only for the request, never logged, and
redacted from any error string. A claim that is not Ready (or has no endpoint,
or no token Secret) yields a clear, actionable error rather than a hang. The
in-sandbox command's exit code becomes the plugin's exit code so it chains in
shell pipelines.
On kind the mock engine has no guest VM, so exec/top/logs of a REAL running
sandbox are the KVM/bare-metal tail; the kind-e2e smoke proves ls/ps/tree
at the object level. cp and port-forward for operators are not yet available.
- workspace verbs (
mitos ws log|diff|revert|branch) pending Workspace; mitos pool create|refreshbeyond whatdev upneeds;- streaming exec / PTY (
exec_stream) pending the Connect protocol; - a
curl | shinstaller andget.mitos.rundistribution; - shell completions and a code-interpreter-compatible API shim.