Claude

iPhoneからMacのターミナルを遠隔操作できるシステムを作った

2026年3月4日 25分で読める AQUA合同会社
iPhoneからMacのターミナルを遠隔操作できるシステムを作った

カフェでiPhoneを取り出して、自宅Macのターミナルをそのまま操作する。

iPhoneからClaude Codeを起動して、自宅MacBookのプロジェクトをAIに修正させる。それを無料で、ブラウザだけで実現したかった。画面共有アプリは重すぎるし、SSHクライアントをインストールするのも面倒だ。

「ブラウザだけでターミナルだけ使えればいい」——そんなシンプルな要求を満たすツールが見つからなかったので、自分で作った。mac-remoteというシステムだ。Cloudflare Tunnelでポート開放不要、PIN認証でセキュア、コマンド一発で起動する。完全無料のオープンソースだ。

この記事は「使い方マニュアル」ではない。なぜこの設計にしたのか、何を捨てて何を選んだのか、セキュリティをどう考えたのか——開発者として意思決定のプロセスを全て語る。技術的に面白い箇所(PTY、Cloudflare Tunnel、認証設計)は特に深掘りした。エンジニアが読んで「なるほど」と思える記事を目指している。



何を作ったのか — 30秒デモ

まず、動いている姿を見てほしい。

Macのターミナルでyuatlと打つ。それだけでサーバーが起動し、ターミナルにQRコードとPINコードが表示される。

$ yuatl

==================================================
  Mac Remote Control
==================================================

  Local: http://192.168.1.100:8443/
  PIN:   K9M2X5P7

  Starting tunnel...

==================================================
  REMOTE ACCESS (share this URL)
==================================================

  URL:  https://abc123.trycloudflare.com
  PIN:  K9M2X5P7

  Open on your iPhone from anywhere.
==================================================

iPhoneでQRコードをスキャンするか、表示されたURLをブラウザで開く。8桁のPINを入力する。すると、iPhoneの画面にMacのターミナルがそのまま現れる。

Mac Remote PIN入力画面 iPhoneからMacのターミナルに8桁PINでログイン

lsgit statuspython3vim——何でも動く。画面の下部にはモバイル用ツールバーがあり、Ctrl+CTab、矢印キーといったターミナル特有のキーをワンタップで入力できる。

ここで重要なのは、何もインストールしていないということだ。iPhoneにアプリを入れる必要はない。SafariやChromeのブラウザだけで完結する。Macのネイティブシェル(zsh)がそのまま動いているので、普段のターミナルと全く同じ環境がiPhoneから使える。

しかも接続が切れても、シェルのプロセスは生き続ける。長時間かかるコマンドを実行して、iPhoneをポケットにしまって、後で戻ってきても出力がそのまま残っている。tmuxやscreenと同じ発想だが、それらをインストールする必要すらない。

Safariの「ホーム画面に追加」を使えば、PWA(Progressive Web App)としてインストールできる。アイコンをタップするだけでフルスクリーンのターミナルが開く。見た目はネイティブアプリそのものだ。


なぜ作ったのか — iPhoneからClaude Codeを使いたかった

きっかけは単純だ。iPhoneから無料で簡単にClaude Codeを起動して、MacBookを操作したかった

iPhoneブラウザでClaude Codeを遠隔操作 mac-remoteによるMacターミナルのリモートアクセス実行画面

iPhoneのブラウザからClaude Codeを遠隔操作している実際の画面。モバイル用ショートカットキーで快適に操作できる

Claude CodeはターミナルベースのAIコーディングツールだ。自宅のMacBookで走らせておけば、コード生成もリファクタリングもファイル操作も全てやってくれる。しかし、外出先からClaude Codeを使おうと思うと「MacBookのターミナルにリモートアクセスする」という壁にぶつかる。

リモートアクセスのツールは山ほどある。しかし、どれも「iPhoneからターミナルだけ使いたい」という場面にはフィットしなかった。

TeamViewerやChrome Remote Desktopは画面全体を共有する。デスクトップの壁紙から何から全部iPhoneに転送される。通信量は膨大で、モバイル回線では実用にならない。しかもiPhoneの小さな画面でマウスカーソルを操作するのは拷問に近い。ターミナルを使いたいだけなのに、画面全体を送るのはオーバーキルだ。

