一个事实,一个所有者
产品语义、路径、发布状态、缓存策略、ReleaseManifest、备份集合各有唯一真源;生成物只做投影。
这是一个“单生产环境、单 VPS、内容与公开投影分离”的官网系统:Strapi / Postgres 管事实,Next.js 生成公开页面,Cloudflare 提供边缘与唯一公网回源,Tailscale 承载全部管理面,R2 管媒体,B2 管备份与加密 IaC state。
架构方向已经稳定,当前还不是完整上线站点。基础设施和内容地基有真实生产证据;公开应用、发布缓存闭环、多语言、询盘、真实媒体恢复仍未完成。
产品语义、路径、发布状态、缓存策略、ReleaseManifest、备份集合各有唯一真源;生成物只做投影。
产品族、locale、slug、schema、镜像 digest、备份点都可变化,但必须经过同一边界或持久状态机。
草稿不能泄漏;跨对象变化必须同事务/快照;“备份成功”必须等于可恢复的完整集合。
主链从左到右;管理面始终走 Tailscale;底部两条链分别是签名发布与 BackupSet 恢复。移动端可横向拖动查看整张图。
系统不是按“前端 / 后端 / 运维”简单切开,而是按事实所有权分层。变化只能经所属边界发生;下游只能保存投影,不能再造第二份真源。
属主:ProductAggregate、PublicContentGateway、AvailableLocales。
不变量:产品族只由 specs dynamic-zone 组件 UID 判别;公开内容只能由已发布且完整的内容派生。
属主:CanonicalPathRegistry、FilterKind、ProductFilterProjection。
不变量:slug 是 URL 身份而非型号;同字段 OR、跨字段 AND,facet 排除自身选择。
属主:CachePolicy、BackupCoordinator、ErasureLedger。
不变量:缓存行为从一处派生;BackupSet 必须封闭同一数据库快照所引用的媒体。
属主:ReleaseManifest、InquirySubmissionKey、NotificationOutbox。
不变量:跨步骤流程必须持久化、可重试、可收敛;不能把进程内存当业务事实。
属主:构建契约、Alloy、版本与签名清单、OpenTofu 与凭证清单。
不变量:平台差异停留在适配器;共享业务逻辑不能感知具体基础设施实现。
下面的“目标”来自 docs/architecture.md v1.3;“当前”来自机器真源、测试与已落账证据。代码存在、历史生产证据和本次实时回读是三种不同强度的结论。
| 范围 | 候选目标 / 不变量 | 当前实现与证据 | 判定 |
|---|---|---|---|
| 公网暴露 | 一个 production OpenTofu state、一个 VPS、一个 Tunnel;ExposurePhase 唯一决定 construction / soft-launch / public。 | Cloudflare、Lighthouse、Tunnel 与旧站重定向有局部生产回读;仓库证据仍指向 construction 的旧站重定向基线。本次未做外部实时复核。 | 局部证据 |
| CMS / 数据库 | Strapi 5 管内容事实,Postgres 16 管事务;生产 Strapi 不自动改结构。 | Postgres + Strapi 的 content-foundation 有激活证据;当前 CMS 有 Product、Category、Series、CanonicalPathRegistry。 | 地基已证 |
| Frontend | Next.js 15 App Router 输出完整产品站、博客、SEO/GEO 与公开 API 投影。 | 已有四个 category 路由、动态 series 路由、最小 landing renderer 与 health;主页刻意返回 notFound(),完整应用激活未闭环。 | 局部实现 |
| 产品模型 / 生成器 | 一个定义生成 CMS、运行校验、前端判别联合、筛选描述符和 fixtures;新增产品族时共享引擎零修改。 | 三族定义与多类生成物已存在,嵌套规格有本地/真机探针证据;相关能力账本仍是 Proposed。 | 代码已实现 |
| 多语言 | 所有投影只问 AvailableLocales,且英文完整发布是其他语言出现的前提。 | 目标代数已定义;Gateway 尚未端到端统一 locale 门禁,hreflang / sitemap / canonical / x-default 闭环未完成。 | 目标为主 |
| 缓存与发布 | 单一 CachePolicy;HMAC webhook 入库后依次 revalidateTag 与按 public-html tag purge。 | 策略与部分运行骨架存在;事件表、双层缓存失效和 soft-launch 边缘实测未形成完整证据。 | 未闭环 |
| 询盘 | Inquiry、SubmissionKey、Outbox 同一数据库事务;worker 以 SKIP LOCKED 认领。 | 目标模型已规范化,当前 CMS 尚无 inquiries、submission keys、outbox 实体与全链路实现。 | 尚未实现 |
| 媒体 | R2 不可变对象;SHA-256 进入 key 与 metadata;白名单、magic bytes 和语义化 ProductImage。 | 设计、生成接口与校验方向存在;真实生产媒体源尚未接入 BackupSet 运行入口。 | 未接真实源 |
| 备份 / 恢复 | 数据库快照、其媒体闭包、manifest、Object Lock 和删除账本组成可验证 BackupSet。 | 生产 DB dump、age、B2、字节回读、隔离恢复自检与 heartbeat 有证据;媒体闭包默认为 UnconfiguredMediaSource,完整解密/媒体恢复 drill 未闭环。 | DB 路径已证 |
| 部署 | 签名 digest + ReleaseManifest 准入;foundation 与 application 分状态机激活;CMS schema 变化后只 forward-fix。 | 制品签名、准入和 foundation 状态机有实现/证据;frontend application 的 backup → migration → smoke → active 仍缺闭环证据。 | 部分接通 |
| 可观测性 | exporters → Alloy → Grafana Cloud / Loki;六类故障能独立告警。 | 生产 Compose 与 Alloy 配置已定义采集拓扑;完整 Drill 故障注入、告警区分及 production readback 尚未齐全。 | 拓扑存在 |
状态规则:全部 22 项 capability ledger 条目在本快照仍为 Proposed;“代码已实现”只描述局部代码事实,不提升能力状态。
产品不是一张万能字段表,也不是 EAV。三种产品族共享一个生成代数;各族差异只进入定义,生成器把定义投影到 CMS、校验、前端、筛选与测试。
config/product-family-definitions.tspnpm generate同一概念只经过同一个生成器示例含 13 个 performance profiles、128 行 measurement。定义含 12 个稳定字段、11 个 profile 字段、10 个 measurement 字段与 6 条规则。
17 个稳定字段;waterproof group 有 5 字段 / 7 行;profile 5 字段、measurement 13 字段、7 条规则。
16 个稳定字段;waterproof group 有 5 字段 / 5 行;profile 5 字段、measurement 10 字段、6 条规则。
型号 modelNumber 保留原始业务事实;URL 身份 slug 独立规范化。profile / measurement 保留相关工作点,不能把多个“up to”极值拍平成互不相关枚举。
profile / group 条件是存在量词:同一命中的 profile 行必须同时满足其相关字段。新增族若要求修改 filter.ts,说明变化泄漏进共享逻辑。
所有 slug 变更只经 ChangeCanonicalSlug:同事务写 registry、压成单跳;禁止自环、任意环、历史路径复用和死目标。直接改内容实体的 slug 字段属于边界绕过。
切换器、hreflang、sitemap、canonical、x-default 必须消费同一结果;不可用 locale 返回 404 并给英文链接,不能静默跳转。
category / series landing 读取、published 过滤、字段白名单、documentId 稳定分页、去重与 total 复核、有限重试、生成的 Zod 校验,以及 build / runtime transport 分离。
完整 locale 门禁、独立 no-store PreviewGateway、产品详情、posts / tags / pages、sitemap / hreflang / canonical / JSON-LD,以及询盘与 webhook 相关边界尚未端到端接通。
CI 只制造并准入不可变候选,不能顺手把它变成线上事实。激活权在独立管理通道;每一步由持久 manifest 记录,重试必须收敛到唯一合法状态。
用于先建立 Postgres + Strapi 的生产内容底座。它证明的是 foundation 激活,不等于公开网站已经完成。
完整 frontend application 尚无闭环激活证据。任何一步失败只能从 manifest 状态继续,不能凭进程内猜测“上次做到哪里”。
schemaPhase = none 或本次只有 expand 时允许回到上一 digest。“备份任务成功”不是上传了一个 dump。一个有效 BackupSet 必须能证明:数据库快照可恢复、该快照引用的媒体版本都在、manifest 未被篡改、保留期有效、被法律删除的数据不会因恢复重新出现。
pg_dump 产生 DB@sBackupSet(s) = DB@s + closure(mediaRefs(DB@s)) + manifest。任何一个对象缺失或字节不符,整集失败。
匹配既有 version、延长 compliance retention、验证前一 manifest 的首次绑定,并按 168 个滚动槽做字节抽检。
删除事实独立持久化。恢复旧 BackupSet 后,必须在开放一次性 hostname 之前重放账本,防止已删除主体复活。
pg_dump、age 加密、B2 上传、对象字节回读、隔离 restore self-check 与成功 heartbeat 均有历史生产证据。这些证据只覆盖 DB 路径和相应控制面。
backup-run-cli.ts 默认仍使用 UnconfiguredMediaSource;因此当前生产 BackupSet 的媒体 closure 为空。尚无完整 decrypt + DB + media + ErasureLedger 的 Drill 闭环。
安全模型的核心不是多开防火墙规则,而是把公开读面压成一条路径,并让所有管理、构建、CMS、诊断与 SSH 流量走身份网络。
生产 Compose 服务不写公网 ports。唯一 Web 入口是 Cloudflare Tunnel → cloudflared → 内网 Caddy;公开 health 只允许 status 与短 SHA。
CMS、Preview、SSH、诊断、生产构建读取与激活全部只经 Tailscale。历史证据中人类 ACL 曾存在过宽全局 member grant,最小权限边界仍需闭环。
R2 保存公开不可变媒体;B2 保存 age 加密 BackupSet 与加密 OpenTofu state。age 私钥不得进入 VPS 或仓库。
生产 Compose 共 9 个服务:postgres、cms、frontend、caddy、cloudflared、node-exporter、cadvisor、postgres-exporter、alloy。Local Compose 只含 postgres、cms、frontend。
构建读 token、运行读 token、CMS 管理、R2、B2、Tailscale OAuth、Cloudflare/OpenTofu 权限均由生成式凭证清单约束,不能跨边界复用。生产 secret 不进 PR、镜像层、日志或版本库。
日志只经统一 Logger.info/warn/error(event, fields) 输出单行 JSON;error 必带 append-only 注册表错误码。外部响应正文、凭证、请求头、询盘正文和完整联系方式不得进入日志。
路线图描述实施顺序,不等于能力状态。Phase 0 / 1 有多项代码和证据,但 ledger 仍全部 Proposed;当前阶段值只按 decision log 最后一条阶段决策推进。
仓库规则、digest 锁定、本地 Compose、CI、生成器、synthetic fixture、Strapi schema sync PoC。
已有证据唯一 state、旧站重定向、Tailscale、签名发布、Alloy、BackupSet、ErasureLedger、Object Lock。
治理收尾collections、D&P 边界、dynamic-zone 判别器、Registry、三实体表、10 个真实产品、构建契约。
当前阶段Gateway → 详情 → 聚合 → 筛选 → 博客 → 询盘全链路;功能与测试同 PR。
待进入HMAC webhook、事件表、tag purge、CachePolicy 与双模式收敛实测。
待进入SEO/GEO、a11y、privacy、全量恢复与破坏性 Drill、容量压测、同事手册。
待进入初翻草稿、校对、英文门禁、真实域名 noindex soft-launch、public、purge、预热与回读。
待进入本说明先确定规范权威,再与机器真源、状态账本和证据交叉核对。生成文件和描述性文档不能反向覆盖它们的输入真源。
对仓库目录、规范、代码、运行脚本、IaC、测试与证据做了全量清单盘点;另有 1 个用户未跟踪 settings 文件,未读取改写或纳入交付。
22 项 ledger capability 全部保持 Proposed;45 份 evidence Markdown 用于区分本地验证、历史生产回读和仍缺的门禁。
decision log 提供事件顺序与 Phase 2 决策,6 个 ADR 约束架构不变量;机器真源用于确认实际落地范围。
确认 main@7069ad9c54b2 快照中的规范目标、实际代码结构、生成器、测试、运行配置、状态账本与落账证据之间的关系,并明确当前实现缺口。
本次没有登录 Cloudflare、VPS、Tailscale、R2、B2、Grafana 或公开域名做实时外部回读。因此文中生产结论均标为“历史证据”或“局部生产证据”,不是 Live Verified。
Proposed 是 ledger 当前状态;代码已实现 只说明仓库切片;本地验证 需要本轮可复跑测试;Implemented 需要 Construction / SoftLaunch / Drill 门禁;Live Verified 需要进入 public 后针对真实域名、运行时、缓存、备份心跳、告警链和业务冒烟做非破坏性回读。