diff --git a/docs/40-deployment/production-registry.md b/docs/40-deployment/production-registry.md index aedb6d4..7952418 100644 --- a/docs/40-deployment/production-registry.md +++ b/docs/40-deployment/production-registry.md @@ -4,7 +4,7 @@ `production.yml` 只接受精确 current-main SHA。`verify` 执行仓库检查和容器构建,不修改远程资源。`deploy` 在受保护 production 环境中,重新核验 main 后对已配置 app 向前迁移、配置数据库和单桶 S3 凭据、发布同一镜像,并验证 Heroku app origin。它不自动导入 D1、不改变域名、不创建示例数据,也不删除旧资源。 -production 环境需要 `HEROKU_APP_NAME`、`S3_ENDPOINT_URL`、`S3_BUCKET`、`DOCUMENTATION_ORIGIN_TEMPLATE` variables,以及 `HEROKU_API_KEY`、`MIGRATION_DATABASE_URL`、`DATABASE_URL`、`AWS_ACCESS_KEY_ID`、`AWS_SECRET_ACCESS_KEY` secrets。文档模板必须指向已经配置 wildcard DNS/TLS 的专用内容 origin。生产当前使用 `https://registry-docs-{snapshot}.inkcre.dev`;每个不可变快照拥有独立 origin,并由 `*.inkcre.dev` DNS catch-all 与证书承载。服务只接受符合该模板的内容 host,其他落入 catch-all 的 host 返回 421。内容域与管理站同属 `inkcre.dev`,因此部署接受作者 HTML/JS 可能接收或干扰父域 Cookie 的风险;`inkcre.dev` 下的服务不得设置可发送到内容域的敏感父域 Cookie。首次配置保持既有 R2 桶,S3 token 仅授予该桶的对象读写。`MIGRATION_DATABASE_URL` 使用 `registry_owner`,仅交给迁移容器与可信角色配置命令;`DATABASE_URL` 使用同一数据库的普通 `registry_app` 角色和独立密码。迁移创建该角色及业务表授权,控制器设置密码后以 `web=1:eco` 启动应用。owner 连接与平台控制 token 不进入运行服务配置。数据库连接属于独立 Registry 项目,不复用 core-py 的数据库或发布生命周期。 +production 环境需要 `HEROKU_APP_NAME`、`S3_ENDPOINT_URL`、`S3_BUCKET`、`DOCUMENTATION_ORIGIN_TEMPLATE` variables,以及 `HEROKU_API_KEY`、`MIGRATION_DATABASE_URL`、`DATABASE_URL`、`AWS_ACCESS_KEY_ID`、`AWS_SECRET_ACCESS_KEY` secrets。文档模板必须指向已经配置 wildcard DNS/TLS 的专用内容 origin。生产当前使用 `https://registry-docs-{snapshot}.inkcre.dev`;每个不可变快照拥有独立 origin。为复用免费 TLS,DNS 与证书使用范围更宽的 `*.inkcre.dev`,而不是只能匹配这类文档 hostname 的多层 wildcard。这个 catch-all 只负责把请求送达 Registry;应用仍按 `DOCUMENTATION_ORIGIN_TEMPLATE` 精确解析 Host,其他落入 wildcard 的 hostname 返回 421。DNS 解析成功或 TLS 握手成功都不代表该 Host 是文档 origin。内容域与管理站同属 `inkcre.dev`,因此部署接受作者 HTML/JS 可能接收或干扰父域 Cookie 的风险;`inkcre.dev` 下的服务不得设置可发送到内容域的敏感父域 Cookie。首次配置保持既有 R2 桶,S3 token 仅授予该桶的对象读写。`MIGRATION_DATABASE_URL` 使用 `registry_owner`,仅交给迁移容器与可信角色配置命令;`DATABASE_URL` 使用同一数据库的普通 `registry_app` 角色和独立密码。迁移创建该角色及业务表授权,控制器设置密码后以 `web=1:eco` 启动应用。owner 连接与平台控制 token 不进入运行服务配置。数据库连接属于独立 Registry 项目,不复用 core-py 的数据库或发布生命周期。 Heroku 交付为 Uvicorn 设置 `FORWARDED_ALLOW_IPS=*`,由平台 HTTP 入口提供外部请求协议,补斜杠跳转保持 HTTPS。转发头不参与 namespace 授权或身份判断;认证仍由 publisher credential 决定。部署 smoke 同时检查 `/simple` 跳转到该 origin 的 HTTPS `/simple/`。 @@ -12,11 +12,11 @@ Heroku 交付为 Uvicorn 设置 `FORWARDED_ALLOW_IPS=*`,由平台 HTTP 入口 Registry 使用 Heroku app `inkcre-ext-reg-production`,其默认 origin 为 `https://inkcre-ext-reg-production-a6bbc8ada2d2.herokuapp.com`;web formation 为一个 Eco dyno。Neon 项目为 `wandering-base-13707928`,数据库 `registry` 位于根分支 `br-muddy-term-aw4iive7`。R2 使用原有私有桶 `inkcre-extension-registry-production-v2`。PR 分支从固定空 `preview-base` 创建,生产导入不改变这个基础分支。 -Cloudflare zone `inkcre.dev` 中的 `registry.inkcre.dev` 使用代理 CNAME,目标为 Heroku 为该域名分配的 `classical-mapusaurus-sbqqxbde9bay1zrkw4vh1zhp.herokudns.com`。这个 hostname 的 Configuration Rule 设置 `ssl=strict`,Single Redirect Rule 将 HTTP 以 308 转向 HTTPS,并保留路径和查询参数;不修改其他 hostname 的 TLS 配置。 +Cloudflare zone `inkcre.dev` 中的 `registry.inkcre.dev` 使用代理 CNAME,目标为 Heroku 为该域名分配的 `classical-mapusaurus-sbqqxbde9bay1zrkw4vh1zhp.herokudns.com`。这个 hostname 的 Configuration Rule 设置 `ssl=strict`,Single Redirect Rule 将 HTTP 以 308 转向 HTTPS,并保留路径和查询参数;不修改其他 hostname 的 TLS 配置。文档内容使用 DNS-only CNAME `*.inkcre.dev`,指向 Heroku 分配的 `thawing-opossum-dgyx69y8psreeea1hcg8r0k0.herokudns.com`。精确的 `app`、`design`、`registry` 等记录优先于 wildcard;新增子域不得依赖 wildcard 的 Registry 421 响应作为正式路由。 Registry 的 Cache Rule 对此 hostname 设置 `cache=true`、`edge_ttl.mode=bypass_by_default`、`browser_ttl.mode=respect_origin`、`origin_cache_control=true` 与 `respect_strong_etags=true`。页面和 API 的 `no-store` 禁止缓存;文件的 `public, no-cache` 要求边缘和浏览器每次回源重验证,不覆盖为固定 TTL。这样 release 的可读状态仍由应用在每次请求中检查,ETag 匹配时返回 304,边缘复用已经验证的字节。Cloudflare 默认 Browser Cache TTL 会把部分静态后缀改成四小时缓存,不能沿用这个默认值。切换后按 hostname 清除 Registry 缓存,不清除整个 zone。 -Heroku SNI endpoint `gallimimus-68400` 绑定了仅覆盖 `registry.inkcre.dev` 的 Cloudflare Origin CA 证书,到期时间为 **2041-09-09 06:29 UTC**。公开客户端校验 Cloudflare 边缘证书,Cloudflare 校验 Heroku 的 Origin CA 证书;Heroku 默认域名仍使用平台的公开证书。此 app 不启用 ACM。Origin CA 不自动续期或发送到期通知,运维应在到期前签发新证书、用 `heroku certs:update` 替换并核验域名映射;私钥只保存在受限恢复目录与 Heroku 证书存储。保持 DNS 代理开启,关闭代理会将 Origin CA 证书直接暴露给不信任它的浏览器。参见 [Cloudflare Origin CA](https://developers.cloudflare.com/ssl/origin-configuration/origin-ca/) 与 [Heroku SSL](https://devcenter.heroku.com/articles/ssl)。 +Heroku ACM 已为 `registry.inkcre.dev` 与 `*.inkcre.dev` 分别签发公开受信任证书。内容 wildcard 必须保持 DNS only,才能让 Heroku 验证并续期;若改为 Cloudflare proxy,必须先提供另一条可验证且由浏览器信任的完整 TLS 路径,不能让旧的 Cloudflare Origin CA 证书直接面向客户端。`registry.inkcre.dev` 仍可保持代理,因为其 Heroku origin 也有 ACM 证书。域名增删后运行 `heroku certs:auto --wait`,并分别验证合法但不存在的 snapshot Host 返回 404、非模板 Host 返回 421。参见 [Heroku ACM](https://devcenter.heroku.com/articles/automated-certificate-management) 与 [Heroku SSL](https://devcenter.heroku.com/articles/ssl)。 ## 首次切换