Host abstraction, phase 1a: ContainerTarget for container-interior operations #74

Merged
jknapp merged 6 commits from refactor/project-host into main 2026-11-13 19:12:24 +00:00
Owner

Phase 1a of the AWS host design (docs/superpowers/specs/2026-11-13-aws-ecs-host-design.md, "Host abstraction"). Refactor only; behaviour should not change.

What changed

  • New host module. ContainerTarget (one Docker variant today) is now the only way into a project container: one-shot, attached and detached execs, resize, exit status, uploads, archive download, is_running. The Docker variant calls the existing docker/exec.rs code.
  • Host-neutral exec output. AttachedExec now yields ExecChunk instead of bollard's LogOutput. docker/exec.rs converts at the boundary.
  • Terminal sessions moved. ExecSessionManager is now in host::sessions and holds a ContainerTarget. get_target replaces get_container_id, so image paste, file drop and the audio bridge reuse the session's own target.
  • Callers migrated. 14 files no longer touch bollard, docker::exec or get_docker() for container-interior work: file/inspect/terminal/auth-token/marketplace commands, auth bridge, browser view, file viewer, marketplace sync and gh login.
  • Boundary test. host::tests::no_container_interior_bollard_outside_the_host_layer fails on any new direct access. Its allowlist is migration_commands.rs (Docker-only) and container_watch.rs (phase 1b).
  • CLAUDE.md documents the host layer.

Intentional differences

  • Error wording. Download, backup and "run task now" errors now read Failed to start download: <host error>, Failed to start backup: <host error> and Failed to start task: <host error>. Nothing in the frontend matches on the old strings.

Not in this PR (phase 1b)

  • Container lifecycle and events: create/start/stop/remove and the die event stream (container_watch.rs).
  • Calls that still assume Docker: 21 places build ContainerTarget::docker(id) from a container id string. These are mostly public helpers whose callers (browser view commands, file viewer commands, marketplace) still pass ids. They get converted when the lifecycle code hands out targets.

Verification

  • cargo check --tests: no new warnings.
  • cargo test --lib: 911 passed, 0 failed.
  • Independent review of the full diff for behaviour changes (user, working dir, env, tty, stdin, stdout/stderr split, exit status, is_running, upload ownership, session bookkeeping). Its one finding was checked against the old code and was a false positive: the old scheduler exec already set /workspace.
  • Not exercised by hand: I haven't run the app against a live container. The terminal, file transfer, auth bridge and browser view paths are covered only by the type-level and unit tests.

🤖 Generated with Claude Code

Phase 1a of the AWS host design (`docs/superpowers/specs/2026-11-13-aws-ecs-host-design.md`, "Host abstraction"). **Refactor only; behaviour should not change.** ## What changed - **New `host` module.** `ContainerTarget` (one `Docker` variant today) is now the only way into a project container: one-shot, attached and detached execs, resize, exit status, uploads, archive download, `is_running`. The Docker variant calls the existing `docker/exec.rs` code. - **Host-neutral exec output.** `AttachedExec` now yields `ExecChunk` instead of bollard's `LogOutput`. `docker/exec.rs` converts at the boundary. - **Terminal sessions moved.** `ExecSessionManager` is now in `host::sessions` and holds a `ContainerTarget`. `get_target` replaces `get_container_id`, so image paste, file drop and the audio bridge reuse the session's own target. - **Callers migrated.** 14 files no longer touch bollard, `docker::exec` or `get_docker()` for container-interior work: file/inspect/terminal/auth-token/marketplace commands, auth bridge, browser view, file viewer, marketplace sync and gh login. - **Boundary test.** `host::tests::no_container_interior_bollard_outside_the_host_layer` fails on any new direct access. Its allowlist is `migration_commands.rs` (Docker-only) and `container_watch.rs` (phase 1b). - **CLAUDE.md** documents the host layer. ## Intentional differences - **Error wording.** Download, backup and "run task now" errors now read `Failed to start download: <host error>`, `Failed to start backup: <host error>` and `Failed to start task: <host error>`. Nothing in the frontend matches on the old strings. ## Not in this PR (phase 1b) - **Container lifecycle and events:** create/start/stop/remove and the `die` event stream (`container_watch.rs`). - **Calls that still assume Docker:** 21 places build `ContainerTarget::docker(id)` from a container id string. These are mostly public helpers whose callers (browser view commands, file viewer commands, marketplace) still pass ids. They get converted when the lifecycle code hands out targets. ## Verification - `cargo check --tests`: no new warnings. - `cargo test --lib`: 911 passed, 0 failed. - **Independent review** of the full diff for behaviour changes (user, working dir, env, tty, stdin, stdout/stderr split, exit status, `is_running`, upload ownership, session bookkeeping). Its one finding was checked against the old code and was a false positive: the old scheduler exec already set `/workspace`. - **Not exercised by hand:** I haven't run the app against a live container. The terminal, file transfer, auth bridge and browser view paths are covered only by the type-level and unit tests. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
jknapp added 6 commits 2026-11-13 18:34:13 +00:00
Phase 1 of the AWS host work (docs/superpowers/specs/2026-11-13-aws-ecs-host-design.md).

