# Backup

> 多端点备份：把本机不进 git 的私有文件（个人交付物、隐私文档、本机配置等）同时备份到多个云端端点（内置飞书云盘端点，可继续注册 WebDAV / S3 / 各家网盘等），用多端冗余把「重要信息被彻底删除」的风险降到最低。MUST USE 当用户提到「备份、备份一下、飞书备份、备份到飞书、只备份到飞书、云盘备份、多端备份、备份到某端点、把 backup 目录备份、这个文件要备份、注册/添加备份端点、查看备份、恢复备份、备份格式白名单」等任何备份需求时。机制：项目根 backup/ 目录即备份范围（目录制，放进 = 要备份；只收用户明确点名要备份的文件，复制副本进目录、原文件留原位置）；backup/ 下与端点同名的子目录（如 backup/feishu/）内容只备份到该端点，根下文件备份到全部端点；端点分 raw（原文件直传 + 格式白名单，由注册表配置）与 archive（打包 tar.gz）两种模式，由端点注册表配置；注册表（endpoints/endpoints.json）登记已授权端点的凭证位置、私有配置与使用方法，首次使用某端点先走授权流程，之后直接凭证登录备份。NOT for：git 仓库的提交与推送（commit skill）；发布构建产物到 GitHub Release（release skill）。

- Skill: `xhqing/backup` (Agent Skill, multi-file: 6 files)
- Install (CLI): `npx skillmds@latest add xhqing/backup`
- Raw SKILL.md: https://api.skillmd.com/api/skills/xhqing/backup/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: xhqing (https://skillmd.com/u/xhqing)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/xhqing/backup

---


# 多端点备份（backup）

## 为什么做多端备份

AI 已经渗透到线上工作的方方面面，Agent 手里的权限越来越大——能读写文件、能操作云端账户、能执行删除。一旦误操作或凭证泄露，单点存储（只在本机、或只在一个云端）里的重要文件可能被彻底删除、无处找回。对策是把私有文件**同时备份到多个互相独立的端点**（飞书云盘、其它网盘、WebDAV、S3 等）：攻击或误删要同时命中所有端点才能让信息真正消失，端点越多，彻底丢失的概率越低。

本 skill 就是这个对策的落地：统一的备份范围（目录制）+ 可插拔的备份端点（注册制）。做这个 skill 的完整动机与设计考量见 `README.md`。

## 备份范围：目录制（每个项目根一份 backup/）

- 项目 `.gitignore` 必须含 `backup/` 一行（整目录忽略；**只写目录名，绝不写成员文件名**——.gitignore 是公开文件，文件名即信息）。
- **只收用户明确点名的文件**：`backup/` 是用户的备份清单，不是 agent 的收纳箱——只有用户主动说「XX 要备份 / 把 XX 备份一下」的文件才放进去。agent 觉得某文件值得备份时先建议、经用户确认再放——点名之外的文件进备份目录 = 被上传云端，等于替用户决定了什么该上云。
- **backup/ 里放副本，原文件留在原地**：普通文件**复制**进 `backup/`，不移动。完整的多端模型是三份：本地原文件一份（tmp/、docs/ 等工作位置）+ `backup/` 内副本一份 + 云端一份——备份的价值全在冗余，若移动进来（原位置没了），backup/ 那份成了孤本、本地反而少一层，且文件离开工作位置会破坏日常使用。原文件日后有更新，重新复制覆盖 `backup/` 内副本再执行备份。
- **固定路径的配置文件**（如 `CLAUDE.local.md`、`.claude/settings.local.json` 这类有加载机制约定的）：在 `backup/` 里放**符号链接**指向原文件——`ln -s ../CLAUDE.local.md backup/CLAUDE.local.md`。链接自动跟随原件更新、不像副本会过期，且原件始终在场、冗余天然成立。备份脚本 `tar -h` 会解引用链接、打包实际内容；恢复时解压出文件实体，移回原位即完成还原。
- **端点子目录（目录即端点归属）**：`backup/` 下的一级子目录若与某个**已注册端点同名**（如 `backup/feishu/`），该目录内容**只备份到该端点**；根下散文件与其它名字的子目录（普通归档目录）**备份到全部端点**。「只备份到飞书」= 把文件放进 `backup/feishu/`，无需任何额外标记。子目录里同样可放符号链接。

**为什么不加密**：风险模型是「本地丢失为真风险（高概率）、云端泄露为低概率低损失」——加密反而引入「本地密钥丢失则备份作废」的更大风险。明文备份即可。

## 端点体系：注册制

- **注册表**：`endpoints/endpoints.json`（本 skill 目录下），登记每个已授权端点的：name、display、type、credential（凭证位置 + 用法）、reference（详情文档）、authorized_at。做任何端点操作前先读它，确认端点已注册。
- **凭证纪律**：注册表与文档只记「凭证在哪、怎么用」，**凭证本体一律放工具自管的位置**（如 lark-cli auth、rclone config），确需 skill 自存的放 `endpoints/` 目录下独立文件——该目录是本机数据，整体不进开源镜像（CapabilityManagerAgent 仓库已 gitignore），**绝不写进会被 git 跟踪的文件**。
- **本机私有数据同纪律**（「Skill 内容纯净性」全局规则）：端点的其它私有值——云端 folder 标识、个人文件夹命名、机器网络细节（代理等）、个人配置当前值——同样只登记在 `endpoints/`（注册表字段或独立文件），功能文件（SKILL.md / references / scripts）只写通用机制并用占位符表达机器差异，保持可开源纯净。
- **默认全部端点**：用户说「备份」未指定端点 = 执行**全部已注册端点**（多端冗余正是本 skill 的动机）；说「备份到 XX」= 只执行该端点。共享内容（根下文件）进每个端的包、端点子目录内容只进对应端的包；逐端独立执行、独立汇报，**单端失败不影响其它端**（凭证失效的端点按其 reference 文档重新授权后重跑即可）。
- **端点类型两种**：
  - `builtin-*` 内置类型：专用工具对接（如 feishu 走 lark-cli），上传逻辑在 `scripts/backup.sh` 里有对应分支；
  - `rclone` 类型：`rclone` 支持的任意远端（WebDAV / S3 / Google Drive / Dropbox / OneDrive 等），`rclone config` 配好凭证即注册成功，脚本零改动。
