Self-hosted · Rust · WebSocket

浏览器里的本地终端

一个精简、可独立运行的浏览器终端。Rust 服务端管理本地 PTY,浏览器通过 WebSocket 连接 Shell,并保留有限的终端历史。前端资源嵌入单个二进制,无需额外静态文件服务。

版本 v0.5.39 许可 MIT 技术栈 Rust · xterm.js · CodeMirror 6
8独立终端标签页
20000浏览器默认历史行数
8MB服务端每会话保留输出
1单个嵌入式二进制
Features

为日常运维打磨的终端体验

不是把终端搬进浏览器那么简单 —— 多标签、分屏、断线重连、历史搜索与文件操作,围绕真实工作流设计。

01

多标签与可调分屏

最多 8 个独立终端标签页,支持新建、切换与关闭。桌面端可将任意两个终端合并为双面板视图,同时存在多个分屏组合;分隔条可拖动、双击均分、一键交换。

  • 拖动分隔条实时调整两栏宽度
  • 双击均分,一键交换左右面板
  • 尺寸变化按最终结果一次性重排,无闪烁回滚
● shell-1● shell-2
$ docker ps
CONTAINER ID IMAGE STATUS
a3f2c1 redis:7 Up 3 days
$
● shell-1● shell-2
$ tail -f app.log
[info] request handled in 12ms
[info] ws connected from 10.0.0.8
$
02

会话持久与自动重连

每个会话由独立的 session-host 子进程托管:PTY 与滚动历史不随服务进程重启而丢失。断线后自动重连并补回断线期间的内容,服务重启后仍按标签恢复运行中的 shell。

  • 刷新页面后标签页状态原样保留
  • 后台输出、等待输入、进程退出均有状态提醒
  • 可开启浏览器系统通知,或单独静音某个标签
[session-host] pty fd 7 alive · history 8MB ring
$ codex exec "fix the flaky test"
✓ plan accepted · writing patch…
— network dropped, retrying in 1s —
[reconnect] session restored · 0 lines lost
$
03

终端历史搜索

搜索当前终端已加载的屏幕与滚动历史。浏览器默认保留 20000 行(可在设置中调整,移动端自动降为 10000 行),服务端每会话默认保留 8MB 输出。

  • 桌面端 Ctrl / Cmd + Shift + F 快捷唤起
  • 手机端点击终端内搜索按钮
  • 匹配项高亮,逐条跳转
16:02:31 deploy.sh: line 12: command not found
16:02:33 retrying connection to 10.0.0.8:22
16:02:40 error: failed to open /var/log/app.log
16:02:41 checking disk space…
16:02:45 error: error code 500 on POST /api/sync
04

SSH 主机管理

集中管理常用 SSH 主机:分组、默认打开路径与可选密码保存。密码使用由访问 Token 派生密钥加密存储,旧版本明文密码会在读取时自动迁移。

  • 分组归类,按组快速过滤
  • 记住每个主机的默认打开路径
  • 远端文件浏览与终端联动当前目录
prod-web-01 production ssh · 已连接
db-backup infra ssh · 已连接
staging-02 staging 离线
添加新主机…
05

虚拟键盘与自定义命令

桌面端与移动端均可使用虚拟键盘,并支持可编辑的自定义命令按钮;在键盘上左右滑动即可切换默认 / 自定义键盘,触屏操作也能流畅完成日常运维。

  • 常用命令一键补全执行
  • 命令列表可自由增删改
  • 滑动切换键盘模式,随开随用
— custom command palette —
$ deploy # ./scripts/deploy.sh --env prod
$ logs # pm2 logs hylintech-tty --lines 200
$ status # git status --short
deploy logs status restart + 编辑
06

文件浏览、编辑与差异对比

本地与 SSH 远端文件浏览:树形目录、创建、上传、下载、重命名、删除与图片预览;Git 仓库文件夹自动显示 git 标记。编辑器基于 CodeMirror 6,支持多标签、行内增改标记与冲突检测。

  • 文件保存时检测外部修改,冲突可重载 / 对比 / 另存副本
  • Git 改动文件查看与差异对比,行号旁标记新增 / 修改行
  • 大屏下可将文件列表与编辑器固定到屏幕左侧
