Telefondan Claude Code: Sıfırdan Kurulum Rehberi
iPhone'dan Apple Silicon bir Mac'teki Claude Code'a giden zincirin tamamı — Termius, Tailscale, sertleştirilmiş OpenSSH, mosh, tmux — kopyala-yapıştır, her adımın kontrolü ve çözümüyle.
Bu rehber, iPhone'dan Mac'teki Claude Code'u kullanmanı sağlayan zinciri sıfırdan kurar: Termius → Tailscale → OpenSSH → mosh → tmux → Claude Code. Neden böyle kurduğumu ve yolda düştüğüm tuzakları Cebimdeki Claude Code yazısında anlattım; burası yalnızca tarif. Her adımda ne yaptığın, komutlar, nasıl kontrol edeceğin ve ters giderse ne olduğu var. Hiçbir repoya ihtiyacın yok, her şey kopyala-yapıştır.
0. Başlamadan önce
- Apple Silicon bir Mac ve üzerinde yönetici (admin) hakkı. Kabuğun macOS'un
varsayılanı olan zsh olmalı. Intel Mac'te Homebrew
/opt/homebrewyerine/usr/localaltında yaşar; aşağıdaki yolları ona göre değiştir. - Homebrew, macOS'un paket yöneticisi.
- Mac'te kurulu ve giriş yapılmış Claude Code: terminalde
claudeçalışıyor olmalı. - Bir iPhone, bir Tailscale hesabı ve App Store'dan Termius.
Menü adları İngilizce arayüzden; Türkçe macOS'ta karşılıklarını bulursun.
1. Paketler
tmux oturumu Mac'te yaşatır, mosh bağlantıyı kopmaz yapar, Tailscale de telefonla Mac'i aynı özel ağa koyar.
brew install tmux mosh
brew install --cask tailscale-app
Kontrol: tmux -V ve mosh --version birer sürüm yazıyor.
2. Tailscale
Tailscale, cihazlarını nerede olurlarsa olsunlar aynı yerel ağdaymış gibi bağlayan bir VPN; bu özel ağa tailnet deniyor. Router'da port açmak gerekmez.
- Mac: Tailscale uygulamasını aç, System Settings → Privacy & Security'de sistem uzantısına izin ver, giriş yap.
- iPhone: Tailscale'i kur, aynı hesapla giriş yap, "Add VPN Configuration" sorusuna izin ver.
Uygulamanın komut satırı aracı PATH'te değil, tam yoluyla çağır:
/Applications/Tailscale.app/Contents/MacOS/Tailscale status
/Applications/Tailscale.app/Contents/MacOS/Tailscale ping <telefon>
İstersen ~/.zshrc'ye bir kısayol ekle:
alias tailscale=/Applications/Tailscale.app/Contents/MacOS/Tailscale.
Termius'a yazacağın adres Mac'in tam MagicDNS adı. MagicDNS, Tailscale'in her
cihaza verdiği <mac>.<tailnet>.ts.net biçimindeki isim:
/Applications/Tailscale.app/Contents/MacOS/Tailscale status --json \
| python3 -c 'import json,sys; print(json.load(sys.stdin)["Self"]["DNSName"].rstrip("."))'
Kontrol: status iki cihazı da listeliyor, ping cevap veriyor. Cevapta
via <ip>:<port> varsa bağlantı doğrudan, via DERP(...) varsa Tailscale'in
aktarma sunucuları (DERP) üzerinden; ikisi de çalışır.
Ters giderse: tailscale: command not found → tam yolu kullan. Ping cevap
vermiyorsa iki cihazın da aynı hesapla girdiğini ve iPhone'da VPN'in açık
olduğunu kontrol et.
3. SSH oturumlarında PATH
Rehberin en sinsi adımı. mosh, mosh-server'ı etkileşimsiz bir SSH oturumunda
başlatır; o oturum zsh ayarlarından yalnızca ~/.zshenv'i okur ve Homebrew'un
/opt/homebrew/bin'i orada PATH'te değildir. Sonuç: Termius
mosh-server not found der.
cat >> ~/.zshenv <<'EOF'
typeset -U path
path=(/opt/homebrew/bin /opt/homebrew/sbin $HOME/.local/bin $path)
export LANG="${LANG:-en_US.UTF-8}"
EOF
LANG satırı da şart: mosh-server UTF-8 bir locale olmadan çalışmayı reddeder.
'EOF' tırnaklı, yani $HOME ve $path dosyaya olduğu gibi yazılır ve her
oturum açılışında çözülür. ~/.local/bin de bir sonraki adımdaki script için.
Kontrol: temiz, etkileşimsiz bir kabuk mosh-server'ı buluyor mu:
env -i HOME="$HOME" /bin/zsh -c 'command -v mosh-server'
# /opt/homebrew/bin/mosh-server
Ters giderse: Çıktı boşsa satırlar ~/.zshenv'e değil başka bir dosyaya
gitmiştir (~/.zshrc etkileşimsiz oturumda okunmaz).
4. Tek komutla oturuma bağlan: ta
tmux, bağlantıdan bağımsız olarak Mac'te yaşayan bir terminal oturumu. ta,
main adlı oturuma bağlanır, yoksa oluşturur (-A bunu yapar); telefondan her
bağlandığında aynı ekrana düşersin.
mkdir -p ~/.local/bin
cat > ~/.local/bin/ta <<'EOF'
#!/bin/sh
exec tmux new-session -A -s main
EOF
chmod +x ~/.local/bin/ta
Kontrol: Yeni bir terminal aç, ta yaz: altta tmux'un yeşil durum çubuğu
çıkar. Ctrl-b sonra d ile ayrıl; oturum arkada yaşamaya
devam eder.
Ters giderse: ta: command not found → ~/.local/bin PATH'te değil; bkz.
adım 3.
5. Telefon için tmux ayarları
cat >> ~/.tmux.conf <<'EOF'
set -g mouse on
setw -g aggressive-resize on
set -g history-limit 50000
set -s escape-time 10
set -g default-terminal "tmux-256color"
set -g set-clipboard on
EOF
mouse on: Termius'ta parmakla kaydırma. tmux'ta fare varsayılan olarak kapalı.aggressive-resize on: telefon ve Mac aynı anda bağlıyken pencere her birine ayrı uyar.history-limit 50000: geriye kaydırılabilecek satır sayısı.escape-time 10: Esc tuşu gecikmesiz gider.default-terminal "tmux-256color": doğru renkler; bu terminal tanımı güncel macOS'ta hazır geliyor.set-clipboard on: tmux'tan kopyalanan metin terminalin panosuna gider.
Kontrol: Aşağıdaki komut hiçbir şey yazmıyorsa ayar dosyası hatasız:
tmux -L check -f /dev/null new -d \; source-file ~/.tmux.conf \; kill-server
Çalışan bir tmux oturumun varsa ayarları tmux source-file ~/.tmux.conf ile
uygula.
Ters giderse: invalid option: … → o satırda yazım hatası var.
6. SSH anahtarı
Şifre yerine anahtar çifti: özel anahtar telefonda kalır, açık (public) anahtar Mac'e gider, Mac yalnızca o özel anahtarı taşıyanı içeri alır.
Termius'ta Keychain → + → Generate Key, tür ED25519, bir isim ver. Anahtarı açıp public key'i kopyala (Universal Clipboard ile doğrudan Mac'e gelir). Mac'te:
mkdir -p ~/.ssh && chmod 700 ~/.ssh
echo 'ssh-ed25519 AAAA…anahtarın-tamamı… iphone' >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
Anahtarı tek tırnağın içine düz metin olarak yapıştır ve sonuna iphone
gibi bir cihaz adı ekle; telefon kaybolursa sileceğin satırı bu gösterir.
Kontrol:
ssh-keygen -l -f ~/.ssh/authorized_keys
# 256 SHA256:… iphone (ED25519)
Ters giderse: is not a public key file → cat ~/.ssh/authorized_keys ile
bak, bozuk satırı nano ile düzelt. sshd, izinleri gevşek bir ~/.ssh ya da
authorized_keys'i yok sayar; iki chmod'u atlama.
7. sshd'yi sertleştir
macOS'ta SSH sunucusu, OpenSSH'ın sshd servisi, System Settings'te Remote
Login diye geçer. Onu açmadan önce kilitliyoruz: yalnızca anahtarla, yalnızca
senin kullanıcınla, yalnızca tailnet'ten.
U=$(whoami)
sudo tee /etc/ssh/sshd_config.d/010-remote.conf >/dev/null <<EOF
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
AuthenticationMethods publickey
PermitEmptyPasswords no
PermitRootLogin no
AllowUsers $U@100.64.0.0/10 $U@fd7a:115c:a1e0::/48 $U@127.0.0.1 $U@::1
MaxAuthTries 3
LoginGraceTime 30
ClientAliveInterval 30
ClientAliveCountMax 4
X11Forwarding no
AllowAgentForwarding no
AllowTcpForwarding local
EOF
sudo chmod 644 /etc/ssh/sshd_config.d/010-remote.conf
Bu sefer EOF tırnaksız: $U yazılırken senin kullanıcı adına dönüşsün
diye. Dosyada $U görürsen tırnak kaçmıştır.
- Neden
010-:/etc/ssh/sshd_configbu klasörü alfabetik sırayla okur ve çoğu ayarda ilk görülen değer kazanır. macOS'un kendi dosyası100-macos.conf;010-ondan önce gelir. - Neden keyboard-interactive de kapalı: macOS
UsePAM yesile gelir; PAM, sistemin kimlik doğrulama katmanı ve bu yoldan şifreyi yine sorabilir. - Neden
AllowUsers: macOS'ta sshd her ağ arayüzünde dinler,ListenAddressişe yaramaz. Kaynak kısıtı bu yüzden burada:100.64.0.0/10Tailscale'in IPv4 aralığı,fd7a:115c:a1e0::/48IPv6 aralığı, gerisi Mac'in kendisi. - Ötekiler: 30 saniyede bir yoklama, dört cevapsızda kapanış (uyuyan
telefonun ölü bağlantıları ~2 dakikada temizlenir); agent forwarding kapalı,
TCP'de yalnızca yerel
-Lyönlendirme.
Hiç Remote Login açılmamış bir Mac'te host key, yani sunucunun kimliğini
kanıtlayan anahtar yoktur ve test no hostkeys available der. Eksikleri üret
(var olanlara dokunmaz), sonra test et:
sudo ssh-keygen -A
sudo /usr/sbin/sshd -t
Kontrol: sshd -t hiçbir şey yazmıyorsa sözdizimi doğru. Etkin değerlere bak:
sudo /usr/sbin/sshd -T | grep -iE '^(passwordauthentication|kbdinteractiveauthentication|permitrootlogin|authenticationmethods|allowusers)'
# permitrootlogin no
# passwordauthentication no
# kbdinteractiveauthentication no
# allowusers <kullanıcı>@100.64.0.0/10
# allowusers <kullanıcı>@fd7a:115c:a1e0::/48
# allowusers <kullanıcı>@127.0.0.1
# allowusers <kullanıcı>@::1
# authenticationmethods publickey
Ters giderse: Bad configuration option → dosyada yazım hatası. Değerler
farklı çıkıyorsa grep Include /etc/ssh/sshd_config satırının durduğunu ve
dosya adının 010- ile başladığını kontrol et.
8. Remote Login'i aç, öteki kapıları kapat
sudo systemsetup -setremotelogin on terminalin Full Disk Access iznine sahip
olmasını ister; terminale bütün diski açmak yerine arayüzü kullan:
- System Settings → General → Sharing → Remote Login açık.
- Yanındaki (i): Allow full disk access for remote users kapalı; Allow access for → Only these users → yalnızca senin kullanıcın.
- Aynı ekranda Remote Management, Remote Application Scripting ve Screen Sharing açıksa kapat. Bunlar her ağ arayüzünde dinler, macOS giriş şifreni kabul eder ve 7. adımdaki sertleştirme onları kapsamaz.
sshd her bağlantıda yeniden başladığı için ayarlar hemen geçerli.
Kontrol: Uzaktan erişim portlarından yalnızca 22 açık olmalı:
netstat -an -p tcp | grep LISTEN | grep -E '\.(22|5900|3283|3031) '
# tcp4 0 0 *.22 *.* LISTEN
# tcp6 0 0 *.22 *.* LISTEN
*.22 her arayüzde dinlemek demek; bu beklenen, filtreyi AllowUsers yapıyor.
Şifrenin gerçekten kapalı olduğunu da dene:
ssh -o BatchMode=yes -o PubkeyAuthentication=no \
-o PreferredAuthentications=password,keyboard-interactive \
-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
"$(whoami)@127.0.0.1" true
# <kullanıcı>@127.0.0.1: Permission denied (publickey).
Ters giderse: Parantezde password ya da keyboard-interactive da görünüyorsa
drop-in okunmuyor; 7. adımın kontrolüne dön.
9. Termius
Termius'ta yeni bir host:
- IP or Hostname: 2. adımdaki tam MagicDNS adı. MagicDNS kapalıysa Mac'in
100.x.y.zadresi (Tailscale CLI'daip -4). - Username: Mac'te
whoamiçıktısı. - SSH ID, Key, Certificate, FIDO2: 6. adımda ürettiğin anahtar. Password boş.
- Use Mosh: açık. SSH Agent Forwarding: kapalı.
mosh, SSH'ı yalnızca girişte ve mosh-server'ı başlatmak için kullanır; oturumun
kendisi UDP üzerinden akar, bu yüzden Wi-Fi'dan 5G'ye geçmek ya da telefonun
uyuması onu öldürmez.
Sonra Snippets → +: adı ta, komutu ta. Host ayarlarında Startup
Snippet olarak ta'yı seç; her bağlantı doğrudan main oturumuna düşer.
İlk bağlantıda Termius, Mac'in host key parmak izini gösterip güvenip güvenmediğini sorar. Körlemesine kabul etme, Mac'teki değerle karşılaştır:
ssh-keygen -l -f /etc/ssh/ssh_host_ed25519_key.pub
Kontrol: Bağlan: tmux'un yeşil durum çubuğunu görüyorsan zincirin tamamı çalışıyor.
Ters giderse: Aşağıdaki sorun giderme tablosuna bak.
10. Claude Code
Telefondan bağlıyken claude yaz. Çoğu zaman olduğu gibi açılır.
SSH oturumunda macOS'un şifre kasası login keychain kilitli görünebilir ve Claude Code yeniden giriş isteyebilir. İki yol var. Her oturumda kilidi aç (Mac şifreni sorar):
security unlock-keychain ~/Library/Keychains/login.keychain-db
Ya da uzun ömürlü bir token üret ve yalnızca SSH oturumlarının okuduğu bir dosyaya koy:
claude setup-token
mkdir -p ~/.config/remote && chmod 700 ~/.config/remote
printf 'export CLAUDE_CODE_OAUTH_TOKEN=%s\n' '<token>' > ~/.config/remote/env
chmod 600 ~/.config/remote/env
echo '[[ -n "$SSH_CONNECTION" && -r ~/.config/remote/env ]] && source ~/.config/remote/env' >> ~/.zshenv
<token> yerine setup-token'ın verdiği değeri yaz. Bu bir sır: dosyayı kimseyle
paylaşma, hiçbir repoya commit etme.
Kontrol: Yeni bir bağlantıda echo ${CLAUDE_CODE_OAUTH_TOKEN:+var} → var,
ve claude giriş istemeden açılıyor.
11. İsteğe bağlı: şarjdayken uyuma
Uyuyan Mac'e hiçbir şey ulaşamaz. caffeinate, macOS'un uykuyu engelleyen
komutu; -s ile yalnızca şarjdayken geçerli. Bir LaunchAgent, yani oturum
açınca launchd'nin başlattığı arka plan görevi, onu hep çalışır tutar:
mkdir -p ~/Library/LaunchAgents
cat > ~/Library/LaunchAgents/local.keepawake.plist <<'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>local.keepawake</string>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/caffeinate</string>
<string>-s</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
</dict>
</plist>
EOF
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/local.keepawake.plist
Kontrol: Şarjdayken:
launchctl print gui/$(id -u)/local.keepawake | grep 'state ='
# state = running
pmset -g assertions | grep PreventSystemSleep
# PreventSystemSleep 1
Ters giderse: state = not running ise
launchctl kickstart gui/$(id -u)/local.keepawake ile başlat. Pildeyken ve kapak
kapalıyken Mac yine uyur; kapak kapalı çalışmanın tek yolu harici ekranla
clamshell modu.
12. Son test
- Telefondan bağlan,
claudebaşlat. - Telefonu kilitle, iki dakikadan fazla bekle, geri dön: aynı ekran.
- Bağlantıyı Termius'ta tamamen kapat, yeniden bağlan: aynı, hâlâ çalışan oturum.
Sorun giderme
| Belirti | Sebep | Çözüm |
|---|---|---|
| mosh-server not found | Etkileşimsiz SSH oturumu Homebrew'u görmüyor | 3. adım |
| needs a UTF-8 native locale | LANG tanımlı değil | 3. adımdaki LANG satırı |
| Permission denied (publickey) | Anahtar authorized_keys'te yok, izinler gevşek, kullanıcı adı yanlış ya da bağlantı tailnet'ten gelmiyor | 6. adım; Username = whoami; Hostname MagicDNS adı mı, iPhone'da Tailscale açık mı |
| Zaman aşımı | iPhone'da Tailscale kapalı ya da Mac uyuyor | Tailscale'i aç; 11. adım |
| mosh bağlanıyor ama ekran gelmiyor | macOS güvenlik duvarı mosh'un UDP'sini engelliyor | socketfilterfw ile mosh-server'a izin ver (aşağıda) |
| tailscale: command not found | Uygulamanın CLI'ı PATH'te değil | Tam yol ya da alias, 2. adım |
| no hostkeys available | Host key hiç üretilmemiş | sudo ssh-keygen -A |
| Claude giriş istiyor | SSH oturumunda keychain kilitli | 10. adım |
| ta: command not found | ~/.local/bin PATH'te değil | 3. adım |
Güvenlik duvarı açıksa mosh-server'a izin vermek için:
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --add "$(realpath /opt/homebrew/bin/mosh-server)"
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --unblockapp "$(realpath /opt/homebrew/bin/mosh-server)"
Geri alma
sudo rm /etc/ssh/sshd_config.d/010-remote.conf
sed -i '' '/ iphone$/d' ~/.ssh/authorized_keys
launchctl bootout gui/$(id -u)/local.keepawake
rm ~/Library/LaunchAgents/local.keepawake.plist ~/.local/bin/ta
rm -r ~/.config/remote
Sonra System Settings → General → Sharing'de Remote Login'i kapat, ~/.zshenv
ve ~/.tmux.conf'a eklediğin satırları elle sil ve telefonu Tailscale yönetim
panelinden kaldır.
Güvenlik modeli
- Ağ: router'da açık port yok; Mac'e giden yol tailnet'ten geçer.
- Kim, nereden: yalnızca senin kullanıcın, yalnızca tailnet ve localhost'tan
(
AllowUsers). - Nasıl: yalnızca anahtar; şifre, keyboard-interactive (PAM) ve root girişi kapalı.
- Ne kadar: agent forwarding kapalı, TCP yönlendirmede yalnızca
-L. - Başka kapı yok: Remote Management, Remote Application Scripting ve Screen Sharing kapalı; sshd sertleştirmesi onları korumaz.
- Telefon kaybolursa:
authorized_keys'ten o cihazın satırını sil, cihazı Tailscale yönetim panelinden çıkar.
Bu kararların arkasındaki hikâye ve her birini nasıl öğrendiğim Cebimdeki Claude Code yazısında.