SSH(Termius、Blink Shell等)はターミナルに特化しているが、専用アプリのインストールが必要だ。鍵の設定、ポートフォワーディング、ファイアウォールの穴あけ。そしてルーターのポート開放はセキュリティリスクそのものだ。しかも有料のものが多い。

VNCも同様にポート開放が必要で、プロトコル自体の暗号化が弱い。

欲しかったのは、こういうものだった。

  • 無料で使える(OSSで、月額費用なし)
  • ブラウザだけで動く(アプリ不要)
  • ターミナルだけが使える(画面共有は不要)
  • ポートを開放しない(セキュリティリスクなし)
  • セットアップが30秒で終わる

カフェからiPhoneでClaude Codeを起動して、自宅MacBookのプロジェクトを修正させる——それを無料で、ブラウザだけで実現する。このシンプルな要求を満たすツールが見つからなかったので、自分で作ることにした。

比較項目 mac-remote TeamViewer SSH (Termius等) VNC
アプリ不要 ブラウザのみ 専用アプリ必須 専用アプリ必須 専用アプリ必須
ポート開放 不要 不要 必要 必要
通信量 テキストのみ 画面全体 テキストのみ 画面全体
セットアップ 30秒 数分 10分以上 10分以上
料金 無料(OSS) 個人無料/商用有料 アプリ有料 無料
iPhoneでの操作性 ターミナル最適化 マウス操作 良好 マウス操作

アーキテクチャ — 全体像と設計判断

mac-remoteは5つのコンポーネントで構成されている。それぞれの選定理由を含めて、全体像を示す。

mac-remoteアーキテクチャ図iPhoneからCloudflare Tunnel経由でMac上のFlaskサーバーに接続し、PTYでzshを操作するデータフローを示す図mac-remote データフロー$ git statusOn branch mainnothing to commitiPhoneSafari / Chromexterm.js + Socket.IOHTTPSWebSocketCloudflareTunnel (CDN)TLS終端DDoS防御ポート開放不要cloudflaredlocalhost:8443Flask Server+ Socket.IOPIN認証セッション管理CORS制御セキュリティヘッダレート制限ブルートフォース対策FDread/writePTY疑似端末master/slave FDペアos.openpty()zsh -lセッションリーダープロセスグループ分離Mac(自宅/オフィス)双方向リアルタイム通信キー入力 → Socket.IO emit(‘input’) → os.write(master_fd) → zsh実行← os.read(master_fd) ← Socket.IO emit(‘output’) ← xterm.js描画遅延: 数十ms(Cloudflare CDNエッジ経由)切断時もシェルプロセスは生存

設計判断で最も重要だったのは、「何を使わないか」の選択だ。Node.jsやGoでWebSocketサーバーを書く選択肢もあったが、PTYの操作はPythonの標準ライブラリ(pty, os, fcntl)だけで完結する。外部依存を最小にすることを優先した。

Flask + Socket.IOを選んだのは、HTTPとWebSocketの両方を1つのサーバーで扱えるからだ。ターミナル操作にはリアルタイム双方向通信が必須で、REST APIだけでは成立しない。Socket.IOはWebSocket上の抽象化レイヤーで、再接続やフォールバックを自動で処理してくれる。

Cloudflare Tunnelは「ポートを開放しない」という設計の核だ。cloudflaredがMac側からCloudflareのエッジサーバーへアウトバウンド接続を張るため、ルーターに穴を開ける必要がない。これだけでSSHやVNCが抱えるセキュリティリスクの大半が消える。

PTY(疑似端末)は「本物のターミナル体験」の要だ。subprocess.run()でコマンドを実行するだけでは、vim、top、色付き出力が動かない。PTYを使うことで、SSHクライアントと同じ原理のターミナルがブラウザ上で実現する。詳しくは後述する。

xterm.jsは、ブラウザで最も成熟したターミナルエミュレータだ。VS Codeのターミナルもこれをベースにしている。256色対応、マウスイベント、Unicode——ネイティブターミナルと遜色ない描画性能がある。CDNからSRI(Subresource Integrity)付きで読み込んでいるため、改竄検知も行われる。

