sshboard について
sshboardは、AIエージェントとSSHでサーバーを確認するデスクトップアプリです。内蔵MCPサーバーを通じたエージェントの操作は画面上の共通ログに表示され、アクセスは設定した許可リストと承認で制限されます。
主な機能
- 01AIエージェントとのSSH接続共有
- 02操作をアクティビティログに表示
- 03ファイル閲覧とターミナル利用
- 04サーバーのファイル、ログ、状態の読み取り
- 05許可リストによるコマンド制限
活用シーン
- 01稼働中サーバーのサービス障害調査
- 02メール設定とサービスログの確認
- 03複数のSSH接続での状況比較
MCP 対応
アプリ内蔵のMCPサーバーを介して、AIエージェントが接続先サーバーを調査できます。ファイル、ログ、サービス状態などを読むツールを提供し、コマンド実行は設定したリストで管理します。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
このサーバーのパッケージやリモートエンドポイントはまだ取得できていません。プロジェクトの README の手順に従ってください。
ツール
ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。
詳細
sshboard
English / 日本語
あなたと AI エージェントが、1 つの SSH 接続を通じて、同じ画面で同じサーバーを見られます。
自分の SFTP クライアントとターミナルはそのまま使えます。エージェントはあなたのそばで、あなたが設定した境界の内側で動作し、エージェントの操作はすべて、実行中にあなたのウィンドウに表示されます。

