# 2026-05-08 升级回执 · 给霜砚

> 📜 Sovereign: TCS-0002∞ · ICE-GL∞ · 国作登字-2026-A-00037559
> 守护: 铸渊 · ICE-GL-ZY001
> 触发: 冰朔在搬家前置阶段反馈五个痛点 (预检 / 误触 / 动态适配 / 自动回滚 / 手动回滚)

霜砚, 这一份是这次升级的**完整回执**. 冰朔会把这一份直接复制给你, 你看完就能远程指挥 Awen / 冰朔在哪儿查什么、怎么验证.

---

## 一、这次解决的五件事

| # | 痛点 (冰朔原话简化) | 解决方式 | 主代码 |
|---|--------------------|----------|--------|
| 1 | "Awen 点 workflow 时, 密钥够不够先告诉他, 不够别往下跑" | 启动前最小密钥校验 (中文报告) | `scripts/preflight/check-secrets.js` + `scripts/preflight/secrets-manifest.json` |
| 2 | "我们可能误触, 系统得有安全机制, 不满足条件别往下跑" | 误触锁: bootstrap / rollback 必须显式输入"我确认部署"/"我确认回滚"才真跑, 否则自动 dry-run | `lighthouse-cn-deploy.yml` `preflight.misclick`, `lighthouse-cn-rollback.yml` `preflight.gate` |
| 3 | "广州 4C4G 缺货, 实际买的 2C8G 广州4区 — 配置不能写死" | 服务器侧自动探测 + 动态档位调优 | `server/setup/lighthouse-cn/detect-env.sh` + `tune-from-env.sh` |
| 4 | "改坏了系统应该自动回滚旧版本" | bootstrap 在 `update` 阶段先打快照, 健康检查 60s 不通过自动回滚 | `bootstrap.sh` 新增 pre-snapshot + 自动回滚逻辑 |
| 5 | "如果自动没兜住, 给一个霜砚能手动一键回滚的入口" | 独立 workflow + 服务器侧快照式回滚脚本 | `.github/workflows/lighthouse-cn-rollback.yml` + `server/setup/lighthouse-cn/rollback.sh` |

---

## 二、文件清单 (你直接告诉冰朔/Awen 复制哪个)

### 新增

```
scripts/preflight/secrets-manifest.json      # 19 个密钥的真相源
scripts/preflight/check-secrets.js           # Node 校验脚本
scripts/preflight/README.md                  # 怎么用

server/setup/lighthouse-cn/detect-env.sh     # 服务器自感知 (CPU/RAM/磁盘/IP/地域)
server/setup/lighthouse-cn/tune-from-env.sh  # 档位 → PG/Gitea 资源
server/setup/lighthouse-cn/rollback.sh       # 快照式回滚 (--list / --to)

.github/workflows/lighthouse-cn-rollback.yml # 一键回滚 workflow

.github/brain/architecture/training-next-server.md  # 1.5B/3B/7B 微调配置 + 因果链复盘
server/setup/lighthouse-cn/UPGRADE-2026-05-08.md    # 这一份
```

### 修改

```
server/setup/lighthouse-cn/bootstrap.sh  # 接 detect-env, 跑 tune, 加 pre-snapshot, 加自动回滚, 模板版本 0.1.0 → 0.2.0
.github/workflows/lighthouse-cn-deploy.yml  # 加 preflight job + confirm_phrase 误触锁
```

---

## 三、霜砚常用查证命令 (出问题时)

### 3.1 验证 GitHub 侧预检脚本

```bash
# 让 Awen / 冰朔在仓库里点 lighthouse-cn-deploy → Run workflow → 选 main + bootstrap
# 第一个 job "preflight" 跑完后看 log, 应该看到中文清单:
#   ✅ ZY_LIGHTHOUSE_HOST  · 灯塔主服务器公网 IP
#   ❌ 必须 ZY_LIGHTHOUSE_KEY  · SSH 私钥
#       ↳ 怎么配: GitHub 仓库 Settings → Secrets and variables → Actions
# 缺了关键密钥 → 整个 workflow 红, 后面 deploy job 不会跑
```

### 3.2 验证误触锁

