* Set cmux TestAction to Debug for UI tests
* Broaden XCTest detection for debug launch gate
* Fix AutomationSocketUITests launch hang in CI
* Stabilize CI Swift package resolution for test jobs
* Stabilize Xcode Cloud UI test focus and socket handling
* Add Xcode Cloud pre-xcodebuild submodule bootstrap
* Harden Xcode Cloud bonsplit bootstrap fallback
Add a cached lazy keychain fallback to SocketControlPasswordStore so
that authentication paths in TerminalController can transparently read
a legacy keychain password without blocking on every request. The
keychain is read at most once and the result is cached behind an
NSLock. File-based and environment passwords still take priority.
Closes https://github.com/manaflow-ai/cmux/issues/579
Moves socket control password from the macOS login keychain to a
plain file at ~/Library/Application Support/cmux/socket-control-password.
This eliminates the system keychain prompt that interrupts users on
first launch or after keychain changes.
- Directory created with 0700, file written with 0600 permissions
- One-time migration copies existing keychain password to the file,
deletes the keychain entry, and records a migration version in
UserDefaults so it runs only once
- CLI SocketPasswordResolver also reads from the file path
- Security framework import is now conditional (#if canImport)
- Adds SocketControlPasswordStoreTests covering round-trip, env
priority, path resolution, and migration behavior
Fixes https://github.com/manaflow-ai/cmux/issues/541
XCUITest launches the app as a separate process that doesn't inherit
XCTest env vars (XCTestConfigurationFilePath, etc.), so
isRunningUnderXCTest() returns false. The app then hits
shouldBlockUntaggedDebugLaunch() and exits with code 64, causing the
test runner to hang waiting for the app to launch.
Fix: detect CMUX_UI_TEST_* env vars set via XCUIApplication.launchEnvironment
and skip the launch guard. Also revert the failed CMUX_TAG ci.yml workaround.
migrateMode() had no case for "allowAll" rawValue, so it fell
through to the default branch which returned .cmuxOnly. This
silently downgraded any persisted allowAll setting.
* Socket access control: process ancestry check + file permissions
Redesign socket control modes from (off, notifications, full) to
(off, cmuxOnly, allowAll):
- cmuxOnly (default): uses LOCAL_PEERPID + sysctl process tree walk to
verify the connecting process is a descendant of cmux. External
processes (SSH, other terminals) are rejected.
- allowAll: hidden mode accessible only via CMUX_SOCKET_MODE=allowAll
env var, skips ancestry check. Legacy "full"/"notifications" env
values map here for backward compat.
- off: disables socket entirely.
Security hardening:
- Server: chmod 0600 on socket after bind (owner-only access)
- CLI: stat() ownership check before connect (reject fake sockets)
Removes per-command allow-list (isCommandAllowed) — once a process
passes the ancestry check, all commands are available.
Includes migration for persisted UserDefaults values and env var
aliases (cmux_only, cmux-only, allow_all, allow-all).
* Add /sync-branch skill for submodule + main sync