cyberine
UsageRun recipes

Pluggable execution backends — run workers in Docker or on a remote host (T332)

By default every isolate-step worker provisions a local git worktree and executes its CLI or loop work on the local host. The execution backend abstraction decouples WHERE the worker runs from WHAT it runs — it controls the compute target, not the process sandbox (HOST_SANDBOX_MODE remains the process-confinement layer and still applies independently).

Three backends ship out of the box:

BackendMode valuePrerequisitesUse case
Local (default)localnoneexisting behaviour unchanged
Dockerdockerdocker in PATHisolate each worker in a container; useful when the CI/CD machine has Docker but not all CLIs installed, or for stronger worktree isolation than seatbelt/bwrap
SSHsshssh + rsync in PATH, CYBERINE_SSH_HOST setoff-load heavy build workers to a beefier remote machine while the orchestrator runs on a laptop

Docker backend — bind-mount the local worktree into the container at the same absolute path, run the worker CLI inside it, let the bind mount reflect file mutations back to local so captureDiff() works unchanged:

# Build a custom image with the CLI pre-installed (e.g. opencode).
# The container must be able to find the tool that CYBERINE_BUILD_FLEET workers use.
docker build -t cyberine-worker -f Dockerfile.worker .

CYBERINE_EXEC_BACKEND=docker \
CYBERINE_EXEC_DOCKER_IMAGE=cyberine-worker \
CYBERINE_EXEC_FORWARD_ENV=DEEPSEEK_API_KEY,OPENROUTER_API_KEY \
npm run wf -- .local/dogfood/implement.workflow.ts

SSH backend — rsync the worktree to the remote, run there, rsync mutations back:

# Ensure the remote has ssh+rsync access and the worker CLI installed.
CYBERINE_EXEC_BACKEND=ssh \
CYBERINE_SSH_HOST=user@build-server.internal \
CYBERINE_SSH_KEY=~/.ssh/id_ed25519_build \
CYBERINE_SSH_REMOTE_DIR=/tmp/cyberine-exec \
CYBERINE_EXEC_FORWARD_ENV=DEEPSEEK_API_KEY,OPENROUTER_API_KEY \
npm run wf -- .local/dogfood/implement.workflow.ts

Availability detection and fallback — when the requested backend's prerequisites are not satisfied at run time (no docker binary, or CYBERINE_SSH_HOST not set), the engine logs a [exec-backend] warning on stderr and falls back to local automatically, so existing runs are never broken by an unconfigured env. To enforce a specific backend without fallback:

import { resolveExecutionBackend } from './src/engine/executionBackend.ts';

const backend = resolveExecutionBackend({ mode: 'docker', fallbackToLocal: false });
// throws ExecutionBackendError when docker is unavailable

Compose with the host sandbox — the execution backend and host sandbox are independent layers. A worker can run inside Docker via the execution backend (compute target) while simultaneously confined by the seatbelt host sandbox (process confinement). The HOST_SANDBOX_MODE wrapping in CliAgentAdapter still applies on top of the exec backend.

Extend with a custom backend — push to EXECUTION_BACKENDS before the first resolveExecutionBackend() call:

import { EXECUTION_BACKENDS } from './src/engine/executionBackend.ts';
import type { ExecutionBackend, RunCommandOpts, RunCommandResult } from './src/engine/executionBackend.ts';

class DaytonaBackend implements ExecutionBackend {
  readonly mode = 'daytona' as any;
  isAvailable() { return !!process.env['DAYTONA_API_KEY']; }
  async prepareRemoteWorkspace(local: string) { /* provision workspace via Daytona API */ return remoteUrl; }
  async runCommand(cmd: string, args: string[], opts: RunCommandOpts): Promise<RunCommandResult> { /* ... */ }
  async syncBack(local: string, remote: string) { /* download artefacts */ }
  async cleanupRemoteWorkspace(remote: string) { /* destroy workspace */ }
}

EXECUTION_BACKENDS.push(new DaytonaBackend());