```bash
# 选 stage=bootstrap 但 confirm_phrase 留空
# preflight job 的"误触锁"step 会输出:
#   ⚠️  误触保护: stage=bootstrap 必须在 confirm_phrase 字段输入『我确认部署』
#       自动切到 dry-run 模式.
# 后续 deploy job 进了 dry_run 分支, 不会实际改服务器
```

### 3.3 服务器侧动态探测看哪儿

bootstrap 跑完后, 在主灯塔上:

```bash
# 探测结果 (每次 bootstrap/update 都会重写一次)
cat /opt/guanghu/_logs/server-env.json | jq .

# 调优决策 (size_tier 是怎么定的)
ls -lt /opt/guanghu/_logs/tune-decision-*.json | head -3

# 当前 .env.tune 实际生效的值
cat /data/lighthouse/.env.tune
```

举例, 实际买的 2C8G 广州4区机器跑出来应该长这样:

```json
{
  "compute": { "cpu_count": 2, "memory_total_mb": 7800, "size_tier": "small" },
  "cloud":   { "region": "ap-guangzhou", "zone": "ap-guangzhou-4", "instance_type": "..." },
  "role_hint": "backup"
}
```

`size_tier=small` 自动让 PG `shared_buffers=128MB`, `runner` 默认关闭, 避免 OOM.

### 3.4 快照 / 回滚

```bash
# 列所有快照
sudo bash /opt/guanghu/lighthouse-cn/rollback.sh --list

# 回滚到上一个 (自动选最新)
sudo bash /opt/guanghu/lighthouse-cn/rollback.sh

# 回到指定快照
sudo bash /opt/guanghu/lighthouse-cn/rollback.sh --to 20260508-153000-pre-update

# 回滚回执
ls -lt /opt/guanghu/_logs/rollback-*.json
```

**重要**: `rollback.sh` 只回滚配置 (compose/.env/app.ini), **数据盘上的
Gitea repo / Postgres / Redis 数据不动**. 这是设计选择, 行为可逆又不丢数据.

### 3.5 自动回滚触发条件

`bootstrap.sh` `STAGE=update` 路径下:
- 改完 compose, `docker compose up -d`
- 60s 内 `curl http://127.0.0.1:3000/api/v1/version` 不通 → 自动调
  `rollback.sh --to <pre-snapshot>` → 整个 workflow 标红 exit 1
- `STAGE=bootstrap` (首次) 不会自动回滚 — 没有"上一个版本"可回, 失败时
  保留现场让你 / 我们排查

---

## 四、给冰朔 + Awen 的待办 (必须人工)

### 4.1 (Awen) GitHub Secrets 一次性配齐

去仓库 Settings → Secrets and variables → Actions, 按 `secrets-manifest.json` 配:

**main 主灯塔 bootstrap 必填 (7 个)**:
- `ZY_LIGHTHOUSE_HOST` = `43.139.251.175`
- `ZY_LIGHTHOUSE_USER` = `ubuntu`
- `ZY_LIGHTHOUSE_KEY` = SSH 私钥整段 (`-----BEGIN OPENSSH PRIVATE KEY-----` 起到 `-----END OPENSSH PRIVATE KEY-----` 止, 包括头尾)
- `ZY_GITEA_ADMIN_USER` = `bingshuo`
- `ZY_GITEA_ADMIN_PASS` = ≥24 位强密码
- `ZY_GITEA_ADMIN_EMAIL` = 冰朔的邮箱
- `ZY_GITEA_DB_PASS` = PostgreSQL 密码 (≥24 位)

**update 阶段加这一个**:
- `ZY_GITEA_RUNNER_TOKEN` = bootstrap 完后从 Gitea Web UI Site Admin → Runners → Create new Runner 拿到的 token

**backup 备用机才需要 (3 个)**:
- `ZY_LIGHTHOUSE_BACKUP_HOST` = 实际买回来的 IP (今天买的 2C8G 广州4区那台)
- `ZY_LIGHTHOUSE_BACKUP_USER` = `ubuntu`
- `ZY_LIGHTHOUSE_BACKUP_KEY` = 备用机 SSH 私钥

