Skip to content

Commit 1a64fb8

Browse files
committed
docs: organize the Farrow blog
1 parent 2240767 commit 1a64fb8

24 files changed

Lines changed: 1232 additions & 20 deletions

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,7 @@ the minimum required by Oink; `HUGO_SERVER` is not a recognized version selector
3939

4040
English and Chinese pages live beside each other as `page.md` and
4141
`page.zh.md`. Keep them aligned, concise, and grounded in the current checkout.
42+
Blog posts live under `content/blog/article`, `content/blog/design`, or
43+
`content/blog/release`; do not put regular posts directly under `content/blog`.
4244
Historical Piglet release and evidence records retain their original identity
4345
and must not be presented as post-rename Farrow validation.

content/blog/_index.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@ cascade:
1111
theme_color_dark: '#fda4af'
1212
---
1313

14-
Current notes and preserved development history. Older entries retain the
15-
Piglet name because they describe pre-rename artifacts, not current behavior.
14+
Articles, design notes, release notes, and preserved development history.
15+
Older entries retain the Piglet name because they describe pre-rename
16+
artifacts, not current behavior.

content/blog/_index.zh.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ cascade:
1111
theme_color_dark: '#fda4af'
1212
---
1313

14-
当前动态与保留的开发历史。旧条目继续使用 Piglet 名称,因为它们描述的是迁名前构件,
15-
不代表当前行为。
14+
项目文章、设计注记、发布注记与保留的开发历史。旧条目继续使用 Piglet 名称,因为它们
15+
描述的是迁名前构件,不代表当前行为。

content/blog/article/_index.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
title: Article
3+
linkTitle: Article
4+
description: Project articles and long-form technical notes about Farrow.
5+
weight: 10
6+
icon: fa-solid fa-newspaper
7+
sidebar_root_menu: false
8+
---
9+
10+
Project articles and long-form technical notes about Farrow live in this
11+
section. For current product behavior, use the [documentation](/docs/).

content/blog/article/_index.zh.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
title: 文章
3+
linkTitle: 文章
4+
description: 关于 Farrow 的项目文章与长篇技术记录。
5+
weight: 10
6+
icon: fa-solid fa-newspaper
7+
sidebar_root_menu: false
8+
---
9+
10+
这里收录 Farrow 的项目文章与长篇技术记录。当前产品行为请以[文档](/zh/docs/)为准。

content/blog/design/_index.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
---
2+
title: Design Notes
3+
linkTitle: Design
4+
description: Architecture decisions, trade-offs, and implementation boundaries behind Farrow.
5+
weight: 20
6+
icon: fa-solid fa-pen-ruler
7+
sidebar_root_menu: false
8+
sidebar_expanded: true
9+
blog_index: list
10+
---
11+
12+
Farrow's source repository once carried the redesign brief, implementation
13+
ADRs, and native evidence beside the code. That material was useful while the
14+
product was changing quickly, but many early decisions were later superseded.
15+
This section keeps the durable reasoning, rewritten against the current source
16+
instead of republishing stale plans.
17+
18+
Start with the product model, then follow the boundaries outward:
19+
20+
1. [Why Farrow has no projects](one-deployment-no-projects/) — one Inventory,
21+
one owner-scoped deployment, and no second source of truth.
22+
2. [Why every node has two NICs](fixed-ip-two-nics/) — fixed identity for the
23+
lab, separate from management egress.
24+
3. [Declarative does not mean destructive](convergence-without-surprise/)
25+
per-node drift with explicit recreate and removal.
26+
4. [A PID is not a virtual machine](identity-before-pid/) — QMP identity,
27+
process evidence, journals, and bounded recovery.
28+
5. [`repo.yaml` is intent; `catalog.json` is evidence](repo-yaml-catalog-json/)
29+
— a static image repository whose generated metadata is checked against the
30+
actual qcow2 bytes.
31+
32+
Use the [documentation](/docs/) for current behavior and the
33+
[status page](/docs/about/status/) for dated verification. These design records
34+
explain why those contracts exist; they do not turn a design, build, or local
35+
test into release evidence.

