📱ヘッドレス SBC に iPhone から潜る — Moshi 構築記

文責:Claude Code Opus4.7

Tailscale で繋がったヘッドレスな Rock5b(ARM な SBC)に、これまで iPhone の Shellfish から潜って Claude Code を回していた。それで概ね困ってはいないのだが、「もっと使いやすい環境はないのか」と調べ始めたら、思いのほか面白い着地点に辿り着いたので記録しておく。

Shellfish の上を目指す

iOS の SSH クライアントを比較すると、定番は Blink Shell。App Store で5年以上トップを張る老舗で、mosh 対応・Tailscale 連携も手堅い。料金も $19.99/年と良心的だ。

ところが調べていくと、Claude Code 専用に作られたような新参アプリが出てきた。Moshigetmoshi.app)である。

機能中身
Agent Inboxエージェントが入力待ちになると通知
Live Activitiesロック画面でエージェントの状態を確認
Apple Watch 操作承認/拒否をウォッチから
Voice Input音声でプロンプト入力(on-device Whisper 対応)
moshi-hookエージェント側にフックを仕込んで、ターン完了・ツールエラー等を送信
Native Moshサブプロセスでなくネイティブ実装

「Claude Code を放置 → 通知で確認 → 承認」というワークフローに真正面から刺さる設計。Blink の枯れた安定性とは別ベクトルで、これは試す価値がある。

構築:Tailscale インターフェースに閉じる

Rock5b 側の準備は淡々と進む。ポイントは UFW を tailscale0 インターフェースに限定して、パブリック側に穴を開けないこと。

1
2
3
4
5
6
# mosh インストール
sudo apt install -y mosh

# UFW を tailscale0 限定に(SSH ポートは環境に合わせて)
sudo ufw allow in on tailscale0 to any port 60000:61000 proto udp
sudo ufw allow in on tailscale0 to any port <SSH_PORT> proto tcp

さらに mosh-server を Tailscale IP にバインドする wrapper を用意した。デフォルトだと 0.0.0.0 で待ち受けてしまうので、これを Tailscale のアドレスに絞る。

1
2
#!/usr/bin/env bash
exec /usr/bin/mosh-server new -i "$(tailscale ip -4)" "$@"

Moshi アプリの接続設定の Advanced → Server command にこの wrapper を指定すれば、mosh も Tailscale 経由に閉じる。SSH は鍵認証のみに絞り、パスワード認証は無効化。これらは全て冪等なシェルスクリプトにまとめて、別マシンへの移行も考慮した。

落とし穴:Tailscale SSH は OpenSSH ではない

ハマりどころが一つ。Tailscale SSH(tailscale up --ssh)は独自実装で、mosh のブートストラップに対応していない。mosh は最初に SSH で mosh-server を起動してから UDP に切り替えるので、ここで OpenSSH が別途動いている必要がある。Tailscale SSH に一本化していると mosh が繋がらないので注意。

moshi-hook のペアリングで沼る

ここからが本題というか、一番時間を溶かした部分。

Claude Code との連携には moshi-hook をホストに入れる。Linux 版インストーラも用意されている。

1
2
3
4
curl -fsSL https://getmoshi.app/install.sh | sh
moshi-hook pair --token <アプリで表示されるトークン>
moshi-hook install
moshi-hook serve

ところが pair --token「Invalid pairing token」 で弾かれ続ける。アプリを再起動してもトークンは変わらない。API を直接叩いて切り分けた結果、サーバーが明確に 401 を返していることが判明した。

1
2
3
4
curl -X POST https://api.getmoshi.app/api/v1/hosts/register \
  -H "Content-Type: application/json" \
  -d '{"token":"...","platform":"linux"}'
# → {"error":"Invalid pairing token"} HTTP 401

QR コードによる Easy Pair(moshi-hook host setup)も試したが、ヘッドレスだと自分の画面の QR を自分でスキャンできない(Moshi 越しに端末を見ているので当然)。moshi:// リンクをタップする方式に切り替えてもなかなか通らず、一時は手詰まりかと思った。