- **备份模式与格式白名单**：端点注册表每条可配 `mode` 与 `file_formats`：
  - `mode: "raw"` = **原文件直传**（不打包压缩），云端按 `backup/` 内相对路径建对应文件夹结构镜像存放；`file_formats` 为**格式白名单**（glob 数组，大小写敏感），只有匹配的文件才上传，不匹配的逐条 `[SKIP]` 并汇总——飞书端点即此模式（白名单当前值见注册表 `file_formats`）。**端点可约定云端落位形态转换**（如飞书：`.md` 一律转飞书文档 docx 上传、不传原始 md 文件——转换命令与坑见 `references/feishu.md`「MD 文件转为飞书文档」节）；
  - `mode: "archive"`（默认）= 打包 tar.gz 上传单文件，无白名单过滤。
  - **维护白名单**：改 `endpoints/endpoints.json` 对应端点的 `file_formats` 数组即可（增删格式即时生效），用户说「飞书白名单加 / 去掉 XX 格式」就改这里。

## 四个动作

### ① 放入备份目录（= 打标签）

用户说「XX 文件需要备份 / 把 XX 备份一下」→

1. Read 该文件确认存在（不存在先问用户）；
2. **复制**进项目根 `backup/`（原文件留原位置不动）；固定路径配置文件 → 在 `backup/` 建符号链接；用户点名了端点（如「备份到飞书」）→ 放进 `backup/<端点名>/`（端点名 = 注册表里的 name，目录不存在则新建，如 `backup/feishu/`），不放根下——放根下 = 备份到全部端点；
3. 确认项目 `.gitignore` 有 `backup/` 这一行，没有就补上（只此一行，别写文件名）；
4. 放好不必立即备份（用户说备份时才备份）。用户说「取消 XX 的备份」→ 把 `backup/` 里的副本（或链接）移出删除，原文件不受影响；说「XX 不用只备份到飞书了」→ 把文件从 `backup/feishu/` 移回 `backup/` 根下。

没被用户点名过的文件不进 `backup/`——agent 认为值得备份时先建议，用户点头再放入。

### ② 执行备份

```bash
~/.claude/skills/backup/scripts/backup.sh <项目目录>                     # 全部已注册端点
~/.claude/skills/backup/scripts/backup.sh <项目目录> --endpoint feishu   # 只备份到指定端点
~/.claude/skills/backup/scripts/backup.sh <项目目录> --list              # 只列备份范围，不上传
```

- 打包规则：**每个目标端点独立执行**。`raw` 端点逐文件直传（该端可见 = 共享文件 + 本端同名子目录，白名单过滤后原样上传，符号链接解引用为实际内容但保留链接名）；`archive` 端点独立打包（该端的包 = 根下共享文件 + 本端同名子目录，`tar -h` 解引用，包内带 `backup/` 路径前缀，解压到项目根即还原）。某端无任何可传内容时自动跳过该端。
- 备份成功后逐端点、逐文件向用户汇报（`--list` 同款分组：共享 / 各端专属）。
- 脚本备份前强制检查 `backup/` 已被 gitignore（项目是 git 仓库时），未忽略即终止并列出修复方法。

### ③ 查看与恢复

- 查看某端点云端备份列表 / 下载恢复 / 删除旧备份：命令因端点而异，读对应端点文档（飞书：`references/feishu.md`）。
- 恢复的通用路径：从端点下载 `<项目名>-backup-YYYY-MM-DD.tar.gz` → `tar -xzf` 解压到项目根（包内是 `backup/` 前缀的相对路径，解压即还原）。
- 删除云端旧备份是写操作，删除前向用户确认。

### ④ 注册新端点（首次授权）

用户说「备份到 XX」而注册表里没有 XX → 按 `references/new-endpoint.md` 走授权流程。核心四步：向用户拿凭证 → 验证可用（最小只读操作）→ 保存凭证（位置记进注册表、本体放安全位置）→ 写使用方法（`references/<name>.md`）并登记注册表。之后该端点与老端点一视同仁，直接凭证登录备份，无需再授权。

## 通用纪律

1. **脚本必须纯 ASCII**：脚本文件里不要写中文/全角字符（曾因混入不可见 Unicode 字符导致 bash `unbound variable` 报错）——注释与 echo 输出一律英文。
2. **backup/ 必须整目录 gitignore（硬规矩，脚本强制）**：目录下即私有文件。**绝不要把成员文件名写进 `.gitignore`**——.gitignore 是公开文件，文件名即信息（人名、账户、项目代号都算）；只写 `backup/` 一行。项目不是 git 仓库时自动跳过检测。
3. **备份不设自动周期**：用户说「备份」时执行。放入目录与执行备份是两个独立动作，中间可隔任意久。
4. 端点专属细节（认证命令、代理坑、路径坑、云端目录布局）在各端点 reference 文档里，做对应端点操作前先读。
5. **放入前自检两问**：往 `backup/` 放任何文件前先自检——① 这是用户明确点名要备份的文件吗？② 它的本地原文件在原位置还在吗（backup/ 里放的是副本、不是孤本；发现文件只存在于 backup/ 一处，先把原位置补回一份再放）？有一问为「否」就先处理再放入。

