Guest User

Untitled

a guest
Feb 18th, 2026
11
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.21 KB | Source Code | 0 0
  1. # Remote Claude Code Access via Tmux + Tailscale + Termius
  2.  
  3. ## Overview
  4.  
  5. Access running Claude Code sessions from iPhone (Termius) by combining:
  6. - **Tailscale** — Mesh VPN connecting Mac and iPhone over private network
  7. - **tmux** — Terminal multiplexer keeping sessions alive across disconnects
  8. - **Termius** — iOS SSH client with mobile-friendly keyboard
  9.  
  10. ## Architecture
  11.  
  12. ```
  13. Mac (Claude Code) iPhone (Termius)
  14. ┌─────────────────────┐ ┌──────────────┐
  15. │ tmux session │ │ Termius app │
  16. │ ┌───────────────┐ │ SSH over │ │
  17. │ │ Claude Code │──┼──Tailscale──▶│ tmux attach │
  18. │ │ (running) │ │ │ │
  19. │ └───────────────┘ │ └──────────────┘
  20. └─────────────────────┘
  21. ```
  22.  
  23. ## Components
  24.  
  25. ### 1. Tailscale (Mesh VPN)
  26.  
  27. - Installed via `brew install --cask tailscale`
  28. - Creates private network between devices
  29. - No port forwarding or firewall configuration needed
  30. - Sign-in with Apple ID (most privacy-friendly option)
  31.  
  32. **Aliases in `.zshrc`:**
  33. ```bash
  34. alias ts_status='tailscale status'
  35. alias ts_ip='tailscale ip -4'
  36. alias ts_up='tailscale up'
  37. alias ts_down='tailscale down'
  38. alias ts_devices='tailscale status | grep -v "^$"'
  39. ```
  40.  
  41. ### 2. macOS Remote Login (SSH)
  42.  
  43. - Enabled via `sudo systemsetup -setremotelogin on`
  44. - Required for SSH access from Termius
  45.  
  46. ### 3. tmux Configuration
  47.  
  48. File: `~/.tmux.conf` (outside repo — not managed by master-template)
  49.  
  50. Key settings:
  51. - **Mouse support** — Essential for phone/tablet interaction
  52. - **OSC 52 clipboard** — Enables clipboard sync over SSH
  53. - **vi copy mode** — Mouse drag auto-copies to system clipboard via `pbcopy`
  54. - **50k scrollback** — Large history buffer
  55. - **escape-time 10** — Reduce latency over SSH
  56. - **destroy-unattached off** — Keep sessions alive when client disconnects
  57.  
  58. ### 4. tmux Auto-Wrapping in `_claude()` and `cr()`
  59.  
  60. Every Claude Code session is automatically wrapped in a tmux session:
  61.  
  62. ```bash
  63. # Creates unique session name per project directory
  64. local base_name=$(basename "$PWD" | tr '.' '-')
  65. # Auto-increment if session name exists: marstek, marstek-2, marstek-3
  66. while tmux has-session -t "$session_name" 2>/dev/null; do
  67. session_name="${base_name}-${n}"
  68. n=$((n + 1))
  69. done
  70. # Session self-destructs when Claude exits
  71. tmux send-keys -t "$session_name" "$claude_cmd; tmux kill-session -t $session_name" Enter
  72. ```
  73.  
  74. **Key design decisions:**
  75. - Sessions auto-kill when Claude exits (prevents stale sessions)
  76. - Unique session names allow multiple Claude instances per project
  77. - If already inside tmux (e.g., from phone SSH), runs directly without nesting
  78. - Zero visible difference on Mac — tmux wrapping is transparent
  79.  
  80. ### 5. Unified Session Picker (`_tmux_pick`)
  81.  
  82. Mobile-friendly numbered menu combining running sessions and project launcher:
  83.  
  84. ```
  85. 1) protest-upgrade ← Running tmux sessions
  86. 2) marstek-2
  87.  
  88. 3) protest-upgrade ← Available projects
  89. 4) swiss-sense
  90. 5) cameraland
  91. ...
  92.  
  93. 0) sleep
  94.  
  95. # _ ← Number input
  96. ```
  97.  
  98. After selecting a project, accepts full alias syntax:
  99. ```
  100. co cs ch cr (+ 1/2/3 -d -c)
  101. > co3 -d -c ← Opus, think high, dangerous, chrome
  102. ```
  103.  
  104. **Aliases:** `ta` and `cc` both map to `_tmux_pick`
  105.  
  106. **Stale session cleanup:** Before showing menu, kills unattached tmux sessions where the foreground process is NOT `claude` (meaning Claude exited and left a shell).
  107.  
  108. ### 6. SSH Auto-Attach with Menu Loop
  109.  
  110. When SSHing in from Termius:
  111. 1. Prompts for keychain unlock (Claude Code stores API creds in macOS keychain)
  112. 2. Shows `_tmux_pick` menu in a loop
  113. 3. After a tmux session ends, returns to menu
  114. 4. Empty input breaks the loop → drops to regular shell
  115.  
  116. ```bash
  117. if [[ -n "$SSH_CONNECTION" ]] && [[ -z "$TMUX" ]]; then
  118. echo "Unlock keychain for Claude Code:"
  119. security unlock-keychain ~/Library/Keychains/login.keychain-db
  120. while _tmux_pick; do
  121. echo ""
  122. done
  123. fi
  124. ```
  125.  
  126. ### 7. `/sleep` Command
  127.  
  128. Puts Mac to sleep remotely from phone. Runs `pmset sleepnow`.
  129. - Available in all projects (symlinked to all commands directories)
  130. - Also accessible as option `0)` in `_tmux_pick` menu
  131. - Permission added to all 9 `settings.local.json` files
  132.  
  133. ## Termius Setup (iPhone)
  134.  
  135. 1. Install Termius from App Store
  136. 2. Add new host:
  137. - **Address:** Tailscale IP of Mac
  138. - **Username:** Mac username
  139. - **Auth:** SSH key or password
  140. 3. Connect → keychain prompt → session menu
  141.  
  142. ## Solved Problems
  143.  
  144. ### macOS Keychain Lock on SSH
  145.  
  146. Claude Code stores OAuth credentials in macOS keychain, which is locked for SSH sessions. Solution: `security unlock-keychain` prompt on SSH login — user enters Mac password once per session.
  147.  
  148. ### Session Name Collisions
  149.  
  150. Multiple Claude sessions for same project would collide on `basename $PWD`. Solved with auto-incrementing suffix: `marstek`, `marstek-2`, `marstek-3`.
  151.  
  152. ### Stale Session Accumulation
  153.  
  154. Sessions created before auto-kill code persisted indefinitely. Solved with:
  155. 1. Auto-kill on Claude exit: `$claude_cmd; tmux kill-session -t $session_name`
  156. 2. Stale detection in `_tmux_pick`: kills unattached sessions not running `claude`
  157.  
  158. ### Menu Loop for Mobile
  159.  
  160. On phone, exiting a Claude session would drop to a bare shell. Solved by wrapping `_tmux_pick` in a `while` loop (SSH only). After each session ends, the menu reappears. Enter with no input to get a shell.
  161.  
  162. ## Sources
  163.  
  164. - Reddit discussion: r/ClaudeCode — "Tmux + Tailscale + Termius"
  165. - rogs.me guide on remote Claude Code access
  166. - anurajrp.io guide on Tailscale + tmux setup
  167.  
  168. ## Files Modified
  169.  
  170. | File | Changes |
  171. |------|---------|
  172. | `~/.tmux.conf` | Created (not in repo) |
  173. | `.zshrc` | `_claude()`, `cr()`, `_tmux_pick()`, SSH auto-attach, Tailscale aliases |
  174. | `projects/_overall/.claude/commands/sleep.md` | New `/sleep` command |
  175. | All 9 `settings.local.json` | Added `pmset sleepnow` permission |
  176.  
Advertisement
Add Comment
Please, Sign In to add comment