全体として、Mac側に必要な依存はPython3cloudflaredだけだ。どちらもHomebrewで1コマンドで入る。iPhone側はブラウザだけ。この非対称性が設計の肝で、アクセスする側の敷居を極限まで下げている。

コンポーネント 技術 選定理由
Webサーバー Flask 3.1 + Flask-SocketIO HTTP + WebSocketを1プロセスで。軽量で依存が少ない
リアルタイム通信 Socket.IO 4.8 WebSocket抽象化。自動再接続、フォールバック
トンネリング Cloudflare Tunnel (cloudflared) ポート開放不要。TLS自動。無料
ターミナルエミュレータ xterm.js 5.5 VS Code同等の描画品質。256色、Unicode対応
シェル管理 Python PTY (os.openpty) カーネルレベルの疑似端末。vim、top、色出力が動く
認証 PIN(8桁英数字)+ セッショントークン 62^8 = 218兆通り。タイミング攻撃対策済み
QRコード qrcode (Python) ターミナルにASCII QRを表示。URLの手入力不要

セットアップを30秒にした理由と仕組み

「30秒」はマーケティングの数字ではない。実測値だ。

インストールはワンライナーで完了する。

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/YUALAB/mac-remote/main/install.sh)"

このコマンドを実行すると、install.shが裏で以下の処理を自動で行う。

ステップ1: 環境チェック。macOSであることを確認する。非macOSなら即座に終了する。Homebrewが入っていなければインストールし、Python3、cloudflared、Gitが未導入ならbrew installで入れる。

ステップ2: リポジトリ取得。GitHubからmac-remoteをクローンする。既にインストール済みならgit pullで更新する。

ステップ3: Python環境構築。仮想環境(venv)を作成し、Flask、Flask-SocketIO、qrcodeなどの依存パッケージをインストールする。システムのPython環境を汚さない。

ステップ4: コマンド登録。~/bin/yuatlコマンドを生成し、PATHに追加する。以降はどこからでもyuatl一発で起動できる。

#!/bin/bash
# ~/bin/yuatl の中身(自動生成)
cd ~/mac-remote && source .venv/bin/activate && python3 server.py --tunnel

設計思想は「ユーザーに考えさせない」だ。依存関係の解決、パスの設定、仮想環境の管理——こうした「エンジニアなら分かるけど面倒な作業」を全て自動化した。curl一発でインストールが完了し、yuatl一発でサーバーが起動する。2つのコマンドだけ覚えればいい。

既にインストール済みの環境では、install.shは更新モードで動作する。git pullで最新版を取得し、仮想環境の依存パッケージも更新する。アンインストールは~/mac-remoteディレクトリと~/bin/yuatlを削除するだけだ。設定ファイルやバックグラウンドプロセスは一切残さない。

起動コマンドyuatlは、内部的には3つの処理を直列で実行している。cd ~/mac-remoteでプロジェクトディレクトリに移動し、source .venv/bin/activateで仮想環境を有効化し、python3 server.py --tunnelでサーバーを起動する。--tunnelフラグを外せばローカルネットワーク専用モードで起動し、同じWi-Fi内のデバイスからのみアクセスできる。


セキュリティ設計 — ポートを開けないという選択

ターミナルのリモートアクセスは、セキュリティを間違えると致命的だ。フルアクセスのシェルが外部に晒される。だからこそ、mac-remoteのセキュリティ設計には最も時間をかけた。

設計の基本原則は「多層防御」だ。1つの防御が破られても、次のレイヤーが守る。