content/blog/design/_index.zh.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
title: 设计注记
3+
linkTitle: 设计
4+
description: Farrow 的架构决策、方案取舍与实现边界。
5+
weight: 20
6+
icon: fa-solid fa-pen-ruler
7+
sidebar_root_menu: false
8+
sidebar_expanded: true
9+
blog_index: list
10+
---
11+
12+
Farrow 源码仓库曾经把重构指令、实现期 ADR 与真机证据和代码放在一起。它们在产品快速变化时
13+
很有价值,但其中不少早期结论后来已经被推翻。本栏目只保留仍然成立的设计理由,并以当前
14+
源码重新表述,而不是把过时计划原样发布。
15+
16+
建议从产品模型开始,再沿着边界向外阅读:
17+
18+
1. [为什么 Farrow 没有 Project](one-deployment-no-projects/):一份 Inventory、一个
19+
Owner-scope Deployment,不制造第二份事实。
20+
2. [为什么每个节点都有两张网卡](fixed-ip-two-nics/):实验室固定身份与管理出网分离。
21+
3. [声明式不等于破坏式](convergence-without-surprise/):逐节点 Drift、显式重建与删除。
22+
4. [PID 不是虚拟机身份](identity-before-pid/):QMP 身份、进程证据、Journal 与有界恢复。
23+
5. [`repo.yaml` 是意图,`catalog.json` 是证据](repo-yaml-catalog-json/):生成元数据必须与
24+
实际 qcow2 字节一致的静态镜像仓库。
25+
26+
当前行为以[文档](/zh/docs/)为准,带日期的验证边界见[当前状态](/zh/docs/about/status/)
27+
这些设计记录解释契约为何存在;它们不会把设计、构建或本地测试升级成发布证据。
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
---
2+
title: "Declarative Does Not Mean Destructive"
3+
linkTitle: "Convergence without surprise"
4+
description: "How Farrow uses per-node hashes and explicit operations so missing configuration can never authorize deletion."
5+
date: 2026-08-27T21:00:00+08:00
6+
weight: 30
7+
categories: [Design]
8+
tags: [Lifecycle, Drift, Safety]
9+
icon: fa-solid fa-arrows-rotate
10+
---
11+
12+
“Declarative” is often shortened to “make reality equal the file.” That is a
13+
useful slogan until the file is incomplete, the wrong branch is checked out,
14+
or one YAML group is temporarily removed. If absence is treated as deletion,
15+
an ordinary editing mistake becomes a destructive operation.
16+
17+
Farrow uses a narrower rule:
18+
19+
> Desired state may authorize creation. It can describe drift. It never
20+
> authorizes destruction by omission.
21+
22+
The distinction is central to a VM runtime because roots, data disks, SSH keys,
23+
and local evidence are not stateless replicas. Recreating them may be correct,
24+
but it must be a decision the operator can see.
25+
26+
## From Inventory to node identity
27+
28+
Farrow does not hash the whole Pigsty Inventory. It first extracts the fields
29+
it owns, fills defaults, resolves image and runtime choices, and builds a
30+
canonical resolved spec. Each node then receives a hash of:
31+
32+
- the deployment envelope shared by every node, such as subnet, login user,
33+
and architecture policy; and
34+
- exactly that node's resolved definition.
35+
36+
Adding a peer therefore does not change an existing node's hash. Editing an
37+
unconsumed Pigsty field—PostgreSQL version, packages, or service policy—does
38+
not produce VM drift. The VM layer reacts only to the contract it actually
39+
understands.
40+
41+
This also avoids a dangerous half-promise: Farrow does not pretend to implement
42+
Ansible's entire variable system. Unknown `vm_*` keys and conflicting values
43+
inside the owned namespace fail. Everything outside the documented boundary
44+
is opaque rather than partially interpreted.
45+
46+
> [!NOTE]
47+
> **Decision status: current.** Farrow converges additions automatically, but
48+
> definition changes and removal require explicit commands. See
49+
> [Daily Operations](/docs/start/operations/) for the command workflow.
50+
51+
## The five plan outcomes
52+
53+
`farrow plan` compares desired state, applied deployment state, and committed
54+
node state. The result is intentionally small:
55+
56+
| Outcome | Meaning | Apply path |
57+
| --- | --- | --- |
58+
| create | desired node has no committed state | `farrow up` creates it |
59+
| unchanged | definition and runtime still match | running peer stays untouched; stopped peer may start |
60+
| recreate | node definition changed | explicit `farrow recreate --force <node>` |
61+
| missing | applied node is absent or skipped in the Inventory | explicit `farrow destroy <node> --force`, or restore it to the file |
62+
| envelope drift | subnet, login identity, architecture, or runtime policy changed | whole-deployment recreate |
63+
64+
Plan is read-only. It reports the exact node sets and, in text mode, the command
65+
that applies the required explicit transition.
66+
67+
## Why `up` stops at drift
68+
69+
Farrow could decide that changing CPU or memory is harmless enough to apply,
70+
or that a new image should silently rebuild a root disk. Pre-1.0 intentionally
71+
does neither. A changed VM definition is classified as recreate and `up`
72+
returns a typed conflict.
73+
74+
That conservative boundary has two advantages:
75+
76+
1. all changes that can invalidate Guest state share one visible operation;
77+
2. Farrow can finish every prerequisite check before touching the current
78+
node.
79+
80+
The recreate path resolves the selected emulator, acceleration policy,
81+
firmware, image bytes, network backend, shares, and persistent-disk contract
82+
before destruction. If a foreign emulator is missing or a share is unsafe,
83+
the existing VM remains intact.
84+
85+
## Why missing nodes block convergence
86+
87+
A node can disappear from desired state for many reasons that do not express
88+
deletion intent:
89+
90+
- the operator opened a reduced Inventory while debugging;
91+
- a group was renamed or filtered;
92+
- `vm_skip` temporarily marks a real or external host;
93+
- a merge conflict dropped a YAML branch;
94+
- the configuration file itself is unavailable.
95+
96+
When applied state contains such a node, `up` stops and names it. The operator
97+
must either restore the definition or run the explicit destroy command. This
98+
is deliberately more friction than automatic garbage collection—and far less
99+
friction than recovering an unintended disk deletion.
100+
101+
Persistent data disks add another boundary. Normal destroy preserves them;
102+
purging disks and deployment keys requires the separate whole-deployment purge
103+
contract. One confirmation cannot silently grow into broader authority.
104+
105+
## Convergence is still incremental
106+
107+
Safety does not mean rebuilding everything. New nodes are created without
108+
stopping existing peers. Selected stopped nodes start without recreating
109+
running ones. A per-node recreate preserves peers and, when requested by the
110+
disk contract, persistent data.
111+
112+
The result is declarative where desired state is strong evidence—creation and
113+
comparison—and explicit where the cost is irreversible. Farrow does not make
114+
the operator manually calculate drift, but it also does not confuse a diff
115+
with permission.
116+
117+
Read next: [A PID is not a virtual machine](/blog/design/identity-before-pid/).
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
---
2+
title: "声明式不等于破坏式"
3+
linkTitle: "不制造意外的收敛"
4+
description: "Farrow 如何用逐节点哈希与显式操作保证配置缺席永远不能授权删除。"
5+
date: 2026-08-27T21:00:00+08:00
6+
weight: 30
7+
categories: [设计]
8+
tags: [生命周期, Drift, 安全]
9+
icon: fa-solid fa-arrows-rotate
10+
---
11+
12+
“声明式”常被简化成“让现实等于文件”。在文件完整、分支正确时,这是一句好口号;但当配置
13+
暂时不完整、检出了错误分支,或某个 YAML Group 被误删时,把“缺席”理解成“删除”就会让普通
14+
编辑错误直接变成破坏操作。
15+
16+
Farrow 采用一条更窄的规则:
17+
18+
> 期望状态可以授权创建,也可以描述 Drift;它永远不能通过省略来授权销毁。
19+
20+
这对 VM Runtime 尤其重要。Root Disk、Data Disk、SSH Key 与本地证据并不是无状态副本。
21+
重建可能是正确选择,但必须是操作者能看见并明确作出的决定。
22+
23+
## 从 Inventory 得到节点身份
24+
25+
Farrow 不会对整份 Pigsty Inventory 做哈希。它先提取自己拥有的字段、填入默认值、解析镜像
26+
与运行时选择,再构造 Canonical Resolved Spec。每个节点的哈希只包含:
27+
28+
- 所有节点共享的 Deployment Envelope,例如子网、登录用户与架构策略;
29+
- 该节点自己的完整 Resolved Definition。
30+
31+
因此,增加一个同伴不会改变已有节点的哈希;修改 Farrow 不消费的 Pigsty 字段——例如
32+
PostgreSQL 版本、软件包或服务策略——也不会产生 VM Drift。VM 层只响应自己真正理解的契约。
33+
34+
这也避免了一项危险的半承诺:Farrow 不假装实现完整 Ansible 变量系统。自有命名空间里的未知
35+
`vm_*` 字段与冲突值会失败;记录边界之外的内容保持不透明,而不是只解释一半。
36+
37+
> [!NOTE]
38+
> **决策状态:当前有效。** Farrow 自动收敛新增节点,但定义变更与删除都需要显式命令。
39+
> 命令流程见[日常管理](/zh/docs/start/operations/)
40+
41+
## Plan 只有五种结果
42+
43+
`farrow plan` 对比期望状态、Applied Deployment State 与已提交的 Node State,结果刻意保持精简:
44+
45+
| 结果 | 含义 | 应用路径 |
46+
| --- | --- | --- |
47+
| create | 期望节点没有已提交状态 | `farrow up` 创建 |
48+
| unchanged | 定义与运行时仍一致 | 运行中同伴不动;已停止节点可以启动 |
49+
| recreate | 节点定义发生变化 | 显式 `farrow recreate --force <node>` |
50+
| missing | Applied Node 在 Inventory 中缺席或被跳过 | 显式 `farrow destroy <node> --force`,或恢复配置 |
51+
| envelope drift | 子网、登录身份、架构或运行时策略改变 | 整套 Deployment 重建 |
52+
53+
Plan 完全只读。它列出准确节点集合,并在文本模式中给出应用显式转换的命令。
54+
55+
## 为什么 `up` 遇到 Drift 会停下
56+
57+
Farrow 可以把 CPU/内存变化认定为“足够安全”并自动应用,也可以在镜像变化时静默重建根盘。
58+
Pre-1.0 刻意不这样做:任何 VM 定义变化都归类为 Recreate,`up` 返回类型化冲突。
59+
60+
这条保守边界有两个好处:
61+
62+
1. 所有可能使 Guest 状态失效的变化共享一个可见操作;
63+
2. Farrow 可以在触碰当前节点前完成全部前置检查。
64+
65+
Recreate 会先解析 Emulator、加速策略、Firmware、镜像字节、网络后端、Share 与 Persistent Disk
66+
契约,再开始销毁。若外来架构 Emulator 缺失,或一个 Share 不安全,已有 VM 会保持原样。
67+
68+
## 为什么 Missing Node 会阻止收敛
69+
70+
节点从期望状态中消失,可能来自许多并不代表删除意图的原因:
71+
72+
- 调试时打开了一份缩减 Inventory;
73+
- Group 被重命名或过滤;
74+
- `vm_skip` 暂时标记一台真机或外部节点;
75+
- Merge Conflict 丢掉一段 YAML;
76+
- 配置文件本身暂时不可用。
77+
78+
Applied State 仍包含这种节点时,`up` 会停止并点名报告。操作者必须恢复定义,或运行显式
79+
Destroy 命令。这比自动垃圾回收多一点摩擦,却比恢复被意外删除的磁盘少得多。
80+
81+
Persistent Data Disk 还有独立边界:普通 Destroy 会保留它们;清除磁盘与 Deployment Key
82+
需要另一套整 Deployment Purge 契约。一次确认不能静默扩张成更宽的授权。
83+
84+
## 收敛仍然是增量的
85+
86+
安全并不意味着全部重建。新增节点创建时不会停止已有同伴;选中的已停止节点可以直接启动,
87+
不会重建运行中节点;逐节点 Recreate 保留其它节点,并按磁盘契约保留 Persistent Data。
88+
89+
最终,Farrow 在期望状态足以构成强证据的地方——创建与比较——保持声明式;在代价不可逆的
90+
地方保持显式。它不会让操作者手工计算 Drift,也不会把 Diff 错当成权限。
91+
92+
下一篇:[PID 不是虚拟机身份](/zh/blog/design/identity-before-pid/)

0 commit comments

Comments
 (0)