Skip to content

fix(ssh): self-heal daemon bootstrap uploads on remote hosts - #10590

Merged
austinywang merged 2 commits into
mainfrom
issue-10587-daemon-bootstrap-self-heal
Aug 23, 2026
Merged

austinywang merged 2 commits into
mainfrom
issue-10587-daemon-bootstrap-self-heal

Conversation

@austinywang

@austinywang austinywang commented Aug 22, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Closes #10587.

This is a transfer bug, not expected fail-closed behavior. PR #10555 correctly computes the local byte count and SHA-256 and refuses to promote an unverified payload, but its backgrounded remote cat loses SSH stdin under a non-interactive POSIX shell and deterministically writes 0 bytes.

Read-only evidence from tinybox and the local nightly snapshot:

  • /home/tiny/.cmux/bin/cmuxd-remote/0.64.22-nightly.3254304245301/linux-amd64/cmuxd-remote was mode 755 and 0 bytes at 2026-08-22T23:19:16Z.
  • The matching local Linux artifact was 6,295,704 bytes; the older 0.64.22-nightly.3252776701701 remote binary was 6,295,704 bytes and hash-matched its cache. The current 0.64.22-nightly.3260048785601 final path was absent after the rejected attempt.
  • The persisted session at park time showed the integrity-class suspension after repeated hello failures. Upload verification occurs before the existing diagnostic probe, so the parked user-facing record did not include path/size; this PR does not weaken the sanitized logging or fail-closed behavior.
  • The exact old and new stdin forms were exercised over SSH to tinybox without writing remote state: the old form returned 0 bytes, and the fd-preserving form returned all 22 bytes.

Fix and coverage

The upload script now uses the portable exec 3<&0 / cat <&3 pattern and closes fd 3 after the reader exits. Byte-count/SHA-256 verification, temporary cleanup, atomic promotion, and bounded retry/fingerprint accounting are unchanged. Commit 2cc7fcb5ff adds a behavioral regression test first; commit bea3179c4a fixes it. The test executes the generated command with real file-backed stdin and verifies the complete temporary payload and writer-marker cleanup.

No remote state was modified. No user-facing strings changed, so localization catalogs were unaffected.

@coderabbitai

coderabbitai Bot commented Aug 22, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 8d74ff28-40b7-475a-ab8b-116a0fddd8ca

📥 Commits

Reviewing files that changed from the base of the PR and between 456e425 and bea3179.

📒 Files selected for processing (4)
  • Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator+DaemonUpload.swift
  • Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/RemoteDaemonUploadRecoveryTests.swift
  • Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/RemoteDaemonUploadTests.swift
  • cmuxTests/WorkspaceRemoteConnectionTests.swift

Included review availability: Your plan provides up to 10 included reviews per hour; 1 remains after this review.


📝 Walkthrough

Walkthrough

The upload script now preserves SSH stdin on descriptor 3 for background cat execution and closes it after completion. Tests cover binary transfer, cleanup, and both direct and descriptor-based upload command forms.

Changes

Remote daemon upload recovery

Layer / File(s) Summary
Preserve upload input for background cat
Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator+DaemonUpload.swift
The upload script duplicates SSH stdin to descriptor 3, passes it to the background cat, and closes the descriptor after completion.
Validate transfer and command detection
Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/RemoteDaemonUploadRecoveryTests.swift, Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/RemoteDaemonUploadTests.swift, cmuxTests/WorkspaceRemoteConnectionTests.swift
Tests verify a 128 KiB upload, payload integrity, temporary-file cleanup, writer-marker removal, and recognition of both supported cat command forms.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to bea31

This change preserves SSH input during remote daemon uploads and adds regression coverage, preventing empty bootstrap payloads without changing verification or promotion behavior. No actionable merge-blocking risk remains beyond normal checks and review.

