Deploy Jaiph as a standalone runtime image

The published runtime image ghcr.io/jaiphlang/jaiph-runtime (built from runtime/Dockerfile) already contains jaiph, the claude, cursor, and codex agent backends, and a full engineering toolchain. This guide runs that image as the runner itself, with docker run on any Linux machine, in CI, or as a Kubernetes pod. You supply credentials and .jh files, and the container does the rest. There is no host jaiph process and no host Docker daemon involved.

There is a different mode called the host-orchestrated Docker sandbox, where a host jaiph run clones your workspace and launches this same image as a disposable root filesystem. For that mode, see Run in a Docker sandbox and Sandboxing instead. This guide covers the opposite direction, where the image itself is the deployment.

Standalone mode has no jaiph-managed sandbox

Read this before you deploy. In standalone mode Jaiph does not create a sandbox. Isolation is whatever your deployment already provides, which is the container or pod boundary and nothing more:

Under the host-orchestrated sandbox (Sandboxing) Jaiph drops capabilities, filters the workspace to a git-defined snapshot, and enforces an environment allowlist. Standalone mode has none of those steps, and the only boundary is the container runtime you chose.

Run one workflow with docker run

Mount your working directory at /work, set it as the working directory, and write out the full command. The image sets no ENTRYPOINT, so jaiph run … is the container command exactly as you type it:

# claude backend (Anthropic)
docker run --rm -e ANTHROPIC_API_KEY -v "$PWD":/work -w /work \
  ghcr.io/jaiphlang/jaiph-runtime jaiph run flow.jh

The credential env var depends on the backend the entry file selects:

# cursor backend
docker run --rm -e CURSOR_API_KEY -v "$PWD":/work -w /work \
  ghcr.io/jaiphlang/jaiph-runtime jaiph run flow.jh

# codex backend (OpenAI HTTP API)
docker run --rm -e OPENAI_API_KEY -v "$PWD":/work -w /work \
  ghcr.io/jaiphlang/jaiph-runtime jaiph run flow.jh

-e ANTHROPIC_API_KEY with no =value forwards the value from your shell environment. The claude backend also accepts CLAUDE_CODE_OAUTH_TOKEN in place of ANTHROPIC_API_KEY. A workflow with no prompt step needs no credential at all. Run artifacts land under /work/.jaiph/runs/, and because /work is your bind-mounted directory, they persist on the host after the container exits.

Pin the tag or a @sha256: digest for reproducible runs, for example ghcr.io/jaiphlang/jaiph-runtime:<version>.

In CI

The image is not required in CI. Jaiph already runs headless on a standard Linux runner without it. For example, .github/workflows/nightly-engineer.yml installs jaiph via docs/install-from-local.sh (which also builds runtime/Dockerfile and registers it as the sandbox image), installs the agent CLI, and runs a workflow unattended in the normal Docker sandbox. A GitHub-hosted Linux runner is itself a virtual machine with a Docker daemon, so that path keeps the host-orchestrated sandbox and needs no published image.

Use this image when you want the whole toolchain and all three backends preinstalled with nothing to build. Because the container has no nested Docker daemon and the image bakes JAIPH_UNSAFE=true, it runs in host mode where the container is the sandbox, and the docker run one-shot above fits into any CI step:

- name: Run workflow
  run: |
    docker run --rm -e ANTHROPIC_API_KEY -v "$PWD":/work -w /work \
      ghcr.io/jaiphlang/jaiph-runtime:<version> jaiph run flow.jh

Kubernetes

A complete, apply-ready manifest lives at docs/deploy/k8s.yaml. It defines a Deployment and a Service, and it deliberately leaves credentials out of the file. Create the jaiph-credentials Secret out-of-band first:

kubectl create secret generic jaiph-credentials \
  --from-literal=JAIPH_SERVE_TOKEN="$(openssl rand -hex 32)" \
  --from-literal=ANTHROPIC_API_KEY="sk-ant-..."   # only the backend key(s) your workflows use
kubectl apply -f docs/deploy/k8s.yaml

The Deployment references the Secret as a required envFrom, so a missing Secret holds the pod in CreateContainerConfigError instead of ever starting an unauthenticated runner. kubectl apply --dry-run=client -f docs/deploy/k8s.yaml is a fast schema check. The real deployment contract is tested end-to-end on a kind cluster by e2e/tests/150_k8s_deploy.sh, which runs in CI. It applies the manifest, verifies the Secret gate and the hardening described below, invokes the health workflow over HTTP with bearer auth, and reads the run’s journal back from the runs volume.

The manifest runs jaiph serve --host 0.0.0.0 as a long-lived HTTP runner (see Serve workflows over HTTP), with JAIPH_SERVE_TOKEN sourced from the Secret and liveness and readiness probes on GET /healthz, which stays open and needs no bearer token. The same Service port serves both the REST and OpenAPI API and MCP Streamable HTTP at POST /mcp, so a network MCP client reaches the pod’s workflows through the same ingress and bearer token, with no extra port or process. The manifest sets the following, and every item is reflected in the file:

The same security posture applies. The pod runs in host mode because JAIPH_UNSAFE=true is baked, so isolation is the pod boundary. The manifest configures that boundary, and there is no jaiph-managed sandbox inside.