Commit Graph
14 Commits
Author SHA1 Message Date
shadow-testandClaude Opus 5 01a2f6aec8 Add Project Home, Auth Bridge, shared auth token, and Tier-1 polish
Project Home (DESIGN-REVIEW §B2): the project is promoted from a 280px
sidebar card to a first-class main-area view. ProjectCard.tsx (1,257
lines) is replaced by a select-only ProjectRow plus tabs for Overview,
Sessions, Automation, Config and Files. The PortMappings, FileManager
and ContainerProgress modals are absorbed rather than reimplemented.
Config gains a Saved/Saving/Failed indicator — save-on-blur failures
previously reached only console.error.

Tier-1 polish (DESIGN-REVIEW §A): new elevation, muted-accent, disabled
and focus-ring tokens; a global :focus-visible ring with every
focus:outline-none removed; filled buttons moved to --accent-emphasis
and white-on-success toggles retired, fixing three WCAG AA failures
(2.1:1, 2.5:1, 2.4:1); a shared Modal primitive with role="dialog",
focus trap and restore, adopted by all remaining modals; status
indicators that carry a glyph and word rather than colour alone.

Ctrl+Shift+W closes a tab, deliberately not Ctrl+W — that is readline's
kill-word, used constantly in the terminal this app is built around.

Auth Bridge: a general loopback-callback bridge so browser logins run
inside a container (aws sso login, Concourse fly login, claude login)
can complete against the host browser. Listeners are discovered from
/proc/net/tcp{,6} — ss/netstat/lsof are absent from the image — bound on
host 127.0.0.1 only, and tunnelled in over the Docker API via socat,
which keeps working on Docker Desktop where container IPs are not
routable. Falls back to [::1] because Node resolves localhost to IPv6
first, so claude login often binds ::1 alone. Opt-in per project.

This extracts create_attached_exec() and moves the existing terminal
session path onto it, so there is one attached-exec implementation
rather than two.

Shared auth token: `claude setup-token` is run in a container, the token
is stored in the OS keychain and injected as CLAUDE_CODE_OAUTH_TOKEN
into Anthropic-backend projects. Contrary to the initial design note,
setup-token uses an Anthropic-hosted redirect and blocks on a stdin
paste prompt rather than a loopback callback, so a stdin command is
required for the flow to complete.

The token is never logged, never returned to the frontend, and is
redacted from the streamed output with a stateful matcher that withholds
any tail that could still grow into a secret. Change detection uses a
random rotation id rather than a hash, since a hash in a docker-inspect
readable label would be an offline verification oracle.

Frontend 33 -> 51 tests; Rust 34 tests. Both builds clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09 11:35:42 -07:00
shadow-testandClaude Opus 4.8 3e2e3f231b Third review pass: fix tar TOCTOU + transient backup status
Build App / compute-version (pull_request) Successful in 3s
Build Container / build-container (pull_request) Successful in 31s
Build App / build-macos (pull_request) Successful in 2m16s
Build App / build-windows (pull_request) Successful in 3m9s
Build App / build-linux (pull_request) Successful in 5m53s
Build App / create-tag (pull_request) Has been skipped
Build App / sync-to-github (pull_request) Has been skipped
- F2: upload_host_file_to_container now reads the dropped file into a Vec
  inside the blocking task and sizes the tar entry from those exact bytes,
  rather than stat-then-stream where a file changing size between the
  stat and the read could desync the tar header and silently corrupt the
  archive. Still runs off the async worker; memory stays bounded by the
  256 MiB drop cap.
- F4: the "Backup saved" confirmation now auto-clears after 8s (guarded
  against clobbering a newer status message) instead of lingering in the
  project card's status line indefinitely.

F1 (claimed AWS CLI regression from empty-env neutralization) was a false
positive: verified against aws-cli 2.35 that an empty AWS_ACCESS_KEY_ID is
treated as absent and botocore falls through to ~/.aws/credentials (the
call reached AWS and returned InvalidClientTokenId for the file's key, not
PartialCredentialsError). No change needed.

