关于 sshboard
sshboard 是通过 SSH 与服务器协作的桌面应用,AI Agent 可借助内置 MCP 服务检查服务器状态。Agent 的操作会显示在同一活动记录中,其访问权限受配置的允许列表和审批机制约束。
主要功能
- 01与 AI Agent 共用 SSH 连接
- 02在活动记录中查看 Agent 操作
- 03浏览文件并使用终端
- 04读取服务器文件、日志和状态
- 05通过允许列表限制 Agent 命令
适用场景
- 01排查线上服务器的服务故障
- 02检查邮件配置和服务日志
- 03比较多个 SSH 连接上的活动
MCP 相关
应用内置 MCP 服务,供 AI Agent 检查已连接的服务器。它提供读取文件、日志、服务状态等信息的工具,命令权限由配置列表管理。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
sshboard
English / 日本語
你的 AI Agent 和你,通过同一个 SSH 连接,在同一屏幕上查看同一台服务器。
你继续使用自己的 SFTP 客户端和终端。Agent 在你身边工作——待在你划定的围栏内——而且它做的每一件事都会在发生时滚动显示在你的窗口中。

⚠️ 这是 alpha 版本。 它于 2026-09-09 开始用于生产服务器(调查证书续订失败的问题)。747 项测试通过(418 项 Rust 测试、329 项前端测试),并提供适用于 Windows(x64 / ARM64)和 macOS 的安装程序。 但这并不意味着它作为工具能正常工作——我们曾发布一个构建版本,其终端连续三天一个字节都不显示(Issue #10)。
你会在什么时候用到它
“今天要在三台主机上续订证书”
你为每台主机打开一个终端,把同一条命令复制到每台主机上,再盯着看哪台失败。sshboard 为每个连接打开一个控制台——也可以在同一台主机上并排打开两个——Agent 可以在你工作时读取所有控制台。这不是“Agent 替你运行命令”:Agent 会观察你的操作,并告诉你哪个窗格出了问题。

(此截图拍摄于分屏视图推出之前——图中是一个控制台,已打开的连接以标签页显示。0.1.21 版本加入了并排显示的两个窗格。)
“邮件收不到了,我不知道该从哪里查起”
你询问 Agent。它读取 /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)
“我想获得帮助,但不想让 Agent 在我的服务器上随意操作”
Agent 每次只能在你列出的目录中写入。目录列表初始为空,所以在你指定位置之前,它连一个字节都不能写入。 删除、重命名、移动、修改权限、重启、安装软件包、sudo——Agent 都无法执行这些操作。 你在任何地方都不受限制。

五分钟快速上手
安装应用,然后让 Agent 连接到它。无需运行其他任何程序——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,连接后,点击窗口标题栏中的 Copy the MCP command,再将其粘贴到 Agent 中:
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 首次都会拦截。
Agent 工作时你能看到什么
你和 Agent 执行的每项操作都会记录在同一条活动流中。 你无需询问它做了什么。
![活动流:每项操作占一行,[AI] 标记 Agent 的操作](https://github.com/meta-taro/sshboard/raw/HEAD/site/shots/en/band.png)
发生故障时,Agent 通过 MCP 读取的同一份日志也会显示在你的屏幕上——它进行到了哪个阶段、为何停止,以及接下来该怎么做。日志不包含主机名、用户名或机密信息。
为什么要做这个工具
仍有一些服务运行在普通共享主机和 VPS 上。用 AI Agent 维护这类服务时,每次都会发生这样的情况:
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)
这个工具旨在省去这一来一回。 Agent 不需要操作你的服务器。让它查看就够了。
实际使用方式
这不是一款通过点击按钮来操作的工具。 你在终端中与 AI Agent 对话,由 Agent 调用 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)
你不会受到限制。 双窗格文件浏览器和终端标签页与其他 SFTP 客户端和终端一样正常工作。只有 Agent 一侧受到限制。
Agent 可以调用的工具(阶段 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)。 这种工具一个就能做任何事;一旦有了它,Agent 和破坏性命令之间唯一的屏障就是一句承诺:不去使用它。承诺是操作手册,不是门槛。
使用 run_readonly 时,Agent 只能传入由人员写入
readonly.toml 的 ID。没有参数栏。初始列表为空——在你填写之前,什么都不会运行。 拒绝调用会显示在窗口的活动栏中,并记录到 readonly-refused.log。添加条目是人员的工作。
唯一的成功标准
Agent 不再说“请告诉我服务器的状态”。
不是下载量,也不是星标数,而是在实际维护过程中,往返沟通是否减少。
它不会让你的配置现代化
- 这不是一款将服务器迁移到其他地方的工具
- 配合你现有的配置工作才是重点
- 这些服务十年后仍会在这里。目前没有任何东西能为它们带来 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 /。这项机制只防止一件事:Agent 自行编写命令字符串。
人工操作界面(GUI)不受限制。 可以像使用普通 SFTP 客户端和终端一样使用它。只有 Agent 一侧受到限制。
更改状态的操作(operations.toml)
初始列表为空。 在你编写此文件之前,Agent 无法运行任何操作。
这是一个与读取允许列表(readonly.toml)分开的文件,这样写入操作就不会出现在名为 readonly 的文件中。通过名称区分两者,正是这样设计的目的。将它放在 connections.toml 旁边。
version = 1
[[operation]]
id = "restart-httpd"
run = "sudo systemctl restart httpd"
description = "restart Apache"
max_per_hour = 3
| 字段 | 填写内容 |
|---|---|
id | Agent 唯一可以传入的内容。 简短易读 |
run | 实际会输入的内容。 Agent 无法自行编写 |
description | 显示在审批对话框上。 使用六个月后你仍能看懂的文字 |
max_per_hour | 每小时上限。不接受 0(必须设定一个停止点) |
运行前必须通过的关卡
- 命令必须在由人编写的列表中。 未知 ID 会被拒绝,并记录拒绝信息
- 操作不得超过每小时上限
- 由人在屏幕上批准。 Agent 的首次调用总会被拒绝,窗口会显示实际将要运行的内容
- 一次批准只能使用一次。 一次批准,一次运行
- 批准会在 5 分钟后过期。 有人按下允许后,Agent 却再也没有调用(对话已经继续)。我们不会让批准一直搁置——使用
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
Agent 无法扩大自己的隔离范围。 这个列表就是为此而设的。
需要 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 才会被处理。 其他内容会逐字节保持不变。选择 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
这是由操作系统强制执行的关卡,不是应用做出的承诺。权限范围写在 sudoers 中,因此之后可以由人审核,而且sshboard 不会获取任何密钥。
检查是否有效:
sudo -n systemctl reload httpd # passes with no password prompt → good
sudo -n systemctl restart nginx # not listed → should be refused
如果第二条命令成功了,说明你的权限范围过宽。
ask(无法修改 sudoers 时)
无法修改服务器是很现实的情况——比如服务器是租来的、没有权限,或者机器太多。如果服务器上的 sudo -n 返回“需要密码”,那么 sudoers 方案从一开始就行不通。这正是 ask 的用途。
批准对话框中会出现密码输入框,由人在那里输入密码。
- 绝不存储。 不写入磁盘,也不存入操作系统的凭据存储
- 绝不放在命令行中。 密码通过标准输入传入,因此不会显示在
ps中 - 绝不显示在屏幕上,也绝不写入日志。 显示的内容是
$ sudo -S -p '' …,仅此而已 - 一次批准,一次运行。 使用后立即丢弃
- 拒绝后立即丢弃
读取只有 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 能让操作系统强制执行权限限制。
目前无法将批准请求发送给不在屏幕前的人(.claude/decisions.md D47——尚未决定)。如今,只有在屏幕前的人才能作出答复。没有人在场时,操作不会运行。
不会做什么
| 不做的事 | 原因 |
|---|---|
| 赋予 Agent 写入权限(第 1 阶段) | 每次使用都只读。这样我们从第一天起就能面向生产环境 |
| 允许 Agent 执行任意命令 | 通过结构性限制,只允许执行白名单中的命令 |
| 赋予 Agent sudo 权限 | 第 1 阶段不处理权限提升 |
| 构建自己的密钥存储 | 交由 OS 凭据存储和 ssh-agent 处理。不持有的东西,就无需保护 |
| 嵌入 AI 聊天 UI | Agent 在应用之外运行 |
| 生成报告 | 如果它能读取,Agent 就能撰写提案。无需添加其他功能 |
| 推动你迁移 | 与你现有的设置配合使用,正是我们的目标 |
运行
本项目仍处于 alpha 阶段。 在将其连接到生产服务器之前,请先在本地测试服务器上运行一次。
要求
| 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 中启动一台机器。
sh tools/test-server/up.sh # up
sh tools/test-server/up.sh down # down
连接 Agent(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中,也不会出现在 shell 历史记录中 - 更改端口(
SSHBOARD_MCP_PORT)后,无需重新注册 - 如果应用未运行,你会看到:“sshboard is not running. Start the app and try again.”
只有应用会打开 SSH 连接。 中继不持有引擎或波段。 它不会增加任何不可见的 SSH 会话(禁止事项 3)。
直接通过 HTTP 连接
窗口右上角的 “Copy the MCP registration command” 按钮会复制一行命令,其中包含令牌。
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 是否显示警告);这里保证的是更新来自我们。
Alpha 阶段须知
- 此程序未经代码签名。 Windows 是否会拦截,取决于你如何获取它
(问题 #3,实测结果):
- 通过浏览器下载,然后双击 → SmartScreen 会拦截
gh run download/ CI / 脚本 → 不会拦截。 没有 Web 标记,因此 SmartScreen 的信誉检查无从触发- macOS Gatekeeper 未经测试
- Agent 只能在用户为该连接列出的目录下写入。 默认列表为空——一个字节也不能写入
run_readonly允许列表默认也为空。 用户写入readonly.toml之前,不会运行任何内容。专用工具(disk_usage等)不受此限制- 你和 Agent 共用终端。 Agent 占用终端期间,你无法输入,且始终可以使用 [Stop]
- 每个 Release 都附有安装程序(D32)。
Windows 提供
.msi和 NSIS-setup.exe,macOS 提供.app.zip。 这些安装程序未经签名,因此 Windows 会显示 SmartScreen 警告,macOS 会显示 Gatekeeper 警告。 如果警告拦住了你,而你不知道该怎么办,请提交问题——这将决定我们是否购买证书(D12)
技术栈
| Shell | Tauri 2(Windows / macOS) |
| 核心 | Rust |
| UI | SvelteKit + xterm.js(我们不会自己编写 ANSI 解析器) |
| SSH | russh + russh-sftp(决定采用前,已在 9 台真实设备上分别测试——D6) |
| MCP | 内置于应用中(不是独立二进制文件,也不是独立构建) |
| 凭据 | 操作系统凭据存储 + ssh-agent |
许可证
MIT
开发规则
本仓库遵循一套 AI Agent 开发的基础规则。参见
.claude/rules/product-baseline.md 和
CLAUDE.md。
- AI 负责提交,人类负责推送(未经人类审核不得推送)
- 测试绝不延期,也绝不删除
- 这是一个公开仓库。 代码、文档、提交历史或截图中不得包含服务器主机名、IP、用户名、个人姓名或个人电子邮件地址
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。