恢复就绪性
Libre WebUI 提供只读恢复清单,作为备份与恢复的第一道安全门。它报告已知状态和阻止快照的已检测条件,但不会获取维护锁、复制、加密、上传、删除、修复或恢复数据。
libre-webui recovery-check --json > recovery-inventory.json
从源代码检出运行时,先执行一次 npm run build:backend,并将 libre-webui recovery-check 替换为 npm run recovery:check --。打包的 npx 和 Homebrew 安装默认检查 ~/.libre-webui;DATA_DIR 和显式路径选项会覆盖该位置。
未发现阻塞项时命令以状态 0 退出;报告完整但存在恢复阻塞项时为 1;参数无效或收集意外失败时为 2。使用 --data-dir PATH 或 --database PATH 检查非默认位置。默认或 --data-dir 卷清单只接受规范的 DATA_DIR/data.sqlite 文件,并拒绝硬链接、符号链接或非普通的数据库/WAL/SHM 条目。显式 --database 路径可以位于 DATA_DIR 外,但所选数据库及其伴随文件仍必须是普通文件且不能是符号链接。使用 --database 而不使用 --data-dir 时,恢复会把数据库父目录视为数据根目录,从而一起盘点匹配的密钥、blob 和插件定义。
运行时还会从确定性的后端软件包 plugins 目录读取历史插件定义;若 PLUGINS_DIR 是相对路径,还会读取其历史后端相对位置。恢复会盘点这些活跃的旧路径,并在其中包含自定义定义时阻止仅卷快照。若打包部署重新定位了这些兼容目录,可多次传递 --legacy-plugins-dir PATH。
对于私有 Compose 部署,请在已部署容器内运行,使报告描述该容器挂载的卷、代码和机密:
docker exec libre-webui \
libre-webui recovery-check --json --data-dir /app/backend/data
清单检查的内容
带版本的 JSON 报告记录:
- 应用、Node.js、操作系统和架构版本;
- SQLite 文件与 WAL/SHM 大小、
quick_check、外键验证、架构指纹、用户版本、缺失的必需表,以及创建私有检查快照前不跟随链接的源文件验证; - 数据目录的可读性、可写性、文件数量和字节数;
- 所选加密密钥来源及单向 16 字符指纹;
- 对持久
.encryption_key文件进行不跟随链接、单链接验证; - 自定义插件定义,以及加密本地 blob 根目录、嵌入媒体、声音引用、文档文本、旧文档向量和带 ACL/过滤行的平台向量是否存在、数量、大小及是否包含在数据目录中;
- 对每个规范本地 blob 对象和嵌入的平台向量信封进行有界只读身份验证,包括完整的 blob 分块/校验和验证及配置密钥可用性;
- 对聊天、笔记、文档、偏好、插件机密、图库/媒体状态和账户电子邮件中的每个可识别旧文本 AES-GCM 信封,以及每个绑定 AAD 的已保存声音名称、录音和转录信封进行有界只读身份验证;
- Work 任务/运行/预览数量及预期 Docker 卷、Kubernetes PVC 或哈希主机路径身份;Docker 卷必须同时带有托管标签和确切的所属任务 ID;
- 旧媒体生成任务状态,以及按状态划分的持久作业、按结果划分的尝试、事件流/事件数量和最后一个全局事件游标;
- 对每个加密持久作业和事件载荷进行有界只读身份验证,并对每个不透明引用载荷进行有界语法验证;以及
- 显式阻塞项、警告和位于应用数据目录外的数据。
报告绝不包含加密密钥、JWT/会话机密、提供商凭据、插件内容、用户内容或主机工作区字面路径。只会输出机密存在与否的布尔值和不可逆加密密钥指纹。
只读数据挂载可用于恢复检查,只会产生警告而非阻塞项。应用就绪仍要求可写存储;绝不要针对备份辅助程序使用的只读快照启动 Libre WebUI。
阻塞项
任何阻塞项都应视为恢复门禁失败。常见阻塞项包括:数据库缺失或损坏、架构不完整、密钥缺失/冲突、旧版或平台密文损坏或未通过身份验证、超出验证界限、数据目录不可读、SQLite 源已链接或不是普通文件、Work 运行或预览处于活动状态、媒体作业或持久作业处于活动状态、Work 工作区缺失或标签错误、持久事件头不匹配或序列有缺口、自定义插件定义位于数据目录外,或运行时控制平面无法验证外部工作区。快照前请让活动工作静止并解决缺失依赖;不要编辑报告以隐藏阻塞项。
加密持久载荷会根据其作业/事件身份验证,并作为规范的有界 JSON 校验。不透明引用载荷只会接受大小和语法检查:当前基础层没有权威 blob 引用仓库,恢复无法借此证明目标存在或可访问。只要存在此类引用,报告就会将 referenceTargetsVerified 标记为 false 并发出警告;载荷或引用值绝不会暴露。
旧文本字段早于强制信封标记,因此较早架构版本中的真实明文行仍可读取,也不会报告为已验证密文。规范信封始终会验证;具有信封宽度 IV 或身份验证标签的三段式值若格式错误会关闭失败。已保存声音字段具有明确的二进制信封,始终必须针对其配置文件、所有者和字段身份验证。JSON 的 encryption.legacyCiphertext 部分会报告已验证文本/二进制记录和字节总数,但不会暴露明文。
当存在架构 v4 的 users.email_lookup 列时,恢复还会验证每个非 null 电子邮件,并重新计算其域分隔的带密钥查找令牌。令牌缺失、不匹配,或令牌附着于 null 电子邮件,都会阻止快照。v4 之前的数据库没有此派生查找列,因此仍保持兼容。
当前备份边界
私有部署辅助程序会停止正在运行的应用,并使用该容器的不可变镜像、挂载数据卷和环境创建集成式单机归档。清单使用 Ed25519 签名,完整载荷使用操作员持有的 AES-256-GCM 备份密钥加密。它包含 SQLite、本地 blob 与嵌入向量、运行时选择器,以及解密恢复状态所需的受保护配置。辅助程序会在发布归档和元数据报告前验证签名、密文校验和及解密载荷。libre-webui-restore 只接受新的 Docker 卷,会在复制任何数据前验证解密后的恢复清单,并将恢复配置作为私有文件发布到新的目标目录。
受保护的运行时配置包括 PostgreSQL 连接池、连接、空闲、语句和迁移锁超时;Redis 连接超时;两项持久 blob 配额设置;平台选择器;以及 S3 前缀和寻址模式。这些值位于已签名加密的载荷中,而不在明文清单中;应用恢复时会以模式 0600 的配置重新发布。
单机归档不包含 Docker Work 卷、Kubernetes PVC、主机绑定的工作区文件夹、Ollama 模型或外部提供商状态。请保持这些已签名清单排除项清晰可见,并单独快照外部 Work 存储。团队配置使用单独的离线团队工作流:PostgreSQL 导出快照、精确的版本化 S3 密文对象、PGVector 清单、运行时配置和密钥身份会密封到同一种签名/加密归档格式中,并在恢复期间针对干净的 PostgreSQL/S3 目标验证。Redis 缓存、在线状态、唤醒和租约会从规范 SQL 状态重建。
团队备份还会验证确切导出 PostgreSQL 快照中的每个有界加密持久作业和事件载荷。其受保护的签名清单记录作业、事件、流、游标、信封、引用和已验证明文总数。每个事件流都必须恰好包含连续序列 1..last_sequence,PostgreSQL 的全局游标序列不得落后于最大已存储游标。恢复会在干净目标上重复这些检查,并要求完整结果与已签名源清单匹配,才报告成功。不同全局游标值之间的间隔有效,因为 PostgreSQL identity 分配不具事务性;流内序列才是连续排序约定。
当 PLUGINS_DIR 指向 DATA_DIR 外部时,恢复会盘点该确切目录,并将其标记为不包含在应用卷归档内。只要其中有定义,就会阻止仅卷快照,直到操作员安排匹配的插件目录快照。同一规则适用于活跃的旧插件目录。符号链接、非普通或不可读的 JSON 定义始终是阻塞项,绝不会跟随或静默遗漏。
两个配置都启用持久作业和有序事件。作业尝试或 Work 执行处于活动状态时,恢复会阻塞;它还会验证作业/事件载荷和连续流头,并保留其规范 SQL 状态。单机配置运行有界嵌入式工作进程;团队配置则在外部工作进程运行相同已注册处理程序,仅使用 Redis 进行唤醒和扇出。
生产环境请将加密和 JWT 机密存入受保护的机密管理器,在主机外加密保存备份归档,并在干净且兼容的环境中测试恢复。清单是已知状态的预检快照,不是维护锁,也不能独立证明所有外部资源均可恢复。
已签名和加密的备份命令
以下示例使用全局 npm 或 Homebrew 安装的 libre-webui 命令。若未全局安装,请将 libre-webui 替换为 npx --yes libre-webui@latest。从源代码检出时,先构建一次后端,再将 libre-webui backup 替换为 npm run recovery:backup --。生产 Docker 镜像通过 /usr/local/bin/libre-webui 提供同一命令。团队备份和恢复还需要 PostgreSQL 16 pg_dump 和 pg_restore;生产镜像和 Homebrew formula 的命令路径已包含它们。通过普通 npm/npx 使用这些命令前,请显式安装兼容的 PostgreSQL 客户端。
在私有目录中生成操作员持有的 AES-256-GCM 归档密钥和 Ed25519 签名密钥对,然后将私钥移到受保护的主机外存储:
install -d -m 0700 /absolute/private/libre-backup-keys
libre-webui backup keygen \
--directory /absolute/private/libre-backup-keys
对于已静止的单机数据目录,创建并独立验证归档:
libre-webui backup create \
--offline \
--data-dir /absolute/path/to/libre-data \
--output /absolute/backups/libre-solo.lwbackup \
--encryption-key /absolute/private/libre-backup-keys/backup-encryption.key \
--signing-private-key /absolute/private/libre-backup-keys/backup-signing-private.pem
libre-webui backup verify \
--archive /absolute/backups/libre-solo.lwbackup \
--encryption-key /absolute/private/libre-backup-keys/backup-encryption.key \
--signing-public-key /absolute/private/libre-backup-keys/backup-signing-public.pem
先执行恢复预检,然后只应用到全新且为空的目标目录:
libre-webui backup restore-preflight \
--archive /absolute/backups/libre-solo.lwbackup \
--target /absolute/restore/libre-data \
--encryption-key /absolute/private/libre-backup-keys/backup-encryption.key \
--signing-public-key /absolute/private/libre-backup-keys/backup-signing-public.pem
libre-webui backup restore-apply \
--archive /absolute/backups/libre-solo.lwbackup \
--target /absolute/restore/libre-data \
--encryption-key /absolute/private/libre-backup-keys/backup-encryption.key \
--signing-public-key /absolute/private/libre-backup-keys/backup-signing-public.pem
libre-webui backup restore-verify \
--target /absolute/restore/libre-data
对于团队模式,请停止所有应用副本和工作进程,保持源 PostgreSQL/S3/keyring 环境已加载,然后创建协调归档:
libre-webui backup create-team \
--offline \
--output /absolute/backups/libre-team.lwbackup \
--encryption-key /absolute/private/libre-backup-keys/backup-encryption.key \
--signing-private-key /absolute/private/libre-backup-keys/backup-signing-private.pem
恢复前,为独立的空 PostgreSQL 数据库和空的版本化 S3 bucket 加载环境变量。预检会验证签名和加密归档、校验受保护清单,并在不发布数据的情况下证明所选目标数据库和 bucket 前缀为空。应用操作会恢复到这些干净目标,验证生成的 PostgreSQL 架构、确切 S3 对象和 PGVector 记录,并将受保护运行时配置写入新的私有目录:
libre-webui backup restore-team-preflight \
--archive /absolute/backups/libre-team.lwbackup \
--encryption-key /absolute/private/libre-backup-keys/backup-encryption.key \
--signing-public-key /absolute/private/libre-backup-keys/backup-signing-public.pem
libre-webui backup restore-team-apply \
--archive /absolute/backups/libre-team.lwbackup \
--configuration-output /absolute/restore/libre-team-config \
--encryption-key /absolute/private/libre-backup-keys/backup-encryption.key \
--signing-public-key /absolute/private/libre-backup-keys/backup-signing-public.pem
绝不要将恢复目标指向源数据库、源 bucket、现有数据目录或包含文件的配置目录。请将公有签名密钥与恢复运行手册一起保存;只持有归档和公钥无法解密载荷。
如果团队恢复报告回滚不完整,请将两个所选目标均视为脏状态,不要立即重试。检查并清理目标 PostgreSQL 数据库,然后枚举并删除确切目标 S3 前缀下的每个对象版本和删除标记。再次运行 restore-team-preflight;只有干净目标预检成功后,应用操作才可安全重试。
定期验证恢复演练
从未恢复过的备份只是希望,不是恢复能力。演练会端到端执行上述确切流程,在不停机且无需操作员的情况下证明实例确实可恢复:
- 暂存数据目录的静止快照——SQLite 数据库通过在线备份 API,blob 和文件通过物理复制。演练会等待安静时刻:任何持久作业正在执行时都会拒绝运行,与
recovery-check执行相同规则。 - 暂存副本使用临时演练密钥变成已签名、AES-256-GCM 加密的归档,并运行完整恢复清单。
- 验证归档,将其恢复到隔离的临时目标,并再次验证恢复环境。
- 演练记录测量值——恢复时长就是经证明的 RTO,成功演练之间的间隔限定当前计划可实现的 RPO——然后删除所有产物。演练用于验证,而不是备份:不会保留归档或密钥。
使用 RECOVERY_DRILL_INTERVAL_HOURS(例如 24)启用计划;演练会在共享调度器上通过协调器租约运行,因此副本和重叠触发不会重复执行。系统页面会显示演练历史,并向管理员提供“立即运行演练”按钮,由 GET /api/recovery/drills 和 POST /api/recovery/drills/run 提供支持。无人值守演练失败时,会通过通知收件箱(及所有已订阅 webhook 目标)提醒每位管理员;手动运行则会直接报告拒绝原因。RECOVERY_DRILL_HISTORY 限制保留的历史记录(默认 60 条)。
演练覆盖单机(SQLite)配置,其中文件系统归档是权威备份路径。团队配置仍采用协调的 backup create-team 流程,其恢复演练目前仍是操作员运行手册中的步骤。