7.4 KiB
V2 公开政策生产 Promotion 运维手册
日期:2026-08-17
边界
本手册只描述已验签公开政策的受控状态切换。它不替代法务审批、运营审批、容量审批或公网切流审批。候选 keyring、手工修改数据库、HTTP 写接口、政策私钥和共享预发布凭据都不能用于生产激活。
CONTRACT_API_PUBLIC_POLICY_EXECUTOR_ENABLED 必须保持 false。生产读取、注册协议和既有用户重新同意由 Contract API 提供;真正的 promotion 和 rollback 只通过本机受控 CLI 执行,并且不暴露 HTTP 路由。
前置条件
- Contract API 数据库已经迁移到
20260812_0046,public_policy_state存在且状态指针可验证。 - 四份政策候选文件、
manifest.json、具名法务审批文件和 trusted keyring 已由安全/运维安装到生产主机。 - 材料目录和文件均为绝对路径,文件所有者可读,未包含 symlink,目录总量不超过 2 MiB。
CONTRACT_API_PUBLIC_POLICY_RELEASE_DIGEST等于当前 Contract API 不可变镜像 digest。- 生产只读 preflight 通过。
- 已取得单独的运营激活授权文件。该文件只用于计算 SHA-256,内容不得进入仓库、聊天、日志或 CI artifact。
- 已记录 expected generation、current set ID、trace ID、idempotency key 和授权文件 hash。
推荐主机布局:
/etc/yuqei/public-policy/2026-08-12/
manifest.json
user-terms.md
privacy-policy.md
service-agreement.md
ai-use-notice.md
named-policy-legal-approval.json
policy-legal-keyring.trusted.json
生产环境模板中的绑定:
PUBLIC_POLICY_CANDIDATE_DIR_HOST=/etc/yuqei/public-policy/2026-08-12
PUBLIC_POLICY_LEGAL_APPROVAL_FILE_HOST=/etc/yuqei/public-policy/2026-08-12/named-policy-legal-approval.json
PUBLIC_POLICY_TRUSTED_KEYRING_FILE_HOST=/etc/yuqei/public-policy/2026-08-12/policy-legal-keyring.trusted.json
CONTRACT_API_PUBLIC_POLICY_RELEASE_DIGEST=sha256:<contract-api-image-digest>
CONTRACT_API_PUBLIC_POLICY_EXECUTOR_ENABLED=false
1. 只读预检
在生产主机执行:
python3 yuqei-infra/deploy/preflight-production.py \
--env-file /etc/yuqei/production.env \
--artifact-dir /var/lib/yuqei/preflight/public-policy
必须确认以下检查为 pass:
public_policy_deploymentpublic_policy_credential_isolationpublic_policy_candidate_dirpublic_policy_manifestpublic_policy_legal_approval_filepublic_policy_trusted_keyring_file- 相关 owner-only 权限检查
任一失败时停止,不得通过改名、复制、手工改库或跳过 preflight 继续。
2. 查看当前状态
下面的 COMPOSE_ARGS 按生产部署手册替换为真实 project、env 和两个 Compose 文件参数:
COMPOSE_ARGS=(
--project-name yuqei-v2-production
--env-file /etc/yuqei/production.env
-f yuqei-infra/compose/docker-compose.yml
-f yuqei-infra/compose/docker-compose.production.yml
)
docker compose "${COMPOSE_ARGS[@]}" run --rm --no-deps contract-api \
python -m yuqei_contract_api.public_policy_promotion_cli status
输出不包含政策正文,只包含状态指针、版本、生效时间、公开路径和每个文件的 SHA-256。
3. Promotion dry-run
docker compose "${COMPOSE_ARGS[@]}" run --rm --no-deps contract-api \
python -m yuqei_contract_api.public_policy_promotion_cli preflight
显式 promotion dry-run 还必须提供工单上的 expected state:
docker compose "${COMPOSE_ARGS[@]}" run --rm --no-deps contract-api \
python -m yuqei_contract_api.public_policy_promotion_cli \
promote \
--expected-generation 0 \
--expected-current-set-id none \
--idempotency-key policy-promotion-20260812-001 \
--trace-id ops-policy-promotion-20260812-001
Dry-run 不写数据库,也不需要授权文件。
4. 受控 Promotion
授权文件通过一次性只读挂载进入容器。不要把该挂载写入生产 Compose 文件:
AUTHORIZATION_FILE=/run/yuqei/operations/policy-promotion-20260812-001.txt
docker compose "${COMPOSE_ARGS[@]}" run --rm --no-deps \
--volume "$AUTHORIZATION_FILE:/run/yuqei/public-policy/activation-authorization.txt:ro" \
contract-api \
python -m yuqei_contract_api.public_policy_promotion_cli \
promote \
--apply \
--confirmation I_AUTHORIZE_THIS_EXACT_PUBLIC_POLICY_PROMOTION \
--authorization-file /run/yuqei/public-policy/activation-authorization.txt \
--expected-generation 0 \
--expected-current-set-id none \
--idempotency-key policy-promotion-20260812-001 \
--trace-id ops-policy-promotion-20260812-001
命令输出中的 authorization_sha256、generation、set ID、event ID 和 state hash 必须写入运维证据。重复执行同一命令必须使用完全相同的材料;换任何材料必须换 idempotency key 并重新审批。
5. 生效后回归
到达批准的 effective_at 后验证:
GET /api/v1/public-policies/current返回四份政策。- 四份政策页面返回 approved 字节,页面版本和生效时间正确。
- 新注册用户必须绑定四个 effective 协议版本。
- 既有用户下次登录进入重新同意流程。
- 同意提交记录版本、时间和主体。
- 审计和 append-only 政策事件链完整。
- Contract Web 不出现未发布、接口故障与真实空态混淆。
- 生产 health、schema-check、worker、Provider readiness 和核心合同流程保持通过。
6. 受控 Rollback
Rollback 只允许指向曾经 active 的历史 policy set,不能伪造或引用不存在的 set。先执行 dry-run:
docker compose "${COMPOSE_ARGS[@]}" run --rm --no-deps contract-api \
python -m yuqei_contract_api.public_policy_promotion_cli \
rollback \
--target-set-id <historical-policy-set-id> \
--expected-generation <ticket-generation> \
--expected-current-set-id <ticket-current-set-id> \
--idempotency-key policy-rollback-<ticket-id> \
--trace-id ops-policy-rollback-<ticket-id>
确认目标后,使用单独的 rollback 授权文件:
ROLLBACK_AUTHORIZATION=/run/yuqei/operations/policy-rollback-<ticket-id>.txt
docker compose "${COMPOSE_ARGS[@]}" run --rm --no-deps \
--volume "$ROLLBACK_AUTHORIZATION:/run/yuqei/public-policy/rollback-authorization.txt:ro" \
contract-api \
python -m yuqei_contract_api.public_policy_promotion_cli \
rollback \
--apply \
--confirmation I_AUTHORIZE_THIS_EXACT_PUBLIC_POLICY_ROLLBACK \
--authorization-file /run/yuqei/public-policy/rollback-authorization.txt \
--target-set-id <historical-policy-set-id> \
--expected-generation <ticket-generation> \
--expected-current-set-id <ticket-current-set-id> \
--idempotency-key policy-rollback-<ticket-id> \
--trace-id ops-policy-rollback-<ticket-id>
Rollback 也是 append-only 事件,不删除历史政策、同意记录或审计。执行后必须重复第 5 节回归,并通知法务和运营具名复核。
7. 明确禁止
- 禁止把法务私钥、运营授权文件或任何 policy signing key 放入仓库、生产环境文件或 CI artifact。
- 禁止使用 candidate keyring 直接激活。
- 禁止把 trusted keyring 的
policy_activation_authorized=false手工改为 true。 - 禁止直接 UPDATE/DELETE
public_policy_*表。 - 禁止新增 HTTP promotion、rollback 或 execute 路由。
- 禁止在 preflight、容量、备份、法务或运营审批失败时启动 edge 或切换 DNS。
- 禁止把 dry-run 或技术验签输出宣称为法务批准或公网切流授权。