Skip to content

fix(remote): per-user SSH socket isolation + tunnel/iTerm2 jump hardening (#193, #190, #198)#201

Merged
wxtsky merged 1 commit into
mainfrom
fix/remote-socket-isolation-and-jumps
May 30, 2026
Merged

fix(remote): per-user SSH socket isolation + tunnel/iTerm2 jump hardening (#193, #190, #198)#201
wxtsky merged 1 commit into
mainfrom
fix/remote-socket-isolation-and-jumps

Conversation

@wxtsky
Copy link
Copy Markdown
Owner

@wxtsky wxtsky commented May 30, 2026

Summary

Fixes three remote/jump robustness issues reported on the tracker.

#193 — Remote SSH socket shared across users on multi-user hosts

RemoteHost.remoteSocketPath was hardcoded to /tmp/codeisland.sock, while the local app already uses /tmp/codeisland-<uid>.sock. On a shared remote host, with StreamLocalBindUnlink=yes, a second user's reverse forward would unlink/rebind the first user's socket, so their Claude/Codex/OpenCode hooks silently sent events to the wrong tunnel (or failed).

Fix: probe the remote uid once at connect time and route a per-user /tmp/codeisland-<uid>.sock consistently through:

  • the SSH -R reverse forward (SSHForwarder)
  • the remote hook command env CODEISLAND_SOCKET_PATH (command_for in configureRemoteHooksScript)
  • the OpenCode remote plugin (remoteOpencodePluginForInstall)
  • the remote hook script default (codeisland-remote-hook.py now defaults to a uid-scoped path)

If the uid probe fails (older/restricted host), we fall back to the legacy shared path, so nothing regresses. RemoteManager resolves the path before opening the tunnel and reuses it for hook installation.

#190ssh exited (0), connection reported as failed

The forwarder uses ssh -N. If the user's SSH config enables connection multiplexing (ControlMaster auto + ControlPath), ssh -N can hand the forward to a pre-existing master and exit 0 immediately — which the forwarder misread as a dead tunnel (matches the reporter: manual ssh works, CodeIsland shows "ssh exited (0)"). Force ControlMaster=no / ControlPath=none so the tunnel always holds its own dedicated connection.

#198 — iTerm2 fullscreen / cross-Space jump lands on the wrong window

The session-id jump path selected the matched tab + session but never selected the window, so a fullscreen window (its own Space) wasn't raised. Add select <window> on all three iTerm2 match paths (session-id, tty, cwd) so the owning window is brought to the front and macOS switches to its Space.

Tests

  • swift test — 333 passing.
  • New: per-user socket path injection into the remote hook script and OpenCode plugin, plus the legacy-fallback path.

🤖 Generated with Claude Code

…jump

- #193: the remote SSH socket was hardcoded to /tmp/codeisland.sock, so multiple
  OS users on a shared host collided. With StreamLocalBindUnlink a later user's
  reverse forward would unlink/rebind another user's socket, silently stealing
  their events. Probe the remote uid at connect time and route a per-user
  /tmp/codeisland-<uid>.sock through the SSH -R forward, the remote hook command
  (CODEISLAND_SOCKET_PATH), and the OpenCode plugin. Remote hook script also
  defaults to a uid-scoped path. Falls back to the legacy shared path if the
  probe fails.
- #190: force ControlMaster=no / ControlPath=none on the tunnel so `ssh -N`
  doesn't hand the forward to a pre-existing multiplexing master and exit 0
  immediately, which the forwarder misreads as a failed connection ("ssh exited (0)").
- #198: the iTerm2 jump now selects the matched *window* (not just tab+session)
  so a fullscreen / cross-Space session is raised and its Space switched to.

Tests: per-user socket injection into the remote hook script and OpenCode plugin,
plus the legacy fallback path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@wxtsky wxtsky merged commit f42e264 into main May 30, 2026
@wxtsky wxtsky deleted the fix/remote-socket-isolation-and-jumps branch May 30, 2026 01:48
wxtsky added a commit that referenced this pull request May 30, 2026
Follow-up hardening on #201:
- Guard each iTerm2 `select <window>` with its own `try` so an unexpected failure
  (e.g. a window mid fullscreen-transition) can't abort the surrounding script and
  skip the tab/session select — that would have made the jump worse than before.
- Probe the remote uid with a bare `id -u` instead of a `$(...)` pipeline so it
  runs identically under non-POSIX login shells (fish/csh) instead of silently
  falling back to the shared socket path. Also drops the redundant cleanup
  round-trip (StreamLocalBindUnlink already clears stale sockets on bind), so a
  failing host no longer waits on a second SSH.
- Remove the now-unused shellSingleQuoted helper.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
GeorgeDong32 pushed a commit to GeorgeDong32/CodeIsland that referenced this pull request May 31, 2026
…jump (wxtsky#201)

- wxtsky#193: the remote SSH socket was hardcoded to /tmp/codeisland.sock, so multiple
  OS users on a shared host collided. With StreamLocalBindUnlink a later user's
  reverse forward would unlink/rebind another user's socket, silently stealing
  their events. Probe the remote uid at connect time and route a per-user
  /tmp/codeisland-<uid>.sock through the SSH -R forward, the remote hook command
  (CODEISLAND_SOCKET_PATH), and the OpenCode plugin. Remote hook script also
  defaults to a uid-scoped path. Falls back to the legacy shared path if the
  probe fails.
- wxtsky#190: force ControlMaster=no / ControlPath=none on the tunnel so `ssh -N`
  doesn't hand the forward to a pre-existing multiplexing master and exit 0
  immediately, which the forwarder misreads as a failed connection ("ssh exited (0)").
- wxtsky#198: the iTerm2 jump now selects the matched *window* (not just tab+session)
  so a fullscreen / cross-Space session is raised and its Space switched to.

Tests: per-user socket injection into the remote hook script and OpenCode plugin,
plus the legacy fallback path.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
GeorgeDong32 pushed a commit to GeorgeDong32/CodeIsland that referenced this pull request May 31, 2026
…#202)

Follow-up hardening on wxtsky#201:
- Guard each iTerm2 `select <window>` with its own `try` so an unexpected failure
  (e.g. a window mid fullscreen-transition) can't abort the surrounding script and
  skip the tab/session select — that would have made the jump worse than before.
- Probe the remote uid with a bare `id -u` instead of a `$(...)` pipeline so it
  runs identically under non-POSIX login shells (fish/csh) instead of silently
  falling back to the shared socket path. Also drops the redundant cleanup
  round-trip (StreamLocalBindUnlink already clears stale sockets on bind), so a
  failing host no longer waits on a second SSH.
- Remove the now-unused shellSingleQuoted helper.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant