Skip to content

🤖 Pluggable Runtime Phase 2 & 3: DockerRuntime + SSHRuntime, config + UX #238

@ammar-agent

Description

@ammar-agent

Summary

Pluggable Runtime — Phase 2 and 3: add full Docker and SSH support on top of the new Runtime interface. This tracks all remaining work to make runtimes selectable per-workspace, production-ready, and well-tested.

Generated with cmux

Background

  • Phase 1 (PR 🤖 Add pluggable runtime abstraction layer #178) introduced a minimal Runtime interface and LocalRuntime, and refactored file tools to use it. Bash tool remains local-only for now.
  • Goal: provide two additional Runtime implementations: DockerRuntime and SSHRuntime, plus a runtime factory and workspace-level configuration + minimal UX.

Goals

  • End-to-end: tools run seamlessly in Docker containers or over SSH
  • Near-parity with LocalRuntime for file I/O and exec behavior (timeouts, aborts, env injection)
  • Clear UX to select/configure runtime per workspace
  • Strong test coverage and guardrails (security, path sandboxing)

Non-goals

  • Container lifecycle management (build/run images) — assume container exists and is running
  • Remote provisioning or key management — assume credentials/hosts are provided
  • Windows containers or non-POSIX remotes (can be revisited later)

Deliverables

  • DockerRuntime and SSHRuntime classes implementing Runtime
  • Runtime factory and workspace-driven selection
  • Config schema and validation, IPC plumbing
  • Tests: unit + integration; optional e2e probes
  • Docs: user guide and troubleshooting

Work Items

1) Runtime Factory and Workspace Config

  • Add runtimeConfig to WorkspaceMetadata (types + persistence)
    • Types:
      • { type: "local" }
      • { type: "docker"; containerId: string; workdir: string; pathMap?: { host: string; container: string }[] }
      • { type: "ssh"; host: string; user: string; port?: number; keyPath?: string; password?: string; workdir: string }
  • Create src/runtime/runtimeFactory.ts with createRuntime(config: RuntimeConfig): Runtime
  • aiService: choose runtime via workspace metadata (default to local)
  • IPC + preload: plumb set/get runtime config per workspace
  • Validation + helpful error messages when config incomplete

2) DockerRuntime

  • Implement exec() via docker exec -w <cwd> -e ... <containerId> bash -lc <script>
    • Support stdin piping (use --env and printf/pipe; prefer docker exec with -i)
    • Propagate env and secrets
    • Timeouts and abortSignal → kill the docker exec process
  • Implement readFile() as docker exec <id> cat <path> (UTF-8)
  • Implement writeFile() using temp + mv for atomicity
    • Option A: docker exec sh -lc 'umask 077; cat > /tmp/.cmux-XXXX && mv ...' with stdin
    • Option B: docker cp to a temp inside, then docker exec mv
  • Implement stat() using stat -c '%s %Y %F' or portable ls -ld fallback; parse robustly
  • Implement exists() via exit code of test -e or parsing stat
  • Optional path mapping: if host paths appear in inputs, translate to container paths using pathMap
  • Tests
    • Unit tests: command shaping, parsing, error mapping
    • Integration: run ephemeral container (alpine/debian), mount a temp dir, verify exec and file ops
    • Failure cases: container not running, permission denied, long outputs, timeouts

3) SSHRuntime

  • Choose transport: prefer ssh2 (+ ssh2-sftp-client for SFTP) for persistent connection and SFTP
    • Add deps with bun: bun add ssh2 ssh2-sftp-client
  • Implement connection lifecycle and pooling per workspace
  • Implement exec() with PTY disabled; handle env, stdin, timeouts, abort
  • Implement readFile() via SFTP get (utf-8)
  • Implement writeFile() via SFTP to temp + rename (atomic-ish), umask 077
  • Implement stat() and exists() via SFTP stat/lstat
  • Strict host key checking: accept known hosts or config flag; never auto-accept silently
  • Tests
    • Unit tests with fakes/mocks for ssh2 and sftp
    • Integration (optional): spin a local OpenSSH server in CI, key auth, temp user/chroot if feasible
    • Failure cases: auth fail, host unreachable, disconnect mid-transfer, permissions

4) Tooling Parity and Edge Cases

  • Confirm validatePathInCwd works with remote/container paths (POSIX). Document Windows behavior as out-of-scope.
  • Ensure env injection does not leak secrets to logs/stdout/stderr
  • Verify line/byte limits and overflow behavior remain enforced at tool layer (unchanged)
  • Ensure abort + timeout semantics are the same across runtimes

5) UX/DevEx

  • Minimal UI to set runtime per workspace (Projects sidebar or Workspace settings)
    • Docker: containerId, workdir, optional path map
    • SSH: host, user, key/path, port, workdir, “Test connection” button
  • Status indicator showing active runtime (local/docker/ssh)
  • Helpful error surfaces with remediation links

6) Telemetry & Logging

  • Add structured logs around runtime selection, exec durations, I/O sizes (no secret values)
  • Feature flag or config metric to gauge adoption

7) Docs

  • User docs (mdbook): selecting/configuring runtimes, examples, troubleshooting
  • Inline developer docs in runtime classes

8) Security Review

  • Secrets handling across boundaries (env vars, SFTP)
  • Host key verification policy
  • Container path mapping and sandboxing

Acceptance Criteria

  • With DockerRuntime selected, file tools and bash work against a running container-mounted workspace
  • With SSHRuntime selected, file tools and bash work against a reachable POSIX host
  • Tool tests remain unchanged and pass under LocalRuntime; Docker/SSH have dedicated runtime tests
  • Timeouts/abort behave consistently across runtimes
  • Clear UI to configure and confirm a runtime per workspace

Risks / Open Questions

  • Path mapping complexity for Docker (bind mounts vs copy)
  • Cross-distro availability of stat flags (choose portable parsing)
  • Host key management UX (import from ~/.ssh/known_hosts?)
  • Windows remotes and non-POSIX paths (out-of-scope for first cut)

Tracking

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions