Compose development flow
Overview
./bin/occ dev up starts local OpenClaw Enterprise development from a checkout.
The scripts/dev-up entry point selects the same profile. By default OCC and
PostgreSQL run in Compose with Docker Compute. Operators can explicitly select
Kubernetes Compute with either the Compose control plane or the
local k3d-only profile. The
Kubernetes profile can select the OpenShell Sandbox Driver for its fail-closed
path; it prepares pinned OpenShell infrastructure and reconciles rendered
workspace-chart resources before reporting readiness.
Compose-backed profiles perform host preflight, select Docker Engine or Podman,
prepare runtime images, start Compose, and wait for PostgreSQL migration,
Installation bootstrap, API health, and worker readiness. The Kubernetes-only
profile performs the equivalent readiness checks inside its owned cluster.
Startup proves authenticated Installation access with a protected local
bootstrap service key; it does not create an Agent or prove model execution.
For Docker Compute, the worker can reconcile Namespace infrastructure, but Agent deployment stops at harness authentication admission because Docker Compute rejects bindings. Kubernetes Agent execution continues through the selected Kubernetes Compute Driver and the authenticated Agent deployment procedure.
Entry Points
- Trigger:
./bin/occ dev up [--key-output PATH] [-- COMPOSE_GLOBAL_OPTIONS...]from the repository root, followed by authenticated Namespace operations. - Source:
scripts/dev-up:require_command,internal/occdev/up.go:Up, andinternal/occdev/openshell.go:prepareOpenShell. - Assumptions: Docker Engine with Compose, or Podman with
podman-compose; Bash, curl, Python 3, andyqv4 for the Docker profile; writable PostgreSQL and Configuration volumes; loopback API publication; executablebin/occbuilt withpnpm cli:build. Startup needs no model credential.
The Kubernetes profile additionally uses compose.kubernetes.yaml,
internal/occdev, k3d, and kubectl. ./bin/occ dev down owns profile cleanup;
scripts/dev-down dispatches to it. The local Kubernetes development guide
owns the operator procedure and destructive cleanup boundary.
Flow
graph TD
A["./bin/occ dev up"] --> Profile{"Compute profile"}
Profile -->|Docker| B["Preflight host tools, resolve Podman machine connection,<br/>and inspect Compose config"]
Profile -->|Kubernetes| KPre["Pin local engine endpoint<br/>and reject existing resources"]
KPre --> KControl{"Control-plane profile"}
KControl -->|Kubernetes| KOnly["Install PostgreSQL, OCE, and<br/>OpenShell in the owned cluster"]
KOnly --> KWorkspace
KControl -->|Compose| KConfig["Validate Compose and claim<br/>private state with snapshot"]
KConfig --> KStart["Bootstrap OCC and create<br/>the owned k3d cluster"]
KStart --> KReady["Import runtime and start<br/>API and Kubernetes worker"]
KReady --> KSandbox{"Sandbox profile"}
KSandbox -->|none| KProof
KSandbox -->|OpenShell| KOpenShell["Install pinned Agent Sandbox, RuntimeClass,<br/>Gateway, and render workspace resources"]
KOpenShell --> KWorkspace["Driver applies workspace resources and creates<br/>the default Namespace's owned Workspace"]
KWorkspace --> KFailClosed["Default Namespace ready;<br/>Agent projection remains fail closed"]
KFailClosed --> KProof
KProof["Prove authenticated<br/>Installation access"]
KProof --> KDown["./bin/occ dev down reuses<br/>recorded endpoint and project"]
KStart -->|failure| KRollback["Roll back owned resources<br/>retain state if cleanup fails"]
KReady -->|failure| KRollback
KProof -->|failure| KRollback
KDown --> KRemove["Stop reconcilers and delete<br/>owned cluster and volumes"]
KRemove -->|success| KDone["Remove private state"]
KRemove -->|failure| KRetain["Keep state for recovery"]
B --> C["Select quickstart runtime image or validate custom images"]
C --> D["Selected Compose starts PostgreSQL, migrate, bootstrap, API, and worker"]
D --> E["Copy bootstrap service-key response to private local file"]
E --> F["./bin/occ installation get proves authenticated access"]
F --> G["Operator creates Namespace through authenticated API"]
G --> H["Worker claims durable Namespace operation"]
H --> I["Docker Driver ensures owned tenant network"]
I --> J["Namespace becomes ready"]
F --> K["Operator requests Agent deployment on Docker Compute"]
K --> L["Admission rejects missing or unsupported harness binding"]
J --> M["Authorized deletion removes owned Namespace resources"]
D -->|operator cleanup| CDown["occ dev down keeps<br/>selected engine connection"]
M --> CDown
CDown --> CVolumes{"--volumes?"}
CVolumes -->|No| CKeep["Remove Compose containers and network;<br/>retain named volumes"]
CVolumes -->|Yes| CDelete["Remove Compose containers,<br/>network and named volumes"]Execution Trace
1. Start and initialize the local stack
scripts/dev-up, apps/controller/src/server.mjs:start
Docker or Podman Compose startup owns engine selection, database initialization, local API admission and worker startup.
2. Prepare Namespace infrastructure and enforce Agent admission
apps/controller/src/drivers/compute/docker/index.ts:DockerComputeDriver
Namespace execution and Agent admission traces API authorization, durable work, tenant network ownership, unsupported Agent authentication and exact resource removal.
3. Clean up Docker or Podman Compose
internal/occdev/down.go:Down, internal/occdev/down.go:podmanComposeArgs
occ dev down uses the selected engine and forwarded Compose project and file
options, including after partial startup. Podman retains the caller's selected
connection. Its reported API socket supplies OCC_CONTAINER_ENGINE_SOCKET only
to resolve the worker mount; a socket inside a macOS VM is not substituted for
the host connection. An unavailable engine or invalid socket fails cleanup.
Compose removes its project containers and network. Named database,
Configuration, and bootstrap volumes remain unless --volumes is explicit.
Agent-owned containers and Namespace networks remain the responsibility of
their platform deletion workflows; follow safe development shutdown.
4. Start and clean up Kubernetes development
internal/occdev/up.go:Up, internal/occdev/down.go:Down.
The Kubernetes startup and cleanup trace follows profile selection, the private Compose snapshot, k3d creation, runtime import, authenticated readiness, and cleanup through the recorded engine.
5. Prepare the optional OpenShell development profile
internal/occdev/openshell_k3d.go:upK3d,
internal/occdev/openshell.go:prepareOpenShell,
apps/controller/src/drivers/sandbox/openshell.ts:ensureNamespace
With OCC_DEVELOPMENT_CONTROL_PLANE=kubernetes, OpenShell uses the
Kubernetes-only lifecycle before Compose rendering. It pins K3s and OpenShell
inputs, installs PostgreSQL, OCE, and one central Gateway in oce-system, and
publishes only an admitted API proxy on host loopback.
By default, the Kubernetes lifecycle starts PostgreSQL, migration, bootstrap,
the API, and the worker in Compose. It installs the central Gateway in
openshell-system and exposes its fixed NodePort only to the owned container
network. In both modes, the worker
creates the bootstrap Namespace through the regular Compute workflow. The
Sandbox Driver applies rendered workspace resources and the operator label
before creating the corresponding Gateway Workspace. Startup waits until the
OCC Namespace becomes ready. See the
OpenShell flow.
Debugging and Verification
./scripts/dev-upshould show PostgreSQL readiness, migration completion, API listening on127.0.0.1:${OPENCLAW_DEV_PORT:-3000}, fresh-database initialization,worker.startedwithcomputeDriverIdset tocompute-docker-development, a private copied service-key path, and a successful authenticated/installationproof.- With
OCC_DEVELOPMENT_COMPUTE_DRIVER=kubernetes, startup should instead report Kubernetes Compute, a private kubeconfig, and the disposable k3d context; it does not mount the engine socket into the Kubernetes worker. - With
OCC_DEVELOPMENT_SANDBOX_DRIVER=openshellandOCC_DEVELOPMENT_CONTROL_PLANE=kubernetes, startup should also report Kubernetes-only deployment, one readyoce-system/openshell-gatewayService, theopenshell-sandboxRuntimeClass, the Agent Sandbox CRD, and workspace resources in the bootstrap Namespace. The selected real dev-up case also reads the owned Workspace through the Gateway API. This proves infrastructure readiness, not a model turn. The expected Agent result is the explicit unsupportedsecretKeyRefprojection failure with no Sandbox or Agent Pod. - With the default Compose control plane, OpenShell startup should report
Control plane: Compose, retain a private Compose snapshot, install the Gateway inopenshell-system, and still create the bootstrap Namespace's operator-mode Workspace. - Docker Compute on Podman startup verification should show Podman as the
selected engine, mount only its reported API socket into the worker, and
complete the same authenticated Installation proof without a
dockeralias. The startup flow documents the macOS prerequisite. <engine> network ls --filter label=org.openclaw.enterprise.compute-driver=dockershould show the owned network for a ready development Namespace.- Docker Compute Agent deployment must reject a missing or unsupported harness binding before
workload creation. A worker
OPENAI_API_KEYcannot make it supported. - Retained Docker/Podman model suites currently cannot pass through this admission boundary. See Docker test status; old model-turn evidence does not establish current support.
- Namespace deletion removes its owned resources while preserving unrelated ones.
- The real Compose cleanup case verifies the compiled CLI against a partially started project, including volume retention and explicit deletion on the selected engine connection.
