yipjunkai/farwatch

GitHub: yipjunkai/farwatch

farwatch 通过端到端加密的终端镜像技术,让开发者能够从任何设备安全地远程访问和交互本地运行的 AI 编程助手。

Stars: 0 | Forks: 0

# farwatch [![Release](https://img.shields.io/github/v/release/yipjunkai/farwatch)](https://github.com/yipjunkai/farwatch/releases/latest) [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/yipjunkai/farwatch/badge)](https://scorecard.dev/viewer/?uri=github.com/yipjunkai/farwatch) [![License: MIT OR Apache-2.0](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue.svg)](#-license) **从任何设备访问你的 AI 编程助手。** 具备结构化 agent 事件的端到端加密终端镜像 —— 适用于 Claude Code、Aider、Copilot、Gemini 以及任何基于终端的 AI 工具。Rust 核心。零知识中继。可自托管。 ``` farwatch start claude # launches Claude Code in a PTY, prints a QR code ``` 用手机扫描二维码,你将获得一个实时的加密会话 —— 既包含原始终端镜像,也包含带有原生 UI 的结构化视图,用于显示工具调用、思考指示器和提示词。中间的中继只能看到密文。 ## 🧭 工作原理 ``` Your Machine Cloud Your Phone ┌──────────────────┐ ┌──────────────┐ ┌──────────────────┐ │ Claude Code │ │ Relay Server │ │ Structured View │ │ (real TUI) │◄── PTY ──│ (zero- │── E2E ───│ (native cards) │ │ │ │ knowledge) │ encrypted │ │ │ Desktop: Enter │ │ │ │ Terminal View │ │ to take over │ │ │ │ (raw PTY mirror) │ └──────────────────┘ └──────────────┘ └──────────────────┘ ``` **桌面端**通过接管模式获取真实的 Claude Code TUI。**手机端**获取结构化事件(思考、工具调用、文本)作为原生 UI 卡片,并带有终端视图切换功能。两者可以同时发送输入。中继只是一个哑管道 —— 它仅转发不透明的加密字节,永远看不到你的数据。 ## 📦 安装 ``` # Homebrew (macOS / Linux) brew install yipjunkai/farwatch/farwatch # Shell script curl -fsSL https://raw.githubusercontent.com/yipjunkai/farwatch/main/install.sh | sh # 从源码 cargo install --git https://github.com/yipjunkai/farwatch -p cli # Docker (仅限 relay server) docker pull ghcr.io/yipjunkai/farwatch:latest ``` ## 🚀 快速开始 ``` # 启动会话 (自动检测您的 AI 工具) farwatch start # 或者使用额外参数指定工具 farwatch start claude farwatch start aider --model sonnet ``` 从手机扫描二维码进行连接。在主机上: - **Enter** — 接管终端(你将直接在 Claude Code 中输入) - **双击 Esc** — 返回控制面板 - **q** — 退出会话 ### 手动附加(从另一个终端) ``` farwatch attach --pairing-uri "farwatch://pair?..." ``` ## 📱 你的手机端展示内容 对于支持结构化输出的工具(目前为 Claude Code),手机端会显示两个视图。 **结构化视图**(Claude Code 的默认视图): - 思考块(带有推理内容的紫色卡片) - 工具调用卡片(工具名称、参数) - 工具结果(输出,截断至 32KB) - 文本响应 - 轮次标记和忙碌指示器 - 带有麦克风/发送按钮的提示词栏(Telegram 风格) **终端视图**(通过 AppBar 按钮切换): - 原始 PTY 输出镜像 —— 与桌面端看到的完全一致 - 带有终端操作(Ctrl+C、方向键、粘贴等)的底部工作表 (bottom sheet) 这两个视图都会随着 Claude Code 的工作实时更新。结构化事件来自于追踪 Claude Code 的 `.jsonl` 会话日志(`~/.claude/projects/`),而不是通过解析终端输出得到的。 移动端客户端位于一个单独的仓库中:[**farwatch-mobile**](https://github.com/yipjunkai/farwatch-mobile) (Flutter, iOS + Android)。 ## 🤖 支持的 AI 工具 按优先级顺序自动检测: | 工具 | 结构化事件 | 备注 | | ---------------------------------- | :---------------: | ---------------------------------------------- | | **Claude Code** (Anthropic) | 是 | 追踪 JSONL 会话日志以实现原生移动端 UI | | **OpenCode** (开源) | 仅限 PTY | | | **GitHub Copilot CLI** (Microsoft) | 仅限 PTY | | | **Gemini CLI** (Google) | 仅限 PTY | | | **Aider** (开源) | 仅限 PTY | | | PATH 中的任何命令 | 仅限 PTY | `farwatch start my-tool` | 不支持结构化输出的工具通过 PTY 镜像工作 —— 手机端将显示一个终端模拟器。 ## 🏗️ 架构 ### 双通道设计 对于 Claude Code,主机通过同一个加密通道发送两个并行流: ``` Claude Code (PTY) ├── Raw PTY bytes ──→ SecureMessage::PtyOutput ──→ Phone terminal view │ └── ~/.claude/projects//.jsonl └── JSONL watcher (notify/kqueue) ──→ SecureMessage::AgentEvent ──→ Phone structured view ``` JSONL 监视器使用文件系统通知来追踪 Claude Code 的会话日志文件。它解析助手消息、工具调用、工具结果、思考块以及轮次完成状态(`stop_reason: "end_turn"`)。这些事件作为 `AgentEvent` 变体通过同一个端到端加密通道发出。 手机端输入通过 `AgentCommand::Prompt` 回传,并以按键形式(`text + \r`)注入到 PTY 中。工具批准发送 `y\r`,拒绝发送 `n\r`。 ### 接管模式 在主机控制面板上按 Enter 键可直接接管 PTY: 1. TUI 控制面板挂起,终端切换到 raw 模式 2. PTY 输出显示在你的屏幕上(进入时通过 Ctrl+L 重绘) 3. 你的键盘输入直接进入 PTY 4. 终端尺寸调整会被转发到 PTY 5. PTY 输出同时通过中继流向手机 6. 双击 Esc 返回控制面板 桌面端和手机端都可以随时发送输入。这里没有加锁机制 —— PTY 会处理来自这两个源的输入。 ### 协议层 所有 WebSocket 消息通过三层结构进行 MessagePack 编码: **中继层**(`RelayMessage`):Register, Registered, Route, PeerStatus, Ping/Pong, Error **E2E 层**(`Route` payload 中的 `PeerFrame`):Handshake, HandshakeConfirm, Secure (AES-GCM sealed), KeepAlive **应用层**(`Secure` 中的 `SecureMessage`): - 终端: `PtyInput`, `PtyOutput`, `Resize` - Agent: `AgentEvent`, `AgentCommand` - 会话: `Heartbeat`, `VersionNotice`, `Notification`, `SessionEnded`, `ReadOnly` - 语音: `VoiceCommand` ## 📁 项目结构 ``` farwatch/ ├── crates/ │ ├── cli/ # User-facing binary (farwatch): PTY, JSONL watcher, TUI, takeover │ ├── relay/ # Zero-knowledge relay server │ └── protocol/ # Shared types, crypto (X25519 + AES-256-GCM), pairing primitives ├── Dockerfile # Relay image for self-hosters ├── install.sh # Shell installer (downloads a release binary) └── .github/ # Release + relay-image publish workflows ``` | Crate | 描述 | | ---------- | --------------------------------------------------------------------------------- | | `cli` | 面向用户的二进制文件 (`farwatch`),PTY 管理,JSONL 监视器,TUI,接管模式 | | `relay` | 零知识中继服务器 | | `protocol` | 共享协议类型,加密 (X25519 + AES-256-GCM),配对原语 | Flutter 移动应用 (iOS + Android) 是主要的移动端客户端 —— 请参见 [farwatch-mobile](https://github.com/yipjunkai/farwatch-mobile)。 ## 🔒 安全性 Farwatch 的设计确保 **除了你和已连接的设备外,没有任何人能读取你的终端数据** —— 无论是我们、中继运营者,还是网络上的任何人。 ### 威胁模型 中继服务器被假定为 **诚实但好奇 (honest-but-curious)**:它会忠实地转发消息,但也可能试图读取消息内容。所有终端数据在到达中继之前都已进行端到端加密,因此被入侵或恶意的中继除了元数据(会话 ID、对等节点角色、消息时间和大小)之外,无法获取任何信息。 ### 端到端加密 每个会话都会在主机(你的开发机器)和客户端(你的手机/平板电脑/其他终端)之间建立一个唯一的加密通道: 1. **密钥交换**:双方各自生成一个临时的 X25519 密钥对。公钥通过中继在 `Handshake` 消息中进行交换。 2. **密钥派生**:双方通过 Diffie-Hellman 计算出共享密钥,然后使用 HKDF-SHA256 派生出两个 256 位对称密钥(每个方向一个),其中会话 ID 作为盐值 (salt),`farwatch/v1/channel-keys` 作为 info 字符串。 3. **加密**:所有终端 I/O 和 agent 事件在传输前都会使用 AES-256-GCM 进行加密。每一帧都带有一个单调递增的 nonce。 4. **密钥确认**:密钥派生后,双方交换基于握手记录的 HMAC-SHA256,以此证明每个对等节点都持有与其公开的公钥相对应的私钥。 ### 重放和重排保护 每个加密帧都包含一个严格单调递增的 64 位 nonce。接收方会拒绝任何 nonce 小于或等于上一个已接受 nonce 的帧。 ### 身份验证 配对 URI(以二维码形式显示)包含了主机公钥的 SHA-256 指纹。客户端在连接时会验证此指纹,从而检测出中继的任何中间人替换行为。 ### 零知识中继 中继服务器只能看到会话 ID、对等节点角色、消息大小和时间。它 **永远** 看不到明文终端内容、键盘输入、agent 事件、公钥或加密密钥。中继无法解密、修改或伪造消息 —— 任何篡改都会被 AES-GCM 身份验证检测到。 ### 加密原语 | 用途 | 算法 | 备注 | | -------------------- | -------------------------------- | ------------------------------------------------ | | 密钥交换 | X25519 | 每个会话生成临时密钥对 | | 密钥派生 | HKDF-SHA256 | 会话 ID 作为盐值,域分离的 info 字符串 | | 认证加密 | AES-256-GCM | 使用单调 nonce 进行逐帧加密 | | 密钥确认 | HMAC-SHA256 | 基于握手记录的 MAC | | 指纹 | SHA-256 (截断) | 公钥哈希的前 8 字节 | | 静态加密 | AES-256-GCM | 每个文件使用随机 nonce,机器本地密钥 | | Nonce 构造 | 4 个零字节 + 8 字节大端序计数器 | 由 64 位计数器生成 96 位 nonce | ### farwatch 无法防范的威胁 - **被入侵的端点**:如果你的机器或手机被入侵,攻击者将有权访问解密后的会话。 - **流量分析**:中继可以看到消息的时间和大小,从而暴露活动模式。 - **拒绝服务**:恶意中继可以丢弃或延迟消息(但无法读取或伪造内容)。 ## 🖥️ 自托管 运行你自己的中继服务器 —— 无需账户、API 密钥或控制 API: ``` # 从源码 cargo run -p relay -- --bind 0.0.0.0:8080 # 使用 Docker docker run -p 8080:8080 ghcr.io/yipjunkai/farwatch:latest ``` 将 CLI 指向你的中继: ``` FARWATCH_URL=ws://your-server:8080/ws farwatch start ``` ## 🛠️ 开发 ``` # 在本地运行 relay server cargo run -p relay -- --bind 0.0.0.0:8080 # 针对本地 relay 运行 CLI FARWATCH_URL=ws://127.0.0.1:8080/ws cargo run -p cli -- start # 结合托管服务功能运行 (auth, device flow) FARWATCH_URL=ws://127.0.0.1:8080/ws cargo run -p cli --features hosted -- start # 运行测试 cargo test ``` ### 功能开关 (Feature flags) | 构建命令 | 包含身份验证? | 使用场景 | | -------------------------------------- | -------------- | -------------------------- | | `cargo build -p cli` | 否 | 自托管 / 贡献者 | | `cargo build -p cli --features hosted` | 是 | 托管服务用户 | AI 工具会在你当前的工作目录中启动,而不是仓库目录。要从其他地方运行开发版 CLI,请传入 `--manifest-path /path/to/farwatch/Cargo.toml`。 ## 🤔 为什么会有 farwatch 我希望能从手机上查看和引导 Claude Code,同时无需将我的终端、提示词或代码交给第三方服务器。因此,farwatch 对所有内容进行端到端加密,并将中继视为一个只能看到密文的哑管道 —— 加密部分是开源的(`protocol` crate),专门为了让这一说法可被审计。 自从它被开发出来后,第一方远程控制功能已在 Claude Code、Codex 和 Copilot CLI 中推出,因此 farwatch 不再是唯一能做到这一点的工具。但在那些工具无法覆盖的场景中,它依然有用:支持 **任何** 终端工具而不是单一供应商、完全可自托管,并且端到端加密,不在任何人的服务器上存储任何数据。现在我将它作为一个个人工具而不是产品来维护。 ## 🤝 贡献 这是一个个人项目,没有积极的维护承诺 —— 欢迎提交 issue 和 PR,但可能无法得到及时回复。如果你想在此基础上进行开发,鼓励你 fork 该项目(许可证允许这样做)。 ## 📄 许可证 根据你的选择,在 [MIT](LICENSE-MIT) 或 [Apache 2.0](LICENSE-APACHE) 双重许可下授权。 除非你明确声明,否则根据 Apache-2.0 许可证的定义,你为包含在 **farwatch** 中而有意提交的任何贡献,都将按上述方式获得双重许可,没有任何附加的条款或条件。
标签:AI编程助手, Rust, 可视化界面, 端到端加密, 终端工具, 网络流量审计, 请求拦截, 远程访问, 通知系统