Skip to content

Commit ed456f3

Browse files
docs(deploy): 新增个人 VPS 反代 TLS 部署指南 / add personal VPS reverse-proxy TLS guide
Co-authored-by: Yales Peter <noisystreet@users.noreply.github.com>
1 parent cddc28f commit ed456f3

6 files changed

Lines changed: 228 additions & 2 deletions

File tree

README-en.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ cargo tauri build
147147
| [docs/en/CLI.md](docs/en/CLI.md) | Subcommands, HTTP routes, deb | [zh](docs/命令行与路由.md) |
148148
| [docs/en/CLI_CONTRACT.md](docs/en/CLI_CONTRACT.md) | `chat` exit codes, **`--output json`** | [zh](docs/命令行契约.md) |
149149
| [docs/en/DEBUG.md](docs/en/DEBUG.md) | Logging, `doctor`, `GET /web-ui`, … | [zh](docs/调试指南.md) |
150+
| [docs/个人VPS部署指南.md](docs/个人VPS部署指南.md) | Personal VPS: loopback `serve` + TLS reverse proxy + Bearer (Chinese) ||
150151
| [docs/en/TESTING.md](docs/en/TESTING.md) | Tests, pre-commit, audits | [zh](docs/测试指南.md) |
151152
| [docs/基准测试规划.md](docs/基准测试规划.md) | **`bench`** roadmap & benchmarks ||
152153
| [benchmark/README.md](benchmark/README.md) | HumanEval convert/run/smoke ||
@@ -184,6 +185,7 @@ Other **`CM_*`** (including **`CM_TUI_CONVERSATION_ID`**, skills, staged plannin
184185
- **Listen**: default **`127.0.0.1`**; **`0.0.0.0`** needs **`web_api_bearer_token`** or an explicit insecure switch ([docs/en/CONFIGURATION.md](docs/en/CONFIGURATION.md)).
185186
- **Web API**: embedded default **`web_api_require_bearer = false`****`serve`** may start without a shared secret; with **`true`**, a non-empty **`CM_WEB_API_BEARER_TOKEN`** (or TOML **`web_api_bearer_token`**) is required before start. When the token is non-empty, the Bearer layer is mounted; send **`Authorization: Bearer …`** or **`X-API-Key: …`**. The UI may store **`localStorage["crabmate-api-bearer-token"]`**. For exposed or untrusted networks, prefer **`web_api_require_bearer = true`** plus a configured secret.
186187
- **Other**: Web sidebar **Settings** needs **Save all** to persist in the browser; workspace must stay under allowed roots (path checks: [docs/en/CONFIGURATION.md](docs/en/CONFIGURATION.md)). Debug env vars and **`GET /web-ui`**: [docs/en/DEBUG.md](docs/en/DEBUG.md).
188+
- **Personal VPS (reverse-proxy TLS)**: walkthrough (Chinese) in [docs/个人VPS部署指南.md](docs/个人VPS部署指南.md) (**`127.0.0.1` + `CM_WEB_API_BEARER_TOKEN` + Caddy/Nginx**).
187189

188190
## Project structure
189191

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ cargo tauri build
147147
| [docs/命令行与路由.md](docs/命令行与路由.md) | 子命令、HTTP 路由、deb 打包 | [en](docs/en/CLI.md) |
148148
| [docs/命令行契约.md](docs/命令行契约.md) | `chat` 退出码与 **`--output json`** | [en](docs/en/CLI_CONTRACT.md) |
149149
| [docs/调试指南.md](docs/调试指南.md) | 日志、`doctor``GET /web-ui`| [en](docs/en/DEBUG.md) |
150+
| [docs/个人VPS部署指南.md](docs/个人VPS部署指南.md) | 个人自用:本机 `serve` + TLS 反代 + Bearer ||
150151
| [docs/测试指南.md](docs/测试指南.md) | 测试、pre-commit、审计命令 | [en](docs/en/TESTING.md) |
151152
| [docs/基准测试规划.md](docs/基准测试规划.md) | **`bench`** 规划与开源基准衔接 ||
152153
| [benchmark/README.md](benchmark/README.md) | HumanEval 转换、执行与冒烟 ||
@@ -184,6 +185,7 @@ cargo tauri build
184185
- **监听**:默认 **`127.0.0.1`**;监听 **`0.0.0.0`****`web_api_bearer_token`** 或显式不安全开关(见 [docs/配置说明.md](docs/配置说明.md))。
185186
- **Web API**:嵌入默认 **`web_api_require_bearer = false`**,允许无共享密钥启动 **`serve`**;若设为 **`true`**,则启动前须配置非空 **`CM_WEB_API_BEARER_TOKEN`**(或 TOML **`web_api_bearer_token`**)。密钥非空时会挂载 Bearer 层,请求须带 **`Authorization: Bearer …`****`X-API-Key: …`**。前端可存 **`localStorage["crabmate-api-bearer-token"]`**。对外或不可信网络建议 **`web_api_require_bearer = true`** 并配置密钥。
186187
- **其它**:Web 侧栏「设置」须 **「保存全部」** 才写入浏览器;工作区须在允许根内(路径校验见 [docs/配置说明.md](docs/配置说明.md))。调试变量与 **`GET /web-ui`**[docs/调试指南.md](docs/调试指南.md)
188+
- **个人 VPS(反代 TLS)**:见 [docs/个人VPS部署指南.md](docs/个人VPS部署指南.md)**`127.0.0.1` + `CM_WEB_API_BEARER_TOKEN` + Caddy/Nginx**)。
187189