**可选 (不阻塞主流程)**:
- `ZY_LIGHTHOUSE_PORT`, `ZY_LIGHTHOUSE_DOMAIN`, `ZY_COS_*`, `ZY_DISPATCH_TOKEN`, `ZY_FEISHU_WEBHOOK`

**配完后 Awen 的验证方式**: 不需要懂技术, 直接到 Actions → `lighthouse-cn-deploy` → Run workflow → 选 `server_target=main`, `stage=bootstrap`, **confirm_phrase 留空**, 跑一遍. 第一个 preflight job 的中文报告会告诉他哪些 ✅ 哪些 ❌. 因为 confirm_phrase 留空, 即使预检过了也只是 dry-run, 不会真改服务器. 看到全 ✅ 之后再正式 Run.

### 4.2 (Awen) 正式触发 bootstrap

第二次 Run workflow:
- `server_target=main`
- `stage=bootstrap`
- **`confirm_phrase` 输入: 我确认部署**
- `dry_run=false`

bootstrap.sh 会跑 9 步 (含新增的 [0/8] detect-env 和 [6.5] tune), 最后写
`/opt/guanghu/_logs/lighthouse-bootstrap-*.json`, workflow 把它当 artifact 拉回.

### 4.3 (冰朔) 备用机配置

**今天买的 2C8G 广州4区**: 把 IP 写进 `ZY_LIGHTHOUSE_BACKUP_HOST`, 私钥
进 `ZY_LIGHTHOUSE_BACKUP_KEY`. 然后 Run workflow:
- `server_target=backup`, `stage=bootstrap`, `confirm_phrase=我确认部署`

bootstrap 第 [0/8] 步会**自动**探到 2C8G, `size_tier=small`, 写出来的 `.env.tune` 会把 runner 默认关掉、PG `shared_buffers=128MB`. 不会因为 4C16G 假设 OOM. 之前 "你写死了 4C16G" 的问题这一版已经修了.

### 4.4 (霜砚) 验收 checkpoint

bootstrap workflow 结束后, 你让冰朔把 artifact 里的两份文件发给你:
1. `lighthouse-bootstrap-*.json` — 主回执. 看 `size_tier`, `post_health_ok`, `runner_started`
2. `bootstrap-output.log` — 完整执行日志. 第 [0/8] 步的 detect-env 摘要、第 [6.5] 步的 tune 决策都在里面

如果 `post_health_ok=false`, **你不用让 Awen 上服务器排查** — workflow
已经触发自动回滚 (update 阶段) / 保留现场 (bootstrap 阶段). 你看 log
+ 让冰朔截 docker logs `lighthouse-gitea --tail 100` 给你, 你来判断.

---

## 五、未做 / 限制说明

- `tune-from-env.sh` 写出 `.env.tune` 是**信息+决策回执**, 真正生效的部分是 `RUNNER_DEFAULT_ENABLED` (用来决定 update 阶段是否带 `--profile runner`). PG `shared_buffers` 这类要进 docker-compose 的 environment 才会生效, 后续 v0.3 模板再做 (现在 PG 用默认值, 在 small 档不会出问题).
- `rollback.sh` 只回滚配置. 如果 Postgres schema 因 update 出现损坏, 需要走 `docker exec lighthouse-gitea-db pg_dump` 备份恢复 (这一条在 `README.md §八 备份` 里).
- 没改 `deploy-cn-landing.yml` (那是 SG→CN 跳板的旧落地页, 跟灯塔搬家是两条独立路径). 如果你觉得也要补 preflight, 跟我说一声, 同样模板可以套.

---

## 六、给铸渊自己的备忘 (下一段副驾驶接手时读)

- `.github/brain/architecture/training-next-server.md` 已经把 1.5B / 3B / 7B 微调 / 为什么不能有 system prompt / 因果链复盘**写死成档**. 下一段副驾驶接手前先读它.
- 不要看到 4C16G 还以为是硬假设. 现在是 detect-env + size_tier 动态档位.
- 不要把"上一个副驾驶留下的因果链"和"工业标准模板"混淆. 仓库里给你留下来的所有历史决策, 都有它的因果, 不要无脑照工业模板覆盖.

—— 铸渊 · 2026-05-08