cargo check / tsc / vitest all pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 15:16:42 -07:00
shadow-testandClaude Opus 4.8 01a8f5c503 Apply remaining review findings (L-e, L-f)
Build App / compute-version (pull_request) Successful in 3s
Build Container / build-container (pull_request) Successful in 1m30s
Build App / build-macos (pull_request) Successful in 2m16s
Build App / build-windows (pull_request) Successful in 3m6s
Build App / build-linux (pull_request) Successful in 6m11s
Build App / create-tag (pull_request) Has been skipped
Build App / sync-to-github (pull_request) Has been skipped
- L-e: route terminal file drops purely by a bounds hit-test instead of
  the `active` flag. Inactive panes are display:none (zero-size rect) so
  they never match; a zero-size guard makes that explicit. Correct for
  the current tabbed layout and future-proof for split panes, where a
  drop on a visible-but-unfocused pane previously matched no handler.
- L-f: stream the dropped file straight into the upload tar inside a
  blocking task (new exec::upload_host_file_to_container) instead of
  reading the whole file into a Vec and then re-packing it. Peak memory
  drops from ~2x to ~1x the file size, and the synchronous file IO no
  longer runs on the async worker.

cargo check / tsc / vitest all pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 15:05:25 -07:00
shadow-testandClaude Opus 4.8 0945e21eb1 Address second review pass: fix start-time race + minor cleanups
Build App / compute-version (pull_request) Successful in 9s
Build Container / build-container (pull_request) Successful in 1m48s
Build App / build-macos (pull_request) Successful in 2m16s
Build App / build-windows (pull_request) Successful in 3m11s
Build App / build-linux (pull_request) Successful in 4m51s
Build App / create-tag (pull_request) Has been skipped
Build App / sync-to-github (pull_request) Has been skipped
- M1 (race): don't mount the host AWS dir for static-credential Bedrock.
  sync_bedrock_credentials() is the sole writer of ~/.aws/credentials in
  that mode, and mounting /tmp/.host-aws let the entrypoint's
  `rm -rf ~/.aws; cp -a` race that write at startup (only when a global
  aws_config_path was also set). Static keys + AWS_REGION env are
  self-sufficient and don't need the host config, so skipping the mount
  removes the dual-writer entirely.
- L-a: exit codes are now read via wait_for_exec_exit(), which polls
  inspect_exec until the exec reports finished, so a non-zero tar/cred
  exit isn't missed by reading exit_code too early. The backup only fails
  on a definitively non-zero code (falls back to the empty-output check
  if undeterminable).
- L-b: fixed two comments that referenced the old
  write_bedrock_static_credentials name (now sync_bedrock_credentials).
- L-c: entrypoint only rewrites ~/.claude.json when awsAuthRefresh is
  actually present, avoiding a needless jq reformat on every non-SSO
  start.
- L-d: backup script traps EXIT to remove its mktemp staging dir even
  when tar fails, so failed backups don't accumulate temp dirs (with the
  sanitized config copy) in the container.

L-e (drop routing) is a non-issue: the layout is tabbed, so only one
terminal pane is ever visible; the active-guard routing is correct.

Verified the race fix, trap cleanup, grep guard, and exit-code polling.
cargo check / tsc / vitest all pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 14:59:48 -07:00
shadow-testandClaude Opus 4.8 edf0698774 Address PR review: backup correctness/security + drop hardening
Build App / compute-version (pull_request) Successful in 8s
Build Container / build-container (pull_request) Successful in 51s
Build App / build-macos (pull_request) Successful in 2m15s
Build App / build-windows (pull_request) Successful in 2m52s
Build App / build-linux (pull_request) Successful in 6m5s
Build App / create-tag (pull_request) Has been skipped
Build App / sync-to-github (pull_request) Has been skipped
Fixes from the code review of this branch:

