Fix backend-switch AWS auth + add /workspace backup and terminal file drag-and-drop #8
Merged
jknapp
merged 9 commits from 2026-06-30 22:20:00 +00:00
fix/backend-switch-aws-creds into main
9 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
d65872dc94 |
Address remaining review items: L4/M1 + L2/L5 cleanup
Build App / compute-version (pull_request) Successful in 3s
Build Container / build-container (pull_request) Successful in 57s
Build App / build-macos (pull_request) Successful in 2m15s
Build App / build-windows (pull_request) Successful in 2m54s
Build App / build-linux (pull_request) Successful in 5m44s
Build App / create-tag (pull_request) Has been skipped
Build App / sync-to-github (pull_request) Has been skipped
- L4: sync_bedrock_credentials (renamed from write_bedrock_static_ credentials) now also clears a stale ~/.aws/credentials when the project no longer uses static-credential Bedrock, so static keys don't linger unused in the persistent home volume after switching backends. Skipped when /tmp/.host-aws is mounted (host-managed ~/.aws). HOME is also set explicitly on the exec env for robustness. - M1: the Backup button now has a tooltip and the success toast notes that the archive includes MCP/config which may contain MCP-embedded API keys (OAuth tokens are excluded) — keep it private. - L2: backup now uses async file IO (tokio::fs::File + AsyncWriteExt, tokio::fs::remove_file) instead of blocking std::fs between awaits; dropped-file reads use tokio::fs::metadata/read. - L5: upload_host_file_to_terminal explicitly `mkdir -p`s /tmp/triple-c-drops instead of relying on Docker's tar extractor to create the parent dir. Verified L4 cleanup guard, L5 mkdir, async IO, and exit-code paths against real containers. cargo check / tsc / vitest all pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
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> |
||
|
|
84e0bdf7b4 |
Add file drag-and-drop onto the terminal
Build App / compute-version (pull_request) Successful in 6s
Build Container / build-container (pull_request) Successful in 1m25s
Build App / build-macos (pull_request) Successful in 2m31s
Build App / build-windows (pull_request) Successful in 4m14s
Build App / build-linux (pull_request) Successful in 4m54s
Build App / create-tag (pull_request) Has been skipped
Build App / sync-to-github (pull_request) Has been skipped
Drop files onto a terminal pane and they're copied into the container and their in-container paths typed into the prompt, so Claude Code can read them for reference — mirroring the existing image-paste flow. Backend: upload_host_file_to_terminal reads the dropped host file and writes it under /tmp/triple-c-drops/<name> in the session's container, returning that path. Rejects directories and unreadable paths. Frontend: TerminalView subscribes to Tauri's webview onDragDropEvent (OS file drops are intercepted at the webview level, so HTML5 ondrop wouldn't expose paths). The window-wide event is guarded by the pane's `active` flag plus a bounds hit-test so a drop only affects the terminal it landed on; multiple files are uploaded and their paths inserted space-separated (quoted when they contain spaces). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
10e689eaa6 |
Include sanitized home config in project backup
Extends download_container_backup to also capture the container's home config so MCP servers, settings, and skills set up directly via Claude Code (stored in ~/.claude.json / ~/.claude, on the home/config volumes that a Reset wipes) survive a backup/restore cycle. Secrets are stripped per the "exclude secrets" choice: ~/.claude.json is filtered through jq to drop primaryApiKey/oauthAccount/customApiKeyResponses (mcpServers and settings are kept), and ~/.claude/.credentials.json (the OAuth tokens) is omitted. Staged config is archived under home-claude/ in the tarball. Verified on the Ubuntu/jq container base. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
d07dcdfea9 |
Add "Backup" action to download a project's /workspace as .tar.gz
Adds a manual backup button on each project card (next to Start/Reset when stopped, and next to Files when running) that saves a gzipped tarball of the container's /workspace to a host path via the native save dialog. Backend: download_container_backup runs `tar czf -` inside the container (so excludes + compression happen there rather than streaming a 16 GB workspace) and pipes stdout straight to the chosen file. Regenerable build artifacts (node_modules, target, .git/objects) are excluded so the archive stays restore-sized. Returns bytes written; stderr is captured for error reporting and a zero-byte result is treated as failure. Works whether the container is running or stopped (only requires that it exists). Verified on the Ubuntu/GNU-tar container base. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
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> |