zjchina.net

Drogon · ROS 2 · 独立开发笔记

ENGINEERING NOTES/工程笔记

世界负责广度,时间负责深度——先让世界看见,再让时间作证

2026 年 9 月 15 日,我们一行产品代码都没写,只做了一件事:把这个项目的第二句话想清楚,然后存进 git。

一、从一句话,到两句话

这个站开工的第一天,定位只有一句话:让世界看见你的美。

「世界」是空间轴,它只回答一个问题——此刻、任何设备、任何网络,能不能最快看见。这条轴到今天已经有了成绩单:

  • 公开内容页首屏的框架 JavaScript:0。三个手写原生脚本共约 7.7KB(gzip),defer 加载;876KB 的 Dioxus WASM 只做兜底和桌面/移动端;
  • iPhone 13 实测:Service Worker 二访交付 19–42ms,触摸到首绘 83ms,INP 小于 16ms,CLS 为 0;
  • HTTP/3(QUIC)与 HTTP/2 双栈、IPv4/IPv6 双栈、PWA 离线、五个平台可安装、八张启动图、灵动岛与安全区全覆盖;
  • 双进程拓扑:SSR 侧车是高配形态,CSR 薄壳是底线形态,任何一个节点都可以死。

但「世界」回答的是此刻。它不回答:一年后、五年后,这台机器的每一个状态还能不能被验证?每一篇文章的每一个版本还能不能被找回?人与 AI 结对做出的每一个决策,还能不能复盘?

于是今天,这句话长出了第二句:

让时间看见你我的美。

「你我」不是修辞。这个站从第一天起就是人与 AI 结对建造的,那么进化过程本身也是内容:背景、选项、证据、谁拍的板,都要留痕。日后读者看到的不该只是一个成品,而是一条可以倒带的进化轨迹。

两句话不是替代,而是坐标系的两个轴:世界负责广度,时间负责深度。

二、焦虑的真身:不是「像框架」,是同一知识有两份定义

最近有一种焦虑:项目越写越像一个框架了,会不会背离「最前沿、最现代、最性能」的初心?

今天把仓库摊开摸排了一遍,结论是:框架化不是罪,「隐式框架」才是罪。 QoS 式的缓存语义、故障域、降级链、岛屿、内容哈希寻址、侧车熔断——框架的肉身早就长出来了,只是没有统一词汇,每修一次问题都像在给一个没写出来的框架打补丁。

而真正的原罪只有一个:同一知识存在两份定义。 证据是当场抓到的:

  • 两棵同版本(Dioxus 0.8-alpha.1)组件树:CSR 一棵,编译到 wasm/desktop/mobile;SSR 侧车一棵,同名文件平行排列;
  • 三个 Markdown 渲染器:C++ 一个、SSR 侧车 pulldown-cmark 一个、CSR 手写一个;
  • 同一个 enhance_article_html(剥首 H1、注入目录锚点)手抄了两份,其中一份的注释里写着「与 SSR 同语义」。

重复的成本不是「以后可能出事」,而是已经在结账:昨晚发文前给 SSR 端加了 GFM 表格的窄屏滚动包裹,CSR 那份没有同步。这意味着——侧车活着的时候表格完美;一旦侧车死掉、降级到薄壳兜底,含表格的文章底线形态就是破的。我们承诺过「侧车死了用户也不疼」,但在表格这件事上,承诺目前没有兑现。

这就是双树漂移的第一个活证据,也是明天开工第一个要还的债。

三、三条不变量:把审美翻译成可测量

「最前沿、最现代、最性能」是审美宣言,不是工程约束。工程需要可以在 code review 里援引的不变量。我们立了三条:

① 可追溯。 每个新抽象必须能追溯到一个实测数字,或者一次真实事故;说不清债主的债不借,而且借来了也要保持可删。

② 单一事实来源。 同一语义只允许一处定义;一次只动一处契约,禁止两个端各自推断、再祈祷它们一致。

③ 可观测的降级,不静默替换。 每一层兜底都必须自报家门(计划中的响应头:X-Render-Tier: ssr | shell | sw-cache)。因为「系统看似可用、根因长存」比当场宕机更贵——这个教训用真实的联调时间换过。

四、SSR 还是 CSR?答案是:都不选

第二个思变是老问题:组件化的主定义入口到底放在 SSR 还是 CSR?我们认真过了三个选项:

  • 选项 A:All-in 全栈水合。 代价是确定的——876KB WASM 回到首屏,零水合的全部护城河上缴;而 Dioxus 0.8 仍是 alpha,水合硬性要求服务端与客户端生成完全同构的 VirtualDOM。否决。
  • 选项 B:SSR 做主入口。 SSR 真源喂不活 desktop/mobile 打包端,也主导不了后台这类真应用。否决。
  • 选项 C(选定):主入口既不是 SSR 也不是 CSR,而是一份与渲染目标无关的契约核心;SSR 与 CSR 只是它的两个编译后端。

