epic: optionally run managed OpenCode instances in containers #375

Open
opened 2026-07-18 00:16:02 +02:00 by dries · 2 comments
Owner

Goal

Add an optional container runtime for ocman-managed, one-per-project OpenCode instances so agent commands have a narrower filesystem/process boundary and bounded resources, while preserving native tmux mode.

Why this is not only a launcher change

Ocman controls project launch through EnsureProjectOpencode, but it still reconstructs instance identity from a global OpenCode SQLite database plus host lsof, PID cwd, tmux state, and 127.0.0.1:<port>. Containers need explicit lifecycle metadata, published endpoints, canonical mounts, health, credentials, and recovery after an ocman restart.

The security benefit is meaningful only with narrow mounts and secret injection. Host PID/network namespaces, privileged containers, the Docker socket, broad home mounts, or full SSH/config mounts are out of bounds because they largely defeat isolation.

Intended architecture

Keep hostsvc.Host.EnsureProjectOpencode as the owner-resolved outer seam. Add a host-local OpenCode runtime policy behind it:

  • Native tmux runtime remains supported.
  • Container runtime owns ensure/start, inspect/recover, stop/restart, health, endpoint, mounts, environment, labels, and runtime diagnostics.
  • platforms.Platform continues to own session operations and remains unaware of Docker/Podman.
  • Multi-remote hosts select and execute their own runtime locally.

Principal constraints

  • Managed containers need an explicit project-to-instance registry; host lsof/PID cwd discovery does not work reliably across container namespaces.
  • OpenCode must use a fixed container port, listen on 0.0.0.0, and be published only to a host-loopback port known to ocman.
  • Host and container must initially see project and worktree directories at identical absolute paths.
  • Worktree mounts must cover ocman's sibling .worktrees/<repo> layout without exposing unrelated repositories.
  • OpenCode API authentication must cover HTTP, SSE, autoapprove, judge, probes, and session creation.
  • Attachments need transport or a narrow shared mount rather than arbitrary host cache paths.
  • Host terminal/workflow execution and container agent execution are different environments and need explicit semantics.
  • Container defaults must be non-root, unprivileged, resource-bounded, and must not mount the runtime socket.

Database decision

Decision #384 rejects sharing or bind-mounting host OpenCode SQLite into project containers, especially on Docker Desktop for macOS. Each managed project container owns a persistent OpenCode database; ocman will build a host-local API-fed read model for unified history, routing, projects, and analytics. Native mode keeps its current read-only global database path during migration.

Delivery plan

Child tickets cover lifecycle ownership, authenticated transport, the isolated-data container tracer bullet, sandbox/config defaults, attachments, execution-environment semantics, the host-local read model, and user-facing rollout/diagnostics. #384 precedes #378; #382 is superseded. Dependencies are recorded in each child issue.

Completion criteria

  • Native mode remains supported and its lifecycle races/duplicate paths are corrected.
  • A user can opt a host into container mode and launch/recover a project OpenCode instance.
  • Worktree and same-directory sessions work through the managed endpoint.
  • OpenCode traffic is authenticated and not exposed beyond host loopback.
  • Mounts, credentials, UID/GID, capabilities, resources, and attachments have safe defaults.
  • No host/global OpenCode SQLite database is shared or bind-mounted into project containers.
  • Runtime state and failures are observable in ocman.
  • The long-term database isolation direction is explicitly decided.

Non-goals

  • Containerizing ocman itself.
  • Replacing hostsvc.Host or creating a container-specific platform adapter.
  • Host PID/network namespace sharing or privileged runtime access.
  • Silently mounting the complete user home, SSH directory, credential stores, or Docker/Podman socket.
  • Implementing the complete host-local read model inside the runtime launcher ticket.