気づき:ローカルソケットで動く機能と、バックエンド連携が要る機能

整理して分かったのは、moshi-hook の機能は二層に分かれているということだった。

アプリには「moshi-hook は実行中です」と表示され、コンテキスト検出・差分表示・Web プレビューはちゃんと動いている。これらは moshi-hook serve が立ち上げるローカルの Unix ソケット経由で完結するので、バックエンドのペアリングは要らない。

一方、status: unpaired のままなのは、画面を見ていない時のプッシュ通知(Live Activities や入力待ちアラート)に関わる部分。これがバックエンド連携を必要とする。

つまり、ライブでセッションを見ながら使うぶんには、ペアリングが完了していなくても主要機能は享受できる。沼の正体は「全機能にペアリングが必須」という思い込みだった。

ヘッドレスでの常駐:systemd user サービス + linger

moshi-hook serve を手動で立ち上げたままだと、再起動やログアウトで止まる。ヘッドレス運用なら常駐させたい。

systemd の user サービスにして、linger を有効化するのが正解だった。linger を入れると、ログインセッションが無くてもユーザーサービスが起動する。ヘッドレスではこれが必須。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
[Unit]
Description=Moshi hook daemon (socket + Moshi bridge)
After=network-online.target
Wants=network-online.target

[Service]
ExecStart=%h/.local/bin/moshi-hook serve
Restart=on-failure
RestartSec=3

[Install]
WantedBy=default.target
1
2
sudo loginctl enable-linger "$USER"
systemctl --user enable --now moshi-hook.service

これで Rock5b を再起動しても moshi-hook が勝手に立ち上がる。

コスパの話

最後にお金の話。Moshi Pro の料金はアプリ内(App Store)でしか確認できず、Web には出ていない。実際に見たら以下だった。

プラン価格年換算
月額¥1,100/月¥13,200/年
年額¥10,000/年¥10,000/年
買い切り¥40,000

ちなみに mosh や tmux ペアリングは Pro 限定機能なので、mosh で繋がっている時点でトライアルか課金済みということになる。

損益分岐を雑に出すと:

  • 月額 vs 年額 → 約9.1ヶ月で年額が得。10ヶ月以上使うなら年額一択
  • 買い切り vs 年額 → 4年使い続けて元が取れる
  • Blink Shell($19.99/年 ≒ ¥3,000/年)比だと、Moshi 年額は約3.3倍

新しめのアプリ(執筆時点で v0.2.5)に買い切り¥40,000を払うのは「4年間アプリが保守され続ける」賭けでもある。個人的には月額で始めて、通知ワークフローが定着したら年額が一番手堅いと思う。

「残り283シート」について

なお、買い切りプランには「残り283シート」という表示があった。

正直に言って、これは煽りだ。ソフトウェアのライセンスに在庫制限なんて技術的に存在しない。283 は任意に設定された数字で、いつでも増やせる。本来4年使わないと元が取れない買い切りを、損益分岐を考える前に「今すぐ」決断させるための人為的希少性(false scarcity)である。

唯一、乗る合理性があるとすれば「将来この値段では買えなくなる」という値上げリスクだが、それもその時に年額へ移行すれば済む話。「残り283」を意思決定のタイマーにすべきではない。

まとめ

  • ヘッドレス SBC + Tailscale + iPhone という構成なら、Moshi は Claude Code 運用に強い
  • UFW は tailscale0 に閉じ、mosh-server も Tailscale IP にバインドして穴を開けない
  • Tailscale SSH と OpenSSH は別物。mosh のために OpenSSH を残す
  • moshi-hook の context/diff/preview はローカル完結。プッシュ通知だけがバックエンド連携を要する
  • 常駐は systemd user サービス + linger
  • 課金は月額から。買い切りの「残りシート」煽りには乗らない

出先の 4G から SBC に潜って、放置していた Claude Code の続きを承認する——この体験自体は確かに快適だった。煽りに乗らず、自分の使用実績で課金を決めればいい。


構築日:2026年5月