项目 · 2026 · 作者

abualruz-homelab:这个网站和运行它的基础设施

Astro 6 + Svelte 5 个人站点,通过 Caddy 和 Docker 从 homelab 镜像提供服务,按阶段迭代交付,并配有完整的 agent 工作手册--既是个人站点,也是一场关于 AI 编程 agent 能否撑起正经项目的公开实验。

Screenshot of the abualruz-homelab GitHub repo page showing the site + infra monorepo

这是元项目:你正在阅读的这个站点、镜像它的 homelab 基础设施,以及构建了两者的 agent 工作手册。从某种非常字面的意义上说,它也是一场持续进行的实验—用 AI 编程 agent 作为主要劳动力来交付生产级个人站点,最终能不能做出一个让自己不羞于承认的代码库。

目前的答案是:可以—但附带相当多的星号。

为什么它存在

两个原因。第一个无聊:我需要一个个人站点。我”打算做”这件事已经好几年了,而”打算做”是所有 side project 去送死的地方。第二个更有意思:我想要一个真实的项目—不是玩具,不是教程,不是那种「带一个 deploy 按钮的 hello world」—来压力测试我用 Fulcrum 搭建的 agent 辅助开发工作流。

个人站点完美胜任这个角色。需求是真实的。设计决策是有意义的。要写真正的内容,要达到真正的性能预算,还有那些真正会以尴尬方式配错的 CSP header,然后再去修。但踩坑的后果完全由我一个人承担,这意味着我可以比在客户项目上更快地前进、更显眼地失败。

每个 Phase 都附带进度日志和 lessons 文档。这不是摆架子的文档仪式—这些文档是下一次 agent 运行的输入。Chief-of-Staff 在 dispatch Phase N+1 的工作之前,会读 lessons 文档。写下来这件事的纪律感,就是让下一个阶段更快的纪律感。

它是怎么运作的

站点是 Astro 6,交互岛屿用 Svelte 5。Astro 做了它应该做的事:静态页面几乎零 JavaScript,需要交互的部分按需 hydration。Svelte 5 的 runes 模型是真的好用—对于个人站点这种小而专注的交互需求,响应式原语比 React hooks 更干净。

内容是 MDX 文件,放在结构化的 collection schema 里,由 Zod 在构建时做验证。Topic 是 enum(定义在 src/data/topics.ts 里,被 schema 和 hub 页面同时消费),所以 frontmatter 字段里的拼写错误会大声让 build 失败,而不是静默产生一个 404。我越来越喜欢对内容站点做构建时 schema 验证。反馈循环快,报错具体。

homelab 镜像跑在我家网络里的一台机器上,前面是 Caddy 反向代理。Docker Compose 管理服务;Caddy 做 TLS 终结和站点静态文件服务。Content security policy 住在和静态资源一起 serve 的 _headers 文件里—Caddy 原封不动地透传它们,这是正确的职责划分。

Deploy 是一个 GitHub Actions workflow,构建 Astro 站点,把输出推到服务器,重启 Docker 服务。不花哨。也不需要花哨。那种让你觉得自己很聪明的 deploy 流水线,通常就是在你晚上 11 点想发修复时崩掉的那种。

有意思的地方

基于阶段的交付模型不是原计划的一部分。它在第一次 agent 运行里自然涌现—那次运行产生了一个 Phase 0,比我自己事先设计的任何东西都更有条理:一个最小可用站点,有 content schema、能运行的 build 和 deploy 流水线—以及一份列出了接下来该做什么的跟进文档。那个结构是承重的。

「每个 phase 都有 lessons 文档」的有趣之处在于,它迫使你在完全忘掉上下文之前,把你学到的东西说清楚。这听起来是废话,但实际上大多数项目回顾发生在工作结束三周后,具体细节早已褪色,你写出来的全是泛化的废话。在 phase 交付当天写 lessons 文档,产出的是精确得多的观察。「CSP 的 unsafe-inline 权宜之计需要在 Astro 的 meta CSP 方案足够稳定之前解决」是一条有用的笔记。「CSP 很复杂」不是。

Svelte 5 + Astro 的组合也迫使我认真想清楚哪些东西需要交互,哪些不需要。现代 Web 开发的默认假设是一切都要交互;Astro 把这个假设倒过来,在这个约束下工作,产出了一个比我用 SPA 框架做会快得多的站点。

我会改什么

homelab 镜像在单台机器上,没有故障切换。这是刻意的选择—不需要的复杂度是会咬你的复杂度—但它也意味着我做硬件维护时站点会宕机,而这发生的频率比我愿意承认的要高。第二台机器做 active-passive 故障切换、Caddy 作为入口,已经在未来某个 phase 的计划里了。

CSP 的故事还在进行中。当前的 _headers 文件在 Astro 原生 meta-tag CSP 方案成熟之前使用 unsafe-inline 作为权宜之计。这是已知的技术债,有追踪,上游情况稳定时会修。

我对未来阶段最感兴趣的事:把站点本身作为 agent 工作流的输出展示面。现在进度日志和 lessons 文档是我去读的 Markdown 文件。我想要的是一个结构化的 agent 记忆层—Fulcrum,当然—把这些文档作为 L0 来源摄入,在 COS 规划下一个阶段时自动浮现经过策划的 lessons。基础设施已经在那里了。我只需要把它正确地串起来。

这才是真正的元项目:构建工具,然后用工具构建项目,然后把项目的 lessons 反馈回工具。这个循环正在开始闭合。