## Goal Add an optional container runtime for ocman-managed, one-per-project OpenCode instances so agent commands have a narrower filesystem/process boundary and bounded resources, while preserving native tmux mode. ## Why this is not only a launcher change Ocman controls project launch through `EnsureProjectOpencode`, but it still reconstructs instance identity from a global OpenCode SQLite database plus host `lsof`, PID cwd, tmux state, and `127.0.0.1:<port>`. Containers need explicit lifecycle metadata, published endpoints, canonical mounts, health, credentials, and recovery after an ocman restart. The security benefit is meaningful only with narrow mounts and secret injection. Host PID/network namespaces, privileged containers, the Docker socket, broad home mounts, or full SSH/config mounts are out of bounds because they largely defeat isolation. ## Intended architecture Keep `hostsvc.Host.EnsureProjectOpencode` as the owner-resolved outer seam. Add a host-local OpenCode runtime policy behind it: - Native tmux runtime remains supported. - Container runtime owns ensure/start, inspect/recover, stop/restart, health, endpoint, mounts, environment, labels, and runtime diagnostics. - `platforms.Platform` continues to own session operations and remains unaware of Docker/Podman. - Multi-remote hosts select and execute their own runtime locally. ## Principal constraints - Managed containers need an explicit project-to-instance registry; host `lsof`/PID cwd discovery does not work reliably across container namespaces. - OpenCode must use a fixed container port, listen on `0.0.0.0`, and be published only to a host-loopback port known to ocman. - Host and container must initially see project and worktree directories at identical absolute paths. - Worktree mounts must cover ocman's sibling `.worktrees/<repo>` layout without exposing unrelated repositories. - OpenCode API authentication must cover HTTP, SSE, autoapprove, judge, probes, and session creation. - Attachments need transport or a narrow shared mount rather than arbitrary host cache paths. - Host terminal/workflow execution and container agent execution are different environments and need explicit semantics. - Container defaults must be non-root, unprivileged, resource-bounded, and must not mount the runtime socket. ## Database decision Decision #384 rejects sharing or bind-mounting host OpenCode SQLite into project containers, especially on Docker Desktop for macOS. Each managed project container owns a persistent OpenCode database; ocman will build a host-local API-fed read model for unified history, routing, projects, and analytics. Native mode keeps its current read-only global database path during migration. ## Delivery plan Child tickets cover lifecycle ownership, authenticated transport, the isolated-data container tracer bullet, sandbox/config defaults, attachments, execution-environment semantics, the host-local read model, and user-facing rollout/diagnostics. #384 precedes #378; #382 is superseded. Dependencies are recorded in each child issue. ## Completion criteria - [ ] Native mode remains supported and its lifecycle races/duplicate paths are corrected. - [ ] A user can opt a host into container mode and launch/recover a project OpenCode instance. - [ ] Worktree and same-directory sessions work through the managed endpoint. - [ ] OpenCode traffic is authenticated and not exposed beyond host loopback. - [ ] Mounts, credentials, UID/GID, capabilities, resources, and attachments have safe defaults. - [ ] No host/global OpenCode SQLite database is shared or bind-mounted into project containers. - [ ] Runtime state and failures are observable in ocman. - [ ] The long-term database isolation direction is explicitly decided. ## Non-goals - Containerizing ocman itself. - Replacing `hostsvc.Host` or creating a container-specific platform adapter. - Host PID/network namespace sharing or privileged runtime access. - Silently mounting the complete user home, SSH directory, credential stores, or Docker/Podman socket. - Implementing the complete host-local read model inside the runtime launcher ticket.
Author
Owner

Child tickets and dependency frontier

  • #376 Runtime-neutral lifecycle ownership - no blockers
  • #377 Authenticated OpenCode HTTP/SSE transport - no blockers
  • #378 Managed container tracer bullet - blocked by #376, #377
  • #379 Secure mounts, identity, credentials, and limits - blocked by #378
  • #380 Runtime-independent composer attachments - blocked by #378
  • #381 Explicit host versus agent execution semantics - blocked by #378
  • #382 Shared SQLite container qualification - blocked by #378, #379
  • #383 Settings, diagnostics, lifecycle UX, and rollout - blocked by #379, #380, #381, #382
  • #384 Long-term OpenCode data-isolation decision - blocked by #382

Initial frontier: #376 and #377 can proceed in parallel.

## Child tickets and dependency frontier - [ ] #376 Runtime-neutral lifecycle ownership - no blockers - [ ] #377 Authenticated OpenCode HTTP/SSE transport - no blockers - [ ] #378 Managed container tracer bullet - blocked by #376, #377 - [ ] #379 Secure mounts, identity, credentials, and limits - blocked by #378 - [ ] #380 Runtime-independent composer attachments - blocked by #378 - [ ] #381 Explicit host versus agent execution semantics - blocked by #378 - [ ] #382 Shared SQLite container qualification - blocked by #378, #379 - [ ] #383 Settings, diagnostics, lifecycle UX, and rollout - blocked by #379, #380, #381, #382 - [ ] #384 Long-term OpenCode data-isolation decision - blocked by #382 Initial frontier: #376 and #377 can proceed in parallel.
Author
Owner

Dependency correction after #384: #384 now precedes and blocks #378. Shared-SQLite qualification #382 is superseded; container work must use isolated per-project OpenCode databases plus the planned host-local API-fed read model.

Dependency correction after #384: #384 now precedes and blocks #378. Shared-SQLite qualification #382 is superseded; container work must use isolated per-project OpenCode databases plus the planned host-local API-fed read model.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
dries/ocman#375
No description provided.