⚠️ これはアルファ版です。 2026-09-09 に本番サーバーでの使用が始まりました(証明書の更新失敗を調査)。747 件のテストに合格しています(Rust 418 件、フロントエンド 329 件)。Windows(x64 / ARM64)と macOS 向けのインストーラーも同梱しています。 だからといって、ツールとして動作するとは限りません。ターミナルに 3 日間1 バイトも表示されないビルドをリリースしたことがあります(Issue #10)。
⬇ ダウンロード · 開発方針 · すべての判断とその理由
こんなときに役立ちます
「今日、3 台のホストの証明書を更新したい」
ホストごとにターミナルを開き、同じコマンドをそれぞれにコピーして、どれかで失敗しないか見張っています。sshboard は接続ごとにコンソールを開き、同じホスト上に 2 つ並べて表示することもできます。作業中、エージェントはすべてのコンソールを読み取れます。「エージェントが代わりに実行する」のではなく、エージェントがあなたの操作を見て、どのペインで問題が起きたかを教えてくれます。

(この画像は分割表示の導入前のものです。接続中の項目がタブとして並ぶ、単一コンソールの画面です。0.1.21 で 2 つのペインを並べて表示できるようになりました。)
「メールが届かなくなりましたが、どこを調べればよいかわかりません」
エージェントに尋ねます。エージェントは /etc/postfix/main.cf を読み、サービスを確認し、ログを追跡します。すでに接続している SSH セッションを使って、各手順を実行中に確認できます。任意のコマンドを実行して調べることはできません。run_command(cmd) ツールはありません。
[Human] $ cd /var/www
[AI] $ df -h
Filesystem Size Used Avail Use% Mounted on
/dev/vda1 50G 38G 10G 80% /
[AI] read /etc/postfix/main.cf (4.1 KB)
「助けはほしいけれど、エージェントにサーバーを自由に操作させたくありません」
エージェントが書き込みできるのは、あなたが一覧に登録したディレクトリ内だけです。接続ごとに 1 つずつ設定します。**一覧は空の状態で始まるため、書き込み先を指定するまでは 1 バイトも書き込めません。**削除、名前の変更、移動、chmod、再起動、パッケージのインストール、sudo は、**エージェントには使えません。**あなたには制限はありません。

5 分で試す
アプリをインストールし、エージェントから接続します。ほかに実行するものはありません。MCP サーバーはアプリに内蔵されています。
# 1. a throwaway SSH server on your own machine (needs Docker)
sh tools/test-server/up.sh
ssh-add tools/test-server/.key
# 2. the app
pnpm install
pnpm --filter desktop tauri dev
127.0.0.1:2222、ユーザー probe を登録して接続したら、ウィンドウのタイトルバーにある MCP コマンドをコピーを押して、エージェントに貼り付けます。
claude mcp add --transport http sshboard http://127.0.0.1:22022 \
--header "Authorization: Bearer <the token the button copied>"
ビルド済みインストーラーはリリースページにあります(Windows x64 / ARM64、macOS Apple Silicon)。コード署名されていないため、初回起動時に SmartScreen と Gatekeeper の両方で警告が表示されます。
エージェントの動作中に確認できること
**あなたとエージェントのすべての操作が、1 つの帯に表示されます。**何をしたのか尋ねる必要はありません。
![操作ごとに 1 行表示されるアクティビティ帯。エージェントの操作には [AI] が付きます](https://github.com/meta-taro/sshboard/raw/HEAD/site/shots/en/band.png)
何かが失敗した場合、エージェントが MCP 経由で読むログが画面にも表示されます。どの段階まで進んだのか、なぜ停止したのか、次に何をすればよいのかを確認できます。ホスト名、ユーザー名、秘密情報は含まれません。
このツールを作った理由
通常の共有ホスティングや VPS で、今も稼働しているサービスがあります。そうしたサービスの保守に AI エージェントを使うと、毎回必ず次のようなことが起きます。
you : I want to fix this service's config
AI : I can't see the server, so please tell me its state
you : (open a terminal, look, paste)
AI : now show me that config file too
you : (open SFTP, download, paste)
このツールは、その往復の手間をなくすためにあります。 エージェントがサーバーを操作する必要はありません。見せるだけで十分です。
実際の使い方
ボタンをクリックして操作するツールではありません。 ターミナルで AI エージェントに話しかけると、エージェントが sshboard の組み込み MCP サーバーを呼び出します。
you → the AI agent in your terminal: "mail isn't arriving. look into it"
│
│ MCP (inside the app — no extra process)
▼
sshboard ── one SSH ──▶ server
│ (sftp / exec)
▼
an [AI] line scrolls past in sshboard's window
── you are just watching. what it read and what it typed is visible as it happens
[Human] $ cd /var/www
[AI] $ df -h
Filesystem Size Used Avail Use% Mounted on
/dev/vda1 50G 38G 10G 80% /
[AI] read /etc/postfix/main.cf (4.1 KB)
操作が制限されるわけではありません。 2 ペインのファイルブラウザーとターミナルタブは、ほかの SFTP クライアントやターミナルと同じように使えます。制限されるのはエージェント側だけです。
エージェントが呼び出せるもの(フェーズ 1 — 読み取り)
list_connections / list_directory / stat / read_file / search / download /
disk_usage / process_list / service_status / runtime_versions / read_log /
network_listen / run_readonly(許可リスト)
run_command(cmd) はありません。 1 つのツールですべてを実行できてしまい、いったん使えるようになれば、エージェントが破壊的なコマンドを使わないという約束だけが防壁になります。約束は運用手順書であり、ゲートではありません。
run_readonly では、エージェントが渡せるのは、人が readonly.toml に記述した ID だけです。引数を渡す枠はありません。リストは空の状態で提供されるため、人が記述するまでは何も実行されません。 拒否された呼び出しはウィンドウのアクティビティ欄に表示され、readonly-refused.log に記録されます。エントリを追加するのは人の役目です。
唯一の成功基準
エージェントが「サーバーの状態を教えてください」と言わなくなることです。
ダウンロード数でも、スターの数でもありません。実際の保守作業中に、一往復のやり取りがなくなったかどうかです。
セットアップを新しくするものではありません
- サーバーを別の場所へ移行するためのツールではありません
- すでにあるセットアップで作業することが目的です
- これらのサービスは 10 年後もここにあります。今日、AI 開発をこれらのサービスにもたらすものはありません
仕組み
┌──────────┬──────────┐ ┌──────────┐
│ files │ terminal │ │ MCP │
│ two-pane │ tab │ │ │
└────┬─────┴────┬─────┘ └────┬─────┘
└──────────┴───────┬───────────┘
▼
┌────────────────────────┐
│ Operation Engine │ ← exactly one implementation lives here
└───────────┬────────────┘
▼
one SSH connection
(sftp / exec)
SFTP の実装を二重に維持することはなく、見えない SSH セッションを開くこともありません。 見えないことこそ、ここで最大の危険です。
誰が何に触れたかは、すべて同じ欄に記録されます。操作する場所は異なっても、記録は一つです。
[Human] $ cd /var/www
[AI] $ df -h
Filesystem Size Used Avail Use% Mounted on
/dev/vda1 50G 38G 10G 80% /
[AI] read /etc/postfix/main.cf (4.1 KB)
フェーズ 1 は読み取り専用です
書き込みは一切ありません。 インシデントのトリアージ、メール設定の調査、提案書の作成など、想定される用途はすべて読み取りです。そのためリスクはほぼゼロで、初日から本番サーバーに接続できます。
run_command(cmd) はありません
任意のコマンドを実行できるツールを一つ用意すれば、何でもできてしまいます。だからこそ、そのツールは存在しません。
- それを使えるようにすれば、破壊的なコマンドに対する最後の防御は、使わないという約束だけになります
- 約束は運用手順書であり、ゲートではありません
- 許可リストを使えば、危険なコマンドは表現できません
readonly.toml の内容(人が記述します)
version = 1
[[command]]
id = "uptime" # the only value the agent may pass
run = "uptime" # what actually runs. **exactly what a person wrote**
description = "how long it has been up"
製品はエントリが一つもない状態で提供されます。 実際の保守作業に必要なものを網羅したリストを知っている人はいません。推測で埋めても正しくはなりません。拒否された呼び出しは readonly-refused.log に蓄積されます。その内容を読み、本当に必要だったものだけを追加してください。
これでは検証できないこと: 記述した run が本当に読み取り専用かどうかです。製品には判断できません。 uptime と rm -rf / を見分けられる機械はありません。これで防げるのは、ただ一つ、エージェントがコマンド文字列を組み立てることです。
人が使う側(GUI)には制限がありません。 通常の SFTP クライアントやターミナルとして使ってください。制限されるのはエージェント側だけです。
状態を変更する操作(operations.toml)
リストは空の状態で提供されます。 このファイルを記述するまでは、エージェントは何も実行できません。
これは読み取り用の許可リスト(readonly.toml)とは別のファイルです。書き込み操作が readonly という名前のファイルに入ることはありません。名前で区別できることが重要です。connections.toml と同じ場所に置いてください。
version = 1
[[operation]]
id = "restart-httpd"
run = "sudo systemctl restart httpd"
description = "restart Apache"
max_per_hour = 3
| フィールド | 記述する内容 |
|---|---|
id | エージェントが渡せる唯一のものです。 短く、読みやすくしてください |
run | 実際に入力される内容です。 エージェントが組み立てることはできません |
description | 承認ダイアログに表示されます。 半年後でも理解できる言葉にしてください |
max_per_hour | 1 時間あたりの上限です。0 は拒否されます(停止する場所が必要です) |
実行前に通過するゲート
- 人が作成したリストに含まれている。 不明な ID は拒否され、拒否した記録が残ります
- 1 時間あたりの上限内である
- 画面上で人が承認する。 エージェントの最初の呼び出しは必ず拒否され、ウィンドウには実際に実行される内容が表示されます
- 承認は一度だけ使われる。 1 回の承認につき 1 回の実行
- 承認は 5 分後に期限切れになります。 人が許可を押しても、エージェントが呼び出しを返さないことがあります(会話が先に進むため)。承認を放置しません —
become = "ask"の場合、パスワードもここで破棄されます
画面の前にいる人でも 3~5 は通過しません。 その人が確認しているためです。
唯一の例外は become = "ask" のパスワードです — これは承認ではなく認証情報であり、製品が作り出すことはできません。
書き留めても拒否されるもの
これらを有効にする設定はありません。 operations.toml に記述されていても拒否されます。
- ケージそのもの —
sudoers/visudo/authorized_keys - キー —
.ssh/id_rsa/id_ed25519/id_ecdsa - パスワードの保存場所 —
/etc/shadow//etc/gshadow - sshboard 自身の設定 —
connections.toml/readonly.toml/operations.toml - ユーザーとロール —
useradd/usermod/userdel/groupadd - 破壊的な操作 —
mkfs/fdisk/dd/rm -rf
エージェントが自分自身のケージを広げることはできません。 このリストはそのためのものです。
root 権限が必要な操作(become)
operations.toml では、どのコマンドを実行できるかを決めます。権限を取得する方法は、接続ごとに(connections.toml で)指定します。
[[connections]]
id = "..."
become = "sudoers" # scope is cut on the server side (recommended)
# become = "ask" # ask the person when it is needed. **never stored**
# omit it entirely # do not elevate
become | sshboard が入力する内容 | パスワード |
|---|---|---|
| 省略 | run に書かれた内容そのまま | なし |
sudoers | sudo -n … | なし(範囲は sudoers.d で指定) |
ask | sudo -S -p '' … | その場で画面上から人が入力 |
変更されるのは、sudo で始まる run だけです。 それ以外は 1 バイトも変更されません。ask を選んでも、sudo 以外の操作でパスワードを求められることはありません。
sudoers(推奨)
<user> ALL=(root) NOPASSWD: /usr/bin/systemctl restart httpd, /usr/bin/systemctl reload httpd
# always through visudo. get it wrong and nobody can sudo any more
sudo visudo -f /etc/sudoers.d/sshboard
sudo chmod 0440 /etc/sudoers.d/sshboard
これは OS が強制するゲートであり、アプリが約束するものではありません。範囲は sudoers に記述されるため、あとから人が監査でき、sshboard が秘密を得ることもありません。
動作を確認します。
sudo -n systemctl reload httpd # passes with no password prompt → good
sudo -n systemctl restart nginx # not listed → should be refused
2 つ目のコマンドが成功する場合、許可範囲が広すぎます。
ask(sudoers を変更できない場合)
サーバーを変更できないのは現実に起こり得ます — レンタルサーバーである、権限がない、マシンが多すぎるなどです。sudo -n が「パスワードが必要です」と返すサーバーでは、sudoers を使う方法は最初から利用できませんでした。 そのための ask です。
承認ダイアログにパスワード欄が表示され、人がそこにパスワードを入力します。
- 保存されません。 ディスクにも OS の認証情報ストアにも保存されません
- コマンドラインに表示されません。 stdin 経由で渡されるため、
psに表示されません - 画面にもログにも表示されません。 表示されるのは
$ sudo -S -p '' …だけです - 1 回の承認につき 1 回の実行です。 使用した瞬間に破棄されます
- 拒否すると、その場で破棄されます
root しか読めないログを読む場合も、同様に機能します。
[[operation]]
id = "read-letsencrypt-log"
run = "sudo tail -n 200 /var/log/letsencrypt/letsencrypt.log"
description = "find out why the certificate renewal failed"
max_per_hour = 6
これは読み取り許可リスト(readonly.toml)には適用されません。 適用すると、読み取りのたびにパスワードが必要になります。 root 権限が必要な読み取りは operations.toml に記述してください。
su で先に root になることは推奨されません — 範囲が適用されなくなるためです。 su - しか使えないサーバー向けの方法は、まだありません(.claude/decisions.md D48)。
製品が保証できないこと
操作が実際に記載どおりのことだけを行うかどうかは、製品では検証できません。 実行されるのは、人が書いた文字列です。readonly.toml と同様に、人が読んで確認しなければならない部分です。 そのため、sudoers によってOS に実行を強制させます。
画面から離れている人に承認を届ける方法は、まだありません(.claude/decisions.md D47 — 未決定)。現在、応答できるのは画面の前にいる人だけです。誰もいなければ、操作は実行されません。
これは行いません
| 行わないこと | 理由 |
|---|---|
| エージェントに書き込みアクセスを付与する(フェーズ 1) | すべての操作は読み取りです。初日から本番環境を対象にできます |
| エージェントに任意のコマンドを許可する | 許可リストによって、構造的に防止します |
| エージェントに sudo を許可する | フェーズ 1 では権限昇格を扱いません |
| 独自の鍵ストアを構築する | OS の認証情報ストアと ssh-agent に委ねます。保持していないものを保護する必要はありません |
| AI チャット UI を組み込む | エージェントはアプリの外部で動作します |
| レポートを生成する | 読み取りができれば、エージェントが提案を作成します。追加することはありません |
| 移行を促す | 既存のセットアップで使えることが目的です |
実行方法
これはまだアルファ版です。 本番サーバーに接続する前に、ローカルテストサーバーで一度実行してください。
必要条件
| OS | macOS / Windows(Linux は配布対象ではありません) |
| Rust | rust-toolchain.toml で 1.98.0 に固定されています。rustup が処理します |
| Node | 20 以上 |
| pnpm | corepack enable(npm / yarn は使用しません) |
| Docker | ローカルテストサーバーを使う場合のみ |
鍵は ssh-agent に保存しておくことをおすすめします。 そうすれば、sshboard にパスフレーズが渡ることはありません。
起動
pnpm install
pnpm --filter desktop tauri dev
テスト
cargo test --workspace # Rust
pnpm --filter desktop test # frontend
pnpm --filter desktop check # type check
ローカルテストサーバー
お使いのサーバーには一切触れません。 使い捨ての鍵を作成し、Docker で 1 台のマシンを起動します。
sh tools/test-server/up.sh # up
sh tools/test-server/up.sh down # down
エージェントの接続(MCP)
推奨 — トークンはどこにも書き込まれません
# macOS
claude mcp add sshboard -- /Applications/sshboard.app/Contents/MacOS/sshboard --mcp-stdio-proxy
# Windows
claude mcp add sshboard -- "%LOCALAPPDATA%\\sshboard\\sshboard.exe" --mcp-stdio-proxy
この方法では、トークンはどこにも残りません(Issue #2)。--mcp-stdio-proxy を指定して起動すると、sshboard はウィンドウを表示せずにリレーとして動作します。 トークンを読み取り、実行中のアプリに渡します。
- トークンは
~/.claude.jsonにもシェルの履歴にも表示されません - ポート(
SSHBOARD_MCP_PORT)を変更しても、再登録する必要はありません - アプリが実行されていない場合は、次のメッセージが表示されます:「sshboard is not running. Start the app and try again.」
SSH 接続を開くのはアプリだけです。 リレーはエンジンも帯域も保持しません。 見えない SSH セッションが追加されることはありません(禁止事項 3)。
HTTP で直接接続
ウィンドウ右上の 「Copy the MCP registration command」ボタンを押すと、トークンを含む 1 行のコマンドがコピーされます。
claude mcp add --transport http sshboard http://127.0.0.1:22022/mcp \
--header "Authorization: Bearer <token>"
--transport http は必須です。 パスは /mcp で、トークンは Authorization: Bearer に渡します。
ポートは 22022 に固定されています(D33)。トークンは再利用されるため、登録は一度だけで済みます。 再起動しても URL は変わりません。
⚠️
claude mcp addを使うと、トークンが~/.claude.jsonに平文で保存されます。 鍵ではなくループバック用のハンドルですが、これを持つ人は接続中のサーバーを読み取れます。 使用後はclaude mcp remove sshboardを実行してください。
.mcp.json も使用できます(こちらもディスク上では平文です):
{
"mcpServers": {
"sshboard": {
"type": "http",
"url": "http://127.0.0.1:22022/mcp",
"headers": { "Authorization": "Bearer <copy it from the MCP button>" }
}
}
}
ポートが使用中でも、通知なく別のポートに移動することはありません。 ウィンドウに「failed to start (port 22022)」と表示されます。 ポートを変更するには
SSHBOARD_MCP_PORTを設定してください。
更新
新しいバージョンがあると、起動時にウィンドウで通知されます(D34)。
通知なく自動で置き換わることはありません。 ダウンロード、インストール、再起動は、いずれもユーザーがボタンを押して行います。このツールは SSH 鍵を扱うため、指示なしに自身を書き換えることはありません。
更新自体はminisign で署名され、Tauri が必ず検証します。 これはコード署名とは別のものです(D12 — OS が警告を表示するかどうか)。ここで保証されるのは、更新が私たちから提供されたものだということです。
アルファ版で知っておくべきこと
- コード署名されていません。 Windows で警告が表示されるかどうかは、入手方法によって異なります
(Issue #3、実測):
- ブラウザーでダウンロードしてダブルクリック → SmartScreen にブロックされます
gh run download/ CI / スクリプト → ブロックされません。 Mark of the Web が付かないため、SmartScreen の評価チェックが作動しません- macOS Gatekeeper は未テストです
- エージェントが書き込みできるのは、その接続用にユーザーが指定したディレクトリだけです。 デフォルトではリストは空です。1 バイトも書き込めません
run_readonlyの許可リストもデフォルトでは空です。 ユーザーがreadonly.tomlを書き込むまで、何も実行されません。専用のツール(disk_usageなど)はこれがなくても動作します- ターミナルはユーザーとエージェントで共有されます。 エージェントが使用中はユーザーの 入力がロックされますが、[停止]は常に機能します
- インストーラーは各リリースに添付されます(D32)。
Windows 版には
.msiと NSIS の-setup.exeが、macOS 版には.app.zipが含まれます。 署名されていないため、Windows では SmartScreen が、macOS では Gatekeeper が警告を表示します。 警告で先に進めず、対処方法がわからなかった場合は、Issue を作成してください — それによって証明書を購入するかどうかが決まります(D12)
技術スタック
| シェル | Tauri 2(Windows / macOS) |
| コア | Rust |
| UI | SvelteKit + xterm.js(独自の ANSI パーサーは作成しません) |
| SSH | russh + russh-sftp(採用を決める前に、どちらも実機 9 台で試しました — D6) |
| MCP | アプリ内(別のバイナリーでも、別のビルドでもありません) |
| 認証情報 | OS の認証情報ストア + ssh-agent |
ライセンス
MIT
開発ルール
このリポジトリでは、AI エージェント開発の基本ルールに従っています。.claude/rules/product-baseline.md と
CLAUDE.md を参照してください。
- AI がコミットし、人間がプッシュします(人間のレビューなしにプッシュしません)
- テストを先送りしたり、削除したりすることはありません
- これは公開リポジトリです。 コード、ドキュメント、コミット履歴、スクリーンショットに、サーバーのホスト名、IP アドレス、ユーザー名、個人名、個人のメールアドレスを含めないでください
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。