188190
## 项目结构
189191

docs/en/CONFIGURATION.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Default settings are merged from seven embedded TOML fragments under **`config/`
1111
- **Typically hot-reloaded**: **`api_base`**, **`model`**, **`llm_http_auth_mode`**, **`llm_reasoning_split`**, **`llm_bigmodel_thinking`**, **`llm_kimi_thinking_disabled`**, **`thinking_avoid_echo_system_prompt`**, **`thinking_avoid_echo_appendix` / `thinking_avoid_echo_appendix_file`** (resolved appendix text), **`temperature` / `llm_seed`**, timeouts/retries, **`run_command`** allowlist, **`http_fetch_allowed_prefixes`**, **`workspace_allowed_roots`**, **`web_api_bearer_token`** (handler-side check only; see below), **`web_audit_log_write_tools`**, **`web_audit_trust_x_forwarded_for`** (write-tool audit and optional **`X-Forwarded-For`** trust), **`mcp_*`**, **`[tool_registry]`** fields (outer HTTP walls, parallel wall overrides, deny/inline/write-effect lists), **`system_prompt_file` re-read**, context/planning keys (implementation: **`apply_hot_reload_config_subset`**). **`system`→`user` folding** for MiniMax follows **`model` / `api_base`** on the next request after reload (not an `AgentConfig` field).
1212
- **Not hot-reloaded**: **`conversation_store_sqlite_path`** (SQLite opened at startup—change path requires **`serve` restart**). **`reqwest::Client`** is not rebuilt; **`api_timeout_secs`** may lag on pooled idle connections.
1313
- **`API_KEY`**: Still **environment only**; hot reload does not read secret files. After changing **`API_KEY`**, re-**export** and **`/config reload`** (or restart) for **`llm_http_auth_mode=bearer`** consistency.
14-
- **Web API auth layer**: Embedded default **`web_api_require_bearer=false`**: **`serve`** may start without **`web_api_bearer_token`** / **`CM_WEB_API_BEARER_TOKEN`**. After a successful start, if the token is non-empty, the auth middleware is mounted for the process lifetime; clients send **`Authorization: Bearer <same secret>`** or **`X-API-Key: <same secret>`** (either). Set **`web_api_require_bearer=true`** (or **`CM_WEB_API_REQUIRE_BEARER=1`**) to **refuse startup** until a non-empty shared secret is configured. Hot reload **does not** add/remove the layer—switching between “no token” and “token” requires **`serve` restart**. Hot reload still updates the secret string used inside handlers when the layer exists.
14+
- **Web API auth layer**: Embedded default **`web_api_require_bearer=false`**: **`serve`** may start without **`web_api_bearer_token`** / **`CM_WEB_API_BEARER_TOKEN`**. After a successful start, if the token is non-empty, the auth middleware is mounted for the process lifetime; clients send **`Authorization: Bearer <same secret>`** or **`X-API-Key: <same secret>`** (either). Set **`web_api_require_bearer=true`** (or **`CM_WEB_API_REQUIRE_BEARER=1`**) to **refuse startup** until a non-empty shared secret is configured. Hot reload **does not** add/remove the layer—switching between “no token” and “token” requires **`serve` restart**. Hot reload still updates the secret string used inside handlers when the layer exists. For a **personal VPS + TLS reverse proxy** walkthrough (Chinese), see **`docs/个人VPS部署指南.md`**.
1515
- **Write-tool audit (structured logs)**: When **`web_audit_log_write_tools`** defaults **on**, each successful **non-readonly** built-in tool emits one **`info`** line with **`target=crabmate::audit_write_tool`** (timestamp ms, `job_id`, scope id, **`http`** vs **`scheduled`**, `client_ip` / `peer_ip`, **`bearer_fp`** as the first 12 hex chars of SHA-256 of the shared secret when the request’s **`Authorization` / `X-API-Key`** matches, otherwise **`-`**, tool name, redacted **`args_preview`**). **No** raw secrets in log text. Non-Web entrypoints (CLI, bench) do not emit these lines. **`web_audit_trust_x_forwarded_for`** (default **off**): when **on**, `client_ip` prefers the first hop in **`X-Forwarded-For`**; enable only behind a **trusted** reverse proxy.
1616
- **Secrets in memory**: **`web_api_bearer_token`** and **`web_search_api_key`** are **secrecy `SecretString`** in [`AgentConfig`](DEVELOPMENT.md); **`Debug` / structured logs** avoid plaintext; use **`ExposeSecret::expose_secret()`** (re-exported from `config`). **`API_KEY`** is not part of `AgentConfig`.
1717

