← Back
ProbiusOfficial

ProbiusOfficial/NexTerm

一体化开发运维终端 —— SSH · WinRM · 文件 · Docker · 数据库 · AI,装进同一个窗口

View on GitHub ↗https://probiusofficial.github.io/NexTerm/ ↗
aidatabase-clientdevopsdockermysqlport-forwardingreactredisrustsftpsftp-clientsshssh-clientsysadmintauriterminalterminal-emulatortypescriptwindowswinrm
Stars
31
Forks
4
Watchers
31
Open issues
0
Contributors
1
Language
Rust
License
MIT License
Default branch
master
Created Sep 27, 2026Updated Oct 1, 2026

Star growth

Today—
This week—
This month—

Star history will appear here once this repo has been tracked for a couple of days.

README

NexTerm

NexTerm

一体化开发运维终端 —— SSH · WinRM · 文件 · Docker · 数据库 · AI,装进同一个窗口

桌面 App 与浏览器版共用同一份 Rust 内核。凭据加密存储,AI 全程在权限护栏内执行。

Website Platform Server Rust Tauri React License


NexTerm 主界面

运维工具链历来分散:SSH 客户端、SFTP 工具、Docker 面板、数据库客户端各占一个窗口,AI 助手还要切出去粘贴报错。NexTerm 将它们收进同一个三层工作区——工作区、分屏面板、标签,并为 AI 提供一条看得见、管得住的执行通道:每条命令、每次文件修改实时可见,危险操作先经确认。

同一份内核现在有两种跑法:装在自己电脑上的桌面 App,和部署到服务器、浏览器直接打开的服务端。服务端不是「投屏」——终端进程、回滚缓冲和工作区布局真的跑在那台常开的机器上,所以换设备、多人同看、关掉网页再回来,接着的都是同一份现场。两者之间再用资产同步打通:桌面上配好的主机与凭据,一键送到对端。

一个内核,两种形态

一个内核,两种形态

桌面 App 服务端 nexterm-server
装在哪 本机(Windows / macOS) 服务器或容器,浏览器访问
内核 进程内嵌同一份 进程内嵌同一份
运行态 进程内,关窗口即结束 在服务端:关网页不断、换设备接着用
凭据落点 本机(DPAPI / 主密码) 服务端实例(平台注入的主密钥)
适合 日常主力工作台 换设备接着用、多端访问
同步角色 发起方 被同步的对端

两端共用同一份命令清单(139 条),行为一致。「桌面行为逐字节不变」不是口头承诺,而是由双平台 CI 把关的。

服务端版本不装客户端,浏览器打开即用;手机平板受限于没有 Ctrl 键与右键菜单,终端体验有限,已在包元数据里显式声明不支持的平台,而不是让用户装上一个用不了的应用。

「运行态在服务端」不是一句口号,它有一串能验证的后果 —— 见下一节。

服务端:工作台搬到服务器上

服务端权威运行态:多台设备,一个工作台

服务端形态不是「把桌面版塞进浏览器」。在那台常开的机器上,运行态是权威的:布局、终端进程、回滚缓冲都留在服务端,浏览器只是接上去看。下面几件事都由这一条决定。

换一台设备,能接着用吗

能。终端标签在服务端就是一个真 PTY,进程和回滚输出(环形缓冲 + vt100 网格)都留在服务端。另一台设备打开网页时,前端按服务端布局里记着的标签 id 接管这条已有终端:服务端先清屏、再把历史回放过去,所以看到的是原来那条连接,而不是一个新开的空 shell。工作区、分屏与标签结构同样由服务端保存,打开时照此重建。原设备正持着键盘时,新进来的设备默认是观察者,不会把键盘抢走。

若服务端重启过、或那条标签已被结束,接管会拿到「找不到」,界面挂「连接已失效」遮罩,给「重新连接这台主机」与「关闭标签」两个出路,而不是让终端停在一行黄字上敲不动。

两个人同时操作会怎样

终端可以被多台设备一起看,但同一时刻只有一台设备能操作。控制权认的是设备身份(存在浏览器本地、跨刷新稳定),不是页面:同一台设备多开几个页面算同一台。非控制者写入会被拒绝(错误码 not_controller),界面把它当正常状态、提示「接管控制」而不是当报错。点「接管控制」即夺得键盘,原操作者当场转为只读观看;服务端会主动广播这次变化,不必等对方下一次敲键才发现。接管之后,PTY 尺寸按接管者的窗口重排(语义是「最后活跃者赢」)。

界面徽章「N 个设备正在观看」里的数字按设备去重(同一设备多开页面只算一台);它出现的条件是「除本端这条通道外还有别的推送通道」,所以同一设备多开页面时也会出现 —— 出现即代表确实还有人在看。

关掉网页会不会丢

不会。关网页只是断开这一端的订阅,进程与回滚继续留在服务端跑;服务端刻意不做空闲会话清理,所以「人走了」不会触发回收。

一个例外值得说清:Docker 面板的「查看日志」是跟随视图,不是终端标签 —— 它不占「后台会话」,离开页面约 5 秒后服务端会回收那条 docker logs -f。回到页面时会重新接上,从最近 500 行续着看。理由是这事本身没什么可保留的:没人看的时候,留着一口只为刷屏而活的进程没有意义。

要区分「关网页」与「关标签」:关闭带内核标签的终端会问一句,三选一 —— 后台继续运行(只从本端视图拿走,进程留在服务端,之后可在「后台会话」里重新接管)、结束进程(停泵、杀进程)、取消。接口层不传 mode 时缺省是「结束进程」而不是后台:不能因为「关标签」这个动作看着轻,就把用户可能正等着结果的任务默默留成后台僵尸。

多端同看时,「后台继续运行」只摘本端那一条订阅,其他设备的画面照旧 —— 不会把别人也一起摘掉。

两台设备同时改布局呢

布局也归服务端,所以「谁改了工作区」这件事要先说清楚。服务端存的是工作区 / 分屏 / 标签结构外加一个 revision:

  • 乐观锁 —— 每一端写布局时带上「我这份基于哪个 revision」,对不上就不写。
  • 冲突不自动合并 —— 冲突时返回 conflict: true,界面拉最新的一份让你再决定,而不是重试覆盖。布局没有可合并的语义(对端删掉的标签该不该复活?两个窗口的尺寸听谁的?),强行合并只会产出一份谁也看不懂的布局。
  • 跨端广播 —— 写入成功后广播 layout://changed,其余各端重新拉取。事件里带 revision,收到的一方能判断「这是不是我自己刚写的那次」—— 不判断就会自己触发自己,来回刷同一个布局。

服务端上的转发端口,地址由部署形态决定

同一条「把远端端口搬到服务端」的功能,监听在哪在三种形态下不一样:

形态 监听地址 为什么
桌面 App 127.0.0.1 转发是给自己用的。绑 0.0.0.0 会让同一 WiFi 下的人借这条 SSH 进内网,SOCKS5 更是直接变成无认证开放代理
自建服务端 0.0.0.0 这台机器就是你的边界。于是「远端 127.0.0.1:58627 转发到服务端 13306,从别处连 13306」成立
懒猫微服 —— 不可用,界面直接说明并指你去用微服平台自带的转发

懒猫那条是刻意的:平台的端口暴露是裸 TCP、且平台自己写明「从原理上无法提供鉴权流程」,应用侧补不上这个洞。面板若画出一个「外部可访问 http://…:13306」却永远连不上,用户只会一直以为自己端口填错了 —— 宁可明确说不可用。

前端拿到的不是猜出来的结论:这三个值由内核在启动时探测一次,经 forward_env 下发,界面据此决定禁用入口、切换文案、以及把地址显示成 127.0.0.1 还是 0.0.0.0。

跨实例资产同步

跨实例资产同步

桌面版和服务端是两个独立实例,各持一份 SQLite。没有同步,用户就得两边各配一遍,改了一边另一边立刻变旧。同步搬运的范围是资产 + 分组 + 凭据。

  • 方向显式 —— 勾中资产,按「推送到对端」或「从对端拉取」。内核只提供 export / apply 两个原语,不猜方向:SSH 私钥这类载荷根本不可合并,与其做个半吊子的自动合并让人不敢用,不如把选择交给你。
  • 默认不覆盖更新的那一份 —— apply 会跳过「本机这份更新」的条目并逐条说明原因。同步最常见的误操作是「拿一台旧机器的包盖掉新改动」;而「跳过了什么」是能看懂、能补救的(看报告 → 勾强制覆盖 → 重来)。
  • 凭据明文过河,落地重封 —— 两端密钥体系各自独立(桌面是 DPAPI / 主密码,服务端是注入的根密钥),密文搬过去解不开。所以是「源端解密 → HTTPS 传输 → 目标端用自己的密钥重新加密」。源端凭据库没解锁会明确报错,不静默跳过——静默的后果是「资产过去了、密码没过去」,等你在另一端点连接才发现。
  • 公网强制 HTTPS —— 只有本机与私有网段地址才放行明文 HTTP。访问令牌走明文等于交给同链路上的任何人。
  • 不搬的东西各有理由 —— AI 对话 / 审计日志 / 终端录制是本地行为记录,搬过去不是「同一件事」;主机指纹必须以目标端实际握手结果为准(接受远端指纹等于关掉 TOFU 保护)。

连接对端:一把令牌,两个部署位置

连接对端:一把令牌,两个部署位置

填「对端地址 + 同步令牌」就能连上,不用管对端装在哪:

  • 懒猫微服 —— 平台的公网入口默认要求登录,应用只把同步入口这一条路径从登录门里放行,请求这才到达容器,由应用自己校验令牌。
  • 自建服务器 —— 不经平台网关,直接连到服务端,同样由应用自己校验令牌。

两种位置的令牌是同一个机制(对端服务端自己生成的那一串),部署位置只影响界面提示,不影响协议。

⚠️ 完整版服务端上,这个令牌等同于该实例的完全控制权——它调的是同一张 RPC 表,能执行任何已注册命令,不只是同步资产。别外传。

只想在公网上做同步,就用 onlyServer(见下方「部署服务端」):它的命令表只有同步那三条, 令牌泄漏也拿不到终端、文件与容器。这不是"少注册几件事"的省事做法,而是拆开部署的唯一理由。

功能总览

模块 能力
终端 SSH 真 PTY、本地 ConPTY、WinRM;多标签与分屏、搜索、会话录制,支持 UTF-8 / GBK / GB18030 / Big5 编码切换
会话 一台资产一条连接复用,关标签不断连;SSH / WinRM 指数退避自动重连(本机会话没有"重连"这件事,不会假装在重连)
资产 内置「当前设备」本地资产 —— 装好即有一台机器(本机终端 / 文件树 / 容器面板都落在它上面),不可删除、可改名、可配默认 Shell 与起始目录;另有 SSH / WinRM / Docker / MySQL / Redis 资产,支持分组、搜索、拖拽归类
文件 SFTP 浏览与虚拟滚动、带进度与断点续传的传输;内置编辑器支持查找替换、LF/CRLF 转换与编码切换;MD5 / SHA256 校验
挂载 Windows net use 映射盘、Linux sshfs(本机文件直接看文件树,不需要挂载)
Docker 容器列表、日志 follow、容器终端、启停删、镜像管理、容器文件浏览、批量操作;SSH 主机与本机都可用
数据库 MySQL 库表浏览与 SQL 工作台;Redis SCAN 分页、类型感知查看与命令台
端口转发 SSH 本地转发与 SOCKS5 动态转发,复用已有会话直达内网资源;监听地址由部署形态决定(桌面回环 / 自建服务端对外 / 懒猫微服上不可用,见上文)
AI 助手 OpenAI 兼容多模型、工具调用、权限护栏、终端接管、文件变更 diff、计划模式、上下文用量与缓存命中统计
凭据库 两级密钥信封加密、集中管理面板、引用关系与删除保护、日志自动脱敏
服务端 同一套界面在浏览器里跑,部署到服务器或容器;无桌面依赖
多端运行态 工作区 / 分屏 / 标签结构落服务端(revision 乐观锁 + 跨端广播);关标签三选一 + 「后台会话」面板;终端可被多端观看,控制权按设备
资产同步 桌面 ↔ 服务端双向搬运资产、分组与凭据,方向显式、冲突可审

AI 助手

AI 不是贴在旁边的聊天框,而是接进了内核。

  • 工具调用透明 —— AI 执行的每条命令以卡片进入对话流,展开可见完整输出与退出码;命令运行在独立执行通道,不伪装成向终端打字。
  • 权限护栏 —— 操作按风险分级:安全操作直接执行;敏感操作弹确认卡片,可对本会话放行同类;格式化、批量删除等危险命令一律拒绝。只读、读写、静默三档模式随时切换。

AI 权限面板

  • 文件变更可审 —— write_file / edit_file 在确认卡片上先给出逐行 diff(新建文件整份标为新增),执行后再落一张「变更记录」卡片。改动一目了然,而且是批准之前就一目了然。

文件变更 diff

  • 终端接管 · 实验性 —— 基于内核侧终端状态机读屏、判定空闲、发送按键;按任意键立即夺回,全程显示接管横幅。
  • 多模型与成本可见 —— 任意 OpenAI 兼容端点,两步连接测试;上下文用量环与缓存命中率常驻侧栏底部。

模型配置

工作区

三层结构:工作区对应一台机器或一个连接,其下分屏,屏内开标签。标签始终挂载,切换不重连、不丢终端状态;分屏比例持久化,为一边敲命令、一边看日志的场景而设计。

分屏与编辑器

连接资产后左栏切换为文件树,双击进入内置编辑器;命令面板提供宽幅文件浏览器。

端口转发

下载安装

到 Releases 下载。每次发版产出五类产物:

# 产物 文件名 / 形态 装在哪
① Windows 客户端 NexTerm_x.y.z_x64-setup.exe Windows 10/11 x64,NSIS 安装器双击即装
② macOS 客户端 NexTerm_x.y.z_aarch64.dmg Apple Silicon(M 系列),拖入「应用程序」
③ 懒猫微服专版 应用商店安装包 在微服应用中心里安装,不经 Release 下载
④ LinuxServer NexTerm-x.y.z-linux-amd64.tar.gz 任意 Linux(amd64),带浏览器界面
⑤ onlyServer NexTerm-onlyServer-x.y.z-linux-amd64.tar.gz 任意 Linux(amd64),只做资产同步

四个客户端/服务端产物由 CI 自动构建;③ 因为要拉私有镜像仓库(凭证只在微服上), 由维护者手工出包后附到 Release 上。

④ 和 ⑤ 是同一个二进制,--sync-only 一个开关切换形态(见下)。 分两个包只是为了让你拿到手就是对的形态,不用读文档才发现「原来还要加参数」。

macOS 首次打开被拦下怎么办

安装包是 ad-hoc 签名、未公证的(没有 Apple Developer 证书),所以首次打开会被 Gatekeeper 拦一次。 这是预期行为,不是文件损坏:

  1. 双击应用,看到「无法验证开发者 / 无法检查是否包含恶意软件」的提示 → 点完成
  2. 打开 系统设置 → 隐私与安全性,下拉到「安全性」,点 「仍要打开」
  3. 之后正常双击即可

若提示的是**「已损坏,无法打开」**(而不是"无法验证开发者"),那是签名问题,用命令行一次修掉:

xattr -dr com.apple.quarantine /Applications/NexTerm.app

只有 Intel Mac?目前只发 Apple Silicon 包,可用 Rosetta 或从源码构建 (pnpm tauri build --target x86_64-apple-darwin)。

部署服务端

服务端适合放在常开的机器上:换一台设备,浏览器打开接着用;它同时是桌面版资产同步的对端。

三种部署位置,按「你在哪、要什么」选:

部署位置 用哪个包 浏览器界面 公网 端口转发 适合
懒猫微服 应用商店安装包 ✅ ✅(平台登录门 + 应用令牌) ❌ 用平台原生转发 有微服,要一个随处可开的浏览器工作台
LinuxServer NexTerm-x.y.z-linux-amd64.tar.gz ✅ ⚠️ 不建议 ✅ 绑 0.0.0.0 自建机器 / 内网 VPS,自己接反代
onlyServer NexTerm-onlyServer-x.y.z-linux-amd64.tar.gz ❌ ✅ 设计目标 ❌ 没有界面 公网只当同步中转,ssh 上去配

后两者是同一个二进制:onlyServer 就是加了 --sync-only。它少了什么、为什么少,见下。

懒猫微服

本仓库自带 LPK v2 打包与提审链路(lazycat/ 与 scripts/),产物为应用商店可用的安装包:

lzc-cli project build      # 产出 cloud.lazycat.app.nexterm-v<版本>.lpk
lzc-cli project deploy     # 部署到自己的微服

微服的公网入口默认要求登录,应用在 public_path 里只放行了同步入口一条路径, 靠应用自己的令牌把关 —— 用浏览器打开应用,体验不变。

这个平台上不提供端口转发:面板会说明原因并指你去用微服平台自带的转发功能。 平台侧的端口暴露是裸 TCP 且平台自己不做鉴权,应用侧补不上这个洞;画一个连不上的地址比明说不可用更糟。 (这条是编译期与部署时共同决定的:manifest 里写了 NEXTERM_PLATFORM=lazycat,内核据此判定,界面据此隐藏入口。)

自建服务器(LinuxServer)

下载 tarball 解压,里面有二进制、前端产物、systemd 单元和一份 README.md(安装步骤就在里面):

tar xzf NexTerm-x.y.z-linux-amd64.tar.gz
cd NexTerm-x.y.z-linux-amd64 && less README.md

跑起来就三步:建用户 → 放文件 → 起 systemd 单元。单元默认监听 127.0.0.1:8080, 对外要自己接一层带鉴权的反代:

# 凭据库根密钥(≥ 8 位)。不设也能起,但密码类资产与「带凭据同步」不可用
printf 'NEXTERM_MASTER_KEY=%s\n' "$(openssl rand -base64 32)" \
  | sudo tee /etc/nexterm/nexterm.env >/dev/null
sudo chmod 0600 /etc/nexterm/nexterm.env
sudo systemctl enable --now nexterm-server

⛔ /rpc 与浏览器界面没有任何自身鉴权。 懒猫那边是平台登录门在挡,裸 Linux 上那道门不存在 —— 能连上端口的人就拿到了终端、任意文件、Docker 与凭据库。所以:默认只听 127.0.0.1; 别改成 0.0.0.0 了事;不建议公网部署。

onlyServer(公网只做同步)

tar xzf NexTerm-onlyServer-x.y.z-linux-amd64.tar.gz
cd NexTerm-onlyServer-x.y.z-linux-amd64 && less README.md

--sync-only 的形态只有两个端点(/sync/rpc、/healthz)和三条命令 (sync_digest / sync_export / sync_import)—— 没有浏览器界面,没有 /rpc。 这不是「顺手精简」,而是这种部署存在的理由:/sync/rpc 的令牌走的是同一张命令表, 全量注册时它等于整个实例的控制权;只注册对端真正会调的三条,令牌泄漏最多也就读写这份资产库。

所有选项命令行与环境变量等价(命令行 > 环境变量 > 内置默认),懒猫那份 manifest 用的是环境变量,所以两边一份定义:

选项 环境变量 默认 说明
--listen NEXTERM_LISTEN 0.0.0.0:8080 监听地址。默认给容器用(平台从容器网络另一侧访问),裸机请显式改
--data-dir NEXTERM_DATA_DIR 有 /lzcapp/var 用它,否则 ./data SQLite / 日志 / 令牌落点
--web-root NEXTERM_WEB_ROOT 自动探测(/app/dist、dist) 前端静态资源;--sync-only 下用不到
--master-key NEXTERM_MASTER_KEY 无 凭据库根密钥(≥ 8 位)。不给则凭据库保持未初始化
--sync-only — 关 切成只做同步的形态

三条子命令,serve 可省略(容器不带任何参数启动,走的就是 serve):

nexterm-server                      # = serve,容器里就是这一条
nexterm-server --sync-only --listen 0.0.0.0:9000 --data-dir /var/lib/nexterm
nexterm-server token                # 打印同步令牌(没有就生成;只有令牌进 stdout)
nexterm-server rotate-token         # 重新生成 —— 旧令牌立即失效

token 只把令牌写 stdout,说明文字走 stderr,所以可以直接 TOKEN=$(nexterm-server token)—— onlyServer 没有界面,这条命令是拿到令牌的唯一途径。

两句自查:

curl -s 127.0.0.1:8080/healthz   # 完整版应报 commands:139;onlyServer 是 commands:3 + syncOnly:true

--help 会列出全部选项与示例。

公开部署请自行评估:完整版服务端是常开解锁状态,凭据库随进程可用 —— 要公网就换 onlyServer,并确保前面有 TLS(令牌明文放在请求头里)。

快速开始(从源码)

支持 Windows 与 macOS,从源码构建:

# 依赖:Rust stable(≥1.98)、Node 22+、pnpm 11+
#   Windows:MSVC 工具链;macOS:Xcode Command Line Tools
git clone https://github.com/ProbiusOfficial/NexTerm.git
cd NexTerm
pnpm install
pnpm tauri dev      # 开发窗口
pnpm tauri build    # Windows 出 NSIS 安装器;macOS 出 .app + .dmg

双平台 CI(Windows + macOS 各跑 fmt / clippy / test / typecheck / lint)在每次 push 时把关; 打 v* tag 由 release.yml 自动出两个平台的安装包并挂到 Release。

浏览器演示

免编译 Rust 预览全部界面,在线演示直接访问 probiusofficial.github.io/NexTerm/demo。 前后端仅 src/ipc/commands.ts 的 call() 一个接口,纯浏览器运行或 URL 带 ?demo=1 时自动切换内存 mock:

pnpm dev            # http://localhost:1420/

演示包含一台回显虚拟 shell,支持 ls、cat、systemctl、docker ps 与 Tab 补全、历史记录;另有 8 台资产、MySQL / Redis 面板、文件树与编辑器、AI 流式对话与确认卡片,命令块、搜索、录制全部可用。mock 为独立 chunk,生产包不加载;URL 加 ?demo=0 回到真实后端。

架构

前端只有一份,运行环境三态:桌面(Tauri 容器)、服务端(浏览器 + 真后端)、演示(纯前端假数据)。三态靠同步读取的标记判定,不需要探测后端。

┌──────── WebView(React 19 + xterm.js)────────┐   ┌──── 浏览器 ────┐
│  资产树 · 标签/分屏 · 终端 · 文件 · DB · AI    │   │  同一份前端产物 │
└──────────────────── Tauri IPC ────────────────┘   └── HTTP / WS ───┘
                                                              │
        ┌──────────────── Rust 内核(Tokio)────────────────┐  │
        │ session/ 会话池·重连   terminal/ PTY+vt100 状态机   │  │
        │ transport/ ssh·winrm·local·forward   fs/ 传输·挂载  │◄─┘
        │ docker/ CLI 通道   db/ mysql·redis   ai/ agent·护栏 │
        │ vault/ 两级密钥加密  store/ SQLite(WAL)  sync/ 同步 │
        └────────────────────────────────────────────────────┘
                    ▲ 内嵌同一份(桌面)   ▲ 内嵌同一份(服务端 axum)

命令层是一份 macro_rules! nexterm_commands,桌面侧喂给 Tauri 的 generate_handler!,服务端侧喂给一个 Vec<Entry>;平台差异靠 ipc_shim 收束,所以 139 条 #[tauri::command] 一行都不用改。

关键设计:

  • 终端字节流不走 JSON —— PTY 输出经 Channel<Vec<u8>> 直送 xterm.js,输入走 invoke。服务端模式同理走二进制 WebSocket。
  • 内核侧终端状态机 —— PTY 字节流同步喂给内核 vt100::Parser,是 AI 读屏、空闲判定与终端接管的唯一数据源。
  • 端到端背压 —— 本地 PTY 以阻塞线程加有界通道桥接;4MB 暂停、节流事件、隐藏标签批量刷新。
  • 凭据两级密钥 —— 主密码经 Argon2id 派生 KEK,信封加密 DEK;凭据以 XChaCha20-Poly1305 加密;支持 Windows DPAPI 免主密码模式;日志统一脱敏。
  • AI 护栏独立成层 —— 风险分级与放行决策单一入口,不散落在调用点。

质量门

cargo fmt --all --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test --workspace
pnpm typecheck && pnpm lint

docs/diagrams/*.svg 是本仓库架构图的源文件,用 node scripts/render-diagrams.mjs 渲染成 docs/images/*.png。

Roadmap

独立同步核心(可脱离桌面单独部署的密钥中转)在设计中;Linux 桌面版尚未验收 —— 需要 Linux 的话,服务端形态现在就能用(浏览器打开即完整界面)。

致谢

终端模拟基于 xterm.js,SSH 基于 russh,桌面框架为 Tauri。

License

MIT