mac-remoteセキュリティ多層防御図4つのセキュリティレイヤー(Cloudflare Tunnel、PIN認証、ブルートフォース対策、セッション管理)を同心円状に示す防御概念図多層防御アーキテクチャLayer 1: Cloudflare Tunnel攻撃面をゼロにするMac側からCloudflareへアウトバウンド接続。外部からMacへの直接接続は物理的に不可能。ルーターのポート開放が不要 = ポートスキャン無効。TLS暗号化はCloudflareが自動で処理。DDoS防御もCloudflare CDNが吸収。→ ネットワークレベルの攻撃を完全遮断Layer 2: PIN認証218兆通りの認証壁英数字8桁(0-9, A-Z)= 36^8 ≒ 2.82兆通り。起動のたびに新しいPINを自動生成。secrets.compare_digest() でタイミング攻撃を防御。成功時に256ビットのセキュアトークンを発行。パスワードではなくPIN = 使い捨て、毎回変更。→ 認証突破は計算上非現実的Layer 3: ブルートフォース対策指数バックオフでロックアウト60秒間に最大10回の試行制限。5回連続失敗 → 60秒ロック。6回 → 120秒、7回 → 240秒(倍々で増加)。最大30分のロックアウト(上限キャップ)。総当たり攻撃は数十万年かかる計算。→ 自動化された攻撃を無力化Layer 4: セッション管理時間制限 + WebSocket再認証セッションは24時間で自動失効。WebSocket接続時にトークンを再検証。Cookie: HttpOnly + Secure + SameSite=Lax。CORS: 信頼オリジンのみ許可(動的設定)。X-Frame-Options: DENY(クリックジャッキング対策)。→ セッション乗っ取り・XSS・CSRFを防御

レイヤー1: Cloudflare Tunnel — 攻撃面をゼロにする

最も根本的な防御は、ポートを開けないことだ。mac-remoteのサーバーはlocalhost(127.0.0.1)でしか待ち受けない。外部から直接アクセスする経路が物理的に存在しない。

cloudflaredがMacからCloudflareのエッジサーバーへアウトバウンド接続を張る。外部ユーザーはCloudflareのURLにアクセスし、Cloudflareがその通信をトンネル経由でMacに中継する。TLS暗号化はCloudflareが自動で処理し、DDoS攻撃もCDNが吸収する。

SSHやVNCでは「ルーターのポートを開放してください」が前提だが、mac-remoteではその手順自体が存在しない。ポートスキャンの対象にすらならない。

レイヤー2: PIN認証 — 使い捨ての8桁コード

サーバーを起動するたびに、8桁の英数字PIN(0-9, A-Z)がランダムに生成される。Pythonのsecretsモジュール(暗号学的に安全な乱数生成器)を使っている。

なぜ「パスワード」ではなく「PIN」なのか。パスワードはユーザーが決めるものだ。使い回しのリスクがある。PINはシステムが毎回生成する使い捨てコードだ。サーバーを再起動するたびに変わる。

PIN比較にはsecrets.compare_digest()を使っている。これは文字列の長さや内容に関係なく一定時間で比較を完了する関数で、タイミング攻撃(レスポンス時間の差から正解を推測する手法)を防ぐ。

レイヤー3: ブルートフォース対策 — 指数バックオフ

仮にPINを総当たりで突破しようとしても、レート制限がそれを阻む。60秒間に最大10回の試行制限に加え、5回連続で失敗すると指数バックオフが発動する。

5回失敗で60秒ロック。6回で120秒。7回で240秒。倍々で増えていき、最大30分のロックアウトに達する。36^8通りのPINに対して、毎分最大10回の試行しかできないため、総当たりには天文学的な時間がかかる。

レイヤー4: セッション管理 — 多重の防衛線

認証に成功すると、256ビットのセキュアトークンが発行される。このトークンは24時間で自動失効する。WebSocket接続時にもトークンの再検証を行い、期限切れや無効なトークンでは即座に切断する。

CookieにはHttpOnly(JavaScriptからアクセス不可)、Secure(HTTPS専用)、SameSite=Lax(CSRF対策)の3つのフラグを設定。さらにX-Frame-Options: DENYでクリックジャッキングを防ぎ、CORSは信頼オリジンのみ許可する動的設定を行っている。

IPアドレスの取得にも注意を払っている。Cloudflare Tunnel経由のアクセスでは、request.remote_addrはCloudflareのIPを返す。正しいクライアントIPを取得するために、CF-Connecting-IPヘッダーを優先的に読み取る実装にしている。これがないとレート制限が正しく機能しない。

セキュリティ機能 mac-remote TeamViewer SSH VNC
ポート開放 不要(Tunnel) 不要 必要(22番) 必要(5900番)
認証方式 ランダムPIN(毎回変更) 固定パスワード 公開鍵/パスワード 固定パスワード
ブルートフォース対策 指数バックオフ あり fail2ban等で追加 なし(標準)
TLS暗号化 自動(Cloudflare) 独自暗号化 SSH暗号化 なし(標準)
タイミング攻撃対策 secrets.compare_digest 不明 あり なし
セッション有効期限 24時間自動失効 無制限 設定次第 無制限
セキュリティヘッダ 8種類設定 アプリ依存 N/A N/A

