教程

在服务器 / NAS 上用命令行同步(obsync-cli)

手机电脑同步插件 · ·使用教程
命令行服务器NASheadless

这是什么

obsync 是「手机电脑同步」的命令行客户端,让没有桌面环境的机器也能加入你的同步库:

  • 云服务器、NAS、家里的小主机,长期在线地把笔记库和手机、电脑保持一致;
  • 不需要安装 Obsidian,也不需要图形界面;
  • 同步行为与桌面端插件一致(同一套同步逻辑),冲突处理、删除保护等机制都沿用。

适合场景:给一台常开的机器放一份笔记副本、做备份、让脚本/自动化读写笔记库。

1. 环境要求

项目要求
系统Linux,x86_64 或 arm64(需要 glibc;Alpine 请换 glibc 基础镜像)
Node.js22.5 或更高(需带内置 SQLite,使用 nodejs.org 官方 Node 22 即可)
其他需要能联网访问同步服务;系统带 flock(util-linux,绝大多数发行版自带);本机有可写的磁盘目录
账号一个「手机电脑同步」账号(微信登录),并已有或将创建一个同步库

检查 Node 版本:

node --version      # 应显示 v22.5.0 或更高

2. 安装

npm install -g obsync-cli

国内网络访问 npm 官方源较慢时,可以改用镜像源:

npm install -g obsync-cli --registry=https://registry.npmmirror.com

如果 npm 源都连不上,也可以直接从本站下载安装包:

npm install -g https://shoujidiannao.bijitongbu.site/downloads/obsync-cli-latest.tgz

安装后验证:

obsync --help       # 显示命令列表
obsync install      # 自检运行环境(系统、CPU、Node、SQLite),不会下载任何东西

obsync install 成功时返回 ok:true;失败会指出缺什么(例如 Node 版本过低)。

升级:先停掉正在运行的 obsync run,执行 npm install -g obsync-cli@latest,再执行一次 obsync update(见 7.3)把新版同步插件应用到你的笔记库。 卸载:npm uninstall -g obsync-cli。卸载不会删除你的笔记和同步状态目录(见第 3 节)。

3. 两个目录

每条命令都要告诉 obsync 两个位置:

参数含义
--vault <目录>你的笔记库目录,必须已存在(空目录也行,同步后会被填充)
--state-dir <目录>obsync 的私有状态目录,存放登录信息、同步状态、日志。不写时默认 ~/.local/share/obsync

注意:

  • 状态目录必须在笔记库之外,否则会报错。
  • 状态目录里有账号凭据和同步密钥,权限是 0700,不要共享、不要放进会被同步的目录、不要提交到 git。
  • 一个笔记库对应一份状态。换笔记库路径或换状态目录,会被视为一个全新的设备,需要重新登录、重新加入。
  • 同一个笔记库同一时间只能有一个 obsync run,再启第二个会返回「已被占用」(退出码 7)。

为了少打字,下面的示例统一定义一个函数(放进你的 shell 配置里更方便):

mkdir -p ~/notes ~/.local/share/obsync
obs() { obsync "$@" --vault ~/notes --state-dir ~/.local/share/obsync --json; }

以后文中的 obs xxx 都等价于带上这两个目录和 --json 的 obsync xxx。--json 让输出变成单行 JSON,便于脚本处理;人工查看时可以去掉。

4. 快速开始(三步)

# 1) 准备本地环境(安装随包携带的同步插件并校验)
obs init

# 2) 在一个终端里保持同步程序运行(占住当前终端是正常的)
obs run

# 3) 在另一个终端里登录、加入同步库、开始同步(详见第 5、6 节)
obs login start
...

要点:除了 install、init、run、update 之外,所有命令都是在向「正在运行的 obsync run」发指令。 所以必须先让 run 保持运行,再在另一个终端发命令。没有在运行时,这些命令会提示「尚未就绪,请先运行 obsync run」(退出码 5)。