docs/个人VPS部署指南.md

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
# 个人 VPS 部署指南(本机 HTTP + 反向代理 TLS + Bearer)
2+
3+
本文描述一种适合**个人自用**的部署方式:**CrabMate 只监听本机回环地址**(默认行为),由前面的 **Caddy(或 Nginx)终止 TLS** 并反向代理到本机端口;同时启用 **Web API 共享密钥(Bearer / `X-API-Key`**,避免进程在公网暴露面上裸奔。
4+
5+
> **适用范围**:单用户、单实例、你完全信任 VPS 与工作区内容。多租户、水平扩展、按用户审计等不在本文范围;参见 **`docs/改进建议.md`****`docs/未来规划功能.md`**
6+
7+
---
8+
9+
## 1. 架构与数据流
10+
11+
```
12+
浏览器 --HTTPS(443)--> Caddy/Nginx --HTTP(127.0.0.1:8080)--> crabmate serve
13+
```
14+
15+
- **TLS**:由反向代理处理;CrabMate 进程内**不提供**内置 HTTPS(见 **`README.md` → 部署与安全**)。
16+
- **鉴权**:受保护路由需携带与配置一致的 **`Authorization: Bearer …`****`X-API-Key: …`**(与 **`docs/配置说明.md`**「Web API 鉴权层」一致)。浏览器侧可将同一密钥写入 **`localStorage["crabmate-api-bearer-token"]`**,与 Web UI 侧栏「设置」中的 API 共享密钥一致。
17+
- **上游大模型**:调用厂商仍使用环境变量 **`API_KEY`**(或 Web 侧栏 `client_llm`,仅存浏览器);****把真实密钥写入仓库或本文示例。
18+
19+
---
20+
21+
## 2. 前置条件
22+
23+
|| 说明 |
24+
|----|------|
25+
| 操作系统 | 常见 Linux(Debian/Ubuntu 等);下文以 **systemd** + **Caddy** 为例。 |
26+
| 域名(推荐) |**A 记录** 指向 VPS 公网 IP,便于 **Let’s Encrypt** 自动证书。仅用 IP 访问时,自动公网证书不便签发,需自签或云厂商证书,浏览器会告警。 |
27+
| 二进制与前端 |**`cargo build --release`**,且 **`cd frontend && trunk build --release`** 后存在 **`frontend/dist`**(与 **`README.md` → 编译运行与打包** 一致)。 |
28+
| 防火墙 | 计划只对外开放 **443**(及你管理用的 **22** SSH);**不要**把 CrabMate 的 **8080** 直接暴露到公网。 |
29+
30+
---
31+
32+
## 3. 配置 CrabMate(Bearer + 仅本机监听)
33+
34+
### 3.1 推荐:环境变量(避免密钥进 TOML 文件)
35+
36+
**`systemd` 单元**或 shell 启动脚本中设置(示例均为占位符):
37+
38+
```bash
39+
export CM_WEB_API_REQUIRE_BEARER=1
40+
export CM_WEB_API_BEARER_TOKEN='YOUR_LONG_RANDOM_SECRET'
41+
# 可选:未使用 `serve --host` 时,绑定地址也可用环境变量覆盖
42+
export CM_HTTP_HOST=127.0.0.1
43+
```
44+
45+
生成强随机密钥示例(在 VPS 上执行一次即可):
46+
47+
```bash
48+
openssl rand -base64 32
49+
```
50+
51+
### 3.2 或在 TOML 中配置
52+
53+
**`[agent]`** 段(见 **`config/default_config.toml`****`docs/配置说明.md`**):
54+
55+
```toml
56+
[agent]
57+
web_api_require_bearer = true
58+
web_api_bearer_token = "YOUR_LONG_RANDOM_SECRET"
59+
```
60+
61+
仍建议 **`web_api_bearer_token` 仅通过本地权限控制的可写配置或密钥管理注入**,不要提交到 Git。
62+
63+
### 3.3 启动命令
64+
65+
仅本机监听(**默认即为 `127.0.0.1`**,显式写出更清晰):
66+
67+
```bash
68+
/path/to/crabmate serve --host 127.0.0.1 --port 8080
69+
```
70+
71+
端口可改;若修改,下文 Caddy **`reverse_proxy`** 目标端口需一致。
72+
73+
> **`web_api_require_bearer = true`** 时,若启动时仍无有效非空密钥,**`serve` 会拒绝启动**(含仅监听 `127.0.0.1`)。详见 **`docs/配置说明.md`**
74+
75+
---
76+
77+
## 4. 使用 Caddy 反向代理与 HTTPS
78+
79+
### 4.1 安装 Caddy
80+
81+
[Caddy 官方安装文档](https://caddyserver.com/docs/install) 使用发行版包或官方仓库安装。
82+
83+
### 4.2 `Caddyfile` 示例
84+
85+
**`crab.example.com`** 换成你的域名:
86+
87+
```
88+
crab.example.com {
89+
encode zstd gzip
90+
91+
# 上传附件:`serve` 侧受保护路由体上限约 220MiB(见源码 `PROTECTED_API_BODY_LIMIT_BYTES`),反代勿过小
92+
request_body {
93+
max_size 256MB
94+
}
95+
96+
reverse_proxy 127.0.0.1:8080 {
97+
# 流式 SSE:降低缓冲,避免首包延迟或连接被长时间挂起时行为异常
98+
flush_interval -1
99+
}
100+
}
101+
```
102+
103+
启用配置并重载(命令因安装方式略有差异,常见为):
104+
105+
```bash
106+
sudo caddy validate --config /etc/caddy/Caddyfile
107+
sudo systemctl reload caddy
108+
```
109+
110+
浏览器访问 **`https://crab.example.com`** 即可打开 Web UI;首次需在侧栏填写 **`CM_WEB_API_BEARER_TOKEN` 相同** 的共享密钥并保存(或依赖已写入的 **`localStorage`**)。
111+
112+
### 4.3 可选:Nginx 要点
113+
114+
若使用 Nginx,需自行管理证书(如 **certbot**)。对流式接口建议关闭代理缓冲并放宽读超时,例如(片段,勿直接复制路径):
115+
116+
```nginx
117+
proxy_http_version 1.1;
118+
proxy_set_header Host $host;
119+
proxy_set_header X-Real-IP $remote_addr;
120+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
121+
proxy_set_header X-Forwarded-Proto $scheme;
122+
proxy_buffering off;
123+
proxy_read_timeout 86400s;
124+
client_max_body_size 256m;
125+
```
126+
127+
具体 **`server` / `location`** 结构以你站点为准。
128+
129+
---
130+
131+
## 5. 使用 systemd 托管 `serve`(推荐)
132+
133+
**非 root** 用户 **`crabmate`** 为例(请按需替换路径与用户):
134+
135+
`/etc/systemd/system/crabmate.service`
136+
137+
```ini
138+
[Unit]
139+
Description=CrabMate Web (loopback, behind reverse proxy)
140+
After=network-online.target
141+
Wants=network-online.target
142+
143+
[Service]
144+
Type=simple
145+
User=crabmate
146+
Group=crabmate
147+
WorkingDirectory=/home/crabmate/crabmate
148+
Environment=CM_WEB_API_REQUIRE_BEARER=1
149+
Environment=CM_WEB_API_BEARER_TOKEN=YOUR_LONG_RANDOM_SECRET
150+
Environment=CM_HTTP_HOST=127.0.0.1
151+
Environment=RUST_LOG=info
152+
ExecStart=/home/crabmate/bin/crabmate serve --host 127.0.0.1 --port 8080
153+
Restart=on-failure
154+
RestartSec=5
155+
156+
# 安全加固(可按需收紧)
157+
NoNewPrivileges=true
158+
PrivateTmp=true
159+
160+
[Install]
161+
WantedBy=multi-user.target
162+
```
163+
164+
加载并启动:
165+
166+
```bash
167+
sudo systemctl daemon-reload
168+
sudo systemctl enable --now crabmate.service
169+
sudo systemctl status crabmate.service
170+
```
171+
172+
> **不要把 `CM_WEB_API_BEARER_TOKEN` 写进世界可读的文件**若无法接受:改用 **`systemd``EnvironmentFile=`** 指向 **`chmod 600`** 的文件,或发行版提供的 **secrets** 机制。
173+
174+
---
175+
176+
## 6. 防火墙与安全组
177+
178+
- **云安全组 / 本机 `ufw`**:仅允许 **22**(SSH,若需要)、**443**(HTTPS)。**禁止**从公网访问 **8080**
179+
- **SSH**:优先密钥登录,关闭密码登录(按发行版文档配置)。
180+
- **信任模型**:Bearer 为**共享密钥**,泄露即等同他人可调用你的 API;与 **`docs/待办清单.md`** 中「HTTP 身份模型」说明一致。
181+
- **工作区**:仅挂载你愿意让 Agent 读写的目录;工具链仍可能执行 **`run_command`** 等能力,参见 **`README.md`****`docs/工具说明.md`**
182+
183+
---
184+
185+
## 7. 验收与排障
186+
187+
| 现象 | 建议 |
188+
|------|------|
189+
| **`serve` 启动失败** | 检查 **`CM_WEB_API_REQUIRE_BEARER=1`** 时是否已设置非空 **`CM_WEB_API_BEARER_TOKEN`**(或 TOML 等价项)。 |
190+
| **浏览器 401 / 无法加载会话** | 侧栏密钥是否与服务器一致;是否带 **`Authorization`/`X-API-Key`**(前端存 **`crabmate-api-bearer-token`**)。 |
191+
| **流式对话中断** | 检查反代 **`flush_interval` / `proxy_buffering` / `proxy_read_timeout`**;中间设备超时。 |
192+
| **上传失败** | 调大 Caddy **`request_body`** 或 Nginx **`client_max_body_size`**(建议与上节 **256MB** 量级一致,且不超过你信任的磁盘与带宽)。 |
193+
| **证书失败** | 域名 DNS 是否指向本机;80/443 是否对 ACME 开放;查看 Caddy 日志。 |
194+
195+
健康检查:**`GET /health`****`GET /status`** 挂在系统路由上,**不要求** Web API Bearer(与 **`src/web/server.rs`** 路由分层一致);探活可直接:
196+
197+
```bash
198+
curl -fsS https://crab.example.com/health
199+
```
200+
201+
**`POST /chat`****`/workspace/*`** 等受保护接口仍须携带 Bearer(或浏览器侧已配置同一密钥)。
202+
203+
---
204+
205+
## 8. 备份与升级
206+
207+
- **会话库**:默认在工作区 **`.crabmate/conversations.db`**(见 **`README.md`**);定期备份该文件或整个工作区目录。
208+
- **升级**:替换 **`crabmate` 二进制**后重启 **`systemd`**;若前端有变更,重新 **`trunk build --release`** 并部署 **`frontend/dist`**(与 **`scripts/package-release.sh`** 发行包流程一致时可一并打包)。
209+
210+
---
211+
212+
## 9. 相关文档
213+
214+
- **`README.md`**:部署与安全、环境变量摘要。
215+
- **`docs/配置说明.md`****`CM_WEB_API_*`****`web_api_require_bearer`**、热重载与中间件关系。
216+
- **`docs/命令行与路由.md`****`serve`** 参数与 HTTP 路由。
217+
- **`docs/调试指南.md`**:日志 **`RUST_LOG`****`GET /web-ui`** 等。
218+
219+
---
220+
221+
*本文随部署实践增补;与具体发行版包名、路径以你环境为准。*

docs/中英文文档对照.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
| `docs/改进建议.md` | `` |
3333
| `docs/分阶段规划单步设计.md` | `` |
3434
| `docs/界面打磨待办.md` | `` |
35+
| `docs/个人VPS部署指南.md` | `` |
3536
| `docs/中英文文档对照.md` | `` |
3637

3738
## 缺失配对(英文有、中文缺失)

0 commit comments

Comments
 (0)