PTY — ターミナルの裏側で起きていること

mac-remoteの技術的に最も面白いところは、PTY(疑似端末)の実装だ。

ターミナルでコマンドを実行する方法は2つある。subprocess.run("ls")のように単純にプロセスを起動する方法と、PTYを介してシェルを起動する方法だ。前者ではvimtophtop、色付きのls出力が動かない。なぜか。

これらのプログラムは「自分がターミナルに接続されているか」をチェックする。subprocess.run()ではパイプに接続されるため、ターミナルではないと判断され、インタラクティブモードが無効になる。色も消える。

PTYは、カーネルが提供する「偽のターミナル」だ。プロセスから見ると本物のターミナルと区別がつかない。SSHクライアントも、スクリーンコマンドも、tmuxも、全てPTYを使っている。

mac-remoteでの実装はこうなっている。

# 1. PTYペアを作成(master/slave)
master_fd, slave_fd = pty.openpty()

# 2. ウィンドウサイズを設定(iPhoneの画面サイズに合わせる)
winsize = struct.pack("HHHH", rows, cols, 0, 0)
fcntl.ioctl(slave_fd, termios.TIOCSWINSZ, winsize)

# 3. zshをslave側に接続して起動
proc = subprocess.Popen(
    ["/bin/zsh", "-l"],        # ログインシェル
    stdin=slave_fd,
    stdout=slave_fd,
    stderr=slave_fd,
    preexec_fn=os.setsid,      # 新プロセスグループ
    env={"TERM": "xterm-256color", ...}
)
os.close(slave_fd)  # 親プロセスでは不要

master_fdslave_fdはカーネル内でつながったファイルディスクリプタのペアだ。slave_fdにzshを接続し、master_fdを通じてデータを読み書きする。

iPhoneでキーを押すと、Socket.IO経由でサーバーに届き、os.write(master_fd, data)でPTYに書き込まれる。zshがそのコマンドを実行し、出力がslave_fdmaster_fdの経路で戻ってくる。os.read(master_fd, 4096)で読み取り、Socket.IOでiPhoneに送り返す。xterm.jsがそれを描画する。

SSHクライアントと全く同じ原理を、ブラウザとWebSocketで実現している。

読み取りループは別スレッドで動いている。select()で最大100msブロックし、データがあればos.read()で最大4KBを読み取る。ノンブロッキングI/Oで、CPUを無駄に消費しない設計だ。

# 読み取りループ(別スレッドで実行)
while shell["proc"].poll() is None:
    r, _, _ = select.select([shell["fd"]], [], [], 0.1)
    if r:
        data = os.read(shell["fd"], 4096)
        if not data:
            break
        socketio.emit("output", data.decode(errors="replace"), to=active_sid)

ウィンドウサイズの変更も面白い。iPhoneを縦横に回転させると、xterm.jsが新しいサイズを計算し、Socket.IO経由でサーバーにresizeイベントを送る。サーバーはfcntl.ioctl(fd, TIOCSWINSZ, winsize)でPTYのウィンドウサイズを変更し、zshプロセスにSIGWINCHシグナルを送る。これによりvimやtopが画面サイズの変更を検知して再描画する。

もう1つ重要な設計判断がある。WebSocketが切断されても、シェルプロセスは終了しない。iPhoneのブラウザを閉じても、ネットワークが切れても、zshプロセスは生き続ける。再接続すると同じシェルに復帰し、その間の出力も確認できる。長時間のビルドやダウンロードを走らせたまま、安心してiPhoneをポケットにしまえる。


モバイルUI — iPhoneでターミナルを使いやすくする工夫

「ターミナルはPCのもの」——その前提を壊すのがmac-remoteの目標の1つだった。iPhoneの画面サイズとタッチ操作で、ターミナルを実用的に使えるようにするために、いくつかの工夫を入れている。

