# 服务运行、迁移与数据保护

星线 App 的运行涉及静态下载站、Account 身份服务、聊天服务，以及可选的模型、LiveKit 和 APNs。本文介绍源码中可确认的运行机制与维护原则，不提供生产凭据、服务器地址或私有部署目录。本次仅核对代码与历史文档，没有连接生产、读取数据库、执行迁移或演练恢复。

## 运行拓扑与边界

身份服务使用 Node.js 24、MySQL 8.4 和独立 Account／管理站来源；其代码、命令与迁移以当前维护仓库为准，App 工作树的 `identity/` 不能当作现行部署模板。聊天服务使用 Node.js 24 与 SQLite，保存聊天、媒体、新事、设备会话和相关权限。下载介绍与公开技术文档属于静态内容，不需要数据库。App 内的记忆、个人任务和收藏存于设备，不因后端数据库备份而自动备份，也不因账号退出而删除。

| 组件 | 实际入口 | 持久数据或材料 |
| --- | --- | --- |
| 身份服务 | 当前 Account 的 `identity/server/main.ts`；构建后 `dist/server/main.js` | MySQL、稳定身份密钥、PII 密钥、私有 OIDC JWKS |
| 聊天服务 | `chat-service/src/main.mjs` | SQLite、WAL、资料加密密钥、OIDC 客户端配置 |
| 下载与文档 | `app-download/dist/` 的发布产物 | 经过核对的静态文件与下载配置 |
| 通话与推送 | LiveKit、APNs 服务端适配器 | 各自受保护配置与运行状态 |