~/projects/hylintech-tty git
├─ src/
│  ├─ main.rs
│  └─ session/
├─ static/
│  └─ app/
└─ docs/CHANGELOG.next.md
+· 新增终端分屏与会话自动重连
+· 修复 SSH 重连后文件栏定位失败
-· 移除 tmux 运行模式
07

客户端管理

在工具栏查看当前在线的浏览器客户端:浏览器类型、地址、连接时间与所在终端标签,并可踢出其他客户端。被踢出的客户端断开后不会自动重连,当前客户端不能被自己踢出。

  • 实时在线列表与连接信息
  • 一键断开可疑连接
Chrome 128 · macOS tab shell-1
Safari 17 · iPhone tab shell-2
Firefox 129 · Linux tab files
08

内置更新与一键升级

浏览器启动时自动检查更新清单,发现新版本后在工具栏显示 NEW 按钮,并按版本展示当前版本之后的全部可用改动。PM2 托管的部署可直接一键自动升级。

  • 跨版本升级按版本分组展示全部更新说明
  • 非 PM2 环境提供下载链接,手动替换二进制
NEW v0.5.39 → v0.6.0 · 3 个版本待更新
  • 1下载升级包,校验 sha256 与大小
  • 2原子替换,当前二进制备份为 .prev
  • 3pm2 restart,回滚只需移回备份
Architecture

单二进制 · 独立会话进程

前端资源通过 rust-embed 嵌入二进制,浏览器原生 ES modules 直接由静态资源服务提供,无需前端打包步骤;终端会话由 session-host 子进程托管,与服务进程生命周期解耦。

Cargo.toml · tech stack
[dependencies] axum = "0.7" # Web 服务与 WebSocket portable-pty = "0.8" # 本地 PTY 管理 vt100 = "0.15" # 终端状态与历史渲染 rust-embed = "8" # 静态资源嵌入二进制 aes-gcm = "0.10" # SSH 密码加密 tokio = "1" # 异步运行时 # 前端:xterm.js 5.5 · CodeMirror 6 · 原生 ES modules # 运行:session-host 子进程托管每个会话的 PTY 与历史
Quick Start

三分钟跑起来

需要 Rust stable 与一个可用的本地 Shell。默认监听 127.0.0.1:11083,浏览器打开后输入配置中的 Token 完成认证。

01 构建并运行
$ cargo run --release
02 打开浏览器并认证
$ open http://127.0.0.1:11083 # Token 在 configuration/settings/env.json
03 调试模式(可选,不部署生产)
$ ./scripts/dev.sh # 后台启动调试服务器,端口 11088,Token 888888
04 发布构建(GNU + musl 双版本)
$ ./scripts/build.sh

配置项

  • token — 浏览器访问 Token,缺失时随机生成
  • addr — 监听地址,默认 127.0.0.1:11083
  • shell — 本地终端使用的 Shell,默认 /bin/bash
  • history_bytes — 每会话服务端保留输出上限,默认 8MB
Security

安全设计

Token 认证

必须使用高强度 Token,并通过 HTTPS 对外提供服务。SSH 密码使用由 Token 派生密钥加密保存,旧版本明文密码读取时自动迁移;Token 泄露仍意味着密码可被解密。

权限边界

自定义命令按钮中的命令以当前用户权限执行,只配置可信命令。所有已认证客户端共享终端输入、输出与标签列表,运行时目录权限 0700、会话 socket 与元数据 0600。

会话隔离

终端会话由独立的 session-host 子进程托管,PTY 与滚动历史不随服务进程重启而丢失;服务重启后自动恢复仍存活的会话,关闭或失效的会话文件会被清理。

可信升级

自动升级前校验 sha256 与大小,原子替换二进制并备份为 .prev,pm2 restart 后生效;手动回滚只需把备份移回再启动。