iOS PWA対応。Safariの「ホーム画面に追加」で、mac-remoteをネイティブアプリのように使える。apple-mobile-web-app-capableメタタグにより、アドレスバーが消え、画面全体がターミナルになる。viewport-fit=coverでノッチ領域まで使い切る。

モバイルツールバー。ターミナル操作で頻繁に使うが、ソフトキーボードにはない特殊キーをワンタップで入力できるツールバーを画面下部に配置した。

  • Ctrl+C(プロセス中断)、Ctrl+D(EOF送信)、Ctrl+Z(サスペンド)
  • Esc(vimのモード切替等)、Tab(コマンド補完)
  • 矢印キー(↑↓←→)——コマンド履歴の呼び出しやカーソル移動

これらは内部的にANSIエスケープシーケンスやASCII制御文字として送信される。例えばCtrl+C\x03Tab\x09、上矢印は\x1b[Aだ。

ソフトキーボードとの共存。iOSではキーボードが表示されると画面が縮小する。window.visualViewport APIを使って、キーボードの高さ分だけターミナルのコンテナを縮小し、ツールバーが常にキーボードの真上に位置するよう調整している。キーボードを閉じれば元のサイズに戻る。

日本語入力(IME)対応。iPhoneの日本語キーボードはIME(Input Method Editor)で変換候補を表示する。compositionstart/compositionendイベントを監視し、変換中は入力を保留、確定時に一括送信する設計にした。英語入力では1文字ずつリアルタイム送信する。

タッチ操作の最適化。user-select: noneでテキスト選択を無効化し、-webkit-tap-highlight-color: transparentでタップ時のハイライトを消した。touch-action: pan-yでターミナル内の垂直スクロールだけを許可し、水平方向のジェスチャーは無効にしている。


実際の使い道 — 作者の日常

システムを作って終わりではない。実際に毎日使っている。以下は自分のリアルなユースケースだ。

AI学習の進捗確認。自宅のMacで機械学習モデルのトレーニングを走らせ、外出先からiPhoneでログを監視する。GPU使用率、損失関数の推移、推定残り時間をターミナルで確認できる。画面共有の重さに悩まされることなく、テキストベースでサクサク確認できるのが快適だ。

Git操作。コードレビューで指摘を受けた修正を、移動中にiPhoneからcommit、pushする。git diffで変更内容を確認し、git add -pでステージング、git commitでコミット。ターミナルの操作性がそのままなので、複雑なGit操作も問題ない。

サーバーメンテナンス。SSHでリモートサーバーに接続して、ログの確認やプロセスの再起動を行う。mac-remote自体がSSHクライアントの役割を兼ねる——Mac経由で別のサーバーにSSH接続するという二重リモートも普通に動く。

Claude Codeをリモートで実行。これが実は最も頻度が高い使い方だ。Claude CodeはターミナルベースのAIコーディングツールで、iPhoneからmac-remote経由で起動し、コード生成やリファクタリングを指示する。カフェでコーヒーを飲みながら、自宅Macのプロジェクトをclaude codeに修正させる、という使い方が日常になった。

長時間コマンドの放置実行。大量のデータ処理やパッケージのビルドなど、時間のかかるコマンドをiPhoneから起動して放置する。WebSocketが切断されてもプロセスは生き続けるので、後で戻ってきて結果を確認すればいい。

共通しているのは、「画面を共有する必要がない」タスクばかりだということだ。GUIアプリを操作したいならTeamViewerを使えばいい。しかしエンジニアの作業の大半はターミナルで完結する。その「大半」をiPhoneから手軽にカバーできることの価値は、使ってみると想像以上に大きい。


よくある質問(FAQ)

Q1: Windows/Linuxでも使える?

現在はmacOS専用だ。PTYの実装がUnix系OSに依存しており、os.openpty()はmacOSとLinuxで動作する。Linux対応は技術的には可能で、install.shのHomebrew部分をaptやyumに書き換えれば動く見込みだ。実際、サーバー側のPythonコードはLinuxでもそのまま動くはずだ。WindowsはConPTYという別のAPIが必要になるため、対応は将来課題としている。

Q2: 複数人で同時接続できる?

現在のバージョンでは、1つのシェルに対して1つのアクティブ接続という設計だ。2人目が接続すると、1人目のWebSocketは切断されないが、入力権は2人目に移る。これは意図的な設計で、セキュリティ上、複数の入力ストリームが同時にシェルに書き込まれることを防いでいる。

Q3: セキュリティは本当に大丈夫?

4層の防御(Cloudflare Tunnel、PIN認証、ブルートフォース対策、セッション管理)を実装している。ポートを開放しないため、ネットワークレベルの攻撃が成立しない。PINは毎回ランダム生成されるため、過去の漏洩が将来のリスクにならない。セッションは24時間で自動的に無効化され、使い終わったらサーバーを停止すればTunnelも閉じる。ただし、QRコードやPINを第三者に見られないよう注意は必要だ。公共の場でQRコードを表示する際は、周囲に人がいないことを確認しよう。

Q4: Cloudflare TunnelのURLが毎回変わるのは不便じゃない?

Quick Tunnel(無料)を使っているため、起動のたびにURLが変わる。ただし、毎回QRコードが表示されるのでiPhoneのカメラでスキャンするだけだ。固定URLが欲しい場合は、CloudflareのNamed Tunnelを設定すれば対応できる。これは将来のアップデートで公式サポート予定だ。

Q5: 通信速度はどのくらい必要?

ターミナルの通信はテキストデータのみなので、通信量は極めて少ない。3G回線でも実用的に動作する。体感の遅延はCloudflare CDNのエッジサーバーとの距離に依存するが、日本国内なら数十ミリ秒程度だ。1時間使っても消費する通信量は数MB程度で、モバイル回線の負担はほぼゼロに近い。画面共有型のリモートデスクトップとは根本的に異なるポイントだ。


これから作りたいもの

mac-remoteは現在も開発を続けている。今後追加したい機能をいくつか挙げておく。

ファイルマネージャー機能。ブラウザ上でファイルのアップロード・ダウンロードができるようにしたい。iPhoneで撮った写真をMacに送る、MacのファイルをiPhoneにダウンロードする、といった操作をターミナルを介さずに行えるようにする。

通知連携。長時間実行コマンドが完了したら、iPhoneにプッシュ通知を送る仕組み。Web Push APIやService Workerを使えば、ブラウザを閉じていても通知を受け取れる。

Named Tunnel対応。固定URLでアクセスできるようにする。Cloudflare Tunnelの設定ファイルを自動生成し、毎回同じURLでmac-remoteにアクセスできるようにする計画だ。

マルチセッション対応。複数のターミナルタブを同時に開けるようにする。現在は1セッションだが、tmuxのように複数のシェルを切り替えて使えると便利だ。現在の内部実装では_shellを単一の辞書で管理しているが、これをセッションID付きの辞書に拡張すればタブ切り替えが実現できる。

mac-remoteはOSSとしてGitHubで公開している。コードは全てPythonとJavaScriptで書かれており、ファイル数は10未満。コンパクトな設計なので、フォークしてカスタマイズするのも容易だ。フィードバックやプルリクエストは歓迎する。


まとめ

mac-remoteは、「iPhoneのブラウザからMacのターミナルを操作する」というシンプルな目的のために作ったシステムだ。

  • ポート開放不要(Cloudflare Tunnel)
  • アプリ不要(ブラウザだけで完結)
  • 30秒セットアップ(ワンライナーインストール)
  • 多層防御セキュリティ(PIN + ブルートフォース対策 + セッション管理)
  • 本物のターミナル(PTYによるzshフルアクセス)

インストールはこの1行だけだ。

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/YUALAB/mac-remote/main/install.sh)"

起動はyuatlの4文字。QRコードをiPhoneでスキャンして、PINを入力すれば、目の前にMacのターミナルが現れる。

ソースコード: github.com/YUALAB/mac-remote


関連記事

AI開発・導入のご相談

「何から始めればいいか分からない」「費用感を知りたい」など、AI導入に関するご相談を無料で承っております。大手SIerのような高額な費用は不要。経験豊富なエンジニアが直接対応します。

AIスクール受講生募集中

未経験からAIエンジニアへ。現役エンジニアによるマンツーマン指導で、実践的なAIスキルを最短で習得できます。就職・転職サポートも充実。まずは無料カウンセリングへ。

この記事をシェア