-
Notifications
You must be signed in to change notification settings - Fork 5
Open
Description
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
toWorkspaceMetadata
(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 }
- Types:
- Create
src/runtime/runtimeFactory.ts
withcreateRuntime(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()
viadocker exec -w <cwd> -e ... <containerId> bash -lc <script>
- Support stdin piping (use
--env
andprintf
/pipe; preferdocker exec
with-i
) - Propagate env and secrets
- Timeouts and abortSignal → kill the
docker exec
process
- Support stdin piping (use
- Implement
readFile()
asdocker 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, thendocker exec mv
- Option A:
- Implement
stat()
usingstat -c '%s %Y %F'
or portablels -ld
fallback; parse robustly - Implement
exists()
via exit code oftest -e
or parsingstat
- 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
- Add deps with bun:
- 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()
andexists()
via SFTPstat
/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
- Related: PR 🤖 Add pluggable runtime abstraction layer #178 — Phase 1 Runtime abstraction.
Metadata
Metadata
Assignees
Labels
No labels