Unbreak cmux-relay against cmux 0.64.16+ (password-mode socket gate)#7
Open
leduckhc wants to merge 3 commits into
Open
Unbreak cmux-relay against cmux 0.64.16+ (password-mode socket gate)#7leduckhc wants to merge 3 commits into
leduckhc wants to merge 3 commits into
Conversation
cmux 0.64.16 moved automation state to $XDG_STATE_HOME/cmux (default ~/.local/state/cmux/), mirroring the socket-path move addressed in 55e2d45. cmuxSocketPassword() still only checked the legacy ~/Library/Application Support/cmux/socket-control-password, so a relay running against cmux 0.64.16+ in password mode could not find the secret cmux had just written and fell back to no auth. Resolution order now mirrors cmuxSocketPath(): 1. CMUX_SOCKET_PASSWORD env (matches cmux --help precedence) 2. $XDG_STATE_HOME/cmux/socket-control-password (default ~/.local/state/cmux/socket-control-password) 3. legacy ~/Library/Application Support/cmux/socket-control-password A new optional stateDirectory: parameter mirrors the cmuxSocketPath test surface so password tests can isolate the new tier; the existing three password tests inject an empty state dir so the host's real ~/.local/state/cmux/socket-control-password cannot shadow fixtures. Adds two new tests covering the XDG tier and XDG_STATE_HOME override.
cmux 0.64.16 changed the default automation.socketControlMode to
'cmuxOnly' — only cmux-descendant processes may bind the UDS. A
launchd-spawned relay has launchd as PID 1, not cmux, so it is denied
with 'ERROR: Access denied — only processes started inside cmux can
connect' and the events-stream supervisor pegs at the 8 s reconnect
backoff cap.
The fix is to flip cmux to socketControlMode='password' and share the
secret with the relay. Until now the install script and plist template
had no slot for it, forcing operators to plutil-insert the env var by
hand.
- scripts/relay.plist.tmpl: add CMUX_SOCKET_PASSWORD entry to
EnvironmentVariables (rendered token __SOCKET_PASSWORD__).
- scripts/install-launchd.sh: SOCKET_PASSWORD passthrough from
CMUX_SOCKET_PASSWORD env, rendered via sed alongside the existing
knobs. Usage help and dry-run notes document the knob and mask the
value (only the length is printed; the literal password is never
echoed in [install-launchd] notes).
- README.md: add a 'cmux 0.64.16+ socket-control 비밀번호' section
walking through the cmux-settings → install-launchd.sh → cmux
restart sequence, and explain why a one-time cmux restart is
required (socketControlMode is read at app startup; cmux
reload-config does not re-init SocketControlServer).
cmux 0.64.16 reads automation.socketPassword from cmux.json once on
startup and then strips the field as a privacy hardening step. Every
cmux restart therefore wipes the server-side password and a launchd-
spawned cmux-relay starts emitting:
ERROR: auth_unconfigured — Password mode is enabled but no socket
password is configured in Settings.
The recovery is mechanical (re-write the password into cmux.json,
reload-config, kickstart the relay) but easy to forget across cmux
upgrades. Bake it into a single idempotent script so the answer to
'I just restarted cmux, now what' is one command.
The script:
1. Reads CMUX_SOCKET_PASSWORD from the relay's launchd plist (the
canonical secret install-launchd.sh staged there). Never asks the
user to type or paste it.
2. Re-injects it into cmux.json under automation.socketPassword via
a python3 in-place rewrite that preserves every other top-level
section. Drops a once-per-day cmux.json.YYYYMMDD.bak so the user
can audit edits without flooding the config dir.
3. Runs cmux reload-config so cmux ingests the password and re-emits
~/.local/state/cmux/socket-control-password.
4. launchctl kickstart -k on the relay so its events.stream
subscriber re-auths.
5. Tails the relay log briefly and reports attached / unavailable
counts. Exits non-zero with a targeted hint when auth still
fails (signals a plist/cmux password mismatch — re-run
install-launchd.sh with the matching CMUX_SOCKET_PASSWORD).
Also documented in README.md alongside the install steps.
|
Note Currently processing new changes in this PR. This may take a few minutes, please wait... ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (6)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
|
Caution Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted. Error details |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three commits that unbreak cmux Remote against modern cmux (0.64.16+).
Why
cmux 0.64.16 changed the default
automation.socketControlModeto"cmuxOnly"— only cmux-descendant processes can bind the UDS. A launchd-spawnedcmux-relayhas launchd as PID 1, not cmux, so it getsand the events-stream supervisor pegs at the 8 s reconnect cap. The intended fix is to flip cmux to
socketControlMode = "password"and share the secret with the relay, but two things blocked that path:cmuxSocketPassword()only checked the legacy~/Library/Application Support/cmux/socket-control-passwordpath. cmux 0.64.16 writes the password sidecar to$XDG_STATE_HOME/cmux/socket-control-password(default~/.local/state/cmux/socket-control-password), so the relay couldn't find it even after cmux ingested it.scripts/install-launchd.shandscripts/relay.plist.tmplhad no slot forCMUX_SOCKET_PASSWORD, forcing operators toplutil -insertthe env var by hand.Plus, cmux 0.64.16 strips
automation.socketPasswordfromcmux.jsonafter ingesting it on startup (privacy hardening — plaintext doesn't sit in config forever). Side-effect: every cmux restart wipes the server-side password and the relay needs the password re-injected. Mechanical, but easy to forget.What
Follow cmux's relocated socket-control-password file—cmuxSocketPassword()now followsCMUX_SOCKET_PASSWORDenv →$XDG_STATE_HOME/cmux/socket-control-password→ legacy app-support path. Mirrors the tiering thatcmuxSocketPath()already uses (matching the convention from 55e2d45). Adds two new tests covering the XDG tier andXDG_STATE_HOMEoverride; updates the existing 3 password tests to inject an empty state directory so the host's real~/.local/state/cmux/socket-control-passwordcannot shadow fixtures.Surface CMUX_SOCKET_PASSWORD in the launchd installer— addsCMUX_SOCKET_PASSWORDto the plist template'sEnvironmentVariablesblock (placeholder__SOCKET_PASSWORD__);install-launchd.shreadsCMUX_SOCKET_PASSWORDfrom its own env and renders it viased. Usage help and dry-run notes document the knob and mask the value in[install-launchd]notes (<set via CMUX_SOCKET_PASSWORD env, N chars>); the literal password is never echoed by the script. README gets a newcmux 0.64.16+ socket-control 비밀번호section walking through the recipe.Add scripts/refresh-cmux-password.sh for cmux-restart recovery— idempotent helper that reads the canonical secret out of the launchd plist, re-injects it intocmux.json, runscmux reload-config, kickstarts the relay, and confirms the events stream attached. Safe to re-run any time. Usespython3(default on macOS) for the JSON edit so the secret never crosses argv.Verification
swift build -c releaseclean (the pre-existing Sendable warning atHTTPServer.swift:246is unrelated and tracked separately).SharedKitTeststarget imports the swift-testingTestingmodule which the package doesn't declare, blockingswift testend-to-end on stock toolchains — pre-existing, not introduced by this PR; a small package consumingCMUXClientexercised the 6 cases of the new resolution order and all passed).events.stream attached: 1, unavailable: 0after restart +refresh-cmux-password.sh.Out of scope (mentioned for context, not in this PR)
Tests/SharedKitTests/*.swiftfiles import the swift-testingTestingmodule which the package doesn't declare, blockingswift test. Pre-existing.WSProtocolMachineforwards arbitrary cmux RPC method names without an allow-list, andPerDeviceRateLimiteris defined but never instantiated in production — both flagged in a separate security review I shared with the maintainer.Summary by CodeRabbit
New Features
Bug Fixes
Documentation