长期使用请参考第 7 节,把 run 交给 systemd 托管。

5. 登录

5.1 用微信登录(推荐)

命令行没有二维码,登录靠一个 6 位登录码:

obs login start
# {"ok":true,"code":0,"message":"操作成功。",
#  "sessionId":"<一串本地会话ID>","loginCode":"123456","expiresIn":300}
  1. 记下 loginCode(6 位数字)和 sessionId。登录码默认 5 分钟内有效,以返回的 expiresIn(秒)为准。
  2. 打开微信,进入「笔记同步助手」服务号的对话,把这 6 位数字单独作为一条消息发给它。如果还没关注,请先在桌面端插件的登录引导里找到关注二维码,或在微信里搜索服务号关注。
  3. 回到终端查询结果,每次执行只查一次,间隔约 2 秒再查:
obs login poll --session '<上面的 sessionId>'
# 还没处理完:{"ok":true,"code":0,...,"phase":"pending"}
# 登录成功:  {"ok":true,"code":0,...,"phase":"ok"}
phase含义你要做什么
pending服务号还没收到或还没处理完这个码确认已发送,约 2 秒后再 poll
ok登录成功,凭据已保存在状态目录不要再 poll,继续下一步
expired登录码已过期重新 login start,用新的码

常见误区:

  • --session 填的是 sessionId,不是 loginCode。
  • 每次 login start 只保留最近一次会话,旧码作废。
  • 命令返回 phase:"expired" 时退出码仍是 0,写脚本请读 phase,别只看退出码。
  • 已经登录的设备再 login start 会被拒绝(「此设备已登录」,退出码 5)。

5.2 用已有的登录凭据(适合自动化)

如果你已经有账号凭据文件(原始文本,不是 JSON),可以直接导入:

obs login token --token-file /secure/path/account-token.txt
# 或从标准输入读取,避免落盘:
obs login token --token-file - < /secure/path/account-token.txt

所有秘密(登录凭据、恢复密钥、设置文件)只能通过 --token-file、--key-file、--input-file 或标准输入传入,不接受直接写在命令行里。

5.3 查看登录状态与退出登录

obs status        # loggedIn:true 表示本机已保存登录信息
obs logout        # 清除本机的库绑定和登录信息

logout 只清本机,不删除远端的笔记库,也不会让服务端凭据失效。

6. 加入同步库

登录之后,这台机器还没有绑定任何同步库。有三种方式加入,任选其一。 先看看账号下有哪些库:

obs vault list
# vaults:[{vaultId,name,createdAt,fileCount,sizeBytes,deviceCount,connectedCount,lastActiveAt,...}]

如果账号下还没有任何同步库,可以在这台机器上直接新建一个,新建后本机自动绑定到它:

obs vault create --name '我的笔记'
obs status          # bound:true

新建的库是空的,本机笔记库里已有的笔记会在开始同步后上传。之后其它设备用配对码或恢复密钥加入这个库即可;新建后请尽快用 export-key --output 保存恢复密钥(见 6.2)。

加入或新建之后,为安全起见同步会先处于暂停状态,你要先确认同步范围,再手动 resume(见 6.4)。

6.1 方式一:输入配对码(旁边有一台已在同步的设备)

适合:手机或电脑上已经装好并在同步,你想把这台机器加进去。

  1. 在已经在同步的设备(桌面端插件)里打开「配对」功能,生成一个 6 位配对码。
  2. 在这台机器上:
obs pair start --code 123456
# {"ok":true,...,"sessionId":"...","phase":"claiming"}
obs pair status
# {"ok":true,...,"phase":"sas_pending","sas":{"digits":"482913","emojiIndices":[17,203]}}
  1. 人工核对:对照旧设备界面上显示的核对数字/表情,确认两边完全一致。不一致说明配对对象不对,请取消。
  2. 一致后在两端各自确认(旧设备在它的界面点确认;这台机器执行下面命令):
obs pair confirm --session '<pair start 返回的 sessionId>'
obs pair status        # 依次出现 decrypting → ready
obs status             # 确认 bound:true,库名/库 ID 是你要的那个
phase含义
claiming正在提交配对码、交换信息
sas_pending等你人工核对数字并确认
decrypting已确认,正在取得密钥并完成绑定
ready绑定完成
error失败(含配对码过期)。pair cancel 后让旧设备重新出码再来

提示:

  • 配对码是通过命令行参数 --code 传入的,会出现在 shell 历史里。它只有短时有效,用完即作废;介意的话事后清理历史。
  • pair start 失败有时是退出码 0 加 phase:"error",脚本里要读 phase。
  • 放弃配对:obs pair cancel --session '<sessionId>'。

6.2 方式二:用恢复密钥加入(没有别的设备在线时)

适合:旧设备不在身边或不在线,但你保存了这个同步库的恢复密钥。

恢复密钥可以来自桌面端插件导出的文件,也可以是另一台 obsync 机器上导出的:

# 在已绑定的旧机器上(可选,用于导出)
obsync export-key --output /secure/path/recovery-key.txt --vault <旧库> --state-dir <旧状态目录>

在这台机器上加入:

obs vault list
obs vault join-key --vault-id '<vault list 里选定的 vaultId>' \
  --key-file /secure/path/recovery-key.txt
# {"ok":true,...,"vaultId":"...","vaultName":"..."}
obs status          # bound:true
  • 恢复密钥只对你指定的那个库有效,拿别的库的密钥会失败(退出码 6)。
  • 密钥文件可以是纯密钥文本,也可以是桌面端导出的文件(会自动忽略注释行)。
  • 恢复密钥等同于库的钥匙,务必妥善保管,不要发到聊天或工单里。export-key 不加 --output 会把密钥明文打印在终端,请只在安全环境使用。

6.3 方式三:向在线设备发起加入申请

适合:已有设备在线,但你不方便在它上面生成配对码。

在这台新机器上:

obs settings set --input-file - <<'JSON'
{"deviceName":"我的服务器"}
JSON
obs join-request start --vault-id '<目标 vaultId>'
# {"ok":true,...,"sessionId":"...","phase":"waiting","notifiedCount":1}

notifiedCount 是已通知到的在线设备数。为 0 说明没有设备在线能审批,需要先让旧设备上线。

在已绑定且在线的旧设备上审批(桌面端会弹出审批提示;若旧设备也是 obsync 命令行,则用下面命令):

obsync approvals list --vault <旧库> --state-dir <旧状态目录> --json
# requests:[{requestId,requesterLabel,vaultName,expiresAt,phase}]
obsync approvals approve --request '<requestId>' ...   # 同意
# obsync approvals reject  --request '<requestId>' ...   # 拒绝

同意之后双方进入核对阶段:

# 新机器
obs join-request status       # phase:"sas_pending",带 sas 核对数字
# 人工核对两边数字完全一致后,两端各自确认
obs join-request confirm --session '<sessionId>'
# 旧设备若是命令行: obsync approvals confirm --request '<requestId>' ...
obs join-request status       # joining → done
obs status                    # bound:true

放弃:新机器 obs join-request cancel --session '<sessionId>',或旧设备 approvals reject。阶段含义:waiting 等审批,sas_pending 等人工核对,joining 正在加入,done 完成,rejected/expired/error 需要重新发起。

6.4 加入后:确认范围,再开始同步

任何一种方式加入后,同步都先暂停。先看当前同步范围:

obs settings get

可调整的项(只有这几项):

设置项含义
excludedFolders不同步的文件夹/文件(字符串数组)
excludedFileRules不同步的文件规则(每条还必须同时出现在 excludedFolders 中)
includedFolders只同步的文件夹(配合 onlySyncIncluded)
onlySyncIncludedtrue 表示只同步 includedFolders
conflictAction冲突处理:merge(合并)或 conflict(保留冲突副本)
deviceName本设备在同步库里显示的名字
pause是否暂停(false 同时解除「加入后先暂停」)

例如只同步 notes 目录:

obs settings set --input-file - <<'JSON'
{"includedFolders":["notes"],"onlySyncIncluded":true}
JSON

确认无误后开始同步:

obs resume
obs sync --timeout 85     # 触发一轮并等待收敛,最长 85 秒
obs status

如果 status 里出现 identityConfirmation(状态 adopt-pending),说明需要你确认「这个目录采用该同步库的身份」,确认无误后按它给出的值执行:

obs identity-confirm --vault-id '<identityConfirmation.vaultId>' --generation '<identityConfirmation.generation>'

7. 长期运行

7.1 systemd(推荐)

先用将要运行服务的同一用户完成 obs init 和登录、加入。新建 /etc/systemd/system/obsync.service(把用户和路径换成你自己的):

[Unit]
Description=obsync headless vault
Wants=network-online.target
After=network-online.target

[Service]
Type=simple
User=youruser
UMask=0077
ExecStart=/usr/bin/env obsync run --vault /home/youruser/notes --state-dir /home/youruser/.local/share/obsync --json
Restart=on-failure
RestartSec=5
KillSignal=SIGTERM
TimeoutStopSec=90

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now obsync.service
sudo journalctl -u obsync.service -n 50

ExecStart 里的 obsync 要能在服务的 PATH 里找到。用 nvm 装的 Node 请写 obsync 的绝对路径(which obsync),并确保该路径下的 node 是 22.5 以上。

之后在同一用户下用 obs status、obs sync 等命令控制它。换用户或换目录会连不上。

关掉发命令的终端不会停掉 run。停止同步请停进程(sudo systemctl stop obsync.service,前台运行时按 Ctrl-C),不要用 logout 或 unbind 代替。

7.2 只同步一次就退出(适合 cron / 脚本)

obsync run --once --timeout 300 --vault ~/notes --state-dir ~/.local/share/obsync --json

启动、同步到收敛、退出。判断成功请同时检查退出码为 0 且输出里 convergence.stable 为 true;被信号打断时可能也返回 0,但没有收敛信息。--once 与常驻的 run 互斥。

7.3 更新同步插件

同步逻辑由一个插件提供,插件随命令行程序一起打包,不需要另外联网下载。升级命令行程序(见第 2 节)后,插件并不会自动换新,需要执行:

# 先停掉正在运行的 run,然后:
obsync update --vault ~/notes --state-dir ~/.local/share/obsync
# 再启动 run

update 会校验随包插件的大小与 SHA-256 后安装到你的状态目录;失败会自动恢复旧版本,已有的账号和同步状态不受影响。没有新版命令行程序时执行 update 不会有变化。

8. 日常命令速查

命令作用
obs status查看登录、绑定、同步状态与各项阻塞(没登录也能查,退出码 0)
obs sync [--timeout 秒]触发一轮并等待收敛,最长 85 秒;超时返回 5,可重复执行
obs pause / obs resume暂停 / 继续同步(进程仍在运行)
obs settings get / settings set --input-file …查看 / 修改同步范围等设置
obs forks list列出需要你决定的冲突副本
obs forks resolve --path <文件> --since <时间戳> --choice keep-local|use-cloud处理冲突:保留本机 / 采用云端(--since 用 list 返回的原值)
obs failures list / failures retry --uid <标识>查看 / 重试同步失败的文件
obs mass-delete list / mass-delete confirm --fired-at <时间戳>出现「批量删除待确认」时查看并确认(见下)
obs diagnostics [--output <新文件>]导出脱敏的诊断信息,反馈问题时附上
obs unbind解除本机与同步库的绑定(保留登录、本地笔记和云端库)
obs export-key [--output <新文件>]导出恢复密钥(敏感,见 6.2)

批量删除保护:当检测到大量文件将被一次性删除时,同步会暂停并让命令返回 8,等你确认。先 obs mass-delete list 看清要删什么,确认是你的本意再 mass-delete confirm --fired-at <list 里的时间戳>。不是本意就不要确认,先排查原因(比如目录被误清空)。

解绑或退出前,如果这台机器上的是库的唯一一份密钥,请先 export-key --output 保存好恢复密钥。

9. 退出码

码含义常见处理
0成功但 login poll、pair、join-request 的失败可能仍是 0,要读输出里的 phase
1同步进程异常退出(看门狗、收尾失败)查日志;systemd 会按配置自动重启
3未登录先登录(第 5 节)
4未绑定同步库先加入同步库(第 6 节)
5暂时不可用常见:run 没启动、网络问题、会话已过期、同步超时。多数可重试
6参数错误或安装失败按提示修正参数、检查 Node 版本
7目录已被占用同一笔记库已有一个 run,不要重复启动
8批量删除待确认见第 8 节

10. 常见问题

现象原因与处理
提示「尚未就绪,请先运行 obsync run」没有在运行的同步进程,或用了不同的 --vault / --state-dir,或换了用户。保持 run 运行,并使用完全相同的路径和用户
提示「已被占用」(7)同一笔记库已有一个 run。不要启第二个;也不要手动删除锁文件
login poll 一直 pending确认登录码已作为单独一条消息发给了服务号,且未超过 5 分钟
提示「待确认内容已经变化」(5)会话已失效(重启过、已成功、已过期、被新会话覆盖)。重新发起
刚加入,sync 返回 5加入后默认暂停。按 6.4 先确认范围再 resume
status 退出 0,但同步没完成status 只是查询,退出 0 不代表已同步完。看 blockers 和 pending,用 sync 判断
配对一直不成功配对码已过期,或用错了码(登录码不是配对码)。pair cancel 后让旧设备重新生成
pending.persistence 一直为 true,sync 总不收敛已知问题:某些死信恢复后标志残留,导致「假未收敛」。导出 diagnostics 反馈给我们,不要自行改动状态文件
屏幕上出现 ExperimentalWarning: SQLite is an experimental feature这是 Node 自身的提示,写在错误输出里,不影响命令结果和 JSON 输出,可以忽略
想看日志状态目录下 vaults/<库标识>/profile/runtime.log 与 host-stderr.log;systemd 另看 journalctl

11. 安全说明

  • 凭据与密钥保存在状态目录,只有当前用户可读写(0700/0600)。备份状态目录等同于备份你的账号凭据,请按敏感数据对待。
  • 命令行不接受秘密参数(登录凭据、恢复密钥、设置文件只能走文件或标准输入),避免被其他用户通过进程列表看到。唯一例外是配对码 --code,它短时有效、用完作废。
  • 加入同步库需要库的密钥:通过配对、审批或恢复密钥取得。请自行保管好恢复密钥,它是加入这个库的凭证。
  • 同步插件随命令行程序一起分发,安装时会校验文件大小与 SHA-256,校验失败会拒绝安装并保留原版本。
  • 同步使用的插件、状态都放在你指定的状态目录里,笔记只读写你指定的笔记库目录。

12. 许可协议

obsync-cli 以 CC BY-NC-SA 4.0(署名-非商业性使用-相同方式共享)授权:可以免费个人使用、学习和修改;不得用于商业目的;修改后再分发须保留版权声明并以同一协议发布。完整条款见安装包里的 LICENSE 文件。商业使用请联系我们另行授权。

13. 获取帮助

反馈问题时请附上:obsync --help 里看到的命令、出错命令的完整输出、obs status 的输出,以及 obs diagnostics --output /tmp/diag.json 导出的文件(已脱敏)。不要贴出恢复密钥、登录凭据或配对码。