diff --git a/warpgate-prd-v3.md b/warpgate-prd-v3.md
deleted file mode 100644
index 7f99142..0000000
--- a/warpgate-prd-v3.md
+++ /dev/null
@@ -1,1207 +0,0 @@
-# Warpgate — Make your NAS feel local — 产品方案与需求描述(v3)
-
----
-
-## 一、产品定位
-
-**一句话描述**:摄影师的随身存储中枢——外拍插卡自动归档,路上缓存加速访问,全程高速组网,云端容灾兜底。
-
-**核心价值**:
-
-1. **远程访问加速**:用户在外通过 Tailscale 等组网工具访问家中 NAS 时,受限于公网带宽,SMB 协议体验极差(卡顿、超时、缩略图加载慢)。本产品在客户端侧部署一层 SSD 只读缓存,对上层应用(Lightroom、Finder、Explorer 等)完全透明,首次访问按需拉取并缓存到本地 SSD,后续访问直接命中缓存。
-2. **外拍现场备份归档**:摄影师外拍结束插入 SD 卡,一键备份到本地 SSD,后台自动异步归档回家中 NAS。把「现场备份」和「远程归档」打通成一条自动流水线,市面上没有产品做到这一点。
-3. **数据安全兜底**:可选的云端异地容灾,为用户的数据提供多层保护。
-
-**产品形态**:
-- **软件方案**(MVP):配置文件 + 一键部署脚本,部署在任意 Linux 主机上(Docker 镜像在 v2.5 提供)
-- **硬件一体机**(目标形态):定制盒子,内置 SSD + 电池 + SD 卡槽 + WiFi,开箱即用
-
-**市场机会**:
-- Gnarbox(曾最受欢迎的摄影师外拍备份设备)已停产,市场空缺明显
-- UnifyDrive UT2($599)硬件形态相似但软件体验差、电池仅 1 小时
-- ClouZen TAINER 功能单一,只能备份不能联网同步
-- **没有产品把「现场备份」和「远程归档回 NAS」打通成自动流水线**
-
----
-
-## 二、目标用户
-
-| 用户画像 | 典型场景 | 痛点 |
-|----------|----------|------|
-| 摄影师 | 出差/外拍,酒店回看、粗修当天照片(Lightroom) | RAW 文件 25-60MB,SMB 远程逐张打开极慢,预览生成卡死 |
-| 视频创作者 | 远程剪辑,浏览素材库、拖拽代理文件 | 视频文件更大,顺序播放需持续带宽 |
-| 设计师 | 出差访问公司 NAS 上的 PSD/AI 源文件 | 大文件 + 多图层,打开一个文件几分钟 |
-| 远程办公族 | 日常办公文档、项目资料存 NAS | 小文件多,SMB 目录浏览延迟高,体验卡顿 |
-| NAS 重度用户 | 旅行途中访问个人数据 | 没有公网 IP 或带宽不足,现有方案都不理想 |
-
-**核心用户优先级**:摄影师(Lightroom + 外拍备份)> 远程办公 > 视频创作者
-
----
-
-## 三、系统架构
-
-### 3.1 整体架构
-
-```mermaid
-graph LR
- subgraph clients ["客户端设备"]
- LR["Lightroom
macOS"]
- Linux["Linux
客户端"]
- iPad["iPad
移动端"]
- end
-
- subgraph proxy ["Linux Proxy(局域网·高速)"]
- Samba["Samba Server
(read only)"]
- NFS_S["NFS Server
(read only)"]
- WebDAV_S["WebDAV Server
(read only)"]
- VFS["rclone VFS mount
(只读缓存)"]
- Uploader["SD Uploader
(单向上传到 NAS)"]
- SSD["SSD 缓存 + 元数据 DB"]
- Samba --> VFS
- NFS_S --> VFS
- WebDAV_S --> VFS
- VFS --> SSD
- end
-
- subgraph remote ["远程(公网 / Tailscale·低速)"]
- TS["Tailscale /
WireGuard"]
- NAS["任意品牌 NAS
(SFTP)"]
- TS --- NAS
- end
-
- LR -- "SMB" --> Samba
- Linux -- "NFS" --> NFS_S
- iPad -- "WebDAV" --> WebDAV_S
- VFS -- "SFTP (读取)" --> TS
- Uploader -- "SFTP (上传)" --> TS
-```
-
-### 3.2 协议选择说明
-
-| 段 | 协议 | 原因 |
-|----|------|------|
-| 客户端 → Proxy(主) | SMB | 对 Lightroom/macOS/Windows 原生兼容,应用无感 |
-| 客户端 → Proxy(辅) | NFS | Linux 客户端性能更好,内核级支持 |
-| 客户端 → Proxy(辅) | WebDAV | 移动端 App 支持广泛 |
-| Proxy → NAS | SFTP | 高延迟链路下比 SMB 稳定得多,任意品牌 NAS 均支持,无需额外套件 |
-
-### 3.3 多协议对外服务(设计讨论)
-
-**问题**:客户端 → Warpgate 之间只支持 SMB 是否足够?
-
-**讨论**:不同客户端设备对协议的偏好不同。macOS + Lightroom 最适合 SMB,但 Linux 客户端用 NFS 性能更好(内核级支持,且 Linux 侧还能再叠一层 FS-Cache),iPad/移动端 App 则普遍支持 WebDAV。
-
-**设计决策**:所有对外协议服务共享同一个 rclone FUSE 只读挂载点。缓存层只有一份,不会因为多协议而重复缓存。所有协议均为只读模式,客户端无法通过共享写入文件。
-
-```mermaid
-graph LR
- SMB["SMB Server
(read only)"] --> Mount["/mnt/nas-photos
(rclone 只读 FUSE 挂载)"]
- NFS["NFS Server
(read only)"] --> Mount
- WebDAV["WebDAV
(read only)"] --> Mount
- Mount --> SSD["SSD 缓存
(rclone 内部管理)"]
-```
-
-**注意事项**:所有共享均为只读。文件写入通过 SD 卡导入功能单向上传到 NAS(独立于缓存系统)。
-
----
-
-## 四、核心功能
-
-### P0(MVP 必须)
-
-#### 4.1 透明多协议只读代理
-- 对外暴露标准 SMB 只读共享,客户端连接方式与直连 NAS 完全一致
-- 支持 SMB2/SMB3 协议(`read only = yes`)
-- 同时支持 NFS 只读导出(Linux 客户端)和 WebDAV 只读服务(移动端)
-- 支持 macOS(Finder/Lightroom)、Windows(Explorer)、Linux、移动端客户端
-- 文件读取、目录浏览、文件属性(时间戳/权限)均正常工作
-- 所有协议共享同一个 rclone 只读缓存层,不重复存储
-- **只读设计**:Lightroom 等应用读取 RAW 文件,编辑参数存在本地 catalog 中,不需要写入 NAS 文件所在目录
-
-#### 4.2 读缓存(Read-through Cache)
-- 文件首次被访问时,从远程 NAS 拉取并存入本地 SSD 缓存
-- 后续访问同一文件直接从本地 SSD 返回
-- 支持分块读取(chunked read):大文件不需要整个下载完才能开始读取
-- 支持预读(read-ahead):顺序读取场景下提前拉取后续数据
-- 目录列表缓存:目录结构缓存一段时间,避免频繁远程查询
-
-#### 4.3 缓存一致性
-- 缓存为**只读**,不存在本地修改,因此不存在写冲突问题
-- 后台轮询检测远程变更,自动标记缓存失效(详见第六章)
-- 远程文件被修改 → 下次访问时 rclone 自动拉取最新版本
-- 远程文件被删除 → rclone 缓存刷新后文件自动消失
-- 无脏文件、无回写、无冲突检测 —— 架构极简
-
-#### 4.5 远程变更检测
-- 基于 SFTP 的分层轮询机制,自动发现远程数据变化(详见第六章)
-- 不依赖任何 NAS 品牌特有 API,纯 SFTP 协议实现
-- 每日全量校对兜底
-
-#### 4.6 缓存空间管理
-- 设置缓存总大小上限
-- 超出上限时按 LRU(最久未访问)策略自动淘汰
-- 可设置缓存盘最低保留空间,防止磁盘写满
-- 可设置缓存最大保留时间
-- 缓存全部为只读(clean),rclone 可自由淘汰任何缓存文件
-
-#### 4.7 一键部署
-- 提供完整的配置文件 + 部署脚本
-- 自动安装依赖(rclone、Samba、NFS、fuse)
-- 自动配置 systemd 服务,开机自启
-- 自动配置日志轮转
-- 缓存盘文件系统建议 btrfs/ZFS(CoW + journal 保护一致性)
-
-### P1(重要但非 MVP)
-
-#### 4.8 SD 卡导入 + 自动归档(Ingest)
-
-摄影师外拍结束,把 SD 卡插入盒子,一键备份到本地 SSD 暂存,后台独立进程通过 SFTP 直接上传到家中 NAS。**与只读缓存完全独立**——导入是单向的「SD → NAS」管道,不经过缓存系统。
-
-- 检测到 SD/CFexpress 卡插入后,支持物理按钮一键触发导入或自动导入模式
-- **导入前空间检查**:估算 SD 卡总数据量,检查 SSD 暂存区可用空间(扣除 CACHE_MIN_FREE),空间不足时拒绝导入并通知用户
-- 复制文件到 SSD 暂存目录(`ingest_staging/`),导入时计算文件 checksum(SHA-256),确保数据完整
-- 导入记录写入 import_history 持久表(详见 5.6.4)
-- **SD Uploader 独立进程**通过 SFTP 将暂存文件直接上传到 NAS 目标路径,无网络时排队等待
-- 上传成功后清理暂存文件 + 可选 `vfs/forget` 通知 rclone 刷新(使新文件在只读缓存中可见)
-- 支持重复文件检测(基于文件名+大小+checksum),查询 import_history 持久表(详见 5.6.4),避免重复导入
-- 导入完成通过 LED 指示灯 / 蜂鸣器提示
-- 支持按日期模板自动组织目标路径(如 `/{year}/{month}/{date}/`),日期来源为 EXIF 拍摄日期,非 EXIF 文件回退到文件 mtime(详见 INGEST_DATE_SOURCE 配置)
-- **导入中断保护**:导入过程维护状态机(`detecting → copying → checksumming → uploading → complete`),中断的文件(未完成 checksum 校验)将被清理(详见 5.8)
-
-#### 4.9 双卡备份 + 校验
-
-摄影师同时插入两张 SD 卡(或一张 SD 卡 + 一个 USB 移动硬盘),盒子自动做双向 checksum 比对,确保两份备份完全一致。
-
-- 并行读取两个存储设备的文件列表
-- 逐文件比对:文件名 + 大小一致后计算 SHA-256 比对
-- 不一致的文件标记为异常并通知用户
-- 仅一侧存在的文件单独提示
-- 校验完成输出报告(LED 状态 + CLI 可查详情)
-- 校验通过的文件自动进入 4.8 导入归档流程
-
-#### 4.10 配网模式 + Captive Portal 代理(Setup AP)
-
-盒子是 Headless 设备(无屏幕),而绝大多数酒店/机场 WiFi 需要网页认证(Captive Portal)。没有这个功能,旅途场景直接不可用。
-
-**核心流程**:
-
-```
-① 盒子开机,检测到未配置 WiFi 或无法联网
- → 自动进入「配网模式」,WiFi 模块启动临时 AP(SSID: Warpgate-Setup)
-
-② 用户手机连接 Warpgate-Setup 热点
- → 自动弹出配网页面(或手动访问 http://192.168.4.1)
-
-③ 配网页面显示周围可用 WiFi 列表,用户选择酒店 WiFi
-
-④ 盒子连接酒店 WiFi(WiFi 模块切换为 AP+STA 并发模式)
- → 检测到 Captive Portal 重定向
-
-⑤ 盒子将 Captive Portal 认证页面代理到配网页面
- → 用户在手机上完成酒店 WiFi 的网页认证(输入房号/姓名等)
-
-⑥ 认证通过,盒子获得互联网访问,Tailscale 自动连接
- → 配网模式关闭,临时 AP 关闭(或保持为管理入口)
-```
-
-**硬件要求**:WiFi 模块必须支持 **AP+STA 并发模式**(同时作为热点和连接外部 WiFi),这是配网模式的前提。大多数支持 AP 模式的 WiFi 芯片均支持此功能。
-
-**Fallback 方案**(不需要额外开发,文档中列出即可):
-- **USB 网络共享**:手机 USB 连接盒子,共享手机网络(tethering),绕过酒店 WiFi
-- **手机热点**:盒子直连手机 4G/5G 热点
-- **有线以太网**:部分酒店有网口,直插通常无需认证
-- **MAC 克隆**:`warpgate clone-mac ` 克隆已认证设备的 MAC 地址(高级用户)
-
-#### 4.11 缓存预热(Warm-up)
-- 命令行手动预热指定目录
-- 按时间范围预热(如"最近 7 天新增的文件")
-- 定时预热任务(如每天凌晨自动拉取最新数据)
-- 预热进度显示
-
-#### 4.12 管理工具(CLI)
-- `warpgate status` — 查看服务状态、缓存使用量、SD 上传队列、当前带宽限速
-- `warpgate cache-list` — 列出缓存中的文件
-- `warpgate cache-clean` — 清理缓存
-- `warpgate warmup` — 手动预热
-- `warpgate bwlimit` — 动态调整带宽限制
-- `warpgate ingest` — 手动触发 SD 卡导入
-- `warpgate verify` — 双卡校验
-- `warpgate log` — 查看实时日志
-- `warpgate speed-test` — 链路速度测试
-- `warpgate setup-wifi` — 手动进入配网模式
-- `warpgate clone-mac ` — 克隆指定设备的 MAC 地址
-
-#### 4.13 带宽管理
-- 支持上传(SD 导入上传)/下载(缓存拉取)分别限速
-- 运行时动态调整限速(不重启服务)
-- SD 上传带宽不影响读取体验
-- **自适应限速(Adaptive Throttle)**:基于吞吐量观测自动降速,避免 SD 上传占满链路
- - 监控上传吞吐量的滑动窗口(如最近 30s 平均值),当吞吐持续下降超过阈值时判定链路拥塞
- - 拥塞时自动降速,释放带宽给读取和其他流量
- - 每隔一段时间小幅探测提速
- - throttle 状态通过 `warpgate status` 实时可见
- - 用户可通过 `BW_ADAPTIVE` 配置关闭自适应限速
- - 自适应限速**仅控制 SD 上传**,不影响缓存读取拉取
-
-#### 4.14 连接容错
-- Tailscale 断连时自动重试
-- 已缓存的文件在离线时仍可正常读取
-- SD 上传队列在恢复连接后自动续传
-- 连接超时参数可配置
-
-### P2(后续迭代)
-
-#### 4.16 WiFi AP 现场共享
-
-盒子内置 WiFi 模块开启持久热点,现场团队设备连上即可通过 SMB/WebDAV 访问缓存目录。与 4.10 配网模式的区别:配网 AP 是临时的(完成配网后关闭),本功能是持久的团队共享热点。
-
-- 支持 AP 模式,复用已有的 SMB/WebDAV 多协议服务
-- AP 网络与 Tailscale/WAN 网络隔离(安全考虑)
-- AP 模式下仍可同时通过有线/4G 连接 Tailscale 做后台 SD 上传
-- **硬件要求**:需要两个独立网络接口——WiFi 模块用于 AP 热点,有线/USB 4G 网卡用于 WAN/Tailscale 连接。一体机硬件设计需预留双网卡
-- 典型场景:婚礼现场摄影师导入 SD 卡后,助理 iPad 连上 WiFi 即可浏览选片
-
-#### 4.17 Web 管理界面
-- 缓存状态仪表盘(大小、命中率、SD 上传队列、带宽趋势图)
-- 缓存文件浏览器(查看/手动清除/手动预热)
-- 配置修改界面(参数调整无需编辑配置文件)
-- SD 导入历史浏览
-- 实时日志查看器
-
-#### 4.18 NAS 侧 Agent 推送(可选增强)
-- 在 NAS 上运行轻量 Agent(Docker 容器),监听文件变化主动推送给 Proxy
-- 实现秒级远程变更感知(替代分钟级轮询)
-- 不依赖品牌 API,基于 inotify 通用方案
-
-#### 4.19 多 NAS / 多目录支持
-- 同时连接多个远程 NAS(如家里 + 工作室)
-- 每个 NAS 独立共享名,独立缓存策略
-- 每个共享可配置不同的缓存大小和保留时间
-
-#### 4.20 智能缓存策略
-- 根据文件类型自动调整策略:
- - `.lrcat` / `.xmp`(Lightroom catalog/sidecar)→ 高缓存优先级
- - `.CR3` / `.ARW` / `.NEF`(RAW 照片)→ 大块预读,长缓存保留
- - `.mp4` / `.mov`(视频)→ 顺序预读优化
- - `.psd` / `.ai`(设计文件)→ 完整缓存,避免分块导致的兼容问题
-- 基于访问频率自动调整缓存优先级(热数据不被淘汰)
-
-#### 4.21 Docker 镜像
-- 一行命令启动:`docker run -v /mnt/ssd:/cache warpgate`
-- docker-compose 配置
-- 支持环境变量或挂载配置文件
-
-#### 4.22 通知机制
-- SD 上传失败告警(Webhook / Telegram / 邮件)
-- 缓存空间不足告警
-- NAS 离线告警
-- SD 导入完成 / 上传完成通知(可选)
-
----
-
-## 五、数据一致性模型
-
-### 5.1 设计目标
-
-采用**只读缓存 + 单向上传**模型,两个功能完全独立。具体承诺:
-
-1. 远程 NAS 上的变更会在可控时间内被 Proxy 感知并更新本地缓存(后台轮询)
-2. 已缓存的文件在离线时仍可正常访问
-3. SD 卡导入的文件最终会上传到远程 NAS(SD Uploader 异步上传 + 断电恢复靠 SSD 持久化暂存文件)
-4. **无写冲突**:缓存只读,不存在本地修改与远程变更冲突的可能
-
-### 5.2 设计讨论与决策过程
-
-#### 问题 A:为什么选择只读缓存而非读写缓存?
-
-**讨论**:最初设计了 OverlayFS + sync daemon 的读写缓存方案(本地写入 → 异步回写 NAS),但面临大量边界场景:
-- 写冲突检测(本地改了文件 + NAS 也被别人改了)
-- mtime 精度不一致导致误判
-- 断电后脏文件恢复
-- TOCTOU 竞态条件
-- 远程删除后脏文件复活
-
-**最终决策**:只读缓存 + 单向上传。理由:
-- 摄影师核心工作流是「浏览/粗选 RAW」(只读)+ 「SD 卡导入」(单向),不需要通过缓存写回 NAS
-- Lightroom 编辑参数存在本地 catalog(.lrcat),不修改 RAW 文件本身
-- 只读缓存消灭了所有写冲突、脏文件、回写等复杂性
-- SD 导入是纯新文件的单向上传,不存在冲突
-- 写回能力可以作为 v2.0 按需加入
-
-> **MVP 部署建议**:**建议**缓存盘使用 btrfs 或 ZFS 文件系统(CoW + checksum 保护断电一致性)。部署脚本应检测并警告非 CoW 文件系统。
-
-#### 问题 B:SD 导入上传中断怎么办?
-
-**讨论**:SD 卡导入后文件暂存在 SSD 上等待上传到 NAS。上传过程中可能断电、断网、进程崩溃。
-
-**决策**:
-- 暂存文件持久化在 SSD 上,重启后 SD Uploader 扫描暂存目录自动继续上传
-- 上传到 NAS 时使用临时文件名(`.warpgate-tmp-`),完成后 rename 为最终文件名,避免 NAS 上出现不完整文件
-- 上传成功后清理暂存文件,可选 `vfs/forget` 刷新缓存使新文件可见
-
-### 5.3 一致性模型(只读缓存 + 单向上传)
-
-架构分为两个完全独立的子系统:
-
-```
-只读缓存路径:应用 → Samba/NFS(只读) → rclone FUSE 挂载 → SSD 缓存 ← SFTP 从 NAS 按需拉取
-SD 上传路径: SD 卡 → ingest_staging/(SSD 暂存) → SD Uploader → SFTP 直传 NAS
-```
-
-**只读缓存**:
-- rclone 以 `--read-only --vfs-cache-mode full` 挂载,Samba/NFS 以只读模式共享
-- 所有缓存文件都是 clean 的(与远程一致),rclone 自由管理 LRU 淘汰
-- 后台轮询检测远程变更 → `vfs/forget` 通知 rclone 刷新 → 下次访问拿到最新版本
-- 无脏文件、无回写、无冲突 —— **零数据一致性风险**
-
-**SD 单向上传**:
-- SD Uploader 是独立进程,与缓存系统无交互
-- 从 `ingest_staging/` 扫描待上传文件 → SFTP 上传到 NAS(临时文件名 + rename)
-- 上传失败自动重试(指数退避),断电重启后扫描暂存目录继续上传
-- 上传成功后清理暂存文件,可选 `vfs/forget` 刷新缓存使新文件可见
-- **无冲突**:SD 导入的都是新文件,NAS 上不存在同名文件时直接创建;已存在时跳过(import_history 去重已在导入阶段处理)
-
-**缓存淘汰**:rclone 由 `--vfs-cache-max-size` 和 `--vfs-cache-max-age` 控制 LRU 淘汰,所有缓存文件均可安全淘汰。
-
-### 5.4 读取时的缓存验证
-
-缓存为只读,所有文件都是 clean 的,验证逻辑非常简单:
-
-| 远程状态 | 缓存行为 |
-|---|---|
-| 远程没变 | ✅ 直接用缓存 |
-| 远程被修改 | 🔄 轮询发现变化 → `vfs/forget` → rclone 下次访问时拉新版本 |
-| 远程被删除 | 🗑️ 轮询发现变化 → `vfs/forget` → rclone 刷新后文件消失 |
-
-**注意**:读取热路径上**不查远程**(不产生网络请求),直接返回缓存。远程变更由后台轮询线程定期发现(见第六章)。
-
-### 5.5 关键场景走查
-
-#### 场景 1:Cache 关机几天,NAS 上文件被修改
-
-```mermaid
-flowchart TD
- D1["Day 1: Cache 缓存 photo.cr3 (clean)"] --> Off["Day 1: Cache 关机"]
- Off --> D3["Day 3: NAS 上 photo.cr3 被修改"]
- D3 --> D5["Day 5: Cache 开机
rclone 启动,轮询线程开始"]
- D5 --> Detect["轮询发现目录 mtime 变化"]
- Detect --> Forget["vfs/forget → rclone 缓存失效"]
- Forget --> Access["用户访问时 rclone 自动拉新版本"]
- Access --> Result["✅ 用户读到 NAS 最新版本"]
-```
-
-#### 场景 2:NAS 删了文件
-
-```mermaid
-flowchart TD
- D1["Day 1: Cache 缓存 photo.cr3 (clean)"]
- D1 --> D3["Day 3: NAS 上删除 photo.cr3"]
- D3 --> Poll["轮询检测到远程目录变化"]
- Poll --> Forget["vfs/forget → rclone 刷新"]
- Forget --> Gone["rclone 缓存自动消失"]
- Gone --> Result["✅ 本地缓存与远程保持一致"]
-```
-
-#### 场景 3:SD 卡导入 + 上传到 NAS(最常见 happy path)
-
-```mermaid
-flowchart TD
- Insert["摄影师插入 SD 卡"] --> Detect["检测到卡,按按钮触发导入"]
- Detect --> Copy["复制到 ingest_staging/
计算 SHA-256"]
- Copy --> Dedup["查 import_history → 无重复"]
- Dedup --> Record["记录 import_history"]
- Record --> Upload["SD Uploader 通过 SFTP
上传到 NAS(临时文件 + rename)"]
- Upload --> Clean["清理暂存文件
vfs/forget 刷新缓存"]
- Clean --> Result["✅ 文件安全到达 NAS
缓存中可见新文件"]
-```
-
-#### 场景 4:SD 导入后离线,后续上传
-
-```mermaid
-flowchart TD
- Insert["外拍现场,无网络"] --> Import["SD 卡导入到暂存目录
文件安全存在 SSD 上"]
- Import --> Queue["SD Uploader 发现无网络
上传排队等待"]
- Queue --> Hotel["回到酒店,网络恢复"]
- Hotel --> Resume["SD Uploader 自动续传
逐文件上传到 NAS"]
- Resume --> Result["✅ 无需人工干预"]
-```
-
-### 5.6 元数据持久化(metadata DB)
-
-metadata.db 用于 **SD 卡导入去重**(import_history)和**远程变更轮询**(dir_snapshots)。使用 SQLite 存储。只读缓存不需要额外状态数据库——rclone 内部管理所有缓存状态。
-
-#### 5.6.1 设计讨论:metadata DB 应该包含什么?
-
-**问题**:metadata DB 需要存 NAS 侧的所有文件元数据吗?
-
-**分析**:NAS 上可能有几十万甚至上百万文件,但用户一次出差实际访问的可能只有几百到几千个。如果全量存储 NAS 文件元数据,会带来几个问题:
-
-- 初始化成本高——首次使用需要递归扫描整个 NAS 目录树
-- 存储浪费——大量从未访问的文件元数据没有价值
-- 同步负担重——需要持续维护全量数据的一致性
-
-**结论**:metadata DB **只管辅助功能**(轮询和导入去重),不存文件级缓存状态。缓存状态完全由 rclone 内部管理。
-
-**怎么检测远程文件被删了?**
-
-rclone 自动处理,不需要数据库:
-
-假设目录 `/2026/02/` 下有 200 张照片,用户只缓存了 3 张。
-
-场景 A:NAS 上 IMG_0050.cr3 被删了(从未缓存过)
-→ 跟缓存无关,不处理 ✅
-
-场景 B:NAS 上 IMG_0001.cr3 被删了(缓存过)
-→ 轮询发现目录变化 → `vfs/forget` → rclone 刷新 → 缓存文件消失 ✅
-
-场景 C:NAS 上 IMG_0050.cr3 被修改了(缓存过)
-→ 轮询发现目录变化 → `vfs/forget` → rclone 刷新 → 下次读取拿到新版本 ✅
-
-**最终决策**:
-
-| 表 | 用途 | 理由 |
-|---|---|---|
-| dir_snapshots | 轮询目录 mtime 快检 | 记住上次轮询时目录的 mtime,判断是否变化 |
-| import_history | SD 导入去重 | 持久记录历史导入,防止重复导入 |
-
-#### 5.6.2 缓存目录结构
-
-```
-/mnt/ssd/warpgate/
-├── rclone-cache/ # rclone VFS 内部缓存目录(rclone 自动管理)
-│ └── vfs/
-│ └── photos/
-│ └── 2026/02/
-│ └── IMG_0001.cr3 # 远程拉取缓存
-├── metadata.db # SQLite 元数据库(WAL 模式,详见 5.6.7)
-└── ingest_staging/ # SD 卡导入暂存目录(导入状态机使用,详见 5.8)
- └── / # 每次导入会话独立目录
-```
-
-**挂载关系**:
-```bash
-# rclone 只读 FUSE 挂载(= Samba/NFS 共享的根目录)
-rclone mount remote:photos /mnt/nas-photos \
- --read-only \
- --vfs-cache-mode full \
- --vfs-cache-max-size ${CACHE_MAX_SIZE} \
- --vfs-cache-max-age ${CACHE_MAX_AGE} \
- --cache-dir /mnt/ssd/warpgate/rclone-cache \
- --rc
-
-# Samba 配置(只读)
-# [nas-photos]
-# path = /mnt/nas-photos
-# read only = yes
-```
-
-**重要**:
-- `rclone-cache/` 由 rclone 内部管理,外部进程**不得直接操作**
-- `ingest_staging/` 由 SD 导入进程管理,SD Uploader 从此目录读取待上传文件
-- Samba/NFS 直接服务于 rclone FUSE 挂载点 `/mnt/nas-photos`
-
-#### 5.6.3 表结构定义
-
-**表 1:dir_snapshots — 目录级轮询快照**
-
-用于分层轮询的"目录 mtime 快检"。只记录被缓存文件所在的目录,不是 NAS 全量目录。
-
-```sql
-CREATE TABLE dir_snapshots (
- dir_path TEXT PRIMARY KEY, -- 目录相对路径,如 /2026/02/
- remote_mtime INTEGER, -- 上次轮询时远程目录的 mtime
- -- NULL 表示 SD 卡导入创建的目录,远程尚不存在
- last_polled INTEGER NOT NULL, -- 上次轮询时间
- last_accessed INTEGER NOT NULL -- 目录最后被访问时间(决定热/温/冷分级)
-);
-```
-
-生命周期:
-
-```
-某目录下的文件首次被缓存 → INSERT
-SD 卡导入上传到新目录 → INSERT(SD Uploader 上传完成后,remote_mtime 为上传后 stat 的值)
-轮询时目录 mtime 没变 → UPDATE last_polled
-轮询时目录 mtime 变了 → UPDATE remote_mtime,触发 vfs/forget 刷新缓存
-目录下已无缓存文件 → DELETE(可选)
-```
-
-**表 1b:dir_file_list — 目录文件列表快照(后续增强)**
-
-MVP 阶段不需要。后续加入后,记录被关心目录下的全部远程文件,支持精确变更类型识别和智能预热。
-
-```sql
-CREATE TABLE dir_file_list (
- dir_path TEXT NOT NULL, -- 所属目录
- file_name TEXT NOT NULL, -- 文件名
- remote_mtime INTEGER NOT NULL, -- 上次已知的远程 mtime
- remote_size INTEGER NOT NULL, -- 上次已知的远程文件大小
- snapshot_time INTEGER NOT NULL, -- 快照时间
- PRIMARY KEY (dir_path, file_name)
-);
-
-CREATE INDEX idx_dir ON dir_file_list(dir_path);
-```
-
-#### 5.6.4 导入历史表(重复检测用)
-
-**问题**:重复文件检测不能只看当前暂存目录内容——已上传到 NAS 并从暂存区清理的文件,再次导入同一张 SD 卡时无法检测到重复。
-
-**解决方案**:import_history 持久表,记录所有历史导入记录,永不删除。
-
-```sql
-CREATE TABLE import_history (
- id INTEGER PRIMARY KEY AUTOINCREMENT,
- original_path TEXT NOT NULL, -- SD 卡上的原始路径
- target_path TEXT NOT NULL, -- 上传到 NAS 的目标路径
- file_size INTEGER NOT NULL, -- 文件大小
- checksum TEXT NOT NULL, -- SHA-256 校验和
- source_device TEXT, -- SD 卡设备标识(如序列号)
- imported_at INTEGER NOT NULL, -- 导入时间
- uploaded_at INTEGER, -- 上传到 NAS 的时间(NULL=未上传)
- state TEXT NOT NULL -- imported / uploaded / failed
-);
-
-CREATE INDEX idx_checksum ON import_history(checksum);
-CREATE INDEX idx_original_path ON import_history(original_path, file_size);
-```
-
-重复检测流程:
-```
-导入文件前 → 计算 checksum
- → 查 import_history WHERE checksum = ? AND file_size = ?
- → 命中 → 跳过该文件,标记为"已导入过"
- → 未命中 → 执行导入
-```
-
-#### 5.6.5 MVP 表结构关系与数据规模
-
-```mermaid
-erDiagram
- dir_snapshots {
- TEXT dir_path PK "如 /2026/02/"
- INTEGER remote_mtime
- INTEGER last_polled
- INTEGER last_accessed
- }
-
- import_history {
- INTEGER id PK "独立于缓存生命周期"
- TEXT original_path
- TEXT target_path
- TEXT checksum "持续累积,永久保留"
- TEXT state "imported/uploaded/failed"
- }
-```
-
-两张核心表。只读缓存不需要额外的文件状态数据库——rclone 内部管理所有缓存状态。后续可选增加 dir_file_list(精确变更检测)。
-
-数据规模估算:
-
-| 表 | 记录范围 | 预估行数 | 存储开销 |
-|---|---|---|---|
-| dir_snapshots | 有缓存文件的目录 | ~50 | ~5 KB |
-| import_history | 所有历史导入记录(持续累积) | ~5,000 | ~500 KB |
-| **总计** | | | **< 1 MB** |
-
-#### 5.6.6 远程变更检测
-
-后台轮询线程通过 dir_snapshots 表实现分层轮询(热/温/冷目录区分访问频率)。发现目录 mtime 变化后:
-1. `vfs/forget` 通知 rclone 清除该目录的缓存元数据
-2. rclone 下次访问该目录时自动从远程重新读取(删除的文件消失,修改的文件 mtime 更新,新增的文件出现)
-
-**不需要逐文件对比数据库记录**——rclone 的 `vfs/forget` + 自动重新拉取机制覆盖所有远程变更场景。
-
-#### 5.6.7 SQLite 并发访问策略
-
-metadata.db 会被多个进程/线程并发访问:轮询线程(读写 dir_snapshots)、SD 卡导入进程(读写 import_history)、SD Uploader(更新 import_history 状态)、CLI 管理工具(只读查询)。
-
-**要求**:metadata.db 必须存放在**本地文件系统**(SSD 的 ext4/btrfs/ZFS)上,**严禁放在 rclone FUSE 挂载目录中**。SQLite WAL 依赖 POSIX 文件锁和共享内存(`-shm` 文件),FUSE/网络文件系统无法正确支持这些语义,会导致数据库损坏。
-
-metadata.db 必须以 **WAL(Write-Ahead Logging)模式**运行:
-
-```sql
-PRAGMA journal_mode=WAL;
-PRAGMA busy_timeout=5000; -- 锁等待超时 5 秒
-```
-
-WAL 模式的优势:
-- 读操作不阻塞写操作,写操作不阻塞读操作
-- 多个进程可以同时读取,只有写入互斥
-- 适合"多读少写"的缓存元数据场景
-
-所有访问 metadata.db 的进程必须使用同一个 WAL 模式配置。部署脚本在初始化数据库时自动设置。
-
-### 5.7 SD 卡导入与上传
-
-SD 卡导入和上传是**独立于缓存系统的单向管道**:SD 卡 → SSD 暂存 → SFTP 上传 NAS。
-
-**实现要点**:
-1. SD 卡导入进程将文件复制到 `ingest_staging//` 暂存目录并计算 checksum
-2. 查 import_history 去重,校验通过后记录导入历史
-3. SD Uploader 独立进程扫描 `ingest_staging/` 中已校验完成的文件
-4. 通过 SFTP 上传到 NAS 目标路径(临时文件名 `.warpgate-tmp-` → rename 为最终文件名)
-5. 上传成功后更新 import_history 状态 + 清理暂存文件 + 可选 `vfs/forget` 刷新缓存
-
-**性能优势**:导入直接写入 SSD 本地文件系统,无 FUSE 开销。导入速度取决于 SD 卡读取速度和 SSD 写入速度,通常可达 100MB/s+。上传带宽独立于缓存读取带宽。
-
-### 5.8 SD 卡导入状态机(中断保护)
-
-**问题**:SD 卡导入过程中可能发生中断(卡被拔出、电池耗尽、进程崩溃)。部分复制的文件不应进入上传队列。
-
-**解决方案**:导入过程维护严格的状态机,只有完整校验通过的文件才进入上传队列。
-
-```mermaid
-stateDiagram-v2
- [*] --> detecting: SD 卡插入
- detecting --> copying: 列出文件清单
- copying --> checksumming: 复制到 ingest_staging/
- checksumming --> registered: SHA-256 校验通过 +
写入 import_history
- registered --> uploading: SD Uploader 开始上传
- uploading --> complete: SFTP 上传成功 +
清理暂存文件
- complete --> [*]
-
- note right of detecting: 中断 → 清理 staging 临时文件
- note right of copying: 中断 → 清理 staging 临时文件
- note right of checksumming: 中断 → 清理不完整文件
- note right of registered: 中断 → 文件在暂存区 + DB
重启后 SD Uploader 继续上传
- note right of uploading: 中断 → NAS 上临时文件
重启后重新上传覆盖
-```
-
-**中断保护**:每个导入会话有唯一 `session_id`。`detecting`/`copying`/`checksumming` 阶段中断时,`ingest_staging/` 中的临时文件在下次启动时自动清理。`registered`/`uploading` 阶段中断时文件安全在暂存目录中,SD Uploader 重启后扫描暂存目录继续上传。NAS 上的临时文件名(`.warpgate-tmp-`)确保不完整上传不会被当作正常文件。
-
-**实现要点**:
-- 暂存目录 `ingest_staging//` 按导入会话隔离
-- SD Uploader 启动时扫描 `ingest_staging/`,清理未完成校验的会话,续传已校验的文件
-- 导入进度持久化到 `ingest_sessions` 表或简单的 JSON 文件,支持断点续传
-
----
-
-## 六、远程变更检测机制
-
-### 6.1 设计约束
-
-**不依赖任何 NAS 品牌特有 API**。产品需要支持群晖、QNAP、威联通、TrueNAS 等任意品牌 NAS,因此只能基于标准 SFTP 协议实现。
-
-### 6.2 SFTP 协议的能力边界(设计讨论)
-
-**问题**:远程 NAS 上数据更新了,Cache 怎么知道?能否实时感知?
-
-**讨论**:SFTP 协议本身**没有任何通知/推送/订阅机制**。它是无状态的文件传输协议,不支持 inotify、webhook、filesystem watch 等概念。每次想知道远程状态,必须主动发请求查询。
-
-**考虑过的方案**:
-
-| 方案 | 原理 | 实时性 | 品牌依赖 | 取舍 |
-|------|------|--------|----------|------|
-| SFTP 全量轮询 | 递归 ls 对比 mtime | 分钟级 | 无 | 文件多时开销大 |
-| SFTP 分层轮询 | 先查目录 mtime,变了再查文件 | 分钟级 | 无 | 高效,推荐 |
-| 群晖 FileStation API | 调 DSM Web API | 秒级 | 仅群晖 | ❌ 不通用 |
-| NAS 侧 Agent 推送 | inotifywait → HTTP 通知 | 秒级 | 无 | 需要 NAS 装软件 |
-
-**最终决策**:
-
-- **P0(MVP)**:SFTP 分层轮询,零额外依赖
-- **P2(后续)**:可选的 NAS 侧 Agent 推送,作为增强项给需要秒级同步的用户
-
-### 6.3 分层轮询策略
-
-核心优化思路:**SFTP 目录本身也有 mtime**。当目录下有文件新增/修改/删除时,目录的 mtime 会更新。因此可以先查目录 mtime(一个 stat 请求),没变就跳过整个目录下所有文件的检查,大幅减少远程请求量。
-
-```mermaid
-flowchart TD
- Start["轮询触发"] --> L3{"第三层:热度分级
决定轮询间隔"}
- L3 -->|"热目录(7天内访问)
每 30s"| L1
- L3 -->|"温目录(7-30天)
每 5m"| L1
- L3 -->|"冷目录(30天+)
每 1h"| L1
-
- L1["第一层:目录 mtime 快检
SFTP stat 查目录 mtime
(每目录 1 个请求)"]
- L1 -->|"mtime 没变"| Skip["跳过该目录 ✅
所有文件一定没变"]
- L1 -->|"mtime 变了"| L2
-
- L2["第二层:文件级 mtime 对比
SFTP ls -l 该目录"]
- L2 --> Changed["发现变化 → 标记缓存失效
(等访问时再拉)"]
- L2 --> Del["发现删除 → 清理本地缓存"]
- L2 --> New["远程新增 → 不处理"]
-
- L4["第四层:每日全量校对(兜底)
凌晨全量递归对比
捕捉遗漏 + 清理过期条目"]
-```
-
-轮询伪代码(MVP):
-
-```
-# watched_directories = SELECT dir_path FROM dir_snapshots
-# 按热度分级决定轮询间隔(热 30s / 温 5m / 冷 1h)
-
-for dir in watched_directories:
- if now - dir.last_polled < poll_interval_for(dir):
- continue # 还没到该目录的轮询时间
-
- # 第一层:目录 mtime 快检(1 个 SFTP stat 请求)
- dir_mtime = sftp_stat(dir.dir_path).mtime
- if dir_mtime == dir.remote_mtime:
- UPDATE dir_snapshots SET last_polled = now WHERE dir_path = dir.dir_path
- continue # 目录没变,跳过 ✅
-
- # 第二层:目录变了 → 通知 rclone 刷新缓存
- rclone_rc("vfs/forget", dir=dir.dir_path) # 清除 rclone 目录缓存
- # rclone 下次访问时自动从远程重新读取:
- # - 远程删除的文件 → rclone 缓存消失 → 文件不再可见
- # - 远程修改的文件 → rclone 下次访问时拉新版本
- # - 远程新增的文件 → rclone 下次访问目录时可见
- log_info(f"Remote directory changed: {dir.dir_path}, rclone cache invalidated")
-
- # 更新目录快照
- UPDATE dir_snapshots SET remote_mtime = dir_mtime, last_polled = now
- WHERE dir_path = dir.dir_path
-```
-
-### 6.4 后续增强:NAS 侧 Agent 推送(P2)
-
-对于需要秒级同步的用户,可选在 NAS 上部署轻量 Agent(Docker 容器),通过 inotify 监听变化并推送:
-
-```mermaid
-flowchart LR
- subgraph NAS ["群晖 NAS"]
- Agent["inotifywait 监听文件变化
(Docker 容器)"]
- end
-
- subgraph Proxy ["Linux Proxy"]
- HTTP["HTTP 接收端
触发缓存刷新"]
- end
-
- Agent -- "轻量 HTTP POST
(via Tailscale)" --> HTTP
-```
-
-此方案作为分层轮询的增强,不是替代。即使 Agent 不可用,轮询机制仍然工作。
-
----
-
-## 七、缓存行为详细描述
-
-### 7.1 读取流程
-
-```mermaid
-flowchart TD
- App["应用请求读取文件"] --> Samba["① Samba/NFS(只读)"]
- Samba --> FUSE["② rclone FUSE 挂载"]
- FUSE -->|"缓存命中"| Return["直接返回缓存
(SSD 速度)"]
- FUSE -->|"缓存未命中"| Remote["③ rclone 从远程 NAS 拉取"]
- FUSE -->|"缓存已过期"| Refresh["④ rclone 检查远程 mtime
变了则重新拉取"]
-
- Remote --> Chunk["⑤ 按 chunk 分块下载"]
- Chunk --> Cache["⑥ 缓存到 SSD"]
- Cache --> ReturnData["⑦ 返回数据给应用"]
- Refresh --> ReturnData
-```
-
-**设计要点**:
-
-- 读取热路径上**不查远程**(不产生网络请求),直接返回缓存,保证响应速度
-- 目录缓存通过 rclone `--dir-cache-time` 控制过期刷新
-- 后台轮询线程通过 `vfs/forget` 主动刷新已知变化的目录
-
-### 7.2 SD 导入上传流程
-
-```mermaid
-flowchart TD
- SD["① SD 卡插入 → 导入到暂存目录
(SHA-256 校验 + import_history 去重)"]
- SD --> Staging["② 文件安全存在 ingest_staging/
(SSD 本地目录)"]
- Staging --> Scan["③ SD Uploader 扫描暂存目录"]
- Scan --> Upload["④ SFTP 上传到 NAS
(临时文件名 → rename)"]
-
- Upload -->|"上传成功"| Clean["⑤ 更新 import_history
清理暂存文件
vfs/forget 刷新缓存"]
- Upload -->|"上传失败"| Retry["⑤ 自动重试
(指数退避)"]
- Upload -->|"网络不可用"| Queue["⑤ 排队等待
网络恢复后自动续传"]
- Retry --> Upload
- Queue --> Upload
-```
-
-### 7.3 缓存淘汰策略
-
-缓存全部由 rclone 管理,所有文件都是 clean 的(与远程一致),可安全淘汰:
-
-- `--vfs-cache-max-size`:缓存总大小上限,超出时按 LRU 淘汰
-- `--vfs-cache-max-age`:缓存最大保留时间
-- rclone 自行管理淘汰,无需外部干预
-- 无脏文件 → 不存在「不能淘汰」的场景 → 缓存空间管理极简
-
-#### 7.3.1 SD 导入暂存空间保护
-
-**问题**:SD 卡导入的暂存文件占用 SSD 空间,等待上传到 NAS。离线时暂存文件会累积。
-
-**保护措施**:
-
-1. **导入前空间预检**:估算 SD 卡总数据量,检查 SSD 可用空间(扣除缓存 + `CACHE_MIN_FREE`),空间不足时拒绝导入并通知用户(LED 红灯 + CLI 提示),建议先连网上传或清理
-2. **配置验证**(部署时):`INGEST_MAX_IMPORT_SIZE + CACHE_MIN_FREE < SSD 总容量 - CACHE_MAX_SIZE`,不满足时部署脚本报警告
-
-### 7.4 离线行为
-
-| 场景 | 行为 |
-|------|------|
-| 远程不可达,读取已缓存文件 | 正常返回,无影响 |
-| 远程不可达,读取未缓存文件 | 超时报错(可配置超时时间) |
-| 远程不可达,SD 卡导入 | 正常导入到暂存目录,上传排队等待网络恢复 |
-| 远程不可达,后台轮询 | 静默跳过,不报错,下次重试 |
-| 恢复连接后 | SD Uploader 自动续传 + 立即触发一轮轮询 |
-
----
-
-## 八、配置参数清单
-
-### 连接配置
-
-| 参数 | 说明 | 默认值 | 建议值 |
-|------|------|--------|--------|
-| `NAS_HOST` | 远程 NAS 的 Tailscale IP | - | `100.x.x.x` |
-| `NAS_USER` | SFTP 用户名 | - | - |
-| `NAS_PASS` / `NAS_KEY_FILE` | 认证信息 | - | 建议密钥 |
-| `NAS_REMOTE_PATH` | NAS 上的目标路径 | - | `/volume1/photos` |
-| `SFTP_PORT` | SFTP 端口 | `22` | `22` |
-| `SFTP_CONNECTIONS` | SFTP 连接复用数 | `8` | `4-16` |
-
-### 缓存配置
-
-| 参数 | 说明 | 默认值 | 建议值 |
-|------|------|--------|--------|
-| `CACHE_DIR` | 缓存存储路径 | - | SSD 路径,建议 btrfs/ZFS |
-| `CACHE_MAX_SIZE` | 缓存大小上限 | `200G` | SSD 容量的 70-80% |
-| `CACHE_MAX_AGE` | 缓存最大保留时间 | `720h`(30天) | 按使用习惯 |
-| `CACHE_MIN_FREE` | 缓存盘最低可用空间 | `10G` | `10-20G` |
-
-### 读取优化
-
-| 参数 | 说明 | 默认值 | 场景建议 |
-|------|------|--------|----------|
-| `READ_CHUNK_SIZE` | 分块读取大小 | `256M` | RAW 照片: `256M`,视频: `512M`,文档: `64M` |
-| `READ_CHUNK_LIMIT` | chunk 自动增长上限 | `1G` | - |
-| `READ_AHEAD` | 预读缓冲区 | `512M` | 视频场景可加到 `1G` |
-| `BUFFER_SIZE` | 内存缓冲区 | `256M` | - |
-
-### 带宽配置
-
-| 参数 | 说明 | 默认值 | 场景建议 |
-|------|------|--------|----------|
-| `UPLOAD_TRANSFERS` | SD 上传并发线程 | `4` | 带宽小就设 `2` |
-| `BW_LIMIT_UP` | SD 上传限速上限 | `0`(不限) | 酒店 WiFi 建议 `10-20M` |
-| `BW_LIMIT_DOWN` | 缓存拉取下载限速 | `0`(不限) | 一般不限 |
-| `BW_ADAPTIVE` | 自适应上传限速开关 | `yes` | `yes`=根据吞吐量自动降速,`no`=纯手动 |
-
-### 目录缓存与轮询
-
-| 参数 | 说明 | 默认值 | 场景建议 |
-|------|------|--------|----------|
-| `DIR_CACHE_TIME` | 目录列表缓存时间 | `1h` | 个人: `1-2h`,协作: `5-15m` |
-| `POLL_HOT_INTERVAL` | 热目录轮询间隔(7天内有访问) | `30s` | - |
-| `POLL_WARM_INTERVAL` | 温目录轮询间隔(7-30天内访问) | `5m` | - |
-| `POLL_COLD_INTERVAL` | 冷目录轮询间隔(30天+未访问) | `1h` | - |
-| `FULL_SYNC_SCHEDULE` | 每日全量校对时间 | `03:00` | 凌晨低峰期 |
-
-### 多协议配置
-
-| 参数 | 说明 | 默认值 | 建议值 |
-|------|------|--------|--------|
-| `ENABLE_SMB` | 启用 SMB 共享 | `yes` | `yes` |
-| `ENABLE_NFS` | 启用 NFS 导出 | `no` | 有 Linux 客户端时开启 |
-| `ENABLE_WEBDAV` | 启用 WebDAV 服务 | `no` | 有移动端需求时开启 |
-| `NFS_ALLOWED_NETWORK` | NFS 允许访问的网段 | `192.168.0.0/24` | 按实际局域网设置 |
-| `WEBDAV_PORT` | WebDAV 监听端口 | `8080` | - |
-
-### SD 卡导入配置
-
-| 参数 | 说明 | 默认值 | 建议值 |
-|------|------|--------|--------|
-| `INGEST_MAX_IMPORT_SIZE` | 单次导入预留空间上限 | `256G` | 按最大 SD 卡容量设置 |
-| `INGEST_AUTO` | 插卡后自动导入 | `no` | 需按按钮确认 |
-| `INGEST_TARGET_PATH` | 导入到 NAS 的目标路径模板 | `/{year}/{month}/{date}/` | 按个人习惯,变量从 INGEST_DATE_SOURCE 确定 |
-| `INGEST_DATE_SOURCE` | 路径模板中日期变量的来源 | `exif` | `exif`=EXIF拍摄日期(回退到mtime),`mtime`=文件修改时间,`import`=导入时间 |
-| `INGEST_DUPLICATE_CHECK` | 重复文件检测(基于文件名+大小+checksum) | `yes` | `yes` |
-| `INGEST_DELETE_AFTER` | 导入+校验完成后是否删除卡上数据 | `no` | `no`(安全起见) |
-| `INGEST_IO_CLASS` | 导入时的 I/O 调度优先级 | `best-effort:4` | 使用 ionice 设置,避免导入阻塞缓存读取 |
-
-### 配网模式配置
-
-| 参数 | 说明 | 默认值 | 建议值 |
-|------|------|--------|--------|
-| `SETUP_AP_SSID` | 配网热点名称 | `Warpgate-Setup` | - |
-| `SETUP_AP_PASSWORD` | 配网热点密码(空=开放) | 空 | 首次配网建议开放,降低门槛 |
-| `SETUP_AP_AUTO` | 无网络时自动进入配网模式 | `yes` | `yes` |
-| `SETUP_AP_TIMEOUT` | 配网完成后临时 AP 保持时间 | `5m` | 认证成功后自动关闭 |
-| `SETUP_PORTAL_LISTEN` | 配网 Web 服务监听地址 | `192.168.4.1:80` | - |
-
-### WiFi AP 配置
-
-| 参数 | 说明 | 默认值 | 建议值 |
-|------|------|--------|--------|
-| `AP_ENABLED` | 启用 WiFi 热点 | `no` | 现场共享时开启 |
-| `AP_SSID` | 热点名称 | `Warpgate` | - |
-| `AP_PASSWORD` | 热点密码 | 随机生成 | 首次配置时设定 |
-| `AP_ISOLATION` | AP 网络与 WAN 隔离 | `yes` | `yes` |
-| `AP_MAX_CLIENTS` | 最大连接数 | `8` | - |
-
----
-
-## 九、场景预设(模板)
-
-为降低用户配置门槛,提供开箱即用的预设模板。
-
-### 摄影师模式
-
-```
-重点优化:大文件读取性能、RAW 浏览流畅
-- CACHE_MAX_SIZE=500G
-- READ_CHUNK_SIZE=256M
-- READ_AHEAD=512M
-- DIR_CACHE_TIME=2h ← 目录结构不常变
-- POLL_HOT_INTERVAL=30s
-- UPLOAD_TRANSFERS=4 ← SD 上传并发
-- ENABLE_SMB=yes
-- ENABLE_NFS=no
-- ENABLE_WEBDAV=no
-```
-
-### 视频剪辑模式
-
-```
-重点优化:顺序读取性能、大文件预读
-- CACHE_MAX_SIZE=1T
-- READ_CHUNK_SIZE=512M
-- READ_AHEAD=1G ← 大预读保证播放流畅
-- DIR_CACHE_TIME=1h
-- POLL_HOT_INTERVAL=1m
-- UPLOAD_TRANSFERS=2 ← 减少 SD 上传并发,保带宽给播放
-- ENABLE_SMB=yes
-- ENABLE_NFS=no
-- ENABLE_WEBDAV=no
-```
-
-### 文档办公模式
-
-```
-重点优化:小文件快速响应、频繁感知远程变更
-- CACHE_MAX_SIZE=50G
-- READ_CHUNK_SIZE=64M
-- READ_AHEAD=128M
-- DIR_CACHE_TIME=30m ← 协作场景需要较快看到新文件
-- POLL_HOT_INTERVAL=15s ← 更频繁感知远程变更
-- UPLOAD_TRANSFERS=4
-- ENABLE_SMB=yes
-- ENABLE_NFS=no
-- ENABLE_WEBDAV=yes ← 移动端也能访问
-```
-
----
-
-## 十、部署要求
-
-### 硬件要求(通用 Linux 主机部署)
-
-| 组件 | 最低配置 | 推荐配置 |
-|------|----------|----------|
-| CPU | ARMv8 / x86_64 任意 | N100 或同级 |
-| 内存 | 1 GB | 2-4 GB |
-| 缓存盘 | 任意 SSD | NVMe SSD |
-| 缓存容量 | 32 GB | 常用数据量的 30%+ |
-| 网口 | 100M | 千兆(2.5G 更好) |
-| 断电保护 | - | 内置电池或外接 UPS |
-
-### 硬件要求(一体机目标形态)
-
-通用要求之外,一体机额外需要:
-
-| 组件 | 说明 |
-|------|------|
-| SD 卡槽 | SD / microSD,覆盖大多数相机 |
-| CFexpress 槽(可选) | CFexpress Type-B,高端相机用户 |
-| USB-A/C 口 | 至少 2 个,用于外接读卡器(XQD 等)或移动硬盘 |
-| WiFi 模块 | 支持 **AP+STA 并发模式**(配网必须),建议 WiFi 6 |
-| 物理按钮 | 触发 SD 卡导入 / 确认操作 |
-| LED 状态指示 | 导入进度 / 完成 / 错误 / 上传状态 |
-| 内置电池 | 支持断电保护 + 便携使用 |
-
-**缓存盘文件系统建议**:btrfs 或 ZFS。利用 CoW(Copy-on-Write)和 journal 机制,即使意外断电也能保证文件系统级别的一致性。
-
-```bash
-# btrfs 格式化示例
-mkfs.btrfs /dev/ssd_partition
-mount -o compress=zstd /dev/ssd_partition /mnt/ssd/warpgate
-```
-
-### 软件要求
-
-| 组件 | 版本 |
-|------|------|
-| OS | Ubuntu 22.04+ / Debian 12+ / 任意 Linux |
-| rclone | 1.65+(关键参数:`--read-only --vfs-cache-mode full --vfs-cache-max-size {CACHE_MAX_SIZE} --cache-dir {CACHE_DIR}/rclone-cache --rc`)。`--read-only` 确保只读挂载。`--rc` 启用 RC API,供轮询线程调用 `vfs/forget` 刷新目录缓存 |
-| Samba | 4.x |
-| NFS server | nfs-kernel-server(如启用 NFS) |
-| FUSE | 3.x |
-| SQLite | 3.x(元数据存储) |
-| Tailscale / ZeroTier | 已配置并可连通 NAS |
-
-### NAS 侧要求
-
-| 项目 | 要求 |
-|------|------|
-| SFTP 服务 | 开启(群晖:控制面板 → 文件服务 → FTP → 勾选 SFTP) |
-| 用户权限 | SFTP 用户对目标目录有读写权限 |
-| Tailscale | 已安装并登录同一网络 |
-| 品牌 | **无限制**,任何支持 SFTP 的 NAS 均可(群晖/QNAP/威联通/TrueNAS/DIY 等) |
-
----
-
-## 十一、风险与局限
-
-| 风险 | 等级 | 说明 | 缓解措施 |
-|------|------|------|----------|
-| 首次访问慢 | 固有 | 未缓存文件必须走远程 | 预热功能;分块下载优化 |
-| 缓存一致性延迟 | 低 | 远程变更在轮询间隔内不可见 | 分层轮询(热目录 30s);后续可选 Agent 推送 |
-| Tailscale 断连 | 中 | 远程不可达时新文件无法获取 | 已缓存文件仍可用;SD 上传自动排队;恢复后自动续传 |
-| 轮询开销 | 低 | 大量文件目录轮询消耗带宽 | 目录 mtime 快检跳过未变目录;热度分级降低冷目录频率 |
-| SD 卡导入数据损坏 | 低 | 卡本身坏块导致导入不完整 | 导入时计算 SHA-256 校验和;双卡校验比对 |
-| SD 卡导入中断 | 低 | 卡被拔出 / 电池耗尽 / 进程崩溃 | 导入状态机保护(5.8);未完成文件清理 |
-| SD 上传中断 | 低 | 上传过程中断网/断电 | 临时文件名保护 NAS 数据完整;重启后自动续传 |
-| 暂存空间耗尽 | 中 | 离线时 SD 导入暂存累积 | 导入前空间预检(7.3.1)|
-| 中转服务带宽成本 | 中 | DERP 中继带宽随用户增长上升 | 大部分连接走 P2P 直连;按流量分级限速/计费;初期节点少按需扩容 |
-| 云备份存储成本 | 低 | 用户数据增长导致存储费用上升 | 接低价对象存储(B2/R2);按量计费传导成本;增量备份减少传输量 |
-| 酒店 Captive Portal | 中 | Headless 设备无法完成网页认证,旅途场景不可用 | 配网 AP + Portal 代理(4.10);fallback:USB tethering / 手机热点 / MAC 克隆 |
-
----
-
-## 十二、后续演进方向
-
-| 阶段 | 内容 | 重点 |
-|------|------|------|
-| **v1.0 — MVP** | 配置文件 + 部署脚本 + CLI 管理 | SMB 只读共享 + rclone 只读缓存 + 分层轮询 + SD 导入 + SD Uploader 单向上传 NAS |
-| **v1.5 — 硬件原型 + P1 功能** | SD 卡导入 + 双卡校验 + 配网模式 + Captive Portal 代理 + LED/按钮交互 + 缓存预热 + 带宽管理 + 连接容错 | 硬件原型开发,P1 功能完善 |
-| **v2.0 — 组网服务** | 内置 Headscale + 高速 DERP 节点 + WiFi AP 共享 | 开箱即连 + 现场团队协作 |
-| **v2.5 — 容灾 + 附加** | 云端异地备份 + Docker 镜像 + 多协议(NFS/WebDAV)+ NAS 侧 Agent 推送 | 数据安全闭环 + 降低部署门槛 |
-| **v3.0 — 硬件产品** | 定制硬件(SSD + 电池 + SD 槽 + WiFi),工业设计,开箱即用 | 产品化,面向非技术用户 |
-
----
-
-## 十三、付费服务
-
-### 13.1 Headscale + 高速 DERP 中转
-
-**问题**:Tailscale 官方 DERP 是共享资源,跨运营商/跨国时带宽受限。用户自建 DERP 需要有 VPS + 运维能力,门槛高。
-
-**方案**:
-
-```mermaid
-flowchart BT
- subgraph infra ["运营基础设施"]
- HS["Headscale
控制面板
(用户管理)"]
- DERP1["DERP 节点
国内 BGP
(低延迟)"]
- DERP2["DERP 节点
香港/日本
(跨境加速)"]
- end
-
- Box["盒子
开箱即连
(内置配置)"] --> HS
- Box --> DERP1
- NAS_C["NAS 端
自动连接
(安装脚本)"] --> DERP1
- NAS_C --> DERP2
- Travel["出差设备
自动连接
(通过盒子中继)"] --> DERP2
-```
-
-**用户体验**:
-
-1. 买盒子 → 开机 → 扫码绑定账号
-2. NAS 端运行一行安装脚本,加入用户的 Tailnet
-3. 完成。盒子带到任何地方,自动通过最优 DERP 节点连回家
-4. 无需了解 Headscale、DERP、WireGuard 等概念
-
-**迁移路径**(避免 vendor lock-in):
-- 盒子底层使用标准 WireGuard 协议,用户可随时切换到自建 Headscale 或官方 Tailscale
-- 提供配置导出工具:一键导出 WireGuard 配置、节点列表、DERP 自定义映射
-- 如用户不再使用我们的 Headscale 服务,盒子仍可正常工作(手动配置 Tailscale/WireGuard)
-
-**定价思路**:
-
-| 套餐 | 内容 | 参考价 |
-|------|------|--------|
-| 免费 | Headscale 控制面板 + 1 个基础 DERP 节点(限速) | ¥0 |
-| 基础版 | + 多节点智能选路 + 不限速 | ¥15-30/月 |
-| 专业版 | + 优先带宽 + 跨境加速节点 + SLA 保证 | ¥50-100/月 |
-
-**成本控制**:
-
-- DERP 中继只在无法打洞直连时使用,大部分 Tailscale 连接是 P2P 直连,中继流量占比通常不高
-- 可按实际中继流量动态限速/计费,避免被少数大流量用户拖垮
-- 初期节点少(1-2 个),按用户增长逐步扩容
-
-### 13.2 异地容灾备份
-
-**问题**:NAS 在家里是单点故障——硬盘坏、被盗、火灾、水灾都可能导致数据永久丢失。
-
-**方案**:
-
-```mermaid
-flowchart TD
- SSD["盒子 SSD 缓存
(热数据子集)"]
- SSD -->|"空闲时段
加密增量备份"| Cloud
-
- subgraph Cloud ["云存储服务"]
- UX["用户视角: 一键开通,按月付费"]
- Backend["后端: B2 / R2 / MinIO
(~$5/TB/月)"]
- Encrypt["数据加密: 用户本地生成密钥
运营方看不到明文"]
- KeyBackup["密钥备份: 首次设置强制引导
(密钥文件 / 助记词 / 密码管理器)"]
- Sync["增量同步: 只传变化部分"]
- Restore["恢复: 新盒子 → 输入密钥 → 自动拉取"]
- end
-```
-
-**与现有架构的关系**:
-
-- 复用 SD Uploader 的思路:本地文件 → 异步上传到云端
-- 不同点:备份目标是云端对象存储而非 NAS,且可以备份 NAS 全量数据(不限于缓存过的文件)
-- 可以做成两级:
- - **热备份**:盒子 SSD 上缓存过的文件自动备份(几乎零额外成本)
- - **全量备份**:通过以下任一路径从 NAS 全量增量备份到云端:
- - **方案 1(推荐)**:盒子在家局域网时自动执行(高速,零公网带宽消耗)
- - **方案 2**:盒子在外通过 Tailscale 远程执行(速度受限于公网带宽,但保证便携场景也能跑备份)
- - **方案 3(远期)**:在 NAS 侧部署独立的备份 agent(Docker 容器),NAS 直接备份到云端,不依赖盒子在线
- - 便携性说明:盒子的核心场景是带出去用。全量备份不要求盒子必须在家——方案 2 保证在外时也能慢速备份,方案 3 完全解耦盒子和全量备份
-
-**定价思路**:
-
-| 套餐 | 内容 | 参考价 |
-|------|------|--------|
-| 免费 | 热数据备份(仅缓存过的文件),上限 50GB | ¥0 |
-| 基础版 | 全量备份,500GB | ¥15/月 |
-| 专业版 | 全量备份,5TB | ¥50/月 |
-| 按量 | 超出部分 | ¥10/TB/月 |
-
----
-
-## 十四、明确不做的方向
-
-| 方向 | 原因 |
-|------|------|
-| 缩略图/预览生成、Web 相册 | 破坏「透明代理」核心定位,产品本质是协议透传不是数据加工 |
-| AI 选片 | 非核心,远期可选 |
-| 程序员场景(Git 缓存、Docker 镜像等) | 痛点不够强,已有成熟方案(Git 天然分布式、Codespaces 等) |
-| 公网文件分享链接 | 法律风险 + 需求不明确 |
-| 多设备 SaaS 管理面板 | 需求不明确,过早 |
-| Docker 开放运行环境 | 产品定位发散(注:这里指的是允许用户在盒子上运行任意 Docker 容器,而非 4.21 的"将本产品打包为 Docker 镜像部署") |
-
----
-
-## 十五、术语表
-
-| 术语 | 说明 |
-|------|------|
-| **Warpgate / 盒子** | 本产品——部署在用户身边的 SSD 缓存代理设备 |
-| **NAS** | 用户家中的网络存储设备(Network Attached Storage) |
-| **VFS** | rclone 的虚拟文件系统层(Virtual File System),将远程存储挂载为本地目录 |
-| **FUSE** | 用户空间文件系统(Filesystem in Userspace),Linux 内核机制,允许 rclone 在不修改内核的情况下提供文件系统挂载 |
-| **FUSE 挂载点** | rclone 通过 FUSE 暴露的只读本地目录(如 `/mnt/nas-photos`),Samba/NFS 直接服务于此目录 |
-| **SD Uploader** | 独立上传进程,将 SD 卡导入的暂存文件通过 SFTP 单向上传到 NAS |
-| **ingest_staging** | SD 卡导入暂存目录,文件在此完成校验后等待上传到 NAS |
-| **LRU** | 最近最少使用(Least Recently Used),缓存淘汰算法 |
-| **SFTP** | SSH 文件传输协议,本产品与 NAS 通信的主要协议 |
-| **Tailscale** | 基于 WireGuard 的组网工具,用于建立盒子与 NAS 之间的安全隧道 |
-| **Headscale** | Tailscale 控制面板的开源自建实现 |
-| **DERP** | Tailscale 的中继服务器(Designated Encrypted Relay for Packets),在无法直连时中转流量 |
-| **Ingest / 导入** | SD 卡文件导入到缓存并自动归档到 NAS 的过程 |
-| **WAL** | SQLite 的写前日志模式(Write-Ahead Logging),允许并发读写 |
-| **Captive Portal** | 强制门户认证,酒店/机场等 WiFi 连接后重定向到网页要求登录的机制 |
-| **AP+STA 并发** | WiFi 模块同时作为热点(AP)和连接外部网络(STA)的工作模式 |
diff --git a/warpgate-prd-v4.md b/warpgate-prd-v4.md
new file mode 100644
index 0000000..6d1c1b9
--- /dev/null
+++ b/warpgate-prd-v4.md
@@ -0,0 +1,836 @@
+# Warpgate — Make your NAS feel local — 产品方案与需求描述(v4)
+
+---
+
+## 一、产品定位
+
+**一句话描述**:摄影师的随身存储中枢——SSD 读写缓存加速访问,全程高速组网,云端容灾兜底。
+
+**核心价值**:
+
+1. **远程访问加速**:用户在外通过 Tailscale 等组网工具访问家中 NAS 时,受限于公网带宽,SMB 协议体验极差(卡顿、超时、缩略图加载慢)。本产品在客户端侧部署一层 SSD 读写缓存,对上层应用(Lightroom、Finder、Explorer 等)完全透明,读写都走本地 SSD,读取按需拉取并缓存,写入异步回写 NAS。
+2. **数据安全兜底**:可选的云端异地容灾,为用户的数据提供多层保护。
+
+**产品形态**:
+- **软件方案**(MVP):配置文件 + 一键部署脚本,部署在任意 Linux 主机上(Docker 镜像在 v2.5 提供)
+- **硬件一体机**(目标形态):定制盒子,内置 SSD + 电池 + WiFi,开箱即用(SD 卡槽、物理按钮、LED 状态指示等硬件在 SD 卡导入功能启用后加入)
+
+**市场机会**:
+- Gnarbox(曾最受欢迎的摄影师外拍备份设备)已停产,市场空缺明显
+- UnifyDrive UT2($599)硬件形态相似但软件体验差、电池仅 1 小时
+- ClouZen TAINER 功能单一,只能备份不能联网同步
+- **没有产品把「本地高速读写」和「远程 NAS 透明代理」打通成无缝体验**
+
+---
+
+## 二、目标用户
+
+| 用户画像 | 典型场景 | 痛点 |
+|----------|----------|------|
+| 摄影师 | 出差/外拍,酒店回看、粗修当天照片(Lightroom) | RAW 文件 25-60MB,SMB 远程逐张打开极慢,预览生成卡死 |
+| 视频创作者 | 远程剪辑,浏览素材库、拖拽代理文件 | 视频文件更大,顺序播放需持续带宽 |
+| 设计师 | 出差访问公司 NAS 上的 PSD/AI 源文件 | 大文件 + 多图层,打开一个文件几分钟 |
+| 远程办公族 | 日常办公文档、项目资料存 NAS | 小文件多,SMB 目录浏览延迟高,体验卡顿 |
+| NAS 重度用户 | 旅行途中访问个人数据 | 没有公网 IP 或带宽不足,现有方案都不理想 |
+
+**核心用户优先级**:摄影师(Lightroom + 远程访问)> 远程办公 > 视频创作者
+
+---
+
+## 三、系统架构
+
+### 3.1 整体架构
+
+```mermaid
+graph LR
+ subgraph clients ["客户端设备"]
+ LR["Lightroom
macOS"]
+ Linux["Linux
客户端"]
+ iPad["iPad
移动端"]
+ end
+
+ subgraph proxy ["Linux Proxy(局域网·高速)"]
+ Samba["Samba Server"]
+ NFS_S["NFS Server"]
+ WebDAV_S["WebDAV Server"]
+ VFS["rclone VFS mount
(读写缓存)"]
+ SSD["SSD 缓存"]
+ Samba --> VFS
+ NFS_S --> VFS
+ WebDAV_S --> VFS
+ VFS --> SSD
+ end
+
+ subgraph remote ["远程(公网 / Tailscale·低速)"]
+ TS["Tailscale /
WireGuard"]
+ NAS["任意品牌 NAS
(SFTP)"]
+ TS --- NAS
+ end
+
+ LR -- "SMB" --> Samba
+ Linux -- "NFS" --> NFS_S
+ iPad -- "WebDAV" --> WebDAV_S
+ VFS -- "SFTP (读写)" --> TS
+```
+
+### 3.2 协议选择说明
+
+| 段 | 协议 | 原因 |
+|----|------|------|
+| 客户端 → Proxy(主) | SMB | 对 Lightroom/macOS/Windows 原生兼容,应用无感 |
+| 客户端 → Proxy(辅) | NFS | Linux 客户端性能更好,内核级支持 |
+| 客户端 → Proxy(辅) | WebDAV | 移动端 App 支持广泛 |
+| Proxy → NAS | SFTP | 高延迟链路下比 SMB 稳定得多,任意品牌 NAS 均支持,无需额外套件。需要读写权限 |
+
+### 3.3 多协议对外服务(设计讨论)
+
+**问题**:客户端 → Warpgate 之间只支持 SMB 是否足够?
+
+**讨论**:不同客户端设备对协议的偏好不同。macOS + Lightroom 最适合 SMB,但 Linux 客户端用 NFS 性能更好(内核级支持,且 Linux 侧还能再叠一层 FS-Cache),iPad/移动端 App 则普遍支持 WebDAV。
+
+**设计决策**:所有对外协议服务共享同一个 rclone FUSE 挂载点。缓存层只有一份,不会因为多协议而重复缓存。rclone VFS 统一处理读写,本地写入异步回写 NAS。
+
+```mermaid
+graph LR
+ SMB["SMB Server"] --> Mount["/mnt/nas-photos
(rclone 读写 FUSE 挂载)"]
+ NFS["NFS Server"] --> Mount
+ WebDAV["WebDAV"] --> Mount
+ Mount --> SSD["SSD 缓存
(rclone 内部管理)"]
+```
+
+---
+
+## 四、核心功能
+
+### P0(MVP 必须)
+
+#### 4.1 透明多协议代理
+- 对外暴露标准 SMB 共享,客户端连接方式与直连 NAS 完全一致
+- 支持 SMB2/SMB3 协议
+- 同时支持 NFS 导出(Linux 客户端)和 WebDAV 服务(移动端)
+- 支持 macOS(Finder/Lightroom)、Windows(Explorer)、Linux、移动端客户端
+- 文件读取、写入、目录浏览、文件属性(时间戳/权限)均正常工作
+- 所有协议共享同一个 rclone 缓存层,不重复存储
+- **读写支持**:本地写入缓存到 SSD,rclone 异步回写到 NAS。Lightroom 等应用读取 RAW 文件时直接命中缓存;需要写入时(如保存 XMP sidecar),写入先落到 SSD 再由 rclone 异步回写
+
+#### 4.2 读缓存(Read-through Cache)
+- 文件首次被访问时,从远程 NAS 拉取并存入本地 SSD 缓存
+- 后续访问同一文件直接从本地 SSD 返回
+- 支持分块读取(chunked read):大文件不需要整个下载完才能开始读取
+- 支持预读(read-ahead):顺序读取场景下提前拉取后续数据
+- 目录列表缓存:目录结构缓存一段时间,避免频繁远程查询
+
+#### 4.3 缓存一致性
+- rclone VFS 原生管理脏文件回写,LRU 淘汰时自动跳过脏文件(未回写的文件受保护)
+- 单向写约束下(出门期间 NAS 端不动),无冲突
+- rclone 通过 `--dir-cache-time` 控制目录缓存过期,过期后自动从远程重新读取
+- 远程文件被修改 → dir-cache-time 过期后 rclone 自动拉取最新版本
+- 远程文件被删除 → dir-cache-time 过期后 rclone 缓存刷新,文件自动消失
+
+#### 4.5 远程变更检测
+- rclone 通过 `--dir-cache-time` 自动刷新目录缓存,过期后从远程重新读取
+- 不依赖任何 NAS 品牌特有 API,纯 SFTP 协议实现
+- 可选 NAS Agent 推送加速(P2)
+
+#### 4.6 缓存空间管理
+- 设置缓存总大小上限
+- 超出上限时按 LRU(最久未访问)策略自动淘汰
+- rclone 自动管理 LRU 淘汰,脏文件(未回写)受保护不被淘汰
+- 可设置缓存盘最低保留空间,防止磁盘写满
+- 可设置缓存最大保留时间
+- 注意:离线大量写入时缓存可能被脏文件占满,需确保 `--vfs-cache-max-size` 留够空间
+
+#### 4.7 一键部署
+- 提供完整的配置文件 + 部署脚本
+- 自动安装依赖(rclone、Samba、NFS、fuse)
+- 自动配置 systemd 服务,开机自启
+- 自动配置日志轮转
+- 缓存盘文件系统建议 btrfs/ZFS(CoW + journal 保护一致性)
+
+### P1(重要但非 MVP)
+
+#### 4.8 配网模式 + Captive Portal 代理(Setup AP)
+
+盒子是 Headless 设备(无屏幕),而绝大多数酒店/机场 WiFi 需要网页认证(Captive Portal)。没有这个功能,旅途场景直接不可用。
+
+**核心流程**:
+
+```
+① 盒子开机,检测到未配置 WiFi 或无法联网
+ → 自动进入「配网模式」,WiFi 模块启动临时 AP(SSID: Warpgate-Setup)
+
+② 用户手机连接 Warpgate-Setup 热点
+ → 自动弹出配网页面(或手动访问 http://192.168.4.1)
+
+③ 配网页面显示周围可用 WiFi 列表,用户选择酒店 WiFi
+
+④ 盒子连接酒店 WiFi(WiFi 模块切换为 AP+STA 并发模式)
+ → 检测到 Captive Portal 重定向
+
+⑤ 盒子将 Captive Portal 认证页面代理到配网页面
+ → 用户在手机上完成酒店 WiFi 的网页认证(输入房号/姓名等)
+
+⑥ 认证通过,盒子获得互联网访问,Tailscale 自动连接
+ → 配网模式关闭,临时 AP 关闭(或保持为管理入口)
+```
+
+**硬件要求**:WiFi 模块必须支持 **AP+STA 并发模式**(同时作为热点和连接外部 WiFi),这是配网模式的前提。大多数支持 AP 模式的 WiFi 芯片均支持此功能。
+
+**Fallback 方案**(不需要额外开发,文档中列出即可):
+- **USB 网络共享**:手机 USB 连接盒子,共享手机网络(tethering),绕过酒店 WiFi
+- **手机热点**:盒子直连手机 4G/5G 热点
+- **有线以太网**:部分酒店有网口,直插通常无需认证
+- **MAC 克隆**:`warpgate clone-mac ` 克隆已认证设备的 MAC 地址(高级用户)
+
+#### 4.9 缓存预热(Warm-up)
+- 命令行手动预热指定目录
+- 按时间范围预热(如"最近 7 天新增的文件")
+- 定时预热任务(如每天凌晨自动拉取最新数据)
+- 预热进度显示
+
+#### 4.10 管理工具(CLI)
+- `warpgate status` — 查看服务状态、缓存使用量、回写队列、当前带宽限速
+- `warpgate cache-list` — 列出缓存中的文件
+- `warpgate cache-clean` — 清理缓存
+- `warpgate warmup` — 手动预热
+- `warpgate bwlimit` — 动态调整带宽限制
+- `warpgate log` — 查看实时日志
+- `warpgate speed-test` — 链路速度测试
+- `warpgate setup-wifi` — 手动进入配网模式
+- `warpgate clone-mac ` — 克隆指定设备的 MAC 地址
+
+#### 4.11 带宽管理
+- 支持回写限速(写入回传 NAS)/下载限速(缓存拉取)分别控制
+- 运行时动态调整限速(不重启服务)
+- 回写带宽不影响读取体验
+- **自适应限速(Adaptive Throttle)**:基于吞吐量观测自动降速,避免回写占满链路
+ - 监控回写吞吐量的滑动窗口(如最近 30s 平均值),当吞吐持续下降超过阈值时判定链路拥塞
+ - 拥塞时自动降速,释放带宽给读取和其他流量
+ - 每隔一段时间小幅探测提速
+ - throttle 状态通过 `warpgate status` 实时可见
+ - 用户可通过 `BW_ADAPTIVE` 配置关闭自适应限速
+ - 自适应限速**仅控制回写上传**,不影响缓存读取拉取
+
+#### 4.12 连接容错
+- Tailscale 断连时自动重试
+- 已缓存的文件在离线时仍可正常读取
+- rclone 回写队列在恢复连接后自动续传
+- 连接超时参数可配置
+
+### P2(后续迭代)
+
+#### 4.13 WiFi AP 现场共享
+
+盒子内置 WiFi 模块开启持久热点,现场团队设备连上即可通过 SMB/WebDAV 访问缓存目录。与 4.8 配网模式的区别:配网 AP 是临时的(完成配网后关闭),本功能是持久的团队共享热点。
+
+- 支持 AP 模式,复用已有的 SMB/WebDAV 多协议服务
+- AP 网络与 Tailscale/WAN 网络隔离(安全考虑)
+- AP 模式下仍可同时通过有线/4G 连接 Tailscale 做后台回写
+- **硬件要求**:需要两个独立网络接口——WiFi 模块用于 AP 热点,有线/USB 4G 网卡用于 WAN/Tailscale 连接。一体机硬件设计需预留双网卡
+- 典型场景:现场团队设备连上 WiFi 即可浏览共享文件
+
+#### 4.14 Web 管理界面
+- 缓存状态仪表盘(大小、命中率、回写队列、带宽趋势图)
+- 缓存文件浏览器(查看/手动清除/手动预热)
+- 配置修改界面(参数调整无需编辑配置文件)
+- 实时日志查看器
+
+#### 4.15 NAS 侧 Agent 推送(可选增强)
+- 在 NAS 上运行轻量 Agent(Docker 容器),监听文件变化主动推送给 Proxy
+- 实现秒级远程变更感知(替代分钟级 dir-cache-time)
+- 不依赖品牌 API,基于 inotify 通用方案
+
+#### 4.16 多 NAS / 多目录支持
+- 同时连接多个远程 NAS(如家里 + 工作室)
+- 每个 NAS 独立共享名,独立缓存策略
+- 每个共享可配置不同的缓存大小和保留时间
+
+#### 4.17 智能缓存策略
+- 根据文件类型自动调整策略:
+ - `.lrcat` / `.xmp`(Lightroom catalog/sidecar)→ 高缓存优先级
+ - `.CR3` / `.ARW` / `.NEF`(RAW 照片)→ 大块预读,长缓存保留
+ - `.mp4` / `.mov`(视频)→ 顺序预读优化
+ - `.psd` / `.ai`(设计文件)→ 完整缓存,避免分块导致的兼容问题
+- 基于访问频率自动调整缓存优先级(热数据不被淘汰)
+
+#### 4.18 Docker 镜像
+- 一行命令启动:`docker run -v /mnt/ssd:/cache warpgate`
+- docker-compose 配置
+- 支持环境变量或挂载配置文件
+
+#### 4.19 通知机制
+- 缓存空间不足告警(Webhook / Telegram / 邮件)
+- NAS 离线告警
+- 回写失败告警
+
+---
+
+## 五、数据一致性模型
+
+### 5.1 设计目标
+
+采用**读写透明代理**模型。rclone VFS 统一管理缓存和回写。具体承诺:
+
+1. 本地写入持久化在 SSD → rclone 异步回写 NAS → 回写完成数据安全
+2. 远程 NAS 上的变更会在 `--dir-cache-time` 过期后被自动感知
+3. 已缓存的文件在离线时仍可正常访问
+4. **关键约束**:单向写(出门期间 NAS 端不动),此约束下无冲突
+
+### 5.2 设计讨论与决策过程
+
+#### 问题 A:为什么 rclone VFS 就够了?
+
+**讨论**:早期设计曾考虑 OverlayFS + sync daemon 方案,也曾退化到只读缓存 + SD Uploader 方案。最终回到 rclone VFS 读写缓存:
+
+- rclone VFS `--vfs-cache-mode full` 本身就是完整的读写缓存代理——读按需拉取,写本地暂存后异步回写
+- 在单向写约束下(出门期间 NAS 端不动),不存在写冲突
+- 不需要额外的 sync daemon、OverlayFS、SD Uploader 或 metadata DB
+- 架构大幅简化:一个 rclone 进程 + Samba/NFS 对外服务即可
+
+**核心原则**:"回写队列清空 = 数据安全"
+
+**约束条件**:
+- 单向写:出门期间 NAS 端不动
+- 正常 unmount:确保关机前回写完成
+- 缓存空间留够:为脏文件预留足够 SSD 空间
+
+> **MVP 部署建议**:**建议**缓存盘使用 btrfs 或 ZFS 文件系统(CoW + checksum 保护断电一致性)。部署脚本应检测并警告非 CoW 文件系统。
+
+#### 问题 B:回写中断怎么办?
+
+**讨论**:本地写入后、回写完成前,可能发生断电或断网。
+
+**决策**:
+- 断网:rclone 暂停回写,网络恢复后自动继续。脏文件安全保存在 SSD 上。
+- 异常断电:rclone 重启后扫描缓存目录,继续回写未完成的脏文件。但如果断电发生在 SSD 写入过程中(文件只写了一半),可能导致数据丢失。建议使用 UPS/电池保护 + btrfs/ZFS 文件系统。
+
+### 5.3 一致性模型(读写透明代理)
+
+```
+读取路径:应用 → Samba/NFS → rclone FUSE → SSD 缓存 ← SFTP 从 NAS 按需拉取
+写入路径:应用 → Samba/NFS → rclone FUSE → SSD 缓存(dirty) → rclone 异步回写 NAS
+```
+
+**读写缓存**:
+- rclone 以 `--vfs-cache-mode full` 挂载,Samba/NFS 共享支持读写
+- rclone 内部管理 dirty/clean 状态,外部无需干预
+- LRU 淘汰时自动跳过脏文件
+- `--vfs-write-back` 控制回写延迟(默认 5s)
+- 回写完成后文件状态从 dirty 变为 clean,可正常被 LRU 淘汰
+
+**缓存淘汰**:rclone 由 `--vfs-cache-max-size` 和 `--vfs-cache-max-age` 控制 LRU 淘汰,脏文件受保护不被淘汰。
+
+### 5.4 读取时的缓存验证
+
+缓存验证逻辑:
+
+| 远程状态 | 缓存行为 |
+|---|---|
+| 远程没变 | ✅ 直接用缓存 |
+| 远程被修改 | 🔄 dir-cache-time 过期后 rclone 自动感知 → 下次访问时拉新版本 |
+| 远程被删除 | 🗑️ dir-cache-time 过期后 rclone 刷新 → 文件消失 |
+
+**注意**:读取热路径上**不查远程**(不产生网络请求),直接返回缓存。远程变更由 rclone `--dir-cache-time` 过期机制自动发现。
+
+### 5.5 关键场景走查
+
+#### 场景 1:Cache 关机几天,NAS 上文件被修改
+
+```mermaid
+flowchart TD
+ D1["Day 1: Cache 缓存 photo.cr3 (clean)"] --> Off["Day 1: Cache 关机"]
+ Off --> D3["Day 3: NAS 上 photo.cr3 被修改"]
+ D3 --> D5["Day 5: Cache 开机
rclone 启动"]
+ D5 --> Expire["dir-cache-time 过期
rclone 自动刷新目录缓存"]
+ Expire --> Access["用户访问时 rclone 自动拉新版本"]
+ Access --> Result["✅ 用户读到 NAS 最新版本"]
+```
+
+#### 场景 2:NAS 删了文件
+
+```mermaid
+flowchart TD
+ D1["Day 1: Cache 缓存 photo.cr3 (clean)"]
+ D1 --> D3["Day 3: NAS 上删除 photo.cr3"]
+ D3 --> Expire["dir-cache-time 过期
rclone 刷新目录缓存"]
+ Expire --> Gone["rclone 发现远程文件不存在"]
+ Gone --> Result["✅ 本地缓存与远程保持一致"]
+```
+
+#### 场景 3:本地写入文件
+
+```mermaid
+flowchart TD
+ Write["用户通过 Samba 写入文件"] --> Cache["rclone 写入 SSD 缓存
标记为 dirty"]
+ Cache --> Delay["--vfs-write-back 延迟
(默认 5s)"]
+ Delay --> Upload["rclone 通过 SFTP
异步回写到 NAS"]
+ Upload --> Clean["回写完成
dirty → clean"]
+ Clean --> Result["✅ 文件安全到达 NAS"]
+```
+
+#### 场景 4:离线写入
+
+```mermaid
+flowchart TD
+ Offline["远程不可达"] --> Write["用户写入文件到 Samba"]
+ Write --> Cache["rclone 写入 SSD 缓存
标记为 dirty"]
+ Cache --> Queue["rclone 发现网络不可达
回写暂停"]
+ Queue --> Recover["网络恢复"]
+ Recover --> Upload["rclone 自动续传
回写脏文件到 NAS"]
+ Upload --> Result["✅ 无需人工干预"]
+```
+
+---
+
+## 六、远程变更检测机制
+
+### 6.1 设计约束
+
+**不依赖任何 NAS 品牌特有 API**。产品需要支持群晖、QNAP、威联通、TrueNAS 等任意品牌 NAS,因此只能基于标准 SFTP 协议实现。
+
+### 6.2 SFTP 协议的能力边界(设计讨论)
+
+**问题**:远程 NAS 上数据更新了,Cache 怎么知道?能否实时感知?
+
+**讨论**:SFTP 协议本身**没有任何通知/推送/订阅机制**。它是无状态的文件传输协议,不支持 inotify、webhook、filesystem watch 等概念。每次想知道远程状态,必须主动发请求查询。
+
+**考虑过的方案**:
+
+| 方案 | 原理 | 实时性 | 品牌依赖 | 取舍 |
+|------|------|--------|----------|------|
+| SFTP 全量轮询 | 递归 ls 对比 mtime | 分钟级 | 无 | 文件多时开销大 |
+| SFTP 分层轮询 | 先查目录 mtime,变了再查文件 | 分钟级 | 无 | 高效,但增加系统复杂性 |
+| rclone dir-cache-time | rclone 内置目录缓存过期机制 | 可配 | 无 | 零额外代码,推荐 |
+| 群晖 FileStation API | 调 DSM Web API | 秒级 | 仅群晖 | ❌ 不通用 |
+| NAS 侧 Agent 推送 | inotifywait → HTTP 通知 | 秒级 | 无 | 需要 NAS 装软件 |
+
+**最终决策**:
+
+- **P0(MVP)**:rclone 通过 `--dir-cache-time` 控制目录缓存过期时间,过期后自动从远程重新读取。对大多数单用户场景足够。
+- **P2(后续)**:可选的 NAS 侧 Agent 推送,作为增强项给需要秒级同步的用户
+
+### 6.3 后续增强:NAS 侧 Agent 推送(P2)
+
+对于需要秒级同步的用户,可选在 NAS 上部署轻量 Agent(Docker 容器),通过 inotify 监听变化并推送:
+
+```mermaid
+flowchart LR
+ subgraph NAS ["群晖 NAS"]
+ Agent["inotifywait 监听文件变化
(Docker 容器)"]
+ end
+
+ subgraph Proxy ["Linux Proxy"]
+ HTTP["HTTP 接收端
触发 vfs/forget 刷新"]
+ end
+
+ Agent -- "轻量 HTTP POST
(via Tailscale)" --> HTTP
+```
+
+此方案作为 dir-cache-time 的增强,不是替代。即使 Agent 不可用,dir-cache-time 机制仍然工作。
+
+---
+
+## 七、缓存行为详细描述
+
+### 7.1 读取流程
+
+```mermaid
+flowchart TD
+ App["应用请求读取文件"] --> Samba["① Samba/NFS"]
+ Samba --> FUSE["② rclone FUSE 挂载"]
+ FUSE -->|"缓存命中"| Return["直接返回缓存
(SSD 速度)"]
+ FUSE -->|"缓存未命中"| Remote["③ rclone 从远程 NAS 拉取"]
+ FUSE -->|"缓存已过期"| Refresh["④ rclone 检查远程 mtime
变了则重新拉取"]
+
+ Remote --> Chunk["⑤ 按 chunk 分块下载"]
+ Chunk --> Cache["⑥ 缓存到 SSD"]
+ Cache --> ReturnData["⑦ 返回数据给应用"]
+ Refresh --> ReturnData
+```
+
+**设计要点**:
+
+- 读取热路径上**不查远程**(不产生网络请求),直接返回缓存,保证响应速度
+- 目录缓存通过 rclone `--dir-cache-time` 控制过期刷新
+
+### 7.2 写入流程
+
+```mermaid
+flowchart TD
+ App["应用写入文件"] --> Samba["① Samba"]
+ Samba --> FUSE["② rclone FUSE 挂载"]
+ FUSE --> SSD["③ 写入 SSD 缓存
标记为 dirty"]
+ SSD --> Delay["④ --vfs-write-back 延迟"]
+ Delay --> Upload["⑤ rclone 通过 SFTP
回写到 NAS"]
+
+ Upload -->|"回写成功"| Clean["⑥ dirty → clean"]
+ Upload -->|"网络不可达"| Queue["⑥ 暂停回写
网络恢复后自动续传"]
+ Queue --> Upload
+```
+
+### 7.3 缓存淘汰策略
+
+rclone 管理 LRU 淘汰,脏文件受保护不被淘汰:
+
+- `--vfs-cache-max-size`:缓存总大小上限,超出时按 LRU 淘汰
+- `--vfs-cache-max-age`:缓存最大保留时间
+- rclone 自行管理淘汰,无需外部干预
+- 脏文件(未回写到 NAS)不会被淘汰,确保写入数据安全
+
+**脏文件空间说明**:离线大量写入时,脏文件会持续累积在 SSD 上。当脏文件 + clean 缓存接近 `--vfs-cache-max-size` 上限时,rclone 只能淘汰 clean 文件。如果 clean 文件已全部淘汰而脏文件仍在增长,新写入可能失败。应确保 `--vfs-cache-max-size` 为离线写入预留足够空间。
+
+### 7.4 离线行为
+
+| 场景 | 行为 |
+|------|------|
+| 远程不可达,读取已缓存文件 | 正常返回,无影响 |
+| 远程不可达,读取未缓存文件 | 超时报错(可配置超时时间) |
+| 远程不可达,本地写入 | 正常写入到缓存,标记为 dirty,恢复连接后自动回写 |
+| 远程不可达,后台目录缓存刷新 | 静默跳过,下次重试 |
+| 恢复连接后 | rclone 回写队列自动续传 + 目录缓存按 dir-cache-time 正常过期刷新 |
+
+---
+
+## 八、配置参数清单
+
+### 连接配置
+
+| 参数 | 说明 | 默认值 | 建议值 |
+|------|------|--------|--------|
+| `NAS_HOST` | 远程 NAS 的 Tailscale IP | - | `100.x.x.x` |
+| `NAS_USER` | SFTP 用户名 | - | - |
+| `NAS_PASS` / `NAS_KEY_FILE` | 认证信息 | - | 建议密钥 |
+| `NAS_REMOTE_PATH` | NAS 上的目标路径 | - | `/volume1/photos` |
+| `SFTP_PORT` | SFTP 端口 | `22` | `22` |
+| `SFTP_CONNECTIONS` | SFTP 连接复用数 | `8` | `4-16` |
+
+### 缓存配置
+
+| 参数 | 说明 | 默认值 | 建议值 |
+|------|------|--------|--------|
+| `CACHE_DIR` | 缓存存储路径 | - | SSD 路径,建议 btrfs/ZFS |
+| `CACHE_MAX_SIZE` | 缓存大小上限 | `200G` | SSD 容量的 70-80% |
+| `CACHE_MAX_AGE` | 缓存最大保留时间 | `720h`(30天) | 按使用习惯 |
+| `CACHE_MIN_FREE` | 缓存盘最低可用空间 | `10G` | `10-20G` |
+
+### 读取优化
+
+| 参数 | 说明 | 默认值 | 场景建议 |
+|------|------|--------|----------|
+| `READ_CHUNK_SIZE` | 分块读取大小 | `256M` | RAW 照片: `256M`,视频: `512M`,文档: `64M` |
+| `READ_CHUNK_LIMIT` | chunk 自动增长上限 | `1G` | - |
+| `READ_AHEAD` | 预读缓冲区 | `512M` | 视频场景可加到 `1G` |
+| `BUFFER_SIZE` | 内存缓冲区 | `256M` | - |
+
+### 带宽配置
+
+| 参数 | 说明 | 默认值 | 场景建议 |
+|------|------|--------|----------|
+| `BW_LIMIT_UP` | 回写限速上限 | `0`(不限) | 酒店 WiFi 建议 `10-20M` |
+| `BW_LIMIT_DOWN` | 缓存拉取下载限速 | `0`(不限) | 一般不限 |
+| `BW_ADAPTIVE` | 自适应回写限速开关 | `yes` | `yes`=根据吞吐量自动降速,`no`=纯手动 |
+
+### 回写配置
+
+| 参数 | 说明 | 默认值 | 建议值 |
+|------|------|--------|--------|
+| `VFS_WRITE_BACK` | 回写延迟(rclone `--vfs-write-back`) | `5s` | 低延迟链路可设更短 |
+| `TRANSFERS` | rclone 并发传输数(`--transfers`) | `4` | 带宽小就设 `2` |
+
+### 目录缓存
+
+| 参数 | 说明 | 默认值 | 场景建议 |
+|------|------|--------|----------|
+| `DIR_CACHE_TIME` | 目录列表缓存时间(rclone `--dir-cache-time`) | `1h` | 个人: `1-2h`,协作: `5-15m` |
+
+### 多协议配置
+
+| 参数 | 说明 | 默认值 | 建议值 |
+|------|------|--------|--------|
+| `ENABLE_SMB` | 启用 SMB 共享 | `yes` | `yes` |
+| `ENABLE_NFS` | 启用 NFS 导出 | `no` | 有 Linux 客户端时开启 |
+| `ENABLE_WEBDAV` | 启用 WebDAV 服务 | `no` | 有移动端需求时开启 |
+| `NFS_ALLOWED_NETWORK` | NFS 允许访问的网段 | `192.168.0.0/24` | 按实际局域网设置 |
+| `WEBDAV_PORT` | WebDAV 监听端口 | `8080` | - |
+
+### 配网模式配置
+
+| 参数 | 说明 | 默认值 | 建议值 |
+|------|------|--------|--------|
+| `SETUP_AP_SSID` | 配网热点名称 | `Warpgate-Setup` | - |
+| `SETUP_AP_PASSWORD` | 配网热点密码(空=开放) | 空 | 首次配网建议开放,降低门槛 |
+| `SETUP_AP_AUTO` | 无网络时自动进入配网模式 | `yes` | `yes` |
+| `SETUP_AP_TIMEOUT` | 配网完成后临时 AP 保持时间 | `5m` | 认证成功后自动关闭 |
+| `SETUP_PORTAL_LISTEN` | 配网 Web 服务监听地址 | `192.168.4.1:80` | - |
+
+### WiFi AP 配置
+
+| 参数 | 说明 | 默认值 | 建议值 |
+|------|------|--------|--------|
+| `AP_ENABLED` | 启用 WiFi 热点 | `no` | 现场共享时开启 |
+| `AP_SSID` | 热点名称 | `Warpgate` | - |
+| `AP_PASSWORD` | 热点密码 | 随机生成 | 首次配置时设定 |
+| `AP_ISOLATION` | AP 网络与 WAN 隔离 | `yes` | `yes` |
+| `AP_MAX_CLIENTS` | 最大连接数 | `8` | - |
+
+---
+
+## 九、场景预设(模板)
+
+为降低用户配置门槛,提供开箱即用的预设模板。
+
+### 摄影师模式
+
+```
+重点优化:大文件读取性能、RAW 浏览流畅
+- CACHE_MAX_SIZE=500G
+- READ_CHUNK_SIZE=256M
+- READ_AHEAD=512M
+- DIR_CACHE_TIME=2h ← 目录结构不常变
+- VFS_WRITE_BACK=5s
+- TRANSFERS=4
+- ENABLE_SMB=yes
+- ENABLE_NFS=no
+- ENABLE_WEBDAV=no
+```
+
+### 视频剪辑模式
+
+```
+重点优化:顺序读取性能、大文件预读
+- CACHE_MAX_SIZE=1T
+- READ_CHUNK_SIZE=512M
+- READ_AHEAD=1G ← 大预读保证播放流畅
+- DIR_CACHE_TIME=1h
+- VFS_WRITE_BACK=5s
+- TRANSFERS=2 ← 减少并发,保带宽给播放
+- ENABLE_SMB=yes
+- ENABLE_NFS=no
+- ENABLE_WEBDAV=no
+```
+
+### 文档办公模式
+
+```
+重点优化:小文件快速响应、频繁感知远程变更
+- CACHE_MAX_SIZE=50G
+- READ_CHUNK_SIZE=64M
+- READ_AHEAD=128M
+- DIR_CACHE_TIME=30m ← 协作场景需要较快看到新文件
+- VFS_WRITE_BACK=5s
+- TRANSFERS=4
+- ENABLE_SMB=yes
+- ENABLE_NFS=no
+- ENABLE_WEBDAV=yes ← 移动端也能访问
+```
+
+---
+
+## 十、部署要求
+
+### 硬件要求(通用 Linux 主机部署)
+
+| 组件 | 最低配置 | 推荐配置 |
+|------|----------|----------|
+| CPU | ARMv8 / x86_64 任意 | N100 或同级 |
+| 内存 | 1 GB | 2-4 GB |
+| 缓存盘 | 任意 SSD | NVMe SSD |
+| 缓存容量 | 32 GB | 常用数据量的 30%+ |
+| 网口 | 100M | 千兆(2.5G 更好) |
+| 断电保护 | - | 内置电池或外接 UPS |
+
+### 硬件要求(一体机目标形态)
+
+通用要求之外,一体机额外需要:
+
+| 组件 | 说明 |
+|------|------|
+| USB-A/C 口 | 至少 2 个,用于外接存储设备 |
+| WiFi 模块 | 支持 **AP+STA 并发模式**(配网必须),建议 WiFi 6 |
+| 内置电池 | 支持断电保护 + 便携使用 |
+| SD 卡槽(SD 功能启用后需要) | SD / microSD,覆盖大多数相机 |
+| CFexpress 槽(SD 功能启用后可选) | CFexpress Type-B,高端相机用户 |
+| 物理按钮(SD 功能启用后需要) | 触发 SD 卡导入 / 确认操作 |
+| LED 状态指示(SD 功能启用后需要) | 导入进度 / 完成 / 错误 / 上传状态 |
+
+**缓存盘文件系统建议**:btrfs 或 ZFS。利用 CoW(Copy-on-Write)和 journal 机制,即使意外断电也能保证文件系统级别的一致性。
+
+```bash
+# btrfs 格式化示例
+mkfs.btrfs /dev/ssd_partition
+mount -o compress=zstd /dev/ssd_partition /mnt/ssd/warpgate
+```
+
+### 软件要求
+
+| 组件 | 版本 |
+|------|------|
+| OS | Ubuntu 22.04+ / Debian 12+ / 任意 Linux |
+| rclone | 1.65+(关键参数:`--vfs-cache-mode full --vfs-write-back {VFS_WRITE_BACK} --vfs-cache-max-size {CACHE_MAX_SIZE} --cache-dir {CACHE_DIR}/rclone-cache --rc`)。`--rc` 启用 RC API,供外部调用 `vfs/forget` 刷新目录缓存 |
+| Samba | 4.x |
+| NFS server | nfs-kernel-server(如启用 NFS) |
+| FUSE | 3.x |
+| Tailscale / ZeroTier | 已配置并可连通 NAS |
+
+### NAS 侧要求
+
+| 项目 | 要求 |
+|------|------|
+| SFTP 服务 | 开启(群晖:控制面板 → 文件服务 → FTP → 勾选 SFTP) |
+| 用户权限 | SFTP 用户对目标目录有读写权限 |
+| Tailscale | 已安装并登录同一网络 |
+| 品牌 | **无限制**,任何支持 SFTP 的 NAS 均可(群晖/QNAP/威联通/TrueNAS/DIY 等) |
+
+---
+
+## 十一、风险与局限
+
+| 风险 | 等级 | 说明 | 缓解措施 |
+|------|------|------|----------|
+| 首次访问慢 | 固有 | 未缓存文件必须走远程 | 预热功能;分块下载优化 |
+| 缓存一致性延迟 | 低 | 远程变更在 dir-cache-time 内不可见 | 合理设置 dir-cache-time;后续可选 Agent 推送 |
+| Tailscale 断连 | 中 | 远程不可达时新文件无法获取 | 已缓存文件仍可用;回写队列自动排队;恢复后自动续传 |
+| 回写中断 | 中 | 异常断电时未回写的脏文件可能丢失 | UPS/电池保护 + btrfs/ZFS 文件系统保护一致性 |
+| 脏文件占满缓存 | 中 | 离线大量写入时缓存空间不足 | 确保 `--vfs-cache-max-size` 留够空间;监控脏文件占比 |
+| NAS 端并发修改 | 低 | 单向写约束外有人修改 NAS 导致冲突 | 文档明确说明单向写约束;出门前确认 NAS 端无其他修改 |
+| 中转服务带宽成本 | 中 | DERP 中继带宽随用户增长上升 | 大部分连接走 P2P 直连;按流量分级限速/计费;初期节点少按需扩容 |
+| 云备份存储成本 | 低 | 用户数据增长导致存储费用上升 | 接低价对象存储(B2/R2);按量计费传导成本;增量备份减少传输量 |
+| 酒店 Captive Portal | 中 | Headless 设备无法完成网页认证,旅途场景不可用 | 配网 AP + Portal 代理(4.8);fallback:USB tethering / 手机热点 / MAC 克隆 |
+
+---
+
+## 十二、后续演进方向
+
+| 阶段 | 内容 | 重点 |
+|------|------|------|
+| **v1.0 — MVP** | 配置文件 + 部署脚本 + CLI 管理 | SMB 读写共享 + rclone VFS 读写缓存 + 配网模式 + CLI + 缓存预热 + 带宽管理 |
+| **v1.5 — 硬件原型 + P1 功能** | SD 卡导入 + 双卡校验 + LED/按钮交互 + 硬件原型 | SD 功能开发,硬件原型 |
+| **v2.0 — 组网服务** | 内置 Headscale + 高速 DERP 节点 + WiFi AP 共享 | 开箱即连 + 现场团队协作 |
+| **v2.5 — 容灾 + 附加** | 云端异地备份 + Docker 镜像 + 多协议(NFS/WebDAV)+ NAS 侧 Agent 推送 | 数据安全闭环 + 降低部署门槛 |
+| **v3.0 — 硬件产品** | 定制硬件(SSD + 电池 + SD 槽 + WiFi),工业设计,开箱即用 | 产品化,面向非技术用户 |
+
+---
+
+## 十三、付费服务
+
+### 13.1 Headscale + 高速 DERP 中转
+
+**问题**:Tailscale 官方 DERP 是共享资源,跨运营商/跨国时带宽受限。用户自建 DERP 需要有 VPS + 运维能力,门槛高。
+
+**方案**:
+
+```mermaid
+flowchart BT
+ subgraph infra ["运营基础设施"]
+ HS["Headscale
控制面板
(用户管理)"]
+ DERP1["DERP 节点
国内 BGP
(低延迟)"]
+ DERP2["DERP 节点
香港/日本
(跨境加速)"]
+ end
+
+ Box["盒子
开箱即连
(内置配置)"] --> HS
+ Box --> DERP1
+ NAS_C["NAS 端
自动连接
(安装脚本)"] --> DERP1
+ NAS_C --> DERP2
+ Travel["出差设备
自动连接
(通过盒子中继)"] --> DERP2
+```
+
+**用户体验**:
+
+1. 买盒子 → 开机 → 扫码绑定账号
+2. NAS 端运行一行安装脚本,加入用户的 Tailnet
+3. 完成。盒子带到任何地方,自动通过最优 DERP 节点连回家
+4. 无需了解 Headscale、DERP、WireGuard 等概念
+
+**迁移路径**(避免 vendor lock-in):
+- 盒子底层使用标准 WireGuard 协议,用户可随时切换到自建 Headscale 或官方 Tailscale
+- 提供配置导出工具:一键导出 WireGuard 配置、节点列表、DERP 自定义映射
+- 如用户不再使用我们的 Headscale 服务,盒子仍可正常工作(手动配置 Tailscale/WireGuard)
+
+**定价思路**:
+
+| 套餐 | 内容 | 参考价 |
+|------|------|--------|
+| 免费 | Headscale 控制面板 + 1 个基础 DERP 节点(限速) | ¥0 |
+| 基础版 | + 多节点智能选路 + 不限速 | ¥15-30/月 |
+| 专业版 | + 优先带宽 + 跨境加速节点 + SLA 保证 | ¥50-100/月 |
+
+**成本控制**:
+
+- DERP 中继只在无法打洞直连时使用,大部分 Tailscale 连接是 P2P 直连,中继流量占比通常不高
+- 可按实际中继流量动态限速/计费,避免被少数大流量用户拖垮
+- 初期节点少(1-2 个),按用户增长逐步扩容
+
+### 13.2 异地容灾备份
+
+**问题**:NAS 在家里是单点故障——硬盘坏、被盗、火灾、水灾都可能导致数据永久丢失。
+
+**方案**:
+
+```mermaid
+flowchart TD
+ SSD["盒子 SSD 缓存
(热数据子集)"]
+ SSD -->|"空闲时段
加密增量备份"| Cloud
+
+ subgraph Cloud ["云存储服务"]
+ UX["用户视角: 一键开通,按月付费"]
+ Backend["后端: B2 / R2 / MinIO
(~$5/TB/月)"]
+ Encrypt["数据加密: 用户本地生成密钥
运营方看不到明文"]
+ KeyBackup["密钥备份: 首次设置强制引导
(密钥文件 / 助记词 / 密码管理器)"]
+ Sync["增量同步: 只传变化部分"]
+ Restore["恢复: 新盒子 → 输入密钥 → 自动拉取"]
+ end
+```
+
+**与现有架构的关系**:
+
+- 从缓存/NAS 异步上传到云端
+- 可以做成两级:
+ - **热备份**:盒子 SSD 上缓存过的文件自动备份(几乎零额外成本)
+ - **全量备份**:通过以下任一路径从 NAS 全量增量备份到云端:
+ - **方案 1(推荐)**:盒子在家局域网时自动执行(高速,零公网带宽消耗)
+ - **方案 2**:盒子在外通过 Tailscale 远程执行(速度受限于公网带宽,但保证便携场景也能跑备份)
+ - **方案 3(远期)**:在 NAS 侧部署独立的备份 agent(Docker 容器),NAS 直接备份到云端,不依赖盒子在线
+ - 便携性说明:盒子的核心场景是带出去用。全量备份不要求盒子必须在家——方案 2 保证在外时也能慢速备份,方案 3 完全解耦盒子和全量备份
+
+**定价思路**:
+
+| 套餐 | 内容 | 参考价 |
+|------|------|--------|
+| 免费 | 热数据备份(仅缓存过的文件),上限 50GB | ¥0 |
+| 基础版 | 全量备份,500GB | ¥15/月 |
+| 专业版 | 全量备份,5TB | ¥50/月 |
+| 按量 | 超出部分 | ¥10/TB/月 |
+
+---
+
+## 十四、明确不做的方向
+
+| 方向 | 原因 |
+|------|------|
+| 缩略图/预览生成、Web 相册 | 破坏「透明代理」核心定位,产品本质是协议透传不是数据加工 |
+| AI 选片 | 非核心,远期可选 |
+| 程序员场景(Git 缓存、Docker 镜像等) | 痛点不够强,已有成熟方案(Git 天然分布式、Codespaces 等) |
+| 公网文件分享链接 | 法律风险 + 需求不明确 |
+| 多设备 SaaS 管理面板 | 需求不明确,过早 |
+| Docker 开放运行环境 | 产品定位发散(注:这里指的是允许用户在盒子上运行任意 Docker 容器,而非 4.18 的"将本产品打包为 Docker 镜像部署") |
+
+---
+
+## 十五、术语表
+
+| 术语 | 说明 |
+|------|------|
+| **Warpgate / 盒子** | 本产品——部署在用户身边的 SSD 缓存代理设备 |
+| **NAS** | 用户家中的网络存储设备(Network Attached Storage) |
+| **VFS** | rclone 的虚拟文件系统层(Virtual File System),将远程存储挂载为本地目录 |
+| **FUSE** | 用户空间文件系统(Filesystem in Userspace),Linux 内核机制,允许 rclone 在不修改内核的情况下提供文件系统挂载 |
+| **FUSE 挂载点** | rclone 通过 FUSE 暴露的本地目录(如 `/mnt/nas-photos`),Samba/NFS 直接服务于此目录 |
+| **dirty file / 脏文件** | 已写入本地 SSD 缓存但尚未回写到远程 NAS 的文件 |
+| **write-back / 回写** | rclone 将本地修改异步上传到远程 NAS 的过程 |
+| **LRU** | 最近最少使用(Least Recently Used),缓存淘汰算法 |
+| **SFTP** | SSH 文件传输协议,本产品与 NAS 通信的主要协议 |
+| **Tailscale** | 基于 WireGuard 的组网工具,用于建立盒子与 NAS 之间的安全隧道 |
+| **Headscale** | Tailscale 控制面板的开源自建实现 |
+| **DERP** | Tailscale 的中继服务器(Designated Encrypted Relay for Packets),在无法直连时中转流量 |
+| **Captive Portal** | 强制门户认证,酒店/机场等 WiFi 连接后重定向到网页要求登录的机制 |
+| **AP+STA 并发** | WiFi 模块同时作为热点(AP)和连接外部网络(STA)的工作模式 |