yipjunkai/farwatch
GitHub: yipjunkai/farwatch
farwatch 通过端到端加密的终端镜像技术,让开发者能够从任何设备安全地远程访问和交互本地运行的 AI 编程助手。
Stars: 0 | Forks: 0
# farwatch
[](https://github.com/yipjunkai/farwatch/releases/latest)
[](https://scorecard.dev/viewer/?uri=github.com/yipjunkai/farwatch)
[](#-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, 可视化界面, 端到端加密, 终端工具, 网络流量审计, 请求拦截, 远程访问, 通知系统