从 strfry 迁移

用一条命令将现有 strfry 中继的事件迁移到 nostrfy — 准备、dry-run、迁移、验证和回滚。

一览

nostrfy migrate-strfry 读取 strfry 自己的导出格式(JSONL,每行一个 NIP-01 事件),因此可跨 strfry 数据库版本,且不依赖 strfry 内部的 LMDB schema。它从不写入 strfry 数据库。

已迁移未迁移
每个已存储的事件(应用可替换/可寻址语义)没有 nostrfy 等效项的 strfry 设置(合并报告会列出每一项及原因)
NIP-40 过期 — 已过期的事件被跳过Blossom 媒体及其所有者映射(strfry 没有 Blossom 服务器)
NIP-09 删除,包括 strfry 已物理删除的事件的重新发布阻止访问列表(NIP-86 封禁、中继公钥列表、Blossom 白名单)
NIP-29 9005/9008 管理副作用NIP-43 邀请码(用 createclaim 签发新的)
首次出现时间戳(配置了新公钥门槛时)NIP-62 消除请求,除非给出 --apply-vanish
NIP-29 群组、NIP-43 角色及其中继签名元数据,在首次启动时重建中继自身的身份/密钥(它们位于 nostrfy.toml)
等效的 strfry 设置,可选择合并到 nostrfy.toml

摘要中的预期跳过:临时事件(类型 20000-29999,nostrfy 从不存储)和 已过期事件。

快速开始

sh
# 1. 停止 nostrfy 中继(迁移需要数据库目录)
nostrfy --config /etc/nostrfy/nostrfy.toml stop

# 2. dry run — 解析并验证每个事件,不写入任何内容
nostrfy --config /etc/nostrfy/nostrfy.toml migrate-strfry \
    --strfry-db /var/lib/strfry-db --dry-run

# 3. 导入
nostrfy --config /etc/nostrfy/nostrfy.toml migrate-strfry \
    --strfry-db /var/lib/strfry-db

# 4. 启动 — NIP-29 群组和 NIP-43 角色从导入的事件重建
nostrfy --config /etc/nostrfy/nostrfy.toml start
迁移是离线的
它直接写入 database.path,当 nostrfy 守护进程(或另一个迁移)占用数据库目录时会拒绝运行。 请先停止中继。strfry 本身可以继续运行 — strfry export 读取一致的快照。

环境要求

  • strfry 二进制(用于 --strfry-db),或你自己导出的 JSONL 文件。
  • nostrfy v0.1.15 或更高版本(migrate-strfry 子命令)。
  • 目标中继的 nostrfy 配置,已设置 database.path、public_url 和 private_key。
  • 空闲磁盘空间:大约为 strfry 导出加上其索引的大小。NIP-50 词索引会增加 一些;磁盘非常紧张时可先禁用(database.search_index = false), 迁移后再启用(索引在启动时重建)。
  • 目标 database.path 上没有运行中的 nostrfy 实例。

准备配置

toml
[relay]
name = "My Relay"
public_url = "wss://relay.example.com"   # NIP-42/62/98 和 NIP-29 元数据需要
private_key = "..."                       # NIP-29/43 中继签名元数据需要

[server]
host = "0.0.0.0"
port = 8080

[database]
path = "/var/lib/nostrfy"
map_size = 1073741824

如果没有中继密钥,用 nostrfy genkey 生成,然后校验:

sh
nostrfy --config /etc/nostrfy/nostrfy.toml check

合并 strfry 设置(可选)

在打开数据库之前,migrate-strfry 会查找 strfry 的配置 (--strfry-config,然后 $STRFRY_CONFIG、/etc/strfry.conf、 ./strfry.conf),打印有 nostrfy 等效项且与你的 nostrfy.toml 不同的设置,并询问是否合并。只重写列出的键 — 注释 和所有其他行都保留,会使配置无效的值会被跳过并说明原因,其余仍然合并。

  • --merge-config 不经询问直接应用(用于脚本);--no-merge-config 跳过该步骤。
  • 没有终端时,会打印建议,除非给出 --merge-config,否则跳过合并。
  • --dry-run 打印建议但从不写入。

Dry run

先看再跳 — dry run 解析并验证整个导出,而不触碰数据库:

sh
nostrfy migrate-strfry --strfry-db /var/lib/strfry-db --dry-run

非零的 bad signature 计数意味着导出包含 strfry 未经 校验就接受的事件;它们会被跳过。如果信任它们,传 --no-verify 仍然导入。

迁移

选择三种输入模式之一 — 结果完全相同:

sh
# 选项 A — nostrfy 自己运行 `strfry export`(strfry 在 PATH 中)
nostrfy migrate-strfry --strfry-db /var/lib/strfry-db

# 选项 B — 你自己导出到文件
strfry export > /tmp/strfry-export.jsonl
nostrfy migrate-strfry --input /tmp/strfry-export.jsonl

# 选项 C — 管道(stdin 是默认输入)
strfry export | nostrfy migrate-strfry
标志用途
--strfry-bin <PATH>strfry 不在 PATH 中
--since <UNIX>续传/追赶:created_at 为该值或更新的事件(含边界)
--apply-vanish处理导出中发现的 NIP-62 消除请求(默认关闭)
--no-verify对可信转储跳过签名验证(更快)
--batch <N>每个数据库事务的事件数(默认 512)
--dry-run只解析和验证

迁移可安全重复运行:重复项被跳过,删除副作用会 重新应用,因此中断的运行可以直接重跑(或用 --since 续传)。

启动与验证

首次启动会从导入的事件重建 NIP-29 群组存储和 NIP-43 角色存储,并 重新发布中继签名的元数据(每个群组的 39000/39001/39002/39005, 以及 13534 成员列表)。大型数据库可能需要一会儿;请观察日志。

sh
R=wss://relay.example.com      # 用于 nak(WebSocket)
H=https://relay.example.com    # 用于 curl(HTTP)

nak relay "$R"                              # 中继响应并公布其 NIP
curl -s "$H/api/v1/query?limit=1"           # 事件可被提供
nak req -i <deleted-event-id> "$R"          # 已删除的事件保持消失
nak req -k 39000 "$R"                       # NIP-29 群组元数据(如已迁移)
nak req --auth --force-pre-auth --sec <nsec> -k 13534 "$R"   # NIP-43 成员列表(AUTH)

如需精确的数量对比,strfry scan '{}' | wc -l 减去迁移摘要中报告的临时/过期事件 应等于客户端能取回的数量。

恢复中断的迁移

重新运行前不要启动中继
NIP-29 群组副作用(9005/9008)在导入之后应用;中断的运行 已存储这些事件但尚未执行删除,因此首次启动可能会提供删除本应移除的群组 历史。请先重新运行迁移 — 它会完成副作用(清除是幂等的)— 然后再启动中继。
  • 导出到文件/管道:重新运行同一命令。重复项被跳过,删除 阻止会重新应用。
  • 使用了 --strfry-db:摘要会打印续传提示;用那个 --since 重新运行(含边界,边界秒会重新导入并去重)。
  • 如果运行失败并显示 database writer unavailable,检查空闲磁盘空间和 database.map_size,然后重跑。

回滚

迁移只写入 nostrfy 数据库。要回滚,停止中继并恢复迁移前的数据库,或将其删除:

sh
nostrfy --config /etc/nostrfy/nostrfy.toml stop
rm -rf /var/lib/nostrfy            # 或恢复迁移前的备份

故障排除

消息原因 / 修复
cannot lock the database directory ...; stop the relay before migratingnostrfy 守护进程(或另一个迁移)占用了目录:先 nostrfy stop
strfry database directory ... does not exist--strfry-db 必须指向包含 data.mdb 的目录
cannot run 'strfry': ...安装 strfry、设置 --strfry-bin,或使用 --input
database writer unavailable; the migration did not complete写入线程停止或队列过载:检查磁盘/映射大小,重跑(安全)
group purge for <id> did not complete清除被中断:重新运行迁移
高 bad signature 计数strfry 数据库包含未验证的事件:检查它们;仅当信任来源时才用 --no-verify 导入
启动后缺少 NIP-29 元数据没有 relay.private_key:运行 nostrfy genkey 并重启
未提供设置合并未找到 strfry 的配置:传 --strfry-config /etc/strfry.conf

检查清单

  • nostrfy 中继已停止
  • strfry 数据库和 nostrfy 配置已备份
  • nostrfy check 通过
  • strfry 设置已合并(或报告已审阅)
  • dry run 已审阅(无意外的坏签名)
  • 迁移无错误完成
  • 中继启动;群组/角色重建已记录
  • 事件数量匹配(减去临时/过期)
  • 已删除的事件保持消失(重新发布被拒绝)
  • 以匿名和成员身份检查了私密群组可见性
  • 反向代理 / DNS / 客户端中继列表已更新
strfry 还在运行?
如果导出期间 strfry 保持在线,在准备切换时做一次追赶运行:停止 nostrfy, 用 --since <last created_at> 重新运行迁移,然后再次启动。