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

8.0 KiB
Raw Permalink Blame History

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 修复前执行,投入产出比极高

安全加固清单 (实际落地的)

  • Gitea INSTALL_LOCK=true + 注册关闭 + CLI 创建管理员
  • Gitea 二进制 SHA256 校验
  • HTTPS (Let's Encrypt + Nginx 反代 + HSTS)
  • fail2ban (5次/小时 → 封24h)
  • Gitea 仅 127.0.0.1 监听
  • openssl 密码通过 stdin 管道 (不暴露进程列表)
  • PBKDF2 600,000 迭代 (OWASP 2023)
  • 凭证仅进程级环境变量 (不写磁盘)
  • .gitignore 排除凭证/大文件/敏感数据
  • git add 前人工确认 + 大文件检测
  • 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 检测方法速查

# 最可靠: .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