bookworm-boot/lessons-learned.md
bookworm 5e0ff18aa1 feat: Bookworm Portable v1.5 — 8 fixes (P0 NDA + P1 banners + P2 perf)
- P1: Banner v1.3→v1.5, Hooks 29→34
- P1: 卸载脚本补删 更新Bookworm.lnk
- P1: git stash pop 安全检查
- P2: Playwright 检测改用 npm list
- P2: 代理端口扫描 500ms async 超时

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 23:34:27 +08:00

207 lines
8.0 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Bookworm Portable 项目经验与避坑指南
> 2026-04-01 | 从可行性评估到 E2E 验证的完整实战记录
---
## 一、项目概要
**目标**: 让任意 Windows 电脑通过一行命令激活 Bookworm (97 Skills + 18 Agents + 28 Hooks) + 中转站 API
**最终架构**: Gitea 私有仓 (code.letcareme.com) + AES-256 加密凭证 + 自动代理检测 + 一键安装脚本
**耗时**: 1 天 (评估 → 设计 → 三路专家审查 → P0/P1 修复 → 部署 → E2E 验证 → 踩坑修复)
---
## 二、关键决策与转折点
### 决策 1: USB → Git 方案切换
- **初始方案**: U盘存储全部系统文件 + 加密凭证
- **问题**: Windows NTFS Junction 跨驱动器失败 (实测 Accessible: False)、USB IOPS 瓶颈 (hook 延迟 200-600ms)、体积 2.1GB
- **最终方案**: Git + 模板渲染USB 变为可选 (纯云端即可)
- **教训**: 方案评估阶段多投入时间做实测,不要假设跨驱动器操作能工作
### 决策 2: GitHub → 国内 Gitea
- **问题**: 国内无 VPN 无法访问 GitHub
- **方案**: 阿里云 ECS 自建 Gitea国内直连
- **教训**: 面向国内用户的工具链必须全程可达,不能依赖海外服务
### 决策 3: bookworm.letcareme.com → code.letcareme.com
- **问题**: bookworm.letcareme.com 已被 Bookworm Web 项目 Nginx 配置占用
- **解决**: 换用 code.letcareme.com申请新证书
- **教训**: 部署前检查域名占用,`grep -r "域名" /etc/nginx/` 先扫一遍
---
## 三、必踩的坑 (按严重度排序)
### 坑 1: Claude Code 国内启动检查 (CRITICAL)
```
现象: "Failed to connect to api.anthropic.com: ERR_BAD_REQUEST"
原因: Claude Code 启动时硬编码检查 api.anthropic.com与 ANTHROPIC_BASE_URL 无关
影响: 国内无代理 = Claude Code 完全无法启动
解决: 必须有代理/VPN且必须设 HTTPS_PROXY 环境变量 (Node.js 不读系统代理)
耗时: 2 小时排查
```
### 坑 2: Node.js 不读 Windows 系统代理 (CRITICAL)
```
现象: PowerShell (Invoke-WebRequest) 能通Claude Code 不通
原因: Node.js 只读 HTTPS_PROXY/HTTP_PROXY 环境变量,不读 Windows IE/系统代理设置
解决: 用 [System.Net.WebRequest]::DefaultWebProxy.GetProxy() 发现实际代理端口
然后 $env:HTTPS_PROXY = "http://127.0.0.1:端口"
教训: 系统代理 ≠ 进程代理Node.js 应用必须显式设环境变量
```
### 坑 3: settings.json 中 ${VAR} 不展开 (CRITICAL)
```
现象: "API Error: Invalid URL"
原因: settings.template.json 中 "ANTHROPIC_BASE_URL": "${ANTHROPIC_BASE_URL}"
Claude Code 把 ${ANTHROPIC_BASE_URL} 当字面字符串,不展开环境变量
实际发送的 URL 是 "${ANTHROPIC_BASE_URL}/v1/messages"
解决: 从 settings.json env 段删除 ANTHROPIC_API_KEY 和 ANTHROPIC_BASE_URL
让 install.ps1 注入的进程环境变量直接生效
教训: Claude Code settings.json 的 ${} 语法行为未文档化,不要假设它能展开
```
### 坑 4: PowerShell 5.1 的 ?. 语法 (HIGH)
```
现象: "表达式或语句中包含意外的标记 '?.Source'"
原因: ?. (null-conditional operator) 是 PS 7+ 专属语法PS 5.1 不支持
解决: 改为 if ($x) { $x.Property } else { $null }
教训: 目标机可能只有 PS 5.1 (Windows 自带),所有脚本必须 PS 5.1 兼容
```
### 坑 5: PowerShell 5.1 的 git stderr 问题 (HIGH)
```
现象: git clone 实际成功但脚本报 [ERROR] 克隆失败
原因: git 把进度信息写到 stderrPS 5.1 的 $ErrorActionPreference="Stop"
把 stderr 输出当成终止性错误抛出异常
解决: git 命令前临时设 $ErrorActionPreference = "Continue"
用 $LASTEXITCODE 判断实际成功/失败
教训: PS 5.1 + 外部命令 + Stop 模式 = 必炸,所有外部命令都要处理
```
### 坑 6: UTF-8 BOM 问题 (HIGH)
```
现象: "<# : 无法将 '<#' 项识别为 cmdlet" 或中文乱码
原因: Write 工具生成的文件无 BOMPS 5.1 按 ANSI 解析 → 中文乱码
修复时 BOM 加了两次 → 双 BOM 导致 <# 注释块被当成代码
解决: 统一用 bash 添加一次 BOM: printf '\xEF\xBB\xBF' > tmp && cat file >> tmp
每次编辑后检查: head -c 3 file | xxd -p 应为 efbbbf
教训: 面向 PS 5.1 的 .ps1 文件必须有且仅有一个 UTF-8 BOM
```
### 坑 7: openssl 路径不在默认位置 (MEDIUM)
```
现象: "[ERROR] openssl 未找到"
原因: Git for Windows 安装在 D:\Git 而非默认 C:\Program Files\Git
解决: 搜索多个路径: C:\Program Files\Git, D:\Git, D:\Git\mingw64\bin
教训: 不要硬编码第三方软件路径,提供多路径搜索
```
### 坑 8: Gitea 端口被占用 (MEDIUM)
```
现象: 部署脚本设 3000 端口但已被 my-oa-frontend 容器占用
解决: 改为 3300 端口
教训: 部署前 netstat -tlnp | grep 端口 先检查
```
### 坑 9: Nginx 反代不传 Authorization 头 (MEDIUM)
```
现象: git push 通过 Nginx 反代时 404
原因: Nginx 默认传 Authorization 但旧的 bookworm-web.conf 先匹配了域名
解决: 换域名 + 添加 proxy_set_header Authorization $http_authorization
教训: 多个 Nginx 配置文件可能竞争同一个 server_name
```
### 坑 10: git clone 私有仓需要凭证 (LOW)
```
现象: install.ps1 中 git clone 静默失败 (无密码提示)
解决: 先手动 git clone 触发凭证输入 + git config credential.helper store
教训: 自动化脚本中的 git 操作需要预配置凭证
```
---
## 四、安全经验
### 三路专家审查的价值
- **红队**: 发现 14 个攻击向量,其中 INSTALL_LOCK=false 成功率 95%
- **代码审查**: 发现 5 个 Blocker + 13 个 Warning
- **架构审查**: 发现仓库体积失控 (预估 50MB 实际 500MB+)
- **交叉验证**: 三路独立发现的 openssl 命令行密码泄露问题,置信度极高
- **教训**: 多专家并行审查在 P0 修复前执行,投入产出比极高
### 安全加固清单 (实际落地的)
- [x] Gitea INSTALL_LOCK=true + 注册关闭 + CLI 创建管理员
- [x] Gitea 二进制 SHA256 校验
- [x] HTTPS (Let's Encrypt + Nginx 反代 + HSTS)
- [x] fail2ban (5次/小时 → 封24h)
- [x] Gitea 仅 127.0.0.1 监听
- [x] openssl 密码通过 stdin 管道 (不暴露进程列表)
- [x] PBKDF2 600,000 迭代 (OWASP 2023)
- [x] 凭证仅进程级环境变量 (不写磁盘)
- [x] .gitignore 排除凭证/大文件/敏感数据
- [x] git add 前人工确认 + 大文件检测
- [x] stop.ps1 清理 Git Credential Manager 缓存
---
## 五、PowerShell 跨版本兼容速查表
| PS 7+ 写法 | PS 5.1 兼容写法 |
|---|---|
| `$obj?.Property` | `if ($obj) { $obj.Property } else { $null }` |
| `$cmd?.Source` | `$cmd = Get-Command x -EA 0; if ($cmd) { $cmd.Source }` |
| `??` 空合并 | `if ($x) { $x } else { $default }` |
| UTF-8 无 BOM | 必须有 BOM: `\xEF\xBB\xBF` |
| `$ErrorActionPreference="Stop"` + git | 临时改 `Continue`,用 `$LASTEXITCODE` 判断 |
---
## 六、代理/VPN 检测方法速查
```powershell
# 最可靠: .NET DefaultWebProxy (覆盖所有系统代理)
[System.Net.WebRequest]::DefaultWebProxy.GetProxy("https://api.anthropic.com")
# IE 注册表
Get-ItemProperty "HKCU:\...\Internet Settings" | Select ProxyServer, ProxyEnable
# 端口扫描 (Clash 7890, V2Ray 10808, 快柠檬 10792)
Test-NetConnection 127.0.0.1 -Port 7890
```
---
## 七、项目最终产出
```
13 个文件 (bookworm-portable/)
2 个 Gitea 仓库 (bookworm-config 514文件/14MB, bookworm-boot 6文件)
3 个 ECS 部署脚本 (Gitea + HTTPS + 防火墙)
7 项 P0 安全修复
4 项 P1 安全加固
1 个保姆式 HTML 教程
1 个快速参考 TXT 手册
1 份经验避坑指南 (本文档)
E2E 验证: 远程 Windows Server → 从零安装 → Claude Code 启动成功
```
---
## 八、如果重新来过,我会...
1. **一开始就做 PS 5.1 兼容测试** — 不假设目标机有 PS 7
2. **一开始就加代理检测** — 国内 Claude Code 的核心门槛
3. **不在 settings.json env 段放 ${} 变量** — 行为未文档化
4. **先检查域名和端口占用** — 再写部署脚本
5. **三路专家审查提前到设计阶段** — 而非代码写完后再审
---
*Bookworm Portable v1.2 | code.letcareme.com | 2026-04-01*