# IAM 上线 / 灰度 / 回滚 Runbook

> 配合 `docs/standards/09-iam-security.md` + `09-iam-implementation-plan.md` 使用。
> 适用范围：PR #138 IAM 完整重构合并到 develop → staging → production 的全流程。

## 上线前硬检查清单

- [ ] PR #137（IAM 规则文档）已合并到 develop
- [ ] `backend/prisma/migrations/20260425085204_iam_full_terminal_state/migration.sql` 在 PR 中存在
- [ ] Redis HA 部署（Sentinel 或 Cluster）已上线并验证通过
- [ ] Env 变量已就位：`REDIS_HOST` / `REDIS_PORT` / `REDIS_PASSWORD`，`JWT_ACCESS_TTL`（默认 30m）/ `JWT_REFRESH_TTL`（默认 30d）
- [ ] 前端 `refresh_token` 逻辑（api-client 拦截器）已包含在 PR 中
- [ ] UAT smoke 通过：登录 / 30min 自动续期 / 登出失效 / Redis 单节点故障下请求行为
- [ ] `npx prisma migrate deploy` 在 UAT 空 DB / 增量 DB 都跑过并成功

## 部署流程

### Stage 1：UAT（灰度的替身）

1. CI 跑 PR #138 的构建（后端 + 前端）
2. 部署 UAT：
   ```bash
   bash scripts/deploy/deploy.sh uat
   ```
3. 执行 `npx prisma migrate deploy` 应用总迁移（增量 DB，不走 fresh replay）
4. 重启应用服务
5. 运行 UAT smoke 脚本（待补）：
   - 登录获得 access + refresh token
   - 30min 后发起请求 → 应自动刷新
   - 登出后旧 token → 应 401
   - 关闭 Redis 主节点 → 应 Sentinel 切换 → 请求不受影响
   - 关闭 Redis 全部 → 应 fail secure 403（规则 §5.3.5）

### Stage 2：Staging

UAT 通过 → staging（Promote PR）→ 观察 30 分钟业务日志和 Grafana 指标

### Stage 3：Production

staging 通过 + 1~2 天稳定 → production 部署（低峰期）

## 灰度策略

本次 IAM 改造**无法做"某些用户走新版、某些走老版"的灰度**（Token 格式和 Redis 缓存是全局的）。
灰度只能做到"按环境分批"：UAT → staging → prod，每一步等稳定观察期。

如果需要"功能灰度"（某些新端点只对一部分人开放），用权限码控制：
- 预留 `feature:iam-delegation` 这类权限码
- 只给特定 UserRole 分配
- 分步扩大授权

## 回滚方案

### 回滚触发条件

- 大面积 401/403（> 5% 请求失败）
- Redis 故障且无法快速恢复
- 业务功能受权限拒绝影响（核心业务写操作报 403）
- 审计日志爆写 / 数据库压力告警

### 回滚流程

#### 情景 A：应用层 bug（代码问题）

1. **不回滚数据库**（本次迁移只加新表，不动现有列，不需要回滚）
2. 回退代码到上一个稳定 commit：
   ```bash
   git revert <commit-hash>  # 或整包回退 PR #138 的 merge
   bash scripts/deploy/deploy.sh <env>
   ```
3. 新迁移的新表保留（兼容，因为旧代码不查它们）
4. Redis 残留缓存会在 5min TTL 后自然过期

#### 情景 B：Redis 故障无法快速恢复

**立即动作**（< 15min 内）：
1. 启用紧急豁免（只对关键读路径）：
   ```bash
   # 在运维 Redis CLI 或专用工具里
   SET "emergency_bypass:GET /api/v1/users/me" \
       '{"reason":"redis-outage","enabledAt":"<now>","expiresAt":"<+4h>"}' \
       EX 14400
   ```
   豁免期 4h 硬限制（规则 §5.6 P2，`EmergencyBypassService`）
2. 同步启动 Redis 灾备切换
3. 故障解除后主动 `DEL emergency_bypass:*`

**注意**：豁免只对单个端点生效（不是全局 bypass），每次启用必带 reason，全程审计留痕。

#### 情景 C：迁移后发现 schema 问题

1. **不提交反向迁移**（这是违反 CLAUDE.md "禁止修改已合并迁移" 的行为）
2. 写一个 fix-drift 的新迁移，正向补救
3. `npx prisma migrate deploy` 推上去
4. 同步修代码

## 观察指标（Grafana Dashboard）

- `http_requests_total{status="401"}`：401 率
- `http_requests_total{status="403"}`：403 率
- `data_scope.unknown_resource` counter（未知 resource WARN）
- Redis 连接池占用 / hit rate
- `_prisma_migrations` 应用成功（启动时一次性）
- `iam_audit_log` 写入速率（异常飙高可能是配置变更风暴）

## 已知限制

- **Redis 必须 HA**：单实例 Redis 重启窗口内全员 403（fail secure，规则硬要求）
- **5min 撤权 SLA**：角色/scope 配置变更后 5min 内生效，期间用户可能仍持旧权限
- **30min access token**：前端必须有 refresh 拦截器，否则用户频繁被踢（已在 PR #138 包含）
- **新迁移需要 migrate deploy 而非 fresh replay**：存量 DB 增量 apply OK；空 DB（新人本地、CI 重建）受 migration drift 历史债影响，见 `docs/todos/migration-history-debt.md`

## 联系 / 责任人

- IAM 规则问题：[待填]
- Redis 运维：[待填]
- 审计日志告警：[待填]
- 紧急豁免授权：至少 2 人批准（on-call + IAM owner）