- host::ContainerTarget (one Docker variant today) wraps every
  container-interior operation: one-shot/attached/detached execs, resize,
  exit status, uploads, archive download, is_running. It delegates to
  docker/exec.rs with no behaviour change.
- AttachedExec now yields host-neutral ExecChunk instead of bollard's
  LogOutput; docker/exec.rs converts at the boundary.
- ExecSessionManager moves to host::sessions and holds a ContainerTarget.
- A boundary test lists the files that still reach into containers through
  bollard / docker::exec directly; follow-up commits empty it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
setup-token, the viewer's poll/probe execs, the marketplace sync and the
container gh login now reach the container via host::ContainerTarget
instead of docker::exec / is_container_running. No behaviour change: same
user, working dir, env, tty and stdin attachment for every exec.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
file_commands.rs and inspect_commands.rs no longer name bollard,
docker::exec or get_docker. The archive read, running checks, attached
download/backup execs, one-shot execs, uploads and the detached
scheduler run all go through ContainerTarget, with each exec's options
(user, env, no working dir, no TTY, no stdin) and user-facing error
wording preserved.

fetch_container_file and require_running keep their container-id
signatures because file_viewer_commands.rs calls them with an id.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Phase 1a of the AWS host work: these files no longer name bollard or
docker::exec; every exec, upload and is-running check goes through
host::ContainerTarget. No behaviour change: same user, working dir, env,
tty and stdin attachment for every exec.

- auth_bridge: the poller, reconcile and PortForward hold a ContainerTarget
  (AuthBridgeManager::start keeps its container_id signature).
  tunnel_connection_with_prelude keeps its String signature for the
  browser-view proxy and wraps the new tunnel_into_with_prelude.
- browser_view: the supervisor and viewer helpers take a ContainerTarget;
  detect::detect_in is the target-taking form of detect; install.rs's
  run_step uses ContainerTarget::attach with an ExecSpec equal to the old
  create_attached_exec_as call. page.rs builds the target at each exec.
- terminal_commands: open_terminal_session uses ContainerTarget::for_project;
  session-derived calls build ContainerTarget::docker from the session's id.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Finish phase 1a: empty the boundary allowlist, sessions hand out their target
Secret Scan / scan (push) Successful in 13s
Build App (Preview) / compute-version (pull_request) Successful in 12s
Build App (Preview) / create-release (pull_request) Successful in 13s
Secret Scan / scan (pull_request) Successful in 12s
Build App (Preview) / build-macos (pull_request) Successful in 3m40s
Build App (Preview) / test (pull_request) Successful in 5m39s
Build App (Preview) / build-linux (pull_request) Successful in 6m1s
Build App (Preview) / build-windows (pull_request) Successful in 18m30s
Build App (Preview) / prune-previews (pull_request) Successful in 11s
a19643e0e8
- ExecSessionManager::get_target replaces get_container_id, so image paste,
  file drop and the audio bridge reuse the session's own target instead of
  rebuilding a Docker one; the web terminal builds its target per project.
- Drop the error-prefix rewriting helper: download/backup/run-now errors now
  carry the host layer's own message after their prefix.
- Remove the now-unused create_attached_exec and MAX_ONESHOT_OUTPUT re-export.
- The boundary test's allowlist is down to migration (Docker-only) and
  container_watch (phase 1b). CLAUDE.md documents the host layer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jknapp merged commit 3ec100e06c into main 2026-11-13 19:12:24 +00:00
jknapp deleted branch refactor/project-host 2026-11-13 19:12:24 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: CyberCoveLLC/Triple-C#74