配置变量用途见[配置参考](https://document.thestarline.cn/app/configuration-reference.md)。生产身份服务会校验批准的 HTTPS 来源、身份主密钥、PII 密钥和 OIDC 签名材料。当前管理员通行密钥要求由环境开关控制，不能沿用旧副本“生产始终强制 true”的断言；应另核对 `admin-policy.ts` 与实际角色及认证方法。聊天生产启动要求固定批准的身份 issuer、HTTPS public URL 与 confidential 客户端。合成域名只适合测试构造，不能通过替换示例值绕过这些检查。

## 构建与持续运行

在当前 Account 仓库的隔离构建环境准备依赖与身份服务产物：

```sh
npm --prefix identity ci
npm --prefix identity/client ci
npm --prefix identity/client run build
npm --prefix identity run typecheck
npm --prefix identity run build
```

身份服务的源码入口 `npm start` 适合本地运行；生产宿主部署由进程管理器运行构建产物，并注入受保护配置。仓库还提供 `identity/Dockerfile`、`identity/compose.yml`、`chat-service/Dockerfile` 与 `chat-service/compose.yml`。聊天镜像从同一 Rust 源码构建 Node WASM 校验器，复制固定城市库与贴纸素材，并用非 root 用户运行。Compose 将数据库映射到持久卷，应用端口默认仅绑定宿主回环；容器代理要按实际网络选择可达地址。

构建命令可以在新环境准备镜像。第一条在当前 Account 仓库执行，第二条在 App 仓库执行，不能因相对路径相同而混用历史副本：

```sh
docker compose -f identity/compose.yml build
docker compose -f chat-service/compose.yml build
```

这一步只构建，不等于启动、发布或健康。镜像准备后先核对现有部署基线、配置链、持久卷、回滚产物和目标服务，再按维护窗口切换。共享 Account 有独立维护来源，不能用 App 工作区的旧副本覆盖其最新代码、Compose 或代理配置。进程应持续运行，不设置用于展示的时间上限；退出时优先使用 `SIGTERM` 或 `SIGINT`，源码会关闭定时任务和连接。

## 健康检查与业务探测

本地检查命令是：

```sh
curl --fail --silent --show-error http://127.0.0.1:9100/api/health
curl --fail --silent --show-error http://127.0.0.1:4210/health
```

身份健康接口执行 `SELECT 1`，因此检查了数据库连接。聊天 `/health` 只返回进程的 `status: ok`，没有逐项探测 OIDC、LiveKit、APNs、模型或数据库后续写入。公开入口还要分别验证 DNS、TLS、反向代理、Host、重定向及正确响应。生产身份接口有可信代理和 Host 约束，不应直接照搬开发 curl 判断生产不可用，也不应为了探测关闭约束。

业务探测使用专用合成账号，依次确认授权、换票、`/me` 恢复、双方收发、媒体权限与重开读取。通话还需两台真实设备跨网络验证，APNs 还需证明通知实际到达。只记录状态和关联请求信息，不保存 token、code、完整回跳 URL、验证码或用户内容。

## 迁移与版本切换

身份服务启动前自动调用迁移，也提供独立入口：

```sh
cd identity
npm run migrate
```

当前 Account `MySqlStore.migrate()` 顺序处理 `001.sql` 至 `007.sql`，随后执行 PII 与补列逻辑，表前缀取自配置；部分幂等迁移会先检查现有结构。生产要求 64 位十六进制 `IDENTITY_PII_KEY`。已有明文联系方式、flow 或 challenge 转换前需要验证备份，并明确设置 `IDENTITY_PII_MIGRATION_APPROVED=1`；数据库密钥标记不匹配时拒绝启动，不能通过审批开关绕过。修改迁移前应审查 SQL、在隔离副本验证，再记录执行结果；不能由命令返回 0 推断所有业务兼容。

聊天没有独立 `npm run migrate`，`ChatDatabase` 与各仓储在构造时创建表、补充列或重建特定表，企业、城市贴纸、原生授权等结构也在启动阶段处理。因此新旧进程不能同时对同一份数据库随意切换。

上线顺序应为：冻结当前基线与构建产物，取得一致性备份，在隔离环境验证迁移与读写，确认客户端兼容，停止旧实例后切换，再做健康与业务验收。仓库未提供通用自动降级脚本；数据库结构已变化时，单独退回旧镜像可能不兼容，回滚需要与已演练的数据快照一起设计。

## 备份与恢复原则

聊天 SQLite 启用 WAL、外键和 5 秒 busy timeout。备份应使用一致性快照机制，或在有序停止写入后保存数据库及其 WAL 所需状态；不能在服务写入时只随手复制主数据库。媒体 BLOB、新事及权限也属于数据集，不能只备份消息表。不要删除 WAL、手工改表或清空数据作为通用修复步骤。

身份 MySQL 与稳定 `IDENTITY_SECRET`、`IDENTITY_PII_KEY`、私有 OIDC 签名材料需要按受控流程配套保护。PII 密钥同时影响联系方式解密与查找摘要，缺失或换错会拒绝启动或无法使用旧数据。聊天的 `CHAT_PROFILE_ENCRYPTION_KEY` 未设置时从客户端密钥派生；更换这些值可能导致已存资料无法解密或手机号查找索引失效。密钥轮换必须先分析受影响数据与会话，实施迁移或撤销，不把“生成新密钥”当作无损恢复。具体隔离演练见[故障处理与恢复](https://document.thestarline.cn/app/incident-runbooks.md)。

备份应加密、限制访问、设置保留期并定期检查可恢复性。恢复先在隔离环境验证结构、读取、权限和合成写入；正式切换前停止旧实例，避免双写。身份恢复后根据风险撤销旧会话与授权。管理员恢复只能由获授权维护者在受保护环境审计处理，不提供公开提权或绕过验证入口。本机记忆密钥绑定设备安全存储，服务端备份不能重建丢失的设备密钥；换机前应由用户按 App 的授权转出流程处理。

## 日志、队列和保留

身份日志保留方法、去除查询参数的路径、请求 ID 与响应状态，脱敏 Cookie、Authorization 和 CSRF 头。当前验证码 worker 每 250 毫秒调用 `processPendingDeliveries()` 处理持久队列，多实例依靠数据库锁分配任务；不能沿用旧方法名当作当前运维接口。生产邮件为腾讯 SES，短信为阿里云，SMTP 仅非生产备用；“请求已受理”“提供商接受”和“用户已收件”是三个不同结果。聊天源码将未处理异常写入标准错误，目前没有完整集中指标面板，应在进程及代理层补充错误率、延迟、重启、磁盘和资源观测，并先过滤敏感信息。

身份维护入口为 `npm run cleanup`，按过期状态与 `IDENTITY_AUDIT_RETENTION_DAYS` 清理，审计默认保留 180 天。新事未发布媒体会在启动及每小时检查中清理过期孤立项。不能把这些清理误认为完整备份或容量治理。生产状态、发布与商店进度见[版本与分发状态](https://document.thestarline.cn/app/releases.md)，按症状排查见[故障定位](https://document.thestarline.cn/app/troubleshooting.md)。
