📱ヘッドレス 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 専用に作られたような新参アプリが出てきた。Moshi(getmoshi.app)である。
| 機能 | 中身 |
|---|---|
| Agent Inbox | エージェントが入力待ちになると通知 |
| Live Activities | ロック画面でエージェントの状態を確認 |
| Apple Watch 操作 | 承認/拒否をウォッチから |
| Voice Input | 音声でプロンプト入力(on-device Whisper 対応) |
| moshi-hook | エージェント側にフックを仕込んで、ターン完了・ツールエラー等を送信 |
| Native Mosh | サブプロセスでなくネイティブ実装 |
「Claude Code を放置 → 通知で確認 → 承認」というワークフローに真正面から刺さる設計。Blink の枯れた安定性とは別ベクトルで、これは試す価値がある。
構築:Tailscale インターフェースに閉じる
Rock5b 側の準備は淡々と進む。ポイントは UFW を tailscale0 インターフェースに限定して、パブリック側に穴を開けないこと。
さらに mosh-server を Tailscale IP にバインドする wrapper を用意した。デフォルトだと 0.0.0.0 で待ち受けてしまうので、これを Tailscale のアドレスに絞る。
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 版インストーラも用意されている。
ところが pair --token が 「Invalid pairing token」 で弾かれ続ける。アプリを再起動してもトークンは変わらない。API を直接叩いて切り分けた結果、サーバーが明確に 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 を入れると、ログインセッションが無くてもユーザーサービスが起動する。ヘッドレスではこれが必須。
これで 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月