<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>南墙 / SOUTH WALL</title><link>https://south-wall.com/tags/%E5%AE%B6%E5%BA%AD%E7%BD%91%E7%BB%9C/</link><description>南墙 / SOUTH WALL — 个人博客</description><generator>Hugo</generator><language>zh-CN</language><lastBuildDate>Tue, 30 Jun 2026 00:00:00 +0800</lastBuildDate><atom:link href="https://south-wall.com/tags/%E5%AE%B6%E5%BA%AD%E7%BD%91%E7%BB%9C/" rel="self" type="application/rss+xml"/><item><title>把全家照片从云端搬回卧室：一台自组 NAS 的选型全是取舍</title><link>https://south-wall.com/posts/moonshade-nas/</link><pubDate>Tue, 30 Jun 2026 00:00:00 +0800</pubDate><guid>https://south-wall.com/posts/moonshade-nas/</guid><description>一台放卧室、24×7 常开的自组 Mini-ITX NAS——65W APU、ZFS 双池、tailnet-only 不上公网。拆开每处选型的取舍：安静、省电、数据自主，以及至今没做到的异地备份。</description><content:encoded>&lt;section id="ch-00" class="page cover" data-screen-label="00 Cover"&gt;
&lt;div class="imprint no-img"&gt;
&lt;span class="wordmark"&gt;
&lt;img class="brand-mark" src="https://south-wall.com/lotus.png" alt="Lotus brand mark" width="36" height="36" /&gt;
&lt;span&gt;MOONSHADE&amp;nbsp;&amp;nbsp;·&amp;nbsp;&amp;nbsp;&lt;b&gt;Bedroom NAS Architecture&lt;/b&gt;&lt;/span&gt;
&lt;/span&gt;
&lt;div class="by" style="margin-left:auto;font-family:var(--mono);font-size:10px;letter-spacing:.14em;color:var(--ink-500);text-transform:uppercase;line-height:1.5"&gt;
Published&amp;nbsp;&lt;b style="display:block;color:var(--ink-900);font-weight:600;letter-spacing:.1em;margin-top:2px"&gt;2026-06-30 · neutralized&lt;/b&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="kicker"&gt;SELF-HOSTED INFRASTRUCTURE · HOME NAS · 2026-06-30 · V1.0&lt;/div&gt;
&lt;h1&gt;把全家的照片&lt;br/&gt;从云端搬回卧室&lt;/h1&gt;
&lt;p class="lede"&gt;一台放在卧室、24×7 常开的自组 Mini-ITX NAS：&lt;b&gt;65W APU&lt;/b&gt;、&lt;b&gt;ZFS 双池&lt;/b&gt;、&lt;b&gt;tailnet-only 不上公网&lt;/b&gt;，再加一整套自托管照片与门户服务。它叫 MoonShade。&lt;/p&gt;
&lt;p class="lede" style="font-family:var(--mono);font-size:13.5px;color:var(--ink-500);max-width:820px;margin-top:-24px;"&gt;A self-built, always-on bedroom NAS that hosts a family's entire photo pipeline on its own disks — every part chosen against one target: quiet, low-power, private, and yours.&lt;/p&gt;
&lt;div class="toc"&gt;
&lt;div class="toc-item"&gt;&lt;div class="n"&gt;01&lt;/div&gt;&lt;div class="t"&gt;&lt;a href="#ch-01"&gt;为什么自己攒一台卧室 NAS / Why build one at all&lt;/a&gt;&lt;/div&gt;&lt;div class="s"&gt;P. 02&lt;/div&gt;&lt;/div&gt;
&lt;div class="toc-item"&gt;&lt;div class="n"&gt;02&lt;/div&gt;&lt;div class="t"&gt;&lt;a href="#ch-02"&gt;它在家里是什么节点 / The one hub on a private mesh&lt;/a&gt;&lt;/div&gt;&lt;div class="s"&gt;P. 03&lt;/div&gt;&lt;/div&gt;
&lt;div class="toc-item"&gt;&lt;div class="n"&gt;03&lt;/div&gt;&lt;div class="t"&gt;&lt;a href="#ch-03"&gt;硬件底盘：安静是硬指标 / The silent chassis&lt;/a&gt;&lt;/div&gt;&lt;div class="s"&gt;P. 04&lt;/div&gt;&lt;/div&gt;
&lt;div class="toc-item"&gt;&lt;div class="n"&gt;04&lt;/div&gt;&lt;div class="t"&gt;&lt;a href="#ch-04"&gt;存储地基 ZFS / Storage on ZFS&lt;/a&gt;&lt;/div&gt;&lt;div class="s"&gt;P. 05&lt;/div&gt;&lt;/div&gt;
&lt;div class="toc-item"&gt;&lt;div class="n"&gt;05&lt;/div&gt;&lt;div class="t"&gt;&lt;a href="#ch-05"&gt;服务栈：一台机器长出的一整套服务 / The service stack&lt;/a&gt;&lt;/div&gt;&lt;div class="s"&gt;P. 06&lt;/div&gt;&lt;/div&gt;
&lt;div class="toc-item"&gt;&lt;div class="n"&gt;06&lt;/div&gt;&lt;div class="t"&gt;&lt;a href="#ch-06"&gt;网络接入与安全模型 / Access &amp;amp; security&lt;/a&gt;&lt;/div&gt;&lt;div class="s"&gt;P. 07&lt;/div&gt;&lt;/div&gt;
&lt;div class="toc-item"&gt;&lt;div class="n"&gt;07&lt;/div&gt;&lt;div class="t"&gt;&lt;a href="#ch-07"&gt;会自己照顾自己 / Automation, human-in-the-loop&lt;/a&gt;&lt;/div&gt;&lt;div class="s"&gt;P. 08&lt;/div&gt;&lt;/div&gt;
&lt;div class="toc-item"&gt;&lt;div class="n"&gt;08&lt;/div&gt;&lt;div class="t"&gt;&lt;a href="#ch-08"&gt;门户与数据流 / Portal &amp;amp; data flows&lt;/a&gt;&lt;/div&gt;&lt;div class="s"&gt;P. 09&lt;/div&gt;&lt;/div&gt;
&lt;div class="toc-item"&gt;&lt;div class="n"&gt;09&lt;/div&gt;&lt;div class="t"&gt;&lt;a href="#ch-09"&gt;演进史：从 WSL2 到裸机 / How it got here&lt;/a&gt;&lt;/div&gt;&lt;div class="s"&gt;P. 10&lt;/div&gt;&lt;/div&gt;
&lt;div class="toc-item"&gt;&lt;div class="n"&gt;10&lt;/div&gt;&lt;div class="t"&gt;&lt;a href="#ch-10"&gt;备份、内存与取舍 / Backup, memory, honesty&lt;/a&gt;&lt;/div&gt;&lt;div class="s"&gt;P. 11&lt;/div&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="meta-strip"&gt;
&lt;span&gt;SCOPE&lt;b&gt;One machine — hardware to portal&lt;/b&gt;&lt;/span&gt;
&lt;span&gt;CHASSIS&lt;b&gt;Mini-ITX · Ryzen APU · 65 W&lt;/b&gt;&lt;/span&gt;
&lt;span&gt;STORAGE&lt;b&gt;ZFS · RAID-Z1 · ~8 TB usable&lt;/b&gt;&lt;/span&gt;
&lt;span&gt;ACCESS&lt;b&gt;tailnet-only · trusted HTTPS&lt;/b&gt;&lt;/span&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="ch-01" class="page" data-screen-label="01 Why build one"&gt;
&lt;div class="head"&gt;
&lt;div class="kicker"&gt;CHAPTER 01 · 立项&lt;/div&gt;
&lt;h1&gt;它存在的理由不是炫技，&lt;br/&gt;是把照片赎回来&lt;/h1&gt;
&lt;div class="en-sub"&gt;The point isn't a cool rig — it's pulling private data out of a rented cloud and onto a machine you fully control.&lt;/div&gt;
&lt;p class="sub"&gt;这台机器没有一处是为了「跑分好看」。它要解决的是一个很具体的焦虑：全家最重要的照片，正托管在一家随时可以改价、改条款、改可用性的商业云上。把它们搬回一台自己完全掌控、放在卧室、常年开着的盒子里——这是所有后续选型的出发点。&lt;/p&gt;
&lt;/div&gt;
&lt;p class="sub" style="margin-top:24px"&gt;MoonShade 是一台&lt;b&gt;自组 Mini-ITX 一体机&lt;/b&gt;，不是买来的成品 NAS。立项动机是三件事叠在一起：&lt;b&gt;数据主权&lt;/b&gt;（照片「真身」握在自己盘里）、&lt;b&gt;省掉云订阅&lt;/b&gt;（一次性硬件投入换长期不按月付费）、&lt;b&gt;完全掌控&lt;/b&gt;（自己攒、自己定架构）。核心诉求写成一句话——&lt;b&gt;桌面级性能 × 服务器级静音功耗 × 卧室可接受噪音&lt;/b&gt;。&lt;/p&gt;
&lt;p class="sub" style="margin-top:20px"&gt;选择自己攒而不是买成品，代价很清楚：要自己选料、自己装、自己维护。换回来的，是这几样成品机不容易同时给到的东西——核显硬件加速、跑得动完整 Docker 服务栈加机器学习负载、系统盘与数据盘物理分离、以及 ZFS 的完全自主。这台机器还兼一个身份：它自己托管着一个 AI agent 运行时（OpenClaw），由那个 agent 实例充当「NAS 管家」，日常看护这台机器的运维。&lt;/p&gt;
&lt;div class="quote"&gt;
&lt;div class="caps"&gt;立项判断 · THE PREMISE&lt;/div&gt;
&lt;p&gt;一台家用 NAS 值不值得自己攒，不取决于它多快，而取决于你愿不愿意为「数据握在自己手里」承担&lt;b&gt;自攒自维的麻烦&lt;/b&gt;。这篇通篇讲的都是这份麻烦具体长什么样。&lt;br/&gt;&lt;span style="font-family:var(--mono);font-size:13px;color:var(--focus-ink);line-height:1.55;display:block;margin-top:8px;"&gt;Whether to build your own NAS isn't about speed — it's about whether you'll accept the cost of building and maintaining it, in exchange for holding your own data. This whole piece is what that cost looks like.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="ch-02" class="page" data-screen-label="02 One hub on a mesh"&gt;
&lt;div class="head"&gt;
&lt;div class="kicker"&gt;CHAPTER 02 · 家庭网络里的定位&lt;/div&gt;
&lt;h1&gt;七个节点的私有网里，&lt;br/&gt;它是唯一「两个中枢合一」的那台&lt;/h1&gt;
&lt;div class="en-sub"&gt;On a private mesh of seven nodes, the NAS is the only one that is both the storage hub and the service hub.&lt;/div&gt;
&lt;p class="sub"&gt;先把这台 NAS 放回它所在的网络里看。整个家是一张基于 Tailscale（WireGuard mesh）的私有网，七个节点各有各的角色。其中六个要么是纯客户端、要么各自只跑一个 agent 实例；只有 MoonShade 一台，同时身兼&lt;b&gt;存储中枢&lt;/b&gt;与&lt;b&gt;服务中枢&lt;/b&gt;——照片库、文件库、备份、统一门户全落在它身上。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="diagram"&gt;
&lt;div class="dlbl"&gt;
&lt;span class="ttl"&gt;FIG 01 · 家庭 tailnet 拓扑 · HOME MESH&lt;/span&gt;&lt;span&gt;7 NODES · NAS AS SOLE HUB&lt;/span&gt;&lt;/div&gt;
&lt;svg viewBox="0 0 1052 396" preserveAspectRatio="xMidYMid meet"&gt;
&lt;!-- mesh boundary --&gt;
&lt;rect x="8" y="30" width="1036" height="352" rx="10" class="stroke-mid stroke-dash"/&gt;
&lt;text x="24" y="24" class="lbl-cap"&gt;PRIVATE MESH · TAILNET (WIREGUARD) · NO PUBLIC INBOUND&lt;/text&gt;
&lt;!-- center focus: NAS --&gt;
&lt;rect x="396" y="168" width="260" height="86" class="stroke-focus fill-focus"/&gt;
&lt;text x="526" y="198" text-anchor="middle" class="lbl-zh" style="fill:#1f3a5f"&gt;MoonShade · NAS&lt;/text&gt;
&lt;text x="526" y="216" text-anchor="middle" class="lbl-en" style="fill:#1f3a5f;letter-spacing:.08em"&gt;STORAGE HUB + SERVICE HUB&lt;/text&gt;
&lt;text x="526" y="234" text-anchor="middle" class="lbl-en" style="font-size:9px"&gt;ZFS · Docker stack · unified portal · 24×7&lt;/text&gt;
&lt;!-- clients (left) --&gt;
&lt;rect x="40" y="70" width="180" height="52" class="fill-white stroke-ink"/&gt;
&lt;text x="130" y="92" text-anchor="middle" class="lbl-zh"&gt;运维工作站&lt;/text&gt;
&lt;text x="130" y="108" text-anchor="middle" class="lbl-en"&gt;macOS · client only&lt;/text&gt;
&lt;rect x="40" y="300" width="180" height="52" class="fill-white stroke-ink"/&gt;
&lt;text x="130" y="322" text-anchor="middle" class="lbl-zh"&gt;手机 · 移动入口&lt;/text&gt;
&lt;text x="130" y="338" text-anchor="middle" class="lbl-en"&gt;iOS · bookmark entry&lt;/text&gt;
&lt;!-- agent hosts (right) --&gt;
&lt;rect x="832" y="60" width="180" height="52" class="fill-white stroke-ink"/&gt;
&lt;text x="922" y="82" text-anchor="middle" class="lbl-zh"&gt;个人助理 agent&lt;/text&gt;
&lt;text x="922" y="98" text-anchor="middle" class="lbl-en"&gt;macOS host&lt;/text&gt;
&lt;rect x="832" y="150" width="180" height="52" class="fill-white stroke-ink"/&gt;
&lt;text x="922" y="172" text-anchor="middle" class="lbl-zh"&gt;网站运维 agent&lt;/text&gt;
&lt;text x="922" y="188" text-anchor="middle" class="lbl-en"&gt;cloud ARM · only public IP&lt;/text&gt;
&lt;rect x="832" y="240" width="180" height="52" class="fill-white stroke-ink"/&gt;
&lt;text x="922" y="262" text-anchor="middle" class="lbl-zh"&gt;通用助理 agent&lt;/text&gt;
&lt;text x="922" y="278" text-anchor="middle" class="lbl-en"&gt;Linux desktop&lt;/text&gt;
&lt;rect x="832" y="330" width="180" height="52" class="fill-white stroke-ink"/&gt;
&lt;text x="922" y="352" text-anchor="middle" class="lbl-zh"&gt;预留算力 PC&lt;/text&gt;
&lt;text x="922" y="368" text-anchor="middle" class="lbl-en"&gt;Windows · LAN-only&lt;/text&gt;
&lt;!-- spokes from NAS to clients --&gt;
&lt;line x1="396" y1="205" x2="220" y2="96" class="stroke-mid"/&gt;
&lt;line x1="396" y1="220" x2="220" y2="326" class="stroke-mid"/&gt;
&lt;!-- spokes from NAS to agent hosts --&gt;
&lt;line x1="656" y1="196" x2="832" y2="86" class="stroke-focus"/&gt;
&lt;line x1="656" y1="205" x2="832" y2="176" class="stroke-focus"/&gt;
&lt;line x1="656" y1="214" x2="832" y2="266" class="stroke-mid"/&gt;
&lt;line x1="656" y1="223" x2="832" y2="356" class="stroke-mid"/&gt;
&lt;/svg&gt;
&lt;div class="dnote"&gt;七个节点同在一张 WireGuard mesh 私有网内（彼此可直连，图中只画出以 NAS 为中心的服务/存储关系线）。焦点方框 = MoonShade，全网唯一同时承担存储与服务的节点；其余节点各自是客户端或单一 agent 宿主。没有任何一条线通向公网入站。&lt;/div&gt;&lt;/div&gt;
&lt;p class="sub" style="margin-top:8px"&gt;这张网有几条让它「省心」的性质：节点之间&lt;b&gt;点对点直连&lt;/b&gt;（打洞成功后走公网直连而非中转），每台设备拿一个固定的私有地址（&lt;span class="path"&gt;100.x&lt;/span&gt; 段，换 Wi-Fi 也不变），可以用主机名代替 IP 访问，&lt;b&gt;不需要任何公网端口转发&lt;/b&gt;，而且「设备连进这张网」本身就等于完成了一次设备级身份认证。最后一条尤其关键——它让后面第 6 段的「透明单点登录」几乎零成本。&lt;/p&gt;
&lt;div class="axis"&gt;
&lt;div&gt;&lt;div class="k"&gt;存储中枢 · STORAGE&lt;/div&gt;&lt;div class="v"&gt;RAID-Z1 机械盘池 + NVMe + 可插拔移动盘；每日快照，SMB 直接暴露给家中设备读写&lt;/div&gt;&lt;/div&gt;
&lt;div&gt;&lt;div class="k"&gt;服务中枢 · SERVICE&lt;/div&gt;&lt;div class="v"&gt;容器化跑两套照片库 / 文件管理 / SSO / 监控，收进一个&lt;span class="path"&gt;统一门户&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div&gt;&lt;div class="k"&gt;为什么是它 · WHY NAS&lt;/div&gt;&lt;div class="v"&gt;全网唯一 7×24 常在线又有大容量的节点——照片、备份、门户只能落这台&lt;/div&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="ch-03" class="page" data-screen-label="03 The silent chassis"&gt;
&lt;div class="head"&gt;
&lt;div class="kicker"&gt;CHAPTER 03 · 硬件底盘&lt;/div&gt;
&lt;h1&gt;每一处选型，&lt;br/&gt;都在为「安静地长期省电地跑」让路&lt;/h1&gt;
&lt;div class="en-sub"&gt;Every part is a concession to one goal: run quiet, run low-power, run 24×7 — in a bedroom.&lt;/div&gt;
&lt;p class="sub"&gt;卧室、常开、要安静、要省电，但性能得够跑照片库的机器学习索引——这四个约束互相拉扯，硬件选型就是在它们之间找平衡点。下面三张卡片是三个关键取舍。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="grid-3c mt-24"&gt;
&lt;div class="card"&gt;
&lt;div class="caps dim"&gt;CPU · APU&lt;/div&gt;
&lt;h3&gt;Ryzen 5 5600G，要的是核显&lt;/h3&gt;
&lt;p class="lede"&gt;6 核 12 线程、&lt;span class="path"&gt;TDP 65W&lt;/span&gt; 的 APU。选带核显的 G 版而非标准 Ryzen 有两个用意：&lt;b&gt;自带核显&lt;/b&gt;省掉一张独显的功耗与空间，还能给照片服务做硬件加速；&lt;b&gt;65W 低热&lt;/b&gt;更容易压进 Mini-ITX 的静音热预算。&lt;/p&gt;
&lt;p class="lede" style="font-family:var(--mono);font-size:11.5px;color:var(--ink-500);"&gt;An APU, not a CPU — integrated graphics kill the need for a discrete card, and 65 W fits a silent ITX thermal budget.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;div class="caps dim"&gt;BOARD · ITX&lt;/div&gt;
&lt;h3&gt;X570 I，要的是通道与板载 Wi-Fi&lt;/h3&gt;
&lt;p class="lede"&gt;实机是 Gigabyte X570 I AORUS PRO WIFI。比更省的 B550 贵，换来的是更完整的 PCIe / 存储通道和更强供电——足够挂多块 SATA 机械盘加多条 NVMe；&lt;b&gt;板载 Wi-Fi&lt;/b&gt; 让它摆位不受网口位置限制。&lt;/p&gt;
&lt;p class="lede" style="font-family:var(--mono);font-size:11.5px;color:var(--ink-500);"&gt;X570 ITX for full storage lanes and onboard Wi-Fi, so placement isn't tied to an ethernet jack.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card card-focus"&gt;
&lt;span class="focus-tag"&gt;LOAD-BEARING&lt;/span&gt;
&lt;h3&gt;散热：确定性 &amp;gt; 灵活性&lt;/h3&gt;
&lt;p class="lede"&gt;120mm 一体水冷，&lt;b&gt;水泵恒定转速&lt;/b&gt;（不调速，避免忽快忽慢的噪音起伏）。风扇曲线只能在 BIOS 里调——主板监控芯片是 &lt;span class="path"&gt;ITE IT8689E&lt;/span&gt;，Linux 的 &lt;span class="path"&gt;it87&lt;/span&gt; 驱动不认它，系统层拿不到 PWM 控制权。于是牺牲 OS 层动态调速，换 BIOS「设一次就静音」的确定性。&lt;/p&gt;
&lt;p class="lede" style="font-family:var(--mono);font-size:11.5px;color:var(--ink-500);"&gt;Fan curve lives in BIOS only — the mainline it87 driver can't see the IT8689E, so OS-side control is gone. Traded flexibility for set-once silence.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p class="sub" style="margin-top:24px"&gt;内存给了 &lt;b&gt;16GB&lt;/b&gt;（系统可见 14GiB）加 4GiB swap。轻载常态下 used 约 3.6GiB、大量空闲页拿去做文件系统缓存——8 容器加照片库机器学习并发时到底够不够，留到第 10 段用实测峰值正面回答。&lt;/p&gt;
&lt;div class="caps mt-32"&gt;温度与功耗实测 · MEASURED THERMALS &amp;amp; POWER&lt;/div&gt;
&lt;table class="spec"&gt;
&lt;thead&gt;&lt;tr&gt;&lt;th style="width:34%"&gt;传感器 / 指标&lt;/th&gt;&lt;th style="width:22%"&gt;实测&lt;/th&gt;&lt;th&gt;说明 · Meaning&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td class="zh"&gt;CPU（Tctl）&lt;/td&gt;&lt;td class="mono"&gt;~54 °C&lt;/td&gt;&lt;td class="en"&gt;Light idle load — far from any throttle or alarm point.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class="zh"&gt;系统 NVMe&lt;/td&gt;&lt;td class="mono"&gt;~53 °C&lt;/td&gt;&lt;td class="en"&gt;Composite die temp; crit is 87.8 °C.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class="zh"&gt;数据 NVMe&lt;/td&gt;&lt;td class="mono"&gt;~41 °C&lt;/td&gt;&lt;td class="en"&gt;Cooler still.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class="zh"&gt;机械盘 ×4&lt;/td&gt;&lt;td class="mono"&gt;~33–35 °C&lt;/td&gt;&lt;td class="en"&gt;Each SATA HDD; historic peak ~38–41, well under the 65 °C line.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class="zh"&gt;核显（idle）&lt;/td&gt;&lt;td class="mono"&gt;~49 °C · 4–8 W&lt;/td&gt;&lt;td class="en"&gt;Basically idle — single-digit watts.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class="zh"&gt;整机功耗&lt;/td&gt;&lt;td class="mono"&gt;~35 W idle / ~150 W 满载&lt;/td&gt;&lt;td class="en"&gt;24×7 at 35 W idle is the payoff of a 65 W APU + silent design.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class="quote mt-32"&gt;
&lt;div class="caps"&gt;诚实的热天花板 · THE HONEST CEILING&lt;/div&gt;
&lt;p&gt;日常轻载 &lt;b&gt;54 °C&lt;/b&gt; 很凉；但当第二相片库&lt;b&gt;批量做 RAW 显影&lt;/b&gt;（持续满核）时，这块 120mm 薄冷排的紧凑机型会冲到约 &lt;b&gt;92 °C&lt;/b&gt;。这不是缺陷，是「小体积 + 静音」换来的确定性代价——薄排压不住持续重载，但这类批处理是偶发（索引一次、平时不跑）。把这一句写出来，比只报 54 °C 诚实。&lt;br/&gt;&lt;span style="font-family:var(--mono);font-size:13px;color:var(--focus-ink);line-height:1.55;display:block;margin-top:8px;"&gt;Idle sits cool at 54 °C; sustained RAW rendering pushes a thin-radiator compact box to ~92 °C. Not a defect — the price of small-and-silent. Saying so is more honest than quoting only the idle number.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="ch-04" class="page" data-screen-label="04 Storage on ZFS"&gt;
&lt;div class="head"&gt;
&lt;div class="kicker"&gt;CHAPTER 04 · 存储地基&lt;/div&gt;
&lt;h1&gt;普通文件系统「存了就存了」，&lt;br/&gt;ZFS 记账、对账、自愈、还能倒带&lt;/h1&gt;
&lt;div class="en-sub"&gt;A plain filesystem stores and forgets; ZFS journals, reconciles, self-heals, and rewinds.&lt;/div&gt;
&lt;p class="sub"&gt;存全家照片的机器，文件系统不是选型细节，是地基。这台 NAS 全线走 ZFS——把校验和、软件 RAID、快照、卷管理揉在一起。它切成两个池，各司其职。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="diagram"&gt;
&lt;div class="dlbl"&gt;
&lt;span class="ttl"&gt;FIG 02 · ZFS 双池与数据集 · POOLS &amp;amp; DATASETS&lt;/span&gt;&lt;span&gt;RAID-Z1 · 单盘容错&lt;/span&gt;&lt;/div&gt;
&lt;svg viewBox="0 0 1052 372" preserveAspectRatio="xMidYMid meet"&gt;
&lt;text x="0" y="20" class="lbl-cap"&gt;ZFS POOLS&lt;/text&gt;
&lt;text x="1052" y="20" text-anchor="end" class="lbl-en"&gt;ONLINE · 0 ERRORS&lt;/text&gt;
&lt;!-- tank pool --&gt;
&lt;rect x="0" y="32" width="620" height="316" class="stroke-focus fill-focus"/&gt;
&lt;text x="20" y="60" class="lbl-zh" style="fill:#1f3a5f"&gt;tank · 4 × 3TB HDD · RAID-Z1&lt;/text&gt;
&lt;text x="20" y="78" class="lbl-en" style="fill:#1f3a5f"&gt;10.9T raw · 7.56T used · ~69% · survives 1 disk loss&lt;/text&gt;
&lt;rect x="20" y="96" width="580" height="66" class="fill-white stroke-ink"/&gt;
&lt;text x="36" y="122" class="lbl-zh"&gt;tank/share · 5.40T&lt;/text&gt;
&lt;text x="36" y="140" class="lbl-en"&gt;/srv/nas/share — 照片原图 · 媒体 · 文件管理器根&lt;/text&gt;
&lt;rect x="20" y="176" width="580" height="66" class="fill-white stroke-ink"/&gt;
&lt;text x="36" y="202" class="lbl-zh"&gt;tank/backup · 90.2G&lt;/text&gt;
&lt;text x="36" y="220" class="lbl-en"&gt;/tank/backup — 每日备份目标 + ZFS 快照（保留 30 天 · DB dump 14 天）&lt;/text&gt;
&lt;rect x="20" y="256" width="580" height="66" class="fill-surf stroke-mid"/&gt;
&lt;text x="36" y="282" class="lbl-zh"&gt;tank（根）&lt;/text&gt;
&lt;text x="36" y="300" class="lbl-en"&gt;pool root — 几乎不直接存东西&lt;/text&gt;
&lt;!-- ssd-tank --&gt;
&lt;text x="660" y="20" class="lbl-cap"&gt;CACHE&lt;/text&gt;
&lt;rect x="660" y="32" width="392" height="120" class="fill-white stroke-ink"/&gt;
&lt;text x="676" y="60" class="lbl-zh"&gt;ssd-tank · 1 × NVMe 1TB&lt;/text&gt;
&lt;text x="676" y="80" class="lbl-en"&gt;928G · ~全空 · 预留高速暂存层&lt;/text&gt;
&lt;text x="676" y="104" class="lbl-en"&gt;single disk — no redundancy&lt;/text&gt;
&lt;!-- non-pool --&gt;
&lt;text x="660" y="180" class="lbl-cap"&gt;NON-POOL · 与数据物理分离&lt;/text&gt;
&lt;rect x="660" y="192" width="392" height="70" class="fill-surf stroke-mid"/&gt;
&lt;text x="676" y="218" class="lbl-zh"&gt;系统盘 · NVMe 457G（ext4）&lt;/text&gt;
&lt;text x="676" y="236" class="lbl-en"&gt;只装 OS + 容器 + agent 记忆 · 重装不碰 tank&lt;/text&gt;
&lt;rect x="660" y="276" width="392" height="70" class="fill-surf stroke-mid"/&gt;
&lt;text x="676" y="302" class="lbl-zh"&gt;移动硬盘 · exfat（nofail）&lt;/text&gt;
&lt;text x="676" y="320" class="lbl-en"&gt;/mnt/external · 拔插不影响开机 · 离线冷数据&lt;/text&gt;
&lt;/svg&gt;
&lt;div class="dnote"&gt;左：主数据池 tank，4 盘 RAID-Z1，切三个数据集；右上：ssd-tank 单盘快取（预留，近空）；右下：两块不入池的盘——系统盘与外接盘，与数据物理分离，重装系统不碰 tank。&lt;/div&gt;&lt;/div&gt;
&lt;p class="sub" style="margin-top:8px"&gt;ZFS 在这台机器上解决三件普通文件系统解决不了的事。&lt;b&gt;一是端到端校验和&lt;/b&gt;：机械盘长期存放会有静默位翻转，普通文件系统读到坏数据照样返回给你，ZFS 每次读都验校验、有冗余就自动修，还能主动 scrub 全盘扫一遍。&lt;b&gt;二是软件 RAID&lt;/b&gt;：&lt;span class="path"&gt;tank&lt;/span&gt; 用 4 盘 RAID-Z1，拿一块盘的空间换奇偶校验，任意单盘挂掉数据仍在、换盘可重建。&lt;b&gt;三是秒级快照&lt;/b&gt;：copy-on-write，创建瞬间完成、初始不占空间，之后只按变化量长——&lt;span class="path"&gt;tank/backup&lt;/span&gt; 上保着最近约 30 天的每日时间点（数据库 dump 另按 14 天滚动）；每个增量只有几百 K，却能把整库还原到任意一天。采集时两池都是 ONLINE、0 读错 / 0 写错 / 0 校验错。&lt;/p&gt;
&lt;div class="note mt-24"&gt;
&lt;div class="caps"&gt;为什么月度 scrub 是纯手动 · MANUAL BY DESIGN&lt;/div&gt;
&lt;p&gt;scrub 对 CPU、内存几乎无感，唯一的代价是&lt;b&gt;噪音&lt;/b&gt;——4 块机械盘要连续寻道约 3–5 小时。在卧室，「什么时候能容忍这个噪音」只有人知道。所以这条巡检刻意&lt;b&gt;不设自动 timer&lt;/b&gt;：自动化只负责每月提醒一次（提醒文案明写「只提醒、不自动跑」），真正开跑的命令由人挑一个不在卧室的时段手动敲。判读也简单——&lt;span class="path"&gt;with 0 errors&lt;/span&gt; 就是健康，一旦出现非 0 错误或有实际修复量，就是某块盘可能要换的信号。&lt;/p&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="ch-05" class="page" data-screen-label="05 The service stack"&gt;
&lt;div class="head"&gt;
&lt;div class="kicker"&gt;CHAPTER 05 · 服务栈&lt;/div&gt;
&lt;h1&gt;不是功能堆砌，&lt;br/&gt;是一条主线牵出的一串真实需求&lt;/h1&gt;
&lt;div class="en-sub"&gt;Not a pile of features — a chain of real needs pulled by one thread: bring the photos home.&lt;/div&gt;
&lt;p class="sub"&gt;这台 NAS 上跑着八个容器加两个自研服务。它们不是「看别人装了我也装」，每一个都对着一个具体痛点。主线是「把租来的云搬回自己家」，其余都从这条线上长出来。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="grid-3c mt-24"&gt;
&lt;div class="card card-focus"&gt;
&lt;span class="focus-tag"&gt;地基&lt;/span&gt;
&lt;h3&gt;Immich · 照片库 A&lt;/h3&gt;
&lt;p class="lede"&gt;整个系统的地基。&lt;span class="path"&gt;immich-server:v2.7.5&lt;/span&gt; 加 ML、PostgreSQL（带向量扩展做人脸/相似检索）、Valkey 缓存共四容器。它是手机相册的自托管替代——&lt;b&gt;每小时自动从云端增量拉取归档&lt;/b&gt;，把照片真身握回自己盘，库有每日备份写进 ZFS。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;div class="caps dim"&gt;照片库 B&lt;/div&gt;
&lt;h3&gt;PhotoPrism · 专读 RAW&lt;/h3&gt;
&lt;p class="lede"&gt;补第一套的短板：专门只读编目相机 RAW 与 DNG（第一套引擎读不了这些格式）。RAW 预览统一走 darktable 中性渲染，只读编目、不动原始文件。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;div class="caps dim"&gt;文件&lt;/div&gt;
&lt;h3&gt;FileBrowser · 网页文件管理&lt;/h3&gt;
&lt;p class="lede"&gt;挂载三块内置盘加一块外接移动盘。不装客户端，任何设备浏览器打开即用。照片目录挂&lt;b&gt;只读&lt;/b&gt;防误删。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;div class="caps dim"&gt;身份&lt;/div&gt;
&lt;h3&gt;Authelia · 单点登录&lt;/h3&gt;
&lt;p class="lede"&gt;&lt;span class="path"&gt;authelia:4.39.20&lt;/span&gt;。以 OIDC 身份提供方形式给照片库提供统一登录，证书每周自动续。另有 Homepage 作兜底仪表盘——自研门户挂了，「一个书签进所有服务」这条底线也不断。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;div class="caps dim"&gt;门面&lt;/div&gt;
&lt;h3&gt;母港门户 v2 · 自研&lt;/h3&gt;
&lt;p class="lede"&gt;主入口。无框架纯静态 Web App 加一个约 &lt;span class="path"&gt;110 行&lt;/span&gt;、零依赖的 Node 标准库 HTTP 服务器；浏览器侧只有原生 vanilla JS，&lt;b&gt;无 React/Vue、无构建、无 npm 运行时依赖&lt;/b&gt;。把全部服务聚合，叠加照片轮播、状态监控、可视化设置。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;div class="caps dim"&gt;监控&lt;/div&gt;
&lt;h3&gt;oc-fleet-monitor · 只读状态&lt;/h3&gt;
&lt;p class="lede"&gt;纯 Python，暴露一个 &lt;span class="path"&gt;/status&lt;/span&gt; 端点，把本机 agent 运行时状态以 JSON 吐出，喂门户「监控」页做实时可视化。只读、无副作用、零 token 成本，可放心高频轮询。&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p class="sub" style="margin-top:28px"&gt;贯穿这一栈的是两条取向：&lt;b&gt;私有优先&lt;/b&gt;——所有服务 tailnet-only、不开公网端口、统一走受信 HTTPS 门户；&lt;b&gt;数据主权 + 能自己造就自己造&lt;/b&gt;——照片真身都落在自家 ZFS 盘，能自研的门面坚持无框架零依赖手写，现成方案只作兜底。下一段说清「不开公网」到底怎么做到的。&lt;/p&gt;
&lt;/section&gt;
&lt;section id="ch-06" class="page" data-screen-label="06 Access and security"&gt;
&lt;div class="head"&gt;
&lt;div class="kicker"&gt;CHAPTER 06 · 接入与安全&lt;/div&gt;
&lt;h1&gt;最关键的一层防御，&lt;br/&gt;是它压根不在互联网上&lt;/h1&gt;
&lt;div class="en-sub"&gt;The most important layer of defense is that it simply isn't reachable from the internet.&lt;/div&gt;
&lt;p class="sub"&gt;这台 NAS 的攻击面，从「整个互联网」收缩到「几台已认证的自家设备」。它没有端口转发、没有 DDNS、没有一个公网入口。这不是企业级零信任，是恰到好处的家用防御纵深——它的边界在哪，这段会诚实标出来。&lt;/p&gt;
&lt;/div&gt;
&lt;p class="sub" style="margin-top:8px"&gt;&lt;b&gt;接入层——不可从公网访问。&lt;/b&gt;它只加入一张私有 mesh VPN（Tailscale / WireGuard）。主机防火墙（&lt;span class="path"&gt;ufw&lt;/span&gt;，active）入站收得极紧：只放行回环口、mesh 虚拟网卡、WireGuard 握手端口，普通局域网入站一律拒绝——即便某个服务在进程层监听 &lt;span class="path"&gt;0.0.0.0&lt;/span&gt;，ufw 在网络层也会把 LAN 来源的包直接丢掉（容器发布端口是例外：docker 会自行改写 iptables 绕过 ufw，这层由另一条自建守卫链兜底、把发布端口重新锁回 tailnet-only，详见第 9 章「网络收口」）。&lt;b&gt;受信 HTTPS 层——绿锁、无 IP、无警告。&lt;/b&gt;入口用 mesh VPN 自带的反向代理，跑在已存在的 VPN 守护进程里，不新增进程、容器或内存开销；tailnet 给节点 FQDN 自动申请并续期 Let's Encrypt 受信证书。因为浏览器不按端口区分信任，同一域名下每个高端口都是绿锁——门户、两套照片库、文件管理、SSO 各占一个受信入口。&lt;/p&gt;
&lt;div class="axis mt-24"&gt;
&lt;div&gt;&lt;div class="k"&gt;身份① · 设备身份&lt;/div&gt;&lt;div class="v"&gt;设备连进 tailnet 并通过认证 = 已完成一次身份验证，零额外登录页（透明 SSO）&lt;/div&gt;&lt;/div&gt;
&lt;div&gt;&lt;div class="k"&gt;身份② · 应用 SSO&lt;/div&gt;&lt;div class="v"&gt;自托管 OIDC（单用户），两套照片库接入，登一次会话期内其它应用不重登&lt;/div&gt;&lt;/div&gt;
&lt;div&gt;&lt;div class="k"&gt;身份③ · 本地密码兜底&lt;/div&gt;&lt;div class="v"&gt;接了 SSO 的服务同时保留本地密码，防 SSO 故障把人锁在门外&lt;/div&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p class="sub" style="margin-top:24px"&gt;有一处工程细节值得记：docker 容器本身&lt;b&gt;不是 tailnet 节点&lt;/b&gt;，够不到 tailnet-only 的入口端口；而 OIDC 的 issuer（签发者 URL）只能是一个值，浏览器跳转和后端换 token 都得用它。解法是让身份提供方自持一张对 FQDN 有效的受信证书，再用 docker 网络别名把它挂进照片库所在的容器网络——这样容器内和浏览器侧都够到同一个受信 URL，issuer 保持一致。另外，文件管理器&lt;b&gt;特意不接自动 SSO&lt;/b&gt;（它挂了整盘，自动登录一旦被滥用等于「任何设备自动拿到全盘管理员」），并被改成只绑 &lt;span class="path"&gt;127.0.0.1&lt;/span&gt;。&lt;/p&gt;
&lt;div class="quote mt-32"&gt;
&lt;div class="caps"&gt;边界在哪 · WHERE THE LINE IS&lt;/div&gt;
&lt;p&gt;诚实地说：这套模型的安全性，建立在「&lt;b&gt;tailnet 边界可信&lt;/b&gt;」这个前提上。一旦某台已认证设备被攻陷，攻击者就进了内网可达范围，后面只剩应用层密码这一关（这也是文件管理器坚持不上自动 SSO 的原因）。它没有企业级的持续设备态势评估，也没有细到每请求的授权策略。但对家庭威胁模型——挡住互联网扫描器、机会主义攻击，顺便治好「懒得记 IP、怕看证书警告」——这个结构&lt;b&gt;恰到好处&lt;/b&gt;。&lt;br/&gt;&lt;span style="font-family:var(--mono);font-size:13px;color:var(--focus-ink);line-height:1.55;display:block;margin-top:8px;"&gt;Its security rests on trusting the tailnet boundary. Compromise one authenticated device and only app passwords remain. No enterprise posture checks, no per-request policy — but for a home threat model, exactly enough.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="ch-07" class="page" data-screen-label="07 Automation human-in-loop"&gt;
&lt;div class="head"&gt;
&lt;div class="kicker"&gt;CHAPTER 07 · 自动化&lt;/div&gt;
&lt;h1&gt;值得讲的不是「全自动了」，&lt;br/&gt;是它清楚哪些事不该自动&lt;/h1&gt;
&lt;div class="en-sub"&gt;The interesting part isn't what's automated — it's knowing what must not be.&lt;/div&gt;
&lt;p class="sub"&gt;一台 &lt;span class="path"&gt;systemd timer&lt;/span&gt; 织成的自动化系统在后台默默跑。核心哲学是一句话：&lt;b&gt;能全自动的全自动，一旦某个操作不可逆、或会打扰到人，就把最后那一下留给人。&lt;/b&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="caps mt-24"&gt;定时管道一览 · SCHEDULED PIPELINES&lt;/div&gt;
&lt;table class="spec"&gt;
&lt;thead&gt;&lt;tr&gt;&lt;th style="width:24%"&gt;管道&lt;/th&gt;&lt;th style="width:20%"&gt;节奏&lt;/th&gt;&lt;th&gt;做什么 · What&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td class="zh"&gt;云端照片导入&lt;/td&gt;&lt;td class="mono"&gt;每小时 :05&lt;/td&gt;&lt;td class="en"&gt;Pull new photos → trigger library scan → file into albums. Serial + flock; download-only, never deletes cloud.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class="zh"&gt;照片库备份&lt;/td&gt;&lt;td class="mono"&gt;每日 03:30&lt;/td&gt;&lt;td class="en"&gt;DB dump + rsync to HDD pool + ZFS snapshot. Snapshots kept 30d, dumps 14d.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class="zh"&gt;可删账目&lt;/td&gt;&lt;td class="mono"&gt;每小时 :50&lt;/td&gt;&lt;td class="en"&gt;Compute "safe to delete from cloud" per album, atomically write a status file. Read-only — deletes nothing.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class="zh"&gt;磁盘水位&lt;/td&gt;&lt;td class="mono"&gt;每 6 小时&lt;/td&gt;&lt;td class="en"&gt;Warn when system disk ≥ 85%.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class="zh"&gt;门户收藏物化&lt;/td&gt;&lt;td class="mono"&gt;每日 06:30&lt;/td&gt;&lt;td class="en"&gt;Copy 20 random starred photos to portal carousel. Zero root, zero API key.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class="zh"&gt;证书续期&lt;/td&gt;&lt;td class="mono"&gt;每周一 04:10&lt;/td&gt;&lt;td class="en"&gt;Renew SSO cert; restart gateway only if the cert actually changed.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class="zh"&gt;卫生重启&lt;/td&gt;&lt;td class="mono"&gt;每周一 03:00&lt;/td&gt;&lt;td class="en"&gt;Clear memory / handles / zombies — gated by the in-flight guard below.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class="zh"&gt;ZFS scrub&lt;/td&gt;&lt;td class="mono"&gt;手动 · 月度&lt;/td&gt;&lt;td class="en"&gt;Deliberately no timer — reminds monthly, never auto-runs (bedroom noise).&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class="grid-3c mt-32"&gt;
&lt;div class="card"&gt;
&lt;div class="caps dim"&gt;留一手 · 01&lt;/div&gt;
&lt;h3&gt;重启前的「在途守卫」&lt;/h3&gt;
&lt;p class="lede"&gt;重启是破坏性的——若正好有照片在下载或备份 rsync 跑到一半，砍下去数据不完整。所以重启前先跑守卫逐个查完整性关键任务，任一在跑就&lt;b&gt;跳过本周&lt;/b&gt;。设计成 fail-closed：连状态都查不准时一律当「在跑」——宁可漏重启一周，也不冒险砍一次同步。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;div class="caps dim"&gt;留一手 · 02&lt;/div&gt;
&lt;h3&gt;scrub 只提醒，不开跑&lt;/h3&gt;
&lt;p class="lede"&gt;它对性能几乎无感，唯一代价是几小时的机械盘噪音。「何时能容忍」只有人知道，所以自动化只每月提醒一次，扳机由人在不在卧室的时段手动扣。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card card-focus"&gt;
&lt;span class="focus-tag"&gt;不可逆&lt;/span&gt;
&lt;h3&gt;删云端：只算「可以删」&lt;/h3&gt;
&lt;p class="lede"&gt;每小时导入只下载、从不删云端。回收被拆成独立、且&lt;b&gt;没排进任何 timer&lt;/b&gt; 的任务。系统每小时算一次账、标出「已进快照且已同步 = 现在删是安全的」，把结论摊给人看，自己&lt;b&gt;一个字节都不删&lt;/b&gt;——删云端不可逆，扣扳机的永远是人。&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p class="sub" style="margin-top:24px"&gt;那个在途守卫本身还踩过一个很典型的坑：同步任务是 oneshot 类型，跑的时候状态是 &lt;span class="path"&gt;activating&lt;/span&gt; 而不是 &lt;span class="path"&gt;active&lt;/span&gt;；早期版本只认 &lt;span class="path"&gt;active&lt;/span&gt;，导致「正在跑」反而被当成空闲、照样重启。修复后把中间态也显式算作「在跑」——这正是 fail-closed 该有的样子。&lt;/p&gt;
&lt;/section&gt;
&lt;section id="ch-08" class="page" data-screen-label="08 Portal and data flows"&gt;
&lt;div class="head"&gt;
&lt;div class="kicker"&gt;CHAPTER 08 · 门户与数据流&lt;/div&gt;
&lt;h1&gt;把一堆散服务，&lt;br/&gt;收成一个书签&lt;/h1&gt;
&lt;div class="en-sub"&gt;Fold a dozen scattered services into a single bookmark — with credentials that never touch the front end.&lt;/div&gt;
&lt;p class="sub"&gt;「母港」门户把分散在多个后台的数据汇成一个页面。三条数据流各自独立、互不阻塞，贯穿全部的一条红线是——&lt;b&gt;凭据永不进前端&lt;/b&gt;：所有要密钥、数据库、采集权限的活儿都在服务端干完，前端只消费轻量 JSON。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="diagram"&gt;
&lt;div class="dlbl"&gt;
&lt;span class="ttl"&gt;FIG 03 · 照片数据流 · PHOTO PIPELINE&lt;/span&gt;&lt;span&gt;phase-1：只下载不删&lt;/span&gt;&lt;/div&gt;
&lt;svg viewBox="0 0 1052 180" preserveAspectRatio="xMidYMid meet"&gt;
&lt;!-- nodes --&gt;
&lt;rect x="0" y="96" width="150" height="60" class="fill-white stroke-ink"/&gt;
&lt;text x="75" y="120" text-anchor="middle" class="lbl-zh"&gt;手机云相册&lt;/text&gt;
&lt;text x="75" y="137" text-anchor="middle" class="lbl-en"&gt;whitelist albums&lt;/text&gt;
&lt;rect x="196" y="96" width="164" height="60" class="fill-white stroke-ink"/&gt;
&lt;text x="278" y="120" text-anchor="middle" class="lbl-zh"&gt;下载器 · 每小时&lt;/text&gt;
&lt;text x="278" y="137" text-anchor="middle" class="lbl-en"&gt;read-only · no delete&lt;/text&gt;
&lt;rect x="406" y="96" width="164" height="60" class="stroke-focus fill-focus"/&gt;
&lt;text x="488" y="120" text-anchor="middle" class="lbl-zh" style="fill:#1f3a5f"&gt;相册库&lt;/text&gt;
&lt;text x="488" y="137" text-anchor="middle" class="lbl-en" style="fill:#1f3a5f"&gt;read-only index · ML&lt;/text&gt;
&lt;rect x="616" y="96" width="164" height="60" class="fill-white stroke-ink"/&gt;
&lt;text x="698" y="116" text-anchor="middle" class="lbl-zh"&gt;物化 · 每日&lt;/text&gt;
&lt;text x="698" y="133" text-anchor="middle" class="lbl-en"&gt;zero root · zero key&lt;/text&gt;
&lt;text x="698" y="148" text-anchor="middle" class="lbl-en"&gt;copies existing previews&lt;/text&gt;
&lt;rect x="826" y="96" width="150" height="60" class="fill-white stroke-ink"/&gt;
&lt;text x="901" y="120" text-anchor="middle" class="lbl-zh"&gt;门户 Home&lt;/text&gt;
&lt;text x="901" y="137" text-anchor="middle" class="lbl-en"&gt;zero credentials&lt;/text&gt;
&lt;!-- connectors --&gt;
&lt;line x1="150" y1="126" x2="196" y2="126" class="stroke-ink"/&gt;
&lt;polygon points="196,126 189,122 189,130" fill="#1a1a1a" stroke="none"/&gt;
&lt;line x1="360" y1="126" x2="406" y2="126" class="stroke-ink"/&gt;
&lt;polygon points="406,126 399,122 399,130" fill="#1a1a1a" stroke="none"/&gt;
&lt;line x1="570" y1="126" x2="616" y2="126" class="stroke-ink"/&gt;
&lt;polygon points="616,126 609,122 609,130" fill="#1a1a1a" stroke="none"/&gt;
&lt;line x1="780" y1="126" x2="826" y2="126" class="stroke-ink"/&gt;
&lt;polygon points="826,126 819,122 819,130" fill="#1a1a1a" stroke="none"/&gt;
&lt;text x="0" y="40" class="lbl-cap"&gt;PHONE CLOUD → LOCAL DISK → LIBRARY → PORTAL · 权限逐跳递减&lt;/text&gt;
&lt;line x1="0" y1="52" x2="1052" y2="52" class="stroke-rule"/&gt;
&lt;/svg&gt;
&lt;div class="dnote"&gt;从左到右读。每一跳都刻意「减权」：下载器只读拉取、库只读索引、物化脚本零 root 零 API key（直接复制库早已生成的世界可读预览图），到门户前端时手里已经没有任何凭据。&lt;/div&gt;&lt;/div&gt;
&lt;p class="sub" style="margin-top:8px"&gt;第二条&lt;b&gt;监控流&lt;/b&gt;走的是另一套思路：每台主机各跑一个零依赖的 Python &lt;span class="path"&gt;/status&lt;/span&gt; 端点，经 tailnet 反代出受信 HTTPS，读路径免 token、零成本。架构上&lt;b&gt;没有中心聚合器&lt;/b&gt;——门户前端在浏览器里并行 fetch 各端点、自己拼装渲染，用 &lt;span class="path"&gt;Promise.allSettled&lt;/span&gt; 包全队，一台失败不拖累其它。第三条是门户本体，分四层：&lt;/p&gt;
&lt;div class="axis-2c mt-24"&gt;
&lt;div&gt;&lt;div class="k"&gt;皮 · SKIN&lt;/div&gt;&lt;div class="v"&gt;借一套成熟商业网站的母版 CSS，只引用不覆盖&lt;/div&gt;&lt;/div&gt;
&lt;div&gt;&lt;div class="k"&gt;骨 · SKELETON&lt;/div&gt;&lt;div class="v"&gt;捕获的 DOM 骨架&lt;/div&gt;&lt;/div&gt;
&lt;div&gt;&lt;div class="k"&gt;肌 · MUSCLE&lt;/div&gt;&lt;div class="v"&gt;原创 vanilla JS（约 4.5k 行，IIFE 自注册）&lt;/div&gt;&lt;/div&gt;
&lt;div&gt;&lt;div class="k"&gt;数据 · DATA&lt;/div&gt;&lt;div class="v"&gt;&lt;span class="path"&gt;seam&lt;/span&gt; 适配层——mock ↔ real 无缝切换，取数失败自动回退 mock，页面永不空白&lt;/div&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p class="sub" style="margin-top:24px"&gt;这三条流共享一套设计哲学：凭据永不进前端；重活在后台各自定时跑，一条流慢或挂不拖累另一条；&lt;b&gt;强容错优先&lt;/b&gt;——字段可缺、值可为 null、一台/一源失败不拖垮整页。这不是可选项，是家用异构系统现实约束下的必须项。整站无框架、无构建，纯静态加一个零依赖 Node 服务器，图的就是长期低维护、不吃 npm 生态的版本腐坏。&lt;/p&gt;
&lt;/section&gt;
&lt;section id="ch-09" class="page" data-screen-label="09 How it got here"&gt;
&lt;div class="head"&gt;
&lt;div class="kicker"&gt;CHAPTER 09 · 演进史&lt;/div&gt;
&lt;h1&gt;它不是一次设计出来的，&lt;br/&gt;是被真实需求一步步逼出来的&lt;/h1&gt;
&lt;div class="en-sub"&gt;Nothing here was designed up front — each present-day shape was forced by a specific push.&lt;/div&gt;
&lt;p class="sub"&gt;今天看到的每一处选型，几乎都能追溯到一个具体的痛点在推。把这条线倒过来看，比正着讲架构更能说清「为什么是这样」。&lt;/p&gt;
&lt;/div&gt;
&lt;p class="sub" style="margin-top:8px"&gt;&lt;b&gt;起点：寄居在 Windows 里。&lt;/b&gt;NAS 那个 agent 实例最早只是跑在一台 Windows PC 的 WSL2 里的运行时——记忆和会话历史与宿主环境深度耦合在半虚拟层，这台机器既不专职存储、也不适合长期无人值守。&lt;b&gt;决策：整机重装成裸机 NAS，但只要记忆能留下。&lt;/b&gt;关键的范围决策是「只保留 agent 的纯记忆核心（约 &lt;span class="path"&gt;134MB / 1400 个文件&lt;/span&gt;），其余环境性的东西全丢、新机重设」。迁移不做整盘镜像，而是冷快照：停机、narrow-rsync 只抓记忆目录、打包压到 19MB、生成逐文件 sha256 清单，快照留一周观察期后才准删。&lt;/p&gt;
&lt;p class="sub" style="margin-top:16px"&gt;&lt;b&gt;重装后，三层物理隔离。&lt;/b&gt;系统盘（装 OS + agent 记忆）、快取池、数据池分开，第一性原则是「&lt;b&gt;池炸了 ≠ agent 失忆&lt;/b&gt;」。存储最初选了零冗余的 RAID 0，一轮复核标出「任何一块盘坏就全没」的风险后改成 RAID-Z1，牺牲一块盘容量换单盘容错；组池用 &lt;span class="path"&gt;/dev/disk/by-id/&lt;/span&gt; 全名而非会变的 &lt;span class="path"&gt;/dev/sdX&lt;/span&gt;，避免重启后 degraded。一个诚实的已知取舍：那 4 块是 SMR 盘，换盘重建会慢，于是约定未来「4 盘一起换」绕开单盘 resilver。&lt;b&gt;网络收口&lt;/b&gt;时还发现 Docker 会绕过系统防火墙自己改 iptables 把容器端口暴露到 &lt;span class="path"&gt;0.0.0.0&lt;/span&gt;，于是加了一条自建守卫链把所有发布端口重新锁回 tailnet-only——这条守卫是后面所有服务能安心用 &lt;span class="path"&gt;0.0.0.0&lt;/span&gt; 端口的前提。&lt;/p&gt;
&lt;div class="rationale mt-24"&gt;
&lt;div class="row"&gt;&lt;div class="d"&gt;裸机 NAS&lt;/div&gt;&lt;div class="w"&gt;← 要专职存储 + 让 agent 记忆和宿主环境解耦&lt;/div&gt;&lt;/div&gt;
&lt;div class="row"&gt;&lt;div class="d"&gt;RAID-Z1 + 记忆放系统盘&lt;/div&gt;&lt;div class="w"&gt;← 池炸 ≠ 失忆；单盘坏不丢数据&lt;/div&gt;&lt;/div&gt;
&lt;div class="row"&gt;&lt;div class="d"&gt;两套相册&lt;/div&gt;&lt;div class="w"&gt;← 主库引擎读不了相机 RAW / DNG&lt;/div&gt;&lt;/div&gt;
&lt;div class="row"&gt;&lt;div class="d"&gt;自持证书 + 网络别名的 SSO&lt;/div&gt;&lt;div class="w"&gt;← 容器不是 mesh 节点、issuer 只能一个值&lt;/div&gt;&lt;/div&gt;
&lt;div class="row"&gt;&lt;div class="d"&gt;手动月度 scrub&lt;/div&gt;&lt;div class="w"&gt;← 机器在卧室，半夜会吵&lt;/div&gt;&lt;/div&gt;
&lt;div class="row"&gt;&lt;div class="d"&gt;tailnet-only · 凭据不落盘 · 破坏性操作当面确认&lt;/div&gt;&lt;div class="w"&gt;← 这是家用系统，要睡得着觉&lt;/div&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p class="sub" style="margin-top:24px"&gt;服务也是一层层长出来的，不是一次铺开：先是照片主库（自托管相册 + 从手机云端自动归档），发现主库缩略图引擎读不了 RAW 全是裂图，才加了专读 RAW 的第二套；有了照片要管文件，才有网页文件管理器；服务多到记不住 IP，才有统一门户；门户想要能自己改，才把它逆向重写成无框架的自研 v2；多台机器要一屏看状态，才有了那套零成本监控。中途还插进一轮多 agent 并行的大 code review——几十条 finding 分批修（存储型 XSS 收口、监控不再把真宕机伪装成 LIVE、大视频拷贝原子化、在途守卫的 oneshot 漏检改成 fail-closed），修完达到「零 failed 单元、open PR 清零」的干净状态。月度 ZFS scrub 也是在一次评估发现两个池&lt;b&gt;疑似从未 scrub 过&lt;/b&gt;之后才补上的。&lt;/p&gt;
&lt;/section&gt;
&lt;section id="ch-10" class="page" data-screen-label="10 Cost backup honesty"&gt;
&lt;div class="head"&gt;
&lt;div class="kicker"&gt;CHAPTER 10 · 备份、内存与取舍&lt;/div&gt;
&lt;h1&gt;它换来的不是省下的订阅费，&lt;br/&gt;是数据自主，外加睡得着觉&lt;/h1&gt;
&lt;div class="en-sub"&gt;What you buy isn't the saved subscription — it's sovereignty, plus sleeping soundly. And one honest gap.&lt;/div&gt;
&lt;p class="sub"&gt;收尾要诚实：既讲它做到了什么，也讲它至今没做到的那一件事。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="signals mt-24"&gt;
&lt;div class="sig-cell"&gt;
&lt;div class="num"&gt;35 W&lt;/div&gt;
&lt;div class="lbl"&gt;IDLE POWER&lt;/div&gt;
&lt;p&gt;24×7 常开的 idle 功耗，长期电费很低——65W APU + 静音方案的直接回报。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="sig-cell"&gt;
&lt;div class="num"&gt;~8 TB&lt;/div&gt;
&lt;div class="lbl"&gt;USABLE · RAID-Z1&lt;/div&gt;
&lt;p&gt;4× 3TB 机械盘做 RAID-Z1，可用约 8T，容单盘故障 + 每日快照可倒带 30 天。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="sig-cell"&gt;
&lt;div class="num"&gt;12 / 16 GB&lt;/div&gt;
&lt;div class="lbl"&gt;PEAK MEMORY&lt;/div&gt;
&lt;p&gt;8 容器 + agent runtime + 照片 ML 并发下峰值约 12GB，从未触顶、无 swap——正面回答「16GB 够不够」。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="sig-cell"&gt;
&lt;div class="num"&gt;0&lt;/div&gt;
&lt;div class="lbl"&gt;INCIDENTS&lt;/div&gt;
&lt;p&gt;至今未遇盘掉线或服务崩（knock on wood）；RAID-Z1 + 快照 + 离线盘就是为那一天准备的。&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="grid-2c mt-32"&gt;
&lt;div class="sig"&gt;
&lt;div class="caps"&gt;做到了 · WHAT IT DELIVERS&lt;/div&gt;
&lt;h3&gt;这套值得的理由&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;照片回到本地&lt;/b&gt;：手机相册每小时自动归档进自托管库，真身握在自己盘。&lt;/li&gt;
&lt;li&gt;&lt;b&gt;分库管理&lt;/b&gt;：日常照片与专业相机 RAW 各一套，互不干扰。&lt;/li&gt;
&lt;li&gt;&lt;b&gt;数据安全网&lt;/b&gt;：ZFS 校验 + RAID-Z1 + 每日快照 + 一块离线冷盘，四道叠一起。&lt;/li&gt;
&lt;li&gt;&lt;b&gt;一个绿锁进全部&lt;/b&gt;：不记 IP、不看证书警告、透明 SSO，移动端点书签即用。&lt;/li&gt;
&lt;li&gt;&lt;b&gt;基本不用盯&lt;/b&gt;：自动化管道 + 在途守卫，装完之后日常极少人工介入。&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="sig bad"&gt;
&lt;div class="caps"&gt;还没做到 · THE HONEST GAP&lt;/div&gt;
&lt;h3&gt;3-2-1 里缺的那个「1」&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;本地做到了「3 份副本、2 种介质」&lt;/b&gt;：RAID-Z1 + 每日快照 + 一块平时断开的离线冷盘。&lt;/li&gt;
&lt;li&gt;&lt;b&gt;离线盘挡得住&lt;/b&gt;勒索软件横扫 / 误删 / 逻辑损坏——在线冗余挡不住的那类。&lt;/li&gt;
&lt;li&gt;&lt;b&gt;但所有副本目前在同一物理位置&lt;/b&gt;：还没有地理异地备份。&lt;/li&gt;
&lt;li&gt;&lt;b&gt;防不了「火灾 / 失窃 / 水浸」&lt;/b&gt;端掉整个位置——异地那份是明确的 TODO。&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="quote mt-32"&gt;
&lt;div class="caps"&gt;最终判断 · CLOSING JUDGEMENT&lt;/div&gt;
&lt;p&gt;自己攒一台 NAS，换来的从来不是省下的那点云订阅费——那点钱远不够抵自攒自维的麻烦。真正换来的是&lt;b&gt;数据自主权&lt;/b&gt;，和一套「装完之后基本不用盯」的省心。它不完美：薄冷排在满载会烫、异地备份还欠着。但它把照片、文件、备份、服务入口和运维自动化，收进了&lt;b&gt;一台自己完全掌控、默认只在私有网络里可达的机器&lt;/b&gt;。对一个想把数据握回手里的人，这笔取舍，划算。&lt;br/&gt;&lt;span style="font-family:var(--mono);font-size:13px;color:var(--focus-ink);line-height:1.55;display:block;margin-top:8px;"&gt;Building your own NAS never pays back in saved subscriptions — it pays in sovereignty, and in not having to watch it. It isn't perfect: the thin radiator runs hot under load, offsite backup is still owed. But it pulls photos, files, backups, service entry and ops automation onto one machine you fully control. For someone who wants their data back, the trade is worth it.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="footnote"&gt;
END OF MOONSHADE · NEUTRALIZED · 2026-06-30 · V1.0 &amp;nbsp;·&amp;nbsp; Source&amp;nbsp;&lt;span class="path"&gt;MoonShade 架构研究 · 内部素材 · 已中立化&lt;/span&gt;
&lt;/div&gt;
&lt;/section&gt;</content:encoded></item></channel></rss>