这恰好也是 Flutter 给出的真正答案,要分清学什么、不学什么:

  • :一份描述、多个渲染后端;平台能力走通道隔离——凡是碰 windowdocument、canvas 的代码,永远住在客户端节点里,可 SSR 的组件禁止出现副作用。这一条从纪律上免疫水合失配。
  • 不学:canvas 自绘。公开 Web 页的 DOM、SEO 与零 JS 首屏是资产,不是负担。

渲染模式按路由分成三条轨道,写进路由类型,新增页面不声明就编译不过:

  • Content 轨道:公开文章,SSR HTML 终态,永不全页水合,交互交给独立的、可逐个删除的行为岛;
  • App 轨道:后台与未来的重交互工具,CSR WASM,未来可选升级为「水合岛」;
  • Shell 轨道:desktop/mobile 打包内的全部路由,CSR 内核全量。

Dioxus 继续用,但只把它当「组件描述语言 + 可插拔渲染器」,不押注 alpha 阶段的全栈一体化;C++ Drogon 门卫(鉴权、爆破防护、数据源隔离)是差异化资产,不被任何框架吞并。

五、用 ROS 2 的纪律,取 Web3 的零件

第一个思变听起来大:怎么把 Web3 引进来?答案是先把 Web3 切成两层分别审判。

金融上层——发币、token、DAO、链上结算、强制钱包——对单作者内容站是纯负债:我们不存在「互不信任的多方共识」这个问题,却要为此支付性能、合规和国内可达性的全部成本。永不引入。

密码学与分布式原语层才是财富。取法也很朴素:不追逐概念,用我们最熟的 ROS 2 思想去对位,而且每一刀都要对着一笔已经欠着的债:

  • QoS profile 对位缓存契约:Service Worker 的 stale-while-revalidate、nginx 的 immutable、侧车的 micro-cache,本质上是手写的 DDS QoS,却散在三处各写一遍。收敛成一份缓存契约清单,三层行为从同一清单派生。
  • keystore 对位身份:共享口令(管理密码、内部密钥)升级为密钥体系——人用 passkey(WebAuthn)登录,节点之间用签名回环,像 SROS2 的节点证书,而不是一把共享字符串。
  • 内容寻址对位版本债:静态资源已经半指纹化(WASM 内容哈希、CSS 版本号),差最后一步——构建时产出内容清单,SW 的预缓存集合从指纹清单派生,彻底消灭手改缓存常量(v5 一路改到 v9)这类版本债。
  • rosbag 对位事件日志:发布、编辑、部署、熔断、前端错误全部进 append-only 日志,状态可以从日志重放;文章的每个版本留下指纹、指向上一版——「修订历史」正好和《48 小时进化》系列叙事合体,读者真的能追溯机器的每一步。
  • lifecycle 对位降级链:SSR → 薄壳 → SW 缓存,每一级都显式自报层级,降级不再沉默。
  • 欺诈证明对位 SWR:SW 已经在乐观地给缓存、后台重验,补上密码学那一半——重验回来的内容与预期指纹不符,就拒绝采用并告警。
  • 发现与对等(远期):IPFS 只允许做降级链最末端的只读镜像,绝不做主分发;多侧车自动发现,等单侧车出现实测瓶颈那天再说。

一句话:Web3 对这个站的正确身份,是后端与信任层的现代化,不是前端堆料。

六、五个阶段,每一步都可回滚

路线图已经成文(仓库 docs/LET_TIME_SEE.md),顺序是锁死的:

  • Phase 0 · 地基:抽出中立契约层,第一件事就是 Markdown 单份化,顺手补上薄壳兜底下的表格缺口;
  • Phase 1 · 缓存经得住时间:内容指纹清单驱动 SW、X-Render-Tier 上线、缓存契约三处合一;
  • Phase 2 · 身份经得住时间:节点签名回环、passkey 登录、SWR 指纹校验;
  • Phase 3 · 内容经得住时间:append-only 事件日志、文章修订指纹链、决策记录——本文就是第 0 号;
  • Phase 4 · 分发经得住时间:IPFS 末层镜像、多侧车发现,触发制,没有实测瓶颈不动工。

每一步独立上线、独立回滚、带证据验收。明天开工只做 Phase 0 的第一件事:不碰 C++、不碰部署拓扑、不碰渲染模式。完成定义也很朴素——薄壳下表格正常、侧车既有测试全绿、一个注明「行为零变化」的 commit。

七、给时间的承诺

把红线也公开写在这里,方便日后的读者监督:

  • 永不发币、永不发 token、永不搞钱包绑架;
  • 公开内容页永不全页水合,零框架 JS 首屏是已被实测验证的护城河;
  • 永不静默降级,每一层兜底都必须自报层级;
  • 一次只动一处契约,先证据后改动,每次改动都给出回滚路径;
  • 国内可达性与合规,永远排在技术意识形态前面。

尾声

这个站的第一阶段,我们向世界证明了一台 Rust + C++26 + Dioxus 的机器能跑多快;第二阶段,我们想向时间证明它能多可信。

世界负责广度,时间负责深度。先让世界看见,再让时间作证。

← 返回首页