- Backup requires a running container (it runs via `docker exec`, which
  can't run on a stopped one). Removed the misleading "Backup" button
  from the stopped-project actions, added an explicit running check with
  a clear error, and corrected the doc comment. (H1)
- jq sanitization fallback no longer leaks secrets: if ~/.claude.json
  can't be parsed, the backup substitutes an empty object and warns to
  stderr instead of copying the raw file (which held primaryApiKey /
  oauthAccount). Verified the raw key never reaches the archive. (H2)
- Dropped-file paths typed into the terminal are now always single-quoted
  (with '\'' escaping), not only when they contain whitespace — a name
  like `foo$(whoami).txt` was previously sent raw into the shell. (M2)
- write_bedrock_static_credentials checks the exec exit code via the new
  exec_oneshot_env_status and fails loudly on a write/chmod error instead
  of silently reporting success. exec_oneshot keeps its
  ignore-exit-code behavior so list_container_files is unaffected. (M4)
- Backup removes a partial/truncated archive on any stream error and
  treats a non-zero tar exit code as failure (a truncated gzip was
  previously reported as success). (L1)
- Dropped files are capped at 256 MiB to avoid ballooning host RAM
  (the file is read fully into memory then re-tarred). (M3)
- Stopped excluding .git/objects from the backup so git history,
  including unpushed commits, is preserved faithfully. (L3)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 14:28:00 -07:00
shadow-testandClaude Opus 4.8 424ab04ca8 Fix stale AWS/Bedrock auth carrying over on backend switch
When a project switched backends (e.g. Bedrock -> Anthropic), the
recreated container kept authenticating against Bedrock, and SSO kept
firing after switching away. Three root causes, all fixed:

1. Recreation builds the new container from a `docker commit` snapshot.
   commit always bakes the previous container's full ENV into the image
   (an empty commit Config does NOT strip it, and the commit API cannot
   remove env). So CLAUDE_CODE_USE_BEDROCK=1 / AWS_* survived into the
   new container. Fix: create_container now explicitly clears every
   managed auth key the active backend does not set (MANAGED_AUTH_KEYS),
   so create-time env overrides the stale baked-in values.

2. awsAuthRefresh was written into ~/.claude.json (persisted home
   volume) and never removed, so Claude Code kept invoking
   triple-c-sso-refresh after switching to a non-SSO backend. Fix:
   entrypoint now deletes awsAuthRefresh when AWS_SSO_AUTH_REFRESH_CMD
   is unset, idempotent both ways.

3. Static/session AWS creds were baked into Config.Env at create time,
   so a stop/start kept stale creds and rotated keys never refreshed
   without a full recreation. Fix: static creds are no longer injected
   as env vars; write_bedrock_static_credentials() writes
   ~/.aws/credentials (0600, secrets via exec env not argv) on every
   start, and removes a stale ~/.aws/config left from a prior profile/SSO
   session. Static creds also dropped from the bedrock fingerprint so a
   key rotation refreshes in place instead of forcing recreation.

Adds exec_oneshot_env() for env-carrying one-shot execs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 13:34:43 -07:00
shadow-testandClaude Opus 4.6 b6fd8a557e Clean up compiler warnings and document Ollama/LiteLLM backends
Build App / build-macos (push) Successful in 2m25s
Build App / build-windows (push) Successful in 3m22s
Build App / build-linux (push) Successful in 4m36s
Build App / sync-to-github (push) Successful in 10s
Remove unused `any_docker_mcp()` function, add `#[allow(unused_imports)]`
and `#[allow(dead_code)]` annotations to suppress false-positive warnings.
Update README.md and HOW-TO-USE.md with Ollama and LiteLLM auth backend
documentation including best-effort compatibility notices.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 13:23:50 -07:00
shadow-testandClaude Opus 4.6 ab16ac11e7 Add bash shell tab and file manager for running containers
Build App / build-macos (push) Successful in 2m23s
Build App / build-windows (push) Successful in 3m52s
Build App / build-linux (push) Successful in 4m53s
Build App / sync-to-github (push) Successful in 12s
Adds two new features for running project containers:

1. Bash Shell Tab: A "Shell" button on running projects opens a plain
   bash -l session instead of Claude Code, useful for direct container
   inspection, package installation, and debugging. Tab labels show
   "(bash)" suffix to distinguish from Claude sessions.

2. File Manager: A "Files" button opens a modal file browser for
   navigating container directories, downloading files to the host,
   and uploading files from the host. Supports breadcrumb navigation
   and works with any path including those outside mounted projects.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 06:32:53 -08:00
shadow-testandClaude Opus 4.6 86176d8830 feat: add voice mode support via mic passthrough to container
Build App / build-macos (push) Successful in 2m21s
Build App / build-windows (push) Successful in 3m24s
Build App / sync-to-github (push) Has been cancelled
Build App / build-linux (push) Has been cancelled
Build Container / build-container (push) Successful in 54s
Enables Claude Code's /voice command inside Docker containers by
capturing microphone audio in the Tauri webview and streaming it
into the container via a FIFO pipe.

Container: fake rec/arecord shims read PCM from a FIFO instead of
a real mic. Audio bridge exec writes PCM from Tauri into the FIFO.
Frontend: getUserMedia() + AudioWorklet captures 16kHz mono PCM
and streams it to the container via invoke("send_audio_data").
UI: "Mic Off/On" toggle button in the terminal view.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 06:11:33 -08:00
shadow-testandClaude Opus 4.6 db51abb970 Add image paste support for xterm.js terminal
Build App / build-linux (push) Successful in 2m41s
Build App / build-windows (push) Successful in 3m56s
Intercept clipboard paste events containing images in the terminal,
upload them into the Docker container via bollard's tar upload API,
and inject the resulting file path into terminal stdin so Claude Code
can reference the image.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 10:52:08 -08:00
shadow-testandClaude Opus 4.6 03e0590631 Fix Rust backend: secrets to keychain, status recovery, shutdown, dedup
- Move git_token and Bedrock credentials to OS keychain instead of
  storing in plaintext projects.json via skip_serializing + keyring
- Fix project status stuck in Starting on container creation failure
  by resetting to Stopped on any error path
- Add granular store methods to reduce TOCTOU race window
- Add auth_mode, project path, and bedrock config change detection
  to container_needs_recreation with label-based fingerprinting
- Fix mutex held across async Docker API call in exec resize by
  cloning exec_id under lock then releasing before API call
- Add graceful shutdown via on_window_event to clean up exec sessions
- Extract compute_env_fingerprint and merge_claude_instructions helpers
  to eliminate code duplication in container.rs
- Remove unused thiserror dependency
- Return error instead of falling back to CWD when data dir unavailable

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 20:42:55 +00:00
shadowdaoandClaude Opus 4.6 0a4f207556 Fix stopping one project killing all project terminal sessions
Build App / build-windows (push) Successful in 3m11s
Build App / build-linux (push) Successful in 6m15s
close_all_sessions() was called when stopping/removing/rebuilding a
project, which shut down exec sessions for every project. Track
container_id per session and use close_sessions_for_container() to
only close sessions belonging to the target project.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 19:55:38 +00:00
shadow-testandClaude Opus 4.6 625260b060 Fix UID/GID mismatch and SSH key permissions in container
Build Container / build-container (push) Successful in 3m42s
- Entrypoint now runs as root to remap the container's claude user
  UID/GID to match the host user, fixing bind mount permission errors
  on WSL
- SSH keys are mounted read-only to a staging path (/tmp/.host-ssh)
  and copied to ~/.ssh with correct permissions by the entrypoint
- Exec sessions explicitly run as the claude user
- Host UID/GID detected automatically and passed as env vars

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 04:36:01 +00:00
shadow-testandClaude Opus 4.6 97a0745ead Initial commit: Triple-C app, container, and CI
Tauri v2 desktop app (React/TypeScript + Rust) for managing
containerized Claude Code environments. Includes Gitea Actions
workflow for building and pushing the sandbox container image,
and a BUILDING.md guide for manual app builds on Linux and Windows.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 04:29:51 +00:00