🚥 Pre-merge checks | ✅ 25
✅ Passed checks (25 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the SSH daemon bootstrap upload fix and its self-healing purpose.
Description check ✅ Passed The description explains the cause, fix, and regression coverage, but it omits the template's Testing, Demo Video, Review Trigger, and Checklist sections.
Linked Issues check ✅ Passed The PR fixes the transfer-path bug identified in issue #10587 and adds regression coverage for complete, self-healing daemon uploads.
Out of Scope Changes check ✅ Passed All code and test changes support preserving SSH stdin for daemon uploads and validating the corrected upload behavior.
Docstring Coverage ✅ Passed Docstring check was indeterminate for this PR — some files could not be analyzed in time. Not blocking.
Cmux Swift Actor Isolation ✅ Passed The only production diff changes the generated shell upload script; it adds no Swift declarations or isolation annotations. Other changes are tests, and existing @unchecked Sendable coordinator cod...
Cmux Swift Blocking Runtime ✅ Passed The production diff only adds shell fd 3 preservation and closure around the existing background cat; existing sleep/wait remain unchanged, and new process waits are test-only scaffolding.
Cmux Browser Automation Off-Main ✅ Passed The PR range changes only remote daemon upload code and tests; it does not modify TerminalController.swift or ControlCommandExecutionPolicy.swift, add browser.* commands, or alter worker routing.
Cmux Expensive Synchronous Load ✅ Passed The production diff only preserves SSH stdin with shell FD 3; it adds no agent-history loader, transcript/JSONL parsing, directory scan, or interactive synchronous load.
Cmux Cache Substitution Correctness ✅ Passed The production diff only preserves SSH stdin with fd 3 for background cat and closes it afterward; it does not replace an authoritative read or alter cache, persistence, history, undo, or snapsho...
Cmux No Hacky Sleeps ✅ Passed The PR adds no production sleep, timer, polling, or wall-clock wait. The existing shell watchdog sleep is unchanged, and the test-only /bin/sleep scaffolding is explicitly allowed.
Cmux Algorithmic Complexity ✅ Passed The production diff only adds fixed descriptor duplication and closure around background cat; it adds no scalable collection scan. New collection filtering is test-only.
Cmux Swift Concurrency ✅ Passed The PR diff adds shell FD handling and test coverage only; it introduces no Swift DispatchQueue, Combine state, completion-handler, or fire-and-forget Task patterns.
Cmux Swift @Concurrent ✅ Passed The aggregate Swift diff changes a synchronous upload script and adds a synchronous throws test; it introduces no async, nonisolated, @concurrent, or UI-isolated heavy async call site.
Cmux Swift Package Boundaries ✅ Passed The only production change is in the existing CmuxRemoteSession SwiftPM target, not an app-target Sources path; remaining changes are tests, so no package-boundary violation is introduced.
Cmux Swiftpm Lockfiles ✅ Passed PR diff contains only daemon-upload source/tests; no Package.swift, Package.resolved, .gitignore, workflow, or Xcode package-reference changes trigger this policy.
Cmux Swift Logging ✅ Passed The PR adds no production logging calls or Logger declarations; its only runtime change is shell stdin handling. Added stdout/stderr code is test harness plumbing, and existing debugLog is unchange...
Cmux User-Facing Error Privacy ✅ Passed The full PR diff adds only SSH stdin descriptor handling and developer comments in production; it adds no user-facing error, alert, output, or recovery text. Other changes are tests.
Cmux Full Internationalization ✅ Passed The production diff adds only shell descriptor handling and developer comments; tests update command matching. No user-facing Swift text, catalogs, web messages, metadata, or locale files changed.
Cmux Swiftui State Layout ✅ Passed The PR changes only the remote upload shell script and tests. The diff adds no SwiftUI views, state wrappers, GeometryReader, lazy/list row stores, or render-time state writes.
Cmux Architecture Rethink ✅ Passed The production diff only adds the required exec 3<&0/cat <&3 stdin bridge and closes it; existing polling is unchanged, and waitUntilExit is test-only synchronization.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed The full PR diff only changes daemon-upload shell logic and upload tests; it adds or changes no NSWindow, NSPanel, Window, WindowGroup, or close-shortcut routing.
Cmux Source Artifacts ✅ Passed The PR changes only hand-written Swift source and tests; no logs, screenshots, caches, build output, temp folders, or artifact directories enter the diff.
Cmux No Test Or Debug Seam In Production Source ✅ Passed The only production-source diff changes the real SSH upload script to preserve stdin with fd 3; it adds no DEBUG guard, test/debug member, visibility widening, or test-only accessor.
Cmux No Ambient Global State ✅ Passed The only production Swift diff edits an embedded shell script, adding fd 3 preservation and close; it adds no top-level functions, mutable globals, namespaces, or singleton state.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-10587-daemon-bootstrap-self-heal

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes remote daemon bootstrap uploads by preserving SSH stdin before starting the asynchronous payload reader.

  • Duplicates stdin to descriptor 3 for the background cat and closes the descriptor after the reader exits.
  • Adds a behavioral regression test that executes the generated command with file-backed stdin and verifies the complete payload and writer-marker cleanup.
  • Updates upload-command recognition in package and workspace tests.

Confidence Score: 5/5

The PR appears safe to merge with no actionable correctness or security failures identified.

The generated command preserves SSH stdin using POSIX shell redirections, retains bounded timeout, verification, and cleanup behavior, and is covered by a behavioral test using real file-backed stdin.

Important Files Changed

Filename Overview
Packages/macOS/CmuxRemoteSession/Sources/CmuxRemoteSession/Session/RemoteSessionCoordinator+DaemonUpload.swift Preserves file-backed SSH stdin for the asynchronous upload reader and closes the duplicated descriptor after completion without weakening verification or cleanup.
Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/RemoteDaemonUploadRecoveryTests.swift Adds behavioral coverage that executes the generated shell command and verifies payload integrity and writer-marker removal.
Packages/macOS/CmuxRemoteSession/Tests/CmuxRemoteSessionTests/RemoteDaemonUploadTests.swift Updates test request classification to recognize the new descriptor-based upload command.
cmuxTests/WorkspaceRemoteConnectionTests.swift Updates workspace-level SSH upload mocks to recognize and record the new background-reader command.

Sequence Diagram

sequenceDiagram
    participant Local as cmux
    participant SSH as SSH process
    participant Shell as Remote sh
    participant Cat as Background cat
    participant Temp as Temporary payload
    Local->>SSH: Start command with binary as stdin
    SSH->>Shell: sh -c upload script
    Shell->>Shell: "exec 3<&0"
    Shell->>Cat: "cat <&3 (background)"
    Cat->>Temp: Write complete payload
    Shell->>Cat: wait
    Shell->>Shell: "exec 3<&-"
    Shell->>Shell: Remove writer marker
    Shell-->>Local: Exit status
Loading

Reviews (1): Last reviewed commit: "fix(ssh): preserve stdin for background ..." | Re-trigger Greptile

@austinywang

Copy link
Copy Markdown
Contributor Author

CI note: the three failed-job retries all stop before app-host test execution on shared CMUXAuthCore / CmuxAuthRuntime undefined-symbol linker errors (the same failure is present on unrelated PR branches). The Swift package matrix also repeatedly times out in the unrelated CmuxIrohTransport CmxConnectivityPeerSessionTests suite after its built-in retry. The focused remote-daemon Go lane, runtime/warning lane, Linux preflight, and all non-app-host checks are green; no actionable failure points to this diff. Attempt 3 is still running for the final package result.

@austinywang

Copy link
Copy Markdown
Contributor Author

Final CI retry result (attempt 3): the focused CmuxRemoteSession package suite passed 164 tests in 27 suites, including Background upload reader consumes the SSH stdin stream; the runtime/warning lane and all Linux/non-app-host checks also passed. The aggregate workflow remains red only because (1) all four app-host shards fail before test execution on missing CMUXAuthCore/CmuxAuthRuntime linker symbols, reproduced across three fresh-runner attempts and unrelated branches, and (2) the package matrix later times out twice in the unrelated CmuxIrohTransport CmxConnectivityPeerSessionTests suite. No actionable failure involves this PR. PR remains mergeable/clean.

@austinywang
austinywang merged commit 0ae5579 into main Aug 23, 2026
41 of 62 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant