diff --git a/README.md b/README.md index 45f44e8..f30809c 100644 --- a/README.md +++ b/README.md @@ -1,114 +1,62 @@ +
+ # TermFlow -> 通过浏览器、手机与原生客户端安全远程控制 tmux 终端与 Agent Broker 事件流。 +Resilient remote tmux terminal & 4-level guarded Agent Broker across Web, Mobile, and Native clients. [![Family: *Flow](https://img.shields.io/badge/family-*Flow-8A2BE2.svg)](https://github.com/mcocdaa) [![CI](https://github.com/mcocdaa/TermFlow/actions/workflows/ci.yml/badge.svg)](https://github.com/mcocdaa/TermFlow/actions/workflows/ci.yml) [![Release](https://img.shields.io/github/v/release/mcocdaa/TermFlow?display_name=tag&sort=semver)](https://github.com/mcocdaa/TermFlow/releases) -[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) [![Python 3.12](https://img.shields.io/badge/python-3.12-blue.svg)](https://www.python.org/) -[![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue.svg)](https://www.typescriptlang.org/) -[![Docker](https://img.shields.io/badge/docker-ready-blue.svg)](deploy/compose.yaml) +[![TypeScript 5.x](https://img.shields.io/badge/TypeScript-5.x-blue.svg)](https://www.typescriptlang.org/) +[![Docker Ready](https://img.shields.io/badge/docker-ready-blue.svg)](deploy/compose.yaml) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) -

- TermFlow Live Terminal, Geek Themes & Agent Approval Demo -

+[English](README.md) · [简体中文](README_CN.md) -TermFlow 让你通过浏览器、手机或原生客户端远程使用另一台电脑上的 tmux 终端。 +TermFlow Demo -```text -手机浏览器 / 桌面浏览器 / Windows、Linux、macOS、Android、iOS Simulator 客户端 - ↓ HTTPS / WSS - Control Plane B + Web C - ↑ A 主动连接 - Computer A(tmux、命令和工作目录) -``` +
-关闭客户端或短暂断网不会停止 A 上运行的命令。v0.2.0 在 -`Computer → Term → Window → Pane` 之上加入 Agent Broker(OpenCode 后端、人工审批、 -可回放的 Agent 事件流和固定 Compose 参考部署);语音能力不属于这个版本。 +## ✨ Highlights -## 安装 B + Web C +- **Outbound-Only Node A (`Computer → Term → Window → Pane`)**: Computer A dials out over TLS/WSS to Control Plane B with zero inbound ports (`A 主动连接 B`, `A 不开放端口`); closing browsers or losing mobile connectivity never interrupts running `tmux` jobs. +- **Mobile-First Web & Native Terminal**: Responsive `xterm.js` workspace equipped with a tactile `MobileKeyBar` (`Esc`, `Tab`, `Ctrl`, `Alt`, arrow keys, and quick tmux chords) plus 7 curated geek themes. +- **4-Level Guarded Agent Broker (`OpenCode`)**: Replayable agent event streams with a 4-tier risk guard and mandatory human-in-the-loop approval before any write or critical terminal action executes. +- **Zero-Trust TOTP & One-Time Pairing**: Control Plane authentication protected by TOTP MFA, short-lived one-time node enrollment tokens, and device authorization via QR code or pairing code. +- **Multi-Platform Native Clients**: Unified Vue 3 + TypeScript + Tauri 2 client architecture shipping across Web, Windows, Linux, macOS, Android, and iOS Simulator. +- **Strict Control Plane Boundary**: Control Plane B authenticates, routes, and relays terminal streams without executing host shell commands, keeping Node A as the sole terminal authority. -### 发布镜像 Compose(推荐,含 Agent) +## 🚀 Quick Start -发布镜像同时包含 B、Web C 和 Agent Broker。下面的流程从 GitHub 取部署文件,Compose -只拉取 GHCR 上已发布的镜像,不在本地构建,也不需要 checkout 源码: +### 1. Deploy Control Plane B + Web C + Agent Broker (Docker Compose) + +**Option A — Published Release Images (Recommended)** ```bash -TAG=v0.2.0 # 换成 Release 页面的精确 tag +TAG=v0.2.0 BASE="https://raw.githubusercontent.com/mcocdaa/TermFlow/${TAG}" mkdir -p termflow && cd termflow curl -fsSLO "${BASE}/deploy/compose.release.yaml" curl -fsSLO "${BASE}/deploy/opencode-config.yaml" curl -fsSLo .env "${BASE}/.env.example" chmod 0600 .env -``` -按 `.env` 注释生成 `TERMFLOW_ADMIN_TOKEN`、`OPENCODE_SERVER_*`、 -`OPENCODE_AGENT_MCP_TOKEN`、`DEEPSEEK_API_KEY`,把 `TERMFLOW_RELEASE_IMAGE_TAG` 设为上面的 -`TAG`,并按可核验证据填写 DeepSeek disclosure 字段(没有证据时 B 会 fail closed)。 -然后启动并检查: - -```bash +# Configure TERMFLOW_ADMIN_TOKEN, OPENCODE_SERVER_*, OPENCODE_AGENT_MCP_TOKEN, +# DEEPSEEK_API_KEY, and set TERMFLOW_RELEASE_IMAGE_TAG=${TAG} in .env docker compose --env-file .env -f compose.release.yaml up -d -curl -fsS "http://127.0.0.1:${TERMFLOW_HOST_PORT:-8765}/healthz" -``` - -首次启动需要十几秒,`healthz` 立即失败时稍等重试。B 的 Web/API 默认发布到 -`127.0.0.1:${TERMFLOW_HOST_PORT:-8765}`,数据保存在 `termflow-data`、 -`termflow-totp-key`、`termflow-opencode-data` 三个命名卷。升级或回退只改 -`TERMFLOW_RELEASE_IMAGE_TAG`,再 `docker compose --env-file .env -f compose.release.yaml pull` -和 `up -d`;不要删除数据卷,也不要用 `latest` 做需要复现的部署(它只随稳定 tag 移动)。 -公网部署时,把 HTTPS/WSS 反向代理指向该端口,并把 `TERMFLOW_PUBLIC_BASE_URL` 设置为 -用户实际访问的 HTTPS origin,`TERMFLOW_ALLOW_INSECURE_LOOPBACK` 保持 false。 - -### 单容器 B(仅终端,不含 Agent) - -只需要终端、不需要 Agent 时,可以用已发布镜像直接运行 B;下面的命令使用本地目录保存 -数据库与 TOTP 主密钥,并通过宿主机 `127.0.0.1:8000` 发布 Web/API: - -```bash -mkdir -p data totp-secrets -docker network create termflow-net - -docker run -d \ - --name termflow-control-plane \ - --restart unless-stopped \ - --network termflow-net \ - --publish 127.0.0.1:8000:8000 \ - --volume "$PWD/data:/app/data" \ - --volume "$PWD/totp-secrets:/app/totp-secrets" \ - --env TERMFLOW_ADMIN_TOKEN='<至少 32 字节的管理员 token>' \ - --env TERMFLOW_DATABASE_URL=sqlite+aiosqlite:////app/data/termflow.db \ - --env TERMFLOW_PUBLIC_BASE_URL=https://termflow.example.com \ - --env TERMFLOW_ALLOW_INSECURE_LOOPBACK=false \ - --env TERMFLOW_TOTP_AUTO_MASTER_KEY_FILE=/app/totp-secrets/totp-master-key \ - ghcr.io/mcocdaa/termflow-control-plane:v0.2.0 +curl -fsS http://127.0.0.1:8765/healthz ``` -检查服务(首启需要十几秒): +**Option B — Local Source Checkout** ```bash -curl -fsS http://127.0.0.1:8000/healthz +cp .env.example .env +docker compose --env-file .env -f deploy/compose.yaml up -d --build +curl -fsS http://127.0.0.1:8765/healthz ``` -单容器 B 不含 Agent 运行时,Agent 页面会显示 `provider_selection_unavailable`。 -公网部署与反向代理要求和上面相同。B 启动后,在 Web C 的 Computers 页面生成 -Computer A 使用的一次性注册码。 - -B 的 Web/API 通过宿主机 loopback 端口发布;A 主动连接 B,是连接 B 的客户端,不是 B 的网关。 - -发布镜像 Compose 是生产部署路径。开发或验证源码时,使用 -[部署与恢复](docs/operations.md) 中的 `docker compose -f deploy/compose.yaml` 本地参考 -部署(project `termflow-v020-local`);它与发布 Compose 的服务形状一致,但会从当前 -checkout 构建镜像。两种 B 部署对应的 A 网络和服务地址不同,Docker A 一节分别说明。 - -## 安装 Computer A - -### Linux - -Linux x86_64 需要 tmux 3.2 或更高版本: +### 2. Enroll Linux Node A (`tmux 3.2+`) ```bash curl -fsSL \ @@ -117,37 +65,50 @@ curl -fsSL \ termflow login \ --server https://termflow.example.com \ - --code '' + --code '' termflow new --name project-a ``` -使用 tmux 默认的 `Ctrl+B`、再按 `D` 可退出当前界面,Term 会继续运行。重新进入: +Detach anytime with `Ctrl+B` then `D` (`termflow attach project-a` to re-enter). -```bash -termflow attach project-a -``` +
+📦 Standalone Control Plane, Offline Tarball & Packaging Reference + +### Standalone Control Plane Container (Terminal Only, Without Agent Runtime) -### Docker +When you only need remote terminal routing without the OpenCode Agent runtime (`B 的 Web/API 通过宿主机 loopback 端口发布;A 主动连接 B,是连接 B 的客户端,不是 B 的网关;A 不开放端口`): + +```bash +mkdir -p data totp-secrets +docker network create termflow-net -Docker A 是一个后台常驻的计算节点。身份和工作目录保存在本地目录,A 不开放端口,只主动 -通过 WebSocket 连接 B。A 必须与 B 加入同一个普通 bridge 网络,并使用与 B 相同版本的镜像: +docker run -d --name termflow-control-plane \ + --restart unless-stopped \ + --network termflow-net \ + --publish 127.0.0.1:8765:8000 \ + --volume "$PWD/data:/app/data" \ + --volume "$PWD/totp-secrets:/app/totp-secrets" \ + --env TERMFLOW_ADMIN_TOKEN='' \ + --env TERMFLOW_DATABASE_URL=sqlite+aiosqlite:////app/data/termflow.db \ + --env TERMFLOW_PUBLIC_BASE_URL=https://termflow.example.com \ + --env TERMFLOW_ALLOW_INSECURE_LOOPBACK="${TERMFLOW_ALLOW_INSECURE_LOOPBACK:-true}" \ + --env TERMFLOW_TOTP_AUTO_MASTER_KEY_FILE=/app/totp-secrets/totp-master-key \ + ghcr.io/mcocdaa/termflow-control-plane:v0.2.0 -- 已发布版本:把下文的镜像换成 Release 页面对应的 tag,例如 - `ghcr.io/mcocdaa/termflow-node:vX.Y.Z`,不要使用 `latest`; -- 从源码验证:在仓库根目录运行 `scripts/build-node-image.sh termflow-node:v0.2.0`。 +curl -fsS http://127.0.0.1:8765/healthz +``` -先在 Web C 的 Computers 页面生成一次性注册码;无界面环境可以用管理员 token 调用 API -(默认 60 秒内有效,请在启动 A 前立即生成;端口与 B 的发布端口一致,默认 8765): +For offline archive loading and TOTP recovery: ```bash -curl -fsS -X POST \ - -H "Authorization: Bearer $TERMFLOW_ADMIN_TOKEN" \ - http://127.0.0.1:8765/api/v1/enrollment-tokens +docker load -i termflow-control-plane.tar +docker exec termflow-control-plane termflow-control auth totp reset ``` -下面假设 B 是上文的发布 Compose project `termflow`:A 加入它的默认网络 -`termflow_default`,并通过服务名 `control-plane` 访问 B: +### Containerized Node A (Docker) + +Generate a one-time enrollment code via Web C or API (`POST /api/v1/enrollment-tokens`), then launch `termflow-node` on the shared bridge network: ```bash mkdir -p termflow-node-identity termflow-node-work @@ -167,86 +128,44 @@ docker run -d \ --volume "$PWD/termflow-node-identity:/home/termflow" \ --volume "$PWD/termflow-node-work:/work" \ --env TERMFLOW_SERVER=http://control-plane:8000 \ - --env TERMFLOW_CODE='<一次性注册码>' \ + --env TERMFLOW_CODE='' \ --env TERMFLOW_ALLOW_INSECURE_HTTP=true \ --env TERMFLOW_NEW=demo \ - termflow-node:v0.2.0 + ghcr.io/mcocdaa/termflow-node:v0.2.0 ``` -如果 B 用本页前面的单容器示例运行,则改为 `--network termflow-net` 和 -`TERMFLOW_SERVER=http://termflow-control-plane:8000`;如果是源码 checkout 的本地参考 -部署(project `termflow-v020-local`),网络改成 `termflow-v020-local_default`,服务地址 -同样是 `http://control-plane:8000`。 -`TERMFLOW_ALLOW_INSECURE_HTTP=true` 只用于 loopback 上的明文 HTTP;正式 HTTPS/WSS 部署不能 -设置它。 - -Web C 进入 Docker A 的 Term 默认使用 Bash。如需改用 POSIX sh,在上述 -`docker run` 命令中追加: - -```bash ---env TERMFLOW_SHELL=sh -``` - -`TERMFLOW_SHELL` 只接受 `bash` 和 `sh`。修改后需要保留身份和工作目录,并 -重新创建 Docker A 容器;新的 tmux pane 使用所选 shell,已经运行的 pane 不会热切换。 - -进入 Docker A 的 Term、查看状态和排障: +Inspect or attach to the containerized node as UID 1000 (`termflow`): ```bash docker exec --user termflow -it termflow-node termflow attach demo docker exec --user termflow termflow-node termflow doctor docker exec --user termflow termflow-node termflow status demo --json -docker logs termflow-node ``` -`docker exec` 必须带 `--user termflow`:A 以容器内 UID 1000 运行,并为每个 Instance 使用独立 -tmux socket;以 root 执行或直接运行 `tmux list-sessions` 只会看到默认 socket 并报 -“No such file or directory”。`termflow status demo --json` 会返回该 Instance 的精确 -`socket_path`,只有在必须裸用 tmux 时才用 `tmux -S `。注册码过期时 -`docker logs` 会显示注册失败,重新生成注册码并保留身份目录重试即可。 - -`termflow-node-identity/` 保存 A 的身份,`termflow-node-work/` 保存用户文件。容器首次启动会 -把这两个目录改成容器内 `termflow`(UID 1000)所有;删除目录会删除对应数据。 - -## 客户端 - -正式构建版本的解析顺序固定为 -`Git Tag > TERMFLOW_BUILD_VERSION > 0.2.0-dev.0`。Tag Release 直接使用 `vX.Y.Z` -(以及受支持的 prerelease/build metadata)中的版本;手动 workflow 可填写可选版本,本地构建可设置 -`TERMFLOW_BUILD_VERSION=1.2.3`。两者都没有时使用明确的开发版本 -`0.2.0-dev.0`,不会被误认为正式 Release。Web C 已包含在 Control Plane 镜像中。Windows、Linux、macOS、Android 和 iOS Simulator -客户端从 [GitHub Releases](https://github.com/mcocdaa/TermFlow/releases) 下载。 - -## 更新与备份 - -使用新的精确 tag 重建容器即可升级。升级或回退前备份 B 的 `data/`、`totp-secrets/`, -以及 A 使用的 Docker volumes。不要使用 `latest`,也不要在升级时删除数据卷。 - -详细说明见 [部署与恢复](docs/operations.md)。 +### Build Version & Packaging Contracts -## 技术架构 +- **Version precedence**: `Git Tag > TERMFLOW_BUILD_VERSION > 0.2.0-dev.0` +- **Packaging workflows** (`workflow_dispatch`, `workflow_call`, `Actions artifact`, `GitHub Release`, `GHCR`): + - `Package A · Linux Node` (`termflow-node-linux-x86_64`, requires `tmux 3.2`+, offline wheelhouse via `Path.cwd().as_uri()`) + - `Package B + Web C · Control Plane` (pinned `Node 22.23.2` + Python 3.12 runtime) + - `Package C · Native Clients` (Windows, Linux, macOS, Android, iOS Simulator from [GitHub Releases](https://github.com/mcocdaa/TermFlow/releases)) -| 组件 | 技术 | 职责 | -| --- | --- | --- | -| Computer A | Python 3.12、tmux、asyncio、WebSocket | 运行 Term、管理 tmux,并主动连接 B | -| Control Plane B | FastAPI、Uvicorn、SQLAlchemy 2、Alembic、SQLite | 认证、Computer 注册、状态同步和终端流量转发 | -| Web C | Vue 3、TypeScript、Vite、xterm.js | 浏览器和手机端的终端与管理界面 | -| 原生 C | Tauri 2、Rust,共用 Vue UI | Windows、Linux、macOS、Android 和 iOS Simulator 客户端 | -| Protocol | Pydantic 2 | A、B 之间的版本化消息协议 | +
-A 保存并执行真实终端进程;B 不执行 A 的 shell 命令,只负责认证、路由和转发。Web C -由 B 的同一镜像提供,原生客户端复用同一套客户端核心、UI 和协议契约。 +## 📚 Documentation -## 开发与文档 +| Document | Description | +| --- | --- | +| [docs/architecture.md](docs/architecture.md) | System topology, process boundaries, and network security model between Node A, Control Plane B, Web C, and OpenCode | +| [docs/protocol.md](docs/protocol.md) | Versioned WebSocket & Pydantic message contracts between Node A and Control Plane B | +| [docs/security.md](docs/security.md) | TOTP MFA, one-time pairing, 4-level Agent Broker risk guard, and container hardening | +| [docs/api-examples.md](docs/api-examples.md) | REST & WebSocket API usage examples for enrollment, sessions, panes, and agent approvals | +| [docs/web-client.md](docs/web-client.md) | Web C & mobile terminal guide, QR pairing flow, themes, and `MobileKeyBar` | +| [docs/operations.md](docs/operations.md) | Production deployment, upgrades, volume backups, TOTP reset, and recovery procedures | +| [docs/github-actions.md](docs/github-actions.md) | CI/CD pipelines, release artifact matrix, and GHCR image publishing | +| [docs/troubleshooting.md](docs/troubleshooting.md) | Common diagnostics for `tmux` sockets, WebSocket handshakes, and container permissions | +| [docs/superpowers/README.md](docs/superpowers/README.md) | Architecture specs, design decisions, and implementation records | -从源码构建、开发环境和 Release 流程不放在安装入口中: +## 📄 License -- [部署与恢复](docs/operations.md) -- [架构与进程边界](docs/architecture.md) -- [协议](docs/protocol.md) -- [API 示例](docs/api-examples.md) -- [Web C](docs/web-client.md) -- [安全与隐私](docs/security.md) -- [排障](docs/troubleshooting.md) -- [构建与发布](docs/github-actions.md) -- [设计与实施记录](docs/superpowers/README.md) +Released under the [MIT License](LICENSE). diff --git a/README_CN.md b/README_CN.md new file mode 100644 index 0000000..ec6eaa7 --- /dev/null +++ b/README_CN.md @@ -0,0 +1,171 @@ +
+ +# TermFlow + +支持浏览器、移动端与原生客户端的安全远程 tmux 终端与四级风控 Agent Broker 控制平面。 + +[![Family: *Flow](https://img.shields.io/badge/family-*Flow-8A2BE2.svg)](https://github.com/mcocdaa) +[![CI](https://github.com/mcocdaa/TermFlow/actions/workflows/ci.yml/badge.svg)](https://github.com/mcocdaa/TermFlow/actions/workflows/ci.yml) +[![Release](https://img.shields.io/github/v/release/mcocdaa/TermFlow?display_name=tag&sort=semver)](https://github.com/mcocdaa/TermFlow/releases) +[![Python 3.12](https://img.shields.io/badge/python-3.12-blue.svg)](https://www.python.org/) +[![TypeScript 5.x](https://img.shields.io/badge/TypeScript-5.x-blue.svg)](https://www.typescriptlang.org/) +[![Docker Ready](https://img.shields.io/badge/docker-ready-blue.svg)](deploy/compose.yaml) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) + +[English](README.md) · [简体中文](README_CN.md) + +TermFlow Demo + +
+ +## ✨ 核心特性 + +- **单向出站 Node A(`Computer → Term → Window → Pane`)**:计算节点 A 无需开放任何入站端口,仅通过 TLS/WSS 主动连接控制平面 B(`A 主动连接 B`,`A 不开放端口`);关闭客户端或网络闪断绝不中断正在运行的 `tmux` 任务。 +- **移动优先的终端交互体验**:基于 `xterm.js` 的响应式工作区,内置触控友好的 `MobileKeyBar`(`Esc`、`Tab`、`Ctrl`、`Alt`、方向键及 `tmux` 组合快捷键)与 7 套极客终端主题。 +- **内置四级风控 Agent Broker(`OpenCode`)**:提供可回放的 Agent 事件流与四级风险评估守卫,任何写入或高危终端操作均强制执行人工审批确认(Human-in-the-Loop)。 +- **TOTP 双因素认证与一次性配对**:支持 TOTP 动态口令保护、短效一次性节点注册码,以及移动端扫码或配对码远程授权。 +- **全平台原生客户端**:基于 Vue 3 + TypeScript + Tauri 2 共用核心构建,覆盖 Web、Windows、Linux、macOS、Android 与 iOS Simulator。 +- **严格的控制平面安全边界**:控制平面 B 仅负责认证、状态同步与终端流转发,不执行宿主机命令,由节点 A 保持真实终端进程的唯一权威。 + +## 🚀 快速开始 + +### 1. 部署 Control Plane B + Web C + Agent Broker(Docker Compose) + +**方式一:使用已发布镜像(推荐生产使用)** + +```bash +TAG=v0.2.0 +BASE="https://raw.githubusercontent.com/mcocdaa/TermFlow/${TAG}" +mkdir -p termflow && cd termflow +curl -fsSLO "${BASE}/deploy/compose.release.yaml" +curl -fsSLO "${BASE}/deploy/opencode-config.yaml" +curl -fsSLo .env "${BASE}/.env.example" +chmod 0600 .env + +# 按 .env 注释配置 TERMFLOW_ADMIN_TOKEN、OPENCODE_SERVER_*、OPENCODE_AGENT_MCP_TOKEN、 +# DEEPSEEK_API_KEY,并将 TERMFLOW_RELEASE_IMAGE_TAG 设为 ${TAG} +docker compose --env-file .env -f compose.release.yaml up -d +curl -fsS http://127.0.0.1:8765/healthz +``` + +**方式二:从源码本地构建启动** + +```bash +cp .env.example .env +docker compose --env-file .env -f deploy/compose.yaml up -d --build +curl -fsS http://127.0.0.1:8765/healthz +``` + +### 2. 安装并注册 Linux 节点 A(需 `tmux 3.2+`) + +```bash +curl -fsSL \ + https://github.com/mcocdaa/TermFlow/releases/download/v0.1.0/install-termflow-node.sh \ + | bash + +termflow login \ + --server https://termflow.example.com \ + --code '' + +termflow new --name project-a +``` + +按 `Ctrl+B` 再按 `D` 可随时退出当前视图且后台保持运行,使用 `termflow attach project-a` 重新进入。 + +
+📦 Standalone Control Plane, Offline Tarball & Packaging Reference + +### 单容器 Control Plane B(仅终端转发,不含 Agent 运行时) + +当仅需远程终端而不需要 OpenCode Agent 时(`B 的 Web/API 通过宿主机 loopback 端口发布;A 主动连接 B,是连接 B 的客户端,不是 B 的网关;A 不开放端口`): + +```bash +mkdir -p data totp-secrets +docker network create termflow-net + +docker run -d --name termflow-control-plane \ + --restart unless-stopped \ + --network termflow-net \ + --publish 127.0.0.1:8765:8000 \ + --volume "$PWD/data:/app/data" \ + --volume "$PWD/totp-secrets:/app/totp-secrets" \ + --env TERMFLOW_ADMIN_TOKEN='<至少 32 字节的管理员 token>' \ + --env TERMFLOW_DATABASE_URL=sqlite+aiosqlite:////app/data/termflow.db \ + --env TERMFLOW_PUBLIC_BASE_URL=https://termflow.example.com \ + --env TERMFLOW_ALLOW_INSECURE_LOOPBACK="${TERMFLOW_ALLOW_INSECURE_LOOPBACK:-true}" \ + --env TERMFLOW_TOTP_AUTO_MASTER_KEY_FILE=/app/totp-secrets/totp-master-key \ + ghcr.io/mcocdaa/termflow-control-plane:v0.2.0 + +curl -fsS http://127.0.0.1:8765/healthz +``` + +离线镜像导入与 TOTP 重置命令: + +```bash +docker load -i termflow-control-plane.tar +docker exec termflow-control-plane termflow-control auth totp reset +``` + +### 容器化运行节点 A(Docker) + +先在 Web C 或通过 API(`POST /api/v1/enrollment-tokens`)生成一次性注册码,再将 `termflow-node` 加入与 B 相同的普通 bridge 网络启动: + +```bash +mkdir -p termflow-node-identity termflow-node-work + +docker run -d \ + --name termflow-node \ + --restart unless-stopped \ + --network termflow_default \ + --cap-drop ALL \ + --cap-add CHOWN \ + --cap-add DAC_OVERRIDE \ + --cap-add SETUID \ + --cap-add SETGID \ + --security-opt no-new-privileges:true \ + --read-only \ + --tmpfs /tmp \ + --volume "$PWD/termflow-node-identity:/home/termflow" \ + --volume "$PWD/termflow-node-work:/work" \ + --env TERMFLOW_SERVER=http://control-plane:8000 \ + --env TERMFLOW_CODE='<一次性注册码>' \ + --env TERMFLOW_ALLOW_INSECURE_HTTP=true \ + --env TERMFLOW_NEW=demo \ + ghcr.io/mcocdaa/termflow-node:v0.2.0 +``` + +进入容器内终端或执行诊断(需指定容器内 UID 1000 `termflow` 用户): + +```bash +docker exec --user termflow -it termflow-node termflow attach demo +docker exec --user termflow termflow-node termflow doctor +docker exec --user termflow termflow-node termflow status demo --json +``` + +### 构建版本与打包发布契约 + +- **版本号解析优先级**:`Git Tag > TERMFLOW_BUILD_VERSION > 0.2.0-dev.0` +- **发布工作流与制品**(支持 `workflow_dispatch`、`workflow_call`、`Actions artifact`、`GitHub Release`、`GHCR`): + - `Package A · Linux Node`(`termflow-node-linux-x86_64`,要求 `tmux 3.2`+,通过 `Path.cwd().as_uri()` 进行离线 wheel 安装) + - `Package B + Web C · Control Plane`(固定 `Node 22.23.2` 与 Python 3.12 运行时) + - `Package C · Native Clients`(Windows、Linux、macOS、Android 与 iOS Simulator 客户端,可在 [GitHub Releases](https://github.com/mcocdaa/TermFlow/releases) 下载) + +
+ +## 📚 文档导航 + +| 文档 | 说明 | +| --- | --- | +| [docs/architecture.md](docs/architecture.md) | 架构与进程边界:Node A、Control Plane B、Web C 与 OpenCode 的网络及权限模型 | +| [docs/protocol.md](docs/protocol.md) | 协议契约:Node A 与 Control Plane B 之间的版本化 WebSocket 消息协议 | +| [docs/security.md](docs/security.md) | 安全与隐私:TOTP 双因素认证、一次性配对码、Agent 四级风控与容器加固 | +| [docs/api-examples.md](docs/api-examples.md) | API 示例:节点注册、终端会话、窗格控制与 Agent 审批接口示例 | +| [docs/web-client.md](docs/web-client.md) | Web C 指南:桌面与移动端终端界面、扫码授权、主题切换与 `MobileKeyBar` | +| [docs/operations.md](docs/operations.md) | 部署与恢复:生产环境部署、版本升级、数据卷备份与 TOTP 恢复流程 | +| [docs/github-actions.md](docs/github-actions.md) | 构建与发布:CI/CD 流水线、多平台安装包构建矩阵与 GHCR 镜像发布 | +| [docs/troubleshooting.md](docs/troubleshooting.md) | 排障指南:`tmux` 独立 Socket、WebSocket 握手及容器权限常见问题排查 | +| [docs/superpowers/README.md](docs/superpowers/README.md) | 设计与实施记录:架构演进规格说明与工程实施档案 | + +## 📄 许可证 + +本项目基于 [MIT License](LICENSE) 开源。 diff --git a/README_EN.md b/README_EN.md new file mode 100644 index 0000000..f30809c --- /dev/null +++ b/README_EN.md @@ -0,0 +1,171 @@ +
+ +# TermFlow + +Resilient remote tmux terminal & 4-level guarded Agent Broker across Web, Mobile, and Native clients. + +[![Family: *Flow](https://img.shields.io/badge/family-*Flow-8A2BE2.svg)](https://github.com/mcocdaa) +[![CI](https://github.com/mcocdaa/TermFlow/actions/workflows/ci.yml/badge.svg)](https://github.com/mcocdaa/TermFlow/actions/workflows/ci.yml) +[![Release](https://img.shields.io/github/v/release/mcocdaa/TermFlow?display_name=tag&sort=semver)](https://github.com/mcocdaa/TermFlow/releases) +[![Python 3.12](https://img.shields.io/badge/python-3.12-blue.svg)](https://www.python.org/) +[![TypeScript 5.x](https://img.shields.io/badge/TypeScript-5.x-blue.svg)](https://www.typescriptlang.org/) +[![Docker Ready](https://img.shields.io/badge/docker-ready-blue.svg)](deploy/compose.yaml) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) + +[English](README.md) · [简体中文](README_CN.md) + +TermFlow Demo + +
+ +## ✨ Highlights + +- **Outbound-Only Node A (`Computer → Term → Window → Pane`)**: Computer A dials out over TLS/WSS to Control Plane B with zero inbound ports (`A 主动连接 B`, `A 不开放端口`); closing browsers or losing mobile connectivity never interrupts running `tmux` jobs. +- **Mobile-First Web & Native Terminal**: Responsive `xterm.js` workspace equipped with a tactile `MobileKeyBar` (`Esc`, `Tab`, `Ctrl`, `Alt`, arrow keys, and quick tmux chords) plus 7 curated geek themes. +- **4-Level Guarded Agent Broker (`OpenCode`)**: Replayable agent event streams with a 4-tier risk guard and mandatory human-in-the-loop approval before any write or critical terminal action executes. +- **Zero-Trust TOTP & One-Time Pairing**: Control Plane authentication protected by TOTP MFA, short-lived one-time node enrollment tokens, and device authorization via QR code or pairing code. +- **Multi-Platform Native Clients**: Unified Vue 3 + TypeScript + Tauri 2 client architecture shipping across Web, Windows, Linux, macOS, Android, and iOS Simulator. +- **Strict Control Plane Boundary**: Control Plane B authenticates, routes, and relays terminal streams without executing host shell commands, keeping Node A as the sole terminal authority. + +## 🚀 Quick Start + +### 1. Deploy Control Plane B + Web C + Agent Broker (Docker Compose) + +**Option A — Published Release Images (Recommended)** + +```bash +TAG=v0.2.0 +BASE="https://raw.githubusercontent.com/mcocdaa/TermFlow/${TAG}" +mkdir -p termflow && cd termflow +curl -fsSLO "${BASE}/deploy/compose.release.yaml" +curl -fsSLO "${BASE}/deploy/opencode-config.yaml" +curl -fsSLo .env "${BASE}/.env.example" +chmod 0600 .env + +# Configure TERMFLOW_ADMIN_TOKEN, OPENCODE_SERVER_*, OPENCODE_AGENT_MCP_TOKEN, +# DEEPSEEK_API_KEY, and set TERMFLOW_RELEASE_IMAGE_TAG=${TAG} in .env +docker compose --env-file .env -f compose.release.yaml up -d +curl -fsS http://127.0.0.1:8765/healthz +``` + +**Option B — Local Source Checkout** + +```bash +cp .env.example .env +docker compose --env-file .env -f deploy/compose.yaml up -d --build +curl -fsS http://127.0.0.1:8765/healthz +``` + +### 2. Enroll Linux Node A (`tmux 3.2+`) + +```bash +curl -fsSL \ + https://github.com/mcocdaa/TermFlow/releases/download/v0.1.0/install-termflow-node.sh \ + | bash + +termflow login \ + --server https://termflow.example.com \ + --code '' + +termflow new --name project-a +``` + +Detach anytime with `Ctrl+B` then `D` (`termflow attach project-a` to re-enter). + +
+📦 Standalone Control Plane, Offline Tarball & Packaging Reference + +### Standalone Control Plane Container (Terminal Only, Without Agent Runtime) + +When you only need remote terminal routing without the OpenCode Agent runtime (`B 的 Web/API 通过宿主机 loopback 端口发布;A 主动连接 B,是连接 B 的客户端,不是 B 的网关;A 不开放端口`): + +```bash +mkdir -p data totp-secrets +docker network create termflow-net + +docker run -d --name termflow-control-plane \ + --restart unless-stopped \ + --network termflow-net \ + --publish 127.0.0.1:8765:8000 \ + --volume "$PWD/data:/app/data" \ + --volume "$PWD/totp-secrets:/app/totp-secrets" \ + --env TERMFLOW_ADMIN_TOKEN='' \ + --env TERMFLOW_DATABASE_URL=sqlite+aiosqlite:////app/data/termflow.db \ + --env TERMFLOW_PUBLIC_BASE_URL=https://termflow.example.com \ + --env TERMFLOW_ALLOW_INSECURE_LOOPBACK="${TERMFLOW_ALLOW_INSECURE_LOOPBACK:-true}" \ + --env TERMFLOW_TOTP_AUTO_MASTER_KEY_FILE=/app/totp-secrets/totp-master-key \ + ghcr.io/mcocdaa/termflow-control-plane:v0.2.0 + +curl -fsS http://127.0.0.1:8765/healthz +``` + +For offline archive loading and TOTP recovery: + +```bash +docker load -i termflow-control-plane.tar +docker exec termflow-control-plane termflow-control auth totp reset +``` + +### Containerized Node A (Docker) + +Generate a one-time enrollment code via Web C or API (`POST /api/v1/enrollment-tokens`), then launch `termflow-node` on the shared bridge network: + +```bash +mkdir -p termflow-node-identity termflow-node-work + +docker run -d \ + --name termflow-node \ + --restart unless-stopped \ + --network termflow_default \ + --cap-drop ALL \ + --cap-add CHOWN \ + --cap-add DAC_OVERRIDE \ + --cap-add SETUID \ + --cap-add SETGID \ + --security-opt no-new-privileges:true \ + --read-only \ + --tmpfs /tmp \ + --volume "$PWD/termflow-node-identity:/home/termflow" \ + --volume "$PWD/termflow-node-work:/work" \ + --env TERMFLOW_SERVER=http://control-plane:8000 \ + --env TERMFLOW_CODE='' \ + --env TERMFLOW_ALLOW_INSECURE_HTTP=true \ + --env TERMFLOW_NEW=demo \ + ghcr.io/mcocdaa/termflow-node:v0.2.0 +``` + +Inspect or attach to the containerized node as UID 1000 (`termflow`): + +```bash +docker exec --user termflow -it termflow-node termflow attach demo +docker exec --user termflow termflow-node termflow doctor +docker exec --user termflow termflow-node termflow status demo --json +``` + +### Build Version & Packaging Contracts + +- **Version precedence**: `Git Tag > TERMFLOW_BUILD_VERSION > 0.2.0-dev.0` +- **Packaging workflows** (`workflow_dispatch`, `workflow_call`, `Actions artifact`, `GitHub Release`, `GHCR`): + - `Package A · Linux Node` (`termflow-node-linux-x86_64`, requires `tmux 3.2`+, offline wheelhouse via `Path.cwd().as_uri()`) + - `Package B + Web C · Control Plane` (pinned `Node 22.23.2` + Python 3.12 runtime) + - `Package C · Native Clients` (Windows, Linux, macOS, Android, iOS Simulator from [GitHub Releases](https://github.com/mcocdaa/TermFlow/releases)) + +
+ +## 📚 Documentation + +| Document | Description | +| --- | --- | +| [docs/architecture.md](docs/architecture.md) | System topology, process boundaries, and network security model between Node A, Control Plane B, Web C, and OpenCode | +| [docs/protocol.md](docs/protocol.md) | Versioned WebSocket & Pydantic message contracts between Node A and Control Plane B | +| [docs/security.md](docs/security.md) | TOTP MFA, one-time pairing, 4-level Agent Broker risk guard, and container hardening | +| [docs/api-examples.md](docs/api-examples.md) | REST & WebSocket API usage examples for enrollment, sessions, panes, and agent approvals | +| [docs/web-client.md](docs/web-client.md) | Web C & mobile terminal guide, QR pairing flow, themes, and `MobileKeyBar` | +| [docs/operations.md](docs/operations.md) | Production deployment, upgrades, volume backups, TOTP reset, and recovery procedures | +| [docs/github-actions.md](docs/github-actions.md) | CI/CD pipelines, release artifact matrix, and GHCR image publishing | +| [docs/troubleshooting.md](docs/troubleshooting.md) | Common diagnostics for `tmux` sockets, WebSocket handshakes, and container permissions | +| [docs/superpowers/README.md](docs/superpowers/README.md) | Architecture specs, design decisions, and implementation records | + +## 📄 License + +Released under the [MIT License](LICENSE).