SilverBullet 常用命令与运维实战(核心篇)

SilverBullet v2.12.1 运维实战核心篇:space 文件操作(页面=文件)、silverbullet 服务端二进制启动参数、sb CLI 子命令与 HTTP API(条件写/Shell RPC)curl 实测、SLIQ 查询与模板体系实操、备份恢复闭环与真实踩坑沉淀。

SilverBulletSilverBullet知识库Docker2026/10/28

SilverBullet 常用命令与运维实战(核心篇)

目标:把 SilverBullet v2.12.1 的日常运维抓手一次练全——space 文件操作(页面=文件)、silverbullet 服务端二进制启动参数、sb CLI 子命令、HTTP API(页面读写/条件写/Shell RPC/账号)curl 实测、SLIQ 查询与模板体系实操、多端并发访问、备份恢复闭环,并沉淀本环境真实踩坑。全部命令与回显均在下述环境实测通过。

一、环境信息

  • 操作系统:Ubuntu 24.04.5 LTS(x86_64,文件系统大小写敏感)
  • SilverBullet:v2.12.1(构建标识 2.12.1-0-g4d5eac53-2026-10-12T06-21-29Z)
  • 运行形态:Docker Compose 主实例(宿主 8147 → 服务内 3000,数据目录 /opt/silverbullet/data,编排与部署见 《SilverBullet Docker Compose 部署实战》)
  • CLI 工具:官方 releases 的 sb 与 silverbullet 双二进制(版本同为 2.12.1),存放于 /opt/silverbullet/bin/
  • 认证:admin / q1w2e3r4;API 令牌:sbtoken-q1w2e3r4-demo
  • 官方文档实查日期:2026-10-28

二、工具概览

SilverBullet 的运维抓手分四层,按「改数据 → 查状态 → 做自动化」的日常动线选择:

工具载体何时用
space 文件系统本体/opt/silverbullet/data/ 目录宿主脚本批量改页面、git 版本化、rsync 同步——页面就是文件
HTTP APIhttp://127.0.0.1:8147/.fs 等端点远程读写、脚本集成、健康探测、服务端 Shell RPC
sb CLI/opt/silverbullet/bin/sb连接任意 SilverBullet 服务器做文件/Lua/查询/日志操作(面向「正在运行的服务」)
silverbullet 服务端二进制/opt/silverbullet/bin/silverbullet裸机形态启动服务、自升级(面向「服务本身」)

版本核对(两支二进制独立出版,版本未必一致,本文环境一致):

/opt/silverbullet/bin/silverbullet --version
/opt/silverbullet/bin/sb --version

实测回显:

silverbullet 2.12.1-0-g4d5eac53-2026-10-12T06-21-29Z
sb 2.12.1-0-g4d5eac53-2026-10-12T06-21-29Z

三、前置条件

  1. 主实例在线(健康端点直通,无需认证):
curl -s http://127.0.0.1:8147/.ping -w "\nHTTP %{http_code}\n"

实测回显:

OK
HTTP 200
  1. 认证可用(部署篇已实测登录与 Bearer 两种方式,此处只验证令牌):
curl -s -H 'Authorization: Bearer sbtoken-q1w2e3r4-demo' http://127.0.0.1:8147/.fs/Manual/Deploy-Notes.md -w "\nHTTP %{http_code}\n"

实测回显:

# 部署手记

本页由 HTTP API 写入,验证页面即文件。

HTTP 200
  1. sb CLI 已下载解压(官方 releases 页提供 sb-<os>-<arch>.zip 与 silverbullet-server-<os>-<arch>.zip 两个包,前者是 CLI、后者是服务端)。

四、各工具详解

4.1 space 文件操作(页面=文件)

目录即数据库:一个 space 就是数据目录本身——页面 = .md 文件、子页面 = 子目录。本文实验 space 当前的宿主文件树(.chrome-data/ 为运行时浏览器档案,见 4.7):

find /opt/silverbullet/data -maxdepth 2 -not -path '*/.chrome-data*' | sort

实测回显:

/opt/silverbullet/data
/opt/silverbullet/data/.silverbullet.auth.json
/opt/silverbullet/data/Books
/opt/silverbullet/data/Books/Alpha.md
/opt/silverbullet/data/Books/Beta.md
/opt/silverbullet/data/CONFIG.md
/opt/silverbullet/data/Dashboard.md
/opt/silverbullet/data/index.md
/opt/silverbullet/data/Manual
/opt/silverbullet/data/Manual/Deploy-Notes.md
/opt/silverbullet/data/Scratch
/opt/silverbullet/data/Templates

宿主直改立即可见——echo 追加一行到页面文件,API 侧马上读到:

printf -- '- 2026-10-28 宿主直改附录行\n' >> /opt/silverbullet/data/Books/Alpha.md
/opt/silverbullet/bin/sb --url http://127.0.0.1:8147 --token sbtoken-q1w2e3r4-demo fs read 'Books/Alpha.md' | tail -3

实测回显:

ISBN 1234-5678。
- 2026-10-28 宿主直改附录行

⚠️ 注意:文件层立即可见 ≠ 索引立即可查——外部新增页面后,查询体系(4.5)可能仍返回空,需 sb eval 'system.reboot()' 让运行时客户端重建索引(本文真实踩到,见「六、常见问题」Q2)。

文件清单两种形态。HTTP GET /.fs 返回全量清单 JSON(同步引擎用):

curl -s -H 'Authorization: Bearer sbtoken-q1w2e3r4-demo' http://127.0.0.1:8147/.fs | head -c 400

实测回显(节选):

[{"name":"CONFIG.md","created":1793170712549,"lastModified":1793170712549,"contentType":"text/markdown","size":345,"perm":"rw"},{"name":"Manual/Deploy-Notes.md","created":1793169886273,"lastModified":1793169886273,"contentType":"text/markdown","size":69,"perm":"rw"},{"name":"index.md","created":1793169886178,"lastModified":1793169886178,"contentType":"text/markdown","size":1485,"perm":"rw"},{"name

sb fs ls 则返回 entries 包裹、按目录折叠的形态,且能看到服务端内置的只读标准库页面(perm:"ro",宿主磁盘上并不存在,属服务虚拟提供):

/opt/silverbullet/bin/sb --url http://127.0.0.1:8147 --token sbtoken-q1w2e3r4-demo fs ls

实测回显:

{"entries":[{"path":"CONFIG.md","type":"file","size":345,"created":1793170712549,"lastModified":1793170712549,"contentType":"text/markdown","perm":"rw"},{"path":"Library/","type":"directory"},{"path":"Manual/","type":"directory"},{"path":"Repositories/","type":"directory"},{"path":"index.md","type":"file","size":1485,"created":1793169886178,"lastModified":1793169886178,"contentType":"text/markdown","perm":"rw"}],"truncated":false}
/opt/silverbullet/bin/sb --url http://127.0.0.1:8147 --token sbtoken-q1w2e3r4-demo fs ls Library

实测回显:

{"entries":[{"path":"Library/Std.md","type":"file","size":2597,"created":1791786089000,"lastModified":1791786089000,"contentType":"text/markdown","perm":"ro"},{"path":"Library/Std/","type":"directory"}],"truncated":false}

单文件元数据(revision 即 ETag,条件写要用):

/opt/silverbullet/bin/sb --url http://127.0.0.1:8147 --token sbtoken-q1w2e3r4-demo fs stat index.md

实测回显:

{"path":"index.md","revision":"\"sha256:3784e9ac690660c556b6abf420c35fde0fd2bbb19330355d7d88151699f20bd4\"","created":1793169886178,"lastModified":1793169886178,"size":1485,"contentType":"text/markdown","perm":"rw"}

4.2 silverbullet 服务端二进制

单文件自包含服务端,支持 Linux(x86_64/arm64)、macOS(Intel/Apple Silicon)、Windows 64 位、FreeBSD。基础用法是「二进制 + 数据目录」:

mkdir data && ./silverbullet data
  • 空目录启动 → 进入多空间安装向导(v2 默认形态);指向已有页面的目录 → 直接作为经典单空间服务。实测(空目录 + 指定端口与监听地址):
cd /opt/silverbullet
./bin/silverbullet -p 8129 -L 127.0.0.1 wizard-demo

实测回显(启动日志,路径已按本文服务器布局归一):

INFO silverbullet::server: SilverBullet 2.12.1-0-g4d5eac53-2026-10-12T06-21-29Z
INFO silverbullet::server: Data folder: /opt/silverbullet/wizard-demo
INFO silverbullet::boot: boot mode: unconfigured (empty folder, no flags, no legacy env)
INFO silverbullet::boot: SilverBullet setup wizard running: http://localhost:8129/.setup/

浏览器探测确认向导重定向(unconfigured 模式下连 /.ping 都 307 进向导):

curl -s -o /dev/null -w 'GET /.ping -> %{http_code}\n' http://127.0.0.1:8129/.ping
curl -s -o /dev/null -w 'GET / -> %{http_code} redirect=%{redirect_url}\n' http://127.0.0.1:8129/

实测回显:

GET /.ping -> 307
GET / -> 307 redirect=http://127.0.0.1:8129/.setup/

启动参数就两个常用的:-p <端口>(默认 3000)、-L <地址>(默认 127.0.0.1,局域网可达要 -L 0.0.0.0)。自升级(改的就是二进制本体):

./silverbullet upgrade        # 升到最新稳定版
./silverbullet upgrade-edge   # 升到 main 分支构建

⚠️ 注意:端口被占时报错很直白——failed to listen on 127.0.0.1:8151: Address in use (os error 98)(本文实测换端口即解)。升级后浏览器要连续慢速刷新两次才能激活新客户端。

4.3 sb CLI 子命令

sb 是面向运行中服务的客户端工具(不启服务),支持三种认证:Token(Authorization: Bearer,配单空间 SB_AUTH_TOKEN 或多空间账号令牌)、Password(走 POST /.auth 换会话)、None。连接参数用全局 flag(--url/--token/--space,也可 sb space add 保存命名连接)。v2.12.1 实际子命令清单(sb --help 实测):

space         Manage saved space connections
version       Print the version
fs            Read and modify remote space files without the Runtime API
eval          Evaluate a Lua expression
script        Run a Lua script (from arg, --file, or stdin)
query         Run a SLIQ query
describe      Describe query types / a tag's schema
logs          Show server console logs
screenshot    Capture a PNG screenshot of the runtime client
upgrade       Upgrade to the latest release
upgrade-edge  Upgrade to the edge release

以下命令统一用 SB="sb --url http://127.0.0.1:8147 --token sbtoken-q1w2e3r4-demo" 简写(本文为省篇幅写作 sb …)。

fs:远程文件(不依赖 Runtime API)

子命令说明
sb fs ls [path]子项列表;支持 --recursive、可重复 --glob、--limit
sb fs read <path>原样输出到 stdout(二进制附件重定向保存);支持 --lines A:B --number
sb fs stat <path>元数据与 revision
sb fs write <path>从 stdin 或 --file 写入;必须显式选写策略 --create/--if-match <REV>/--overwrite
sb fs edit <path>精确文本替换;支持 JSON 批量、--all、--dry-run
sb fs rm <path>删除单个文件

写/改/删三连实测:

printf '# 测试页\n\n由 sb fs write 创建。\n' | sb fs write 'Scratch/SbTest.md' --create
sb fs edit 'Scratch/SbTest.md' --old '由 sb fs write 创建' --new '由 sb fs edit 改过'
sb fs read 'Scratch/SbTest.md'
sb fs rm 'Scratch/SbTest.md'
sb fs ls Scratch

实测回显(依次):

{"path":"Scratch/SbTest.md","revision":"\"sha256:c50f8cb062d1e65a586bf5d0e5855ee8134e3e8f1abee3c82071a7522b087fac\"","created":1793171155876,"lastModified":1793171155876,"size":39,"contentType":"text/markdown","perm":"rw","operation":"write"}
{"path":"Scratch/SbTest.md","operation":"edit","replacements":1,"changed":true,"dryRun":false,"baseRevision":"\"sha256:c50f8cb062d1e65a586bf5d0e5855ee8134e3e8f1abee3c82071a7522b087fac\"","revision":"\"sha256:480be8f073623f0b1b1343f2fc87336e8e65697a64a33d8bd7862ac075097f92\""}
# 测试页

由 sb fs edit 改过。
{"path":"Scratch/SbTest.md","operation":"delete"}
{"error":{"code":"not_found","message":"File or directory not found","path":"Scratch"}}

edit 的回显里 baseRevision → revision 形成链:它内置了乐观并发控制,替换基于你看到的版本进行。

eval / script:服务端 Lua(依赖 Runtime API,默认镜像内置 Chromium 已具备;首次调用要等无头客户端起来,数秒内属正常):

sb eval '1 + 1'

实测回显:

2

运维最有价值的一个 eval——让服务端重新加载 Space Lua/配置/索引:

sb eval 'system.reboot()'

query:SLIQ 查询(同样走服务端 Runtime API,详见 4.5 实操)。

describe:标签 schema。CONFIG.md 里用 tag.define 定义过 schema 的标签可查(普通 frontmatter 标签没有 schema,会报 Unknown tag,见「六、常见问题」Q4):

sb describe glossary

实测回显(本文 CONFIG.md 中定义的 glossary schema):

{"type":"object","properties":{"description":{"type":"string"}},"required":["description"]}

logs:服务端控制台日志(含无头浏览器客户端日志):

sb logs -n 3

实测回显(节选):

2026-10-28T07:09:23Z [Log] [Client] Now loading space scripts, custom styles and rebuilding editor state
2026-10-28T07:09:23Z [Log] [Client] [Lua] System mode rw
2026-10-28T07:09:23Z [Log] [Client] [Lua] Loaded 50 scripts in 111ms (slowest: Library/Std/Infrastructure/Builtin Tags@95 21.8ms, Library/Std/Config@741 10.7ms, Library/Std/Config@15642 9.5ms)

screenshot:给运行时客户端拍 PNG(支持 --full-page/--selector),做页面截图归档很方便。space:保存命名连接(sb space add <url> --auth token,凭据 AES-256-GCM 加密落盘)。

⚠️ 注意:官方文档还收录了 sb search/sb related 子命令,但 v2.12.1 稳定版 CLI 尚未包含(实测 error: unrecognized subcommand 'search'),对应的服务端 /.search/* JSON API 在该构建同样不可用(实测见「六、常见问题」Q1)——文档领先于稳定构建,属版本差,等待升级即可。

4.4 HTTP API curl 实测

端点前缀约定:.fs 文件、.auth 认证、.config 客户端配置、.shell 服务端 Shell RPC、.accounts 账号列表、.ping 健康检查。客户端请求统一带 X-Sync-Mode: true 头区分「API 调用」与「浏览器导航」(不带时部分 GET 会被重定向到 UI 页面)。

条件写(乐观并发)——PUT/DELETE 支持 If-Match(内容哈希匹配才写)与 If-None-Match: *(仅当不存在才建),预检失败返回 412 而不是静默覆盖。三连实测:

# ① create-only 新建:成功,响应头给 ETag
printf '# 条件写\n' | curl -s -X PUT -H 'Authorization: Bearer sbtoken-q1w2e3r4-demo' \
  -H 'If-None-Match: *' --data-binary @- http://127.0.0.1:8147/.fs/Scratch/CondTest.md -D - -o /dev/null | grep -iE '^(HTTP|etag)'

# ② 重复 create-only:412
printf 'x\n' | curl -s -X PUT -H 'Authorization: Bearer sbtoken-q1w2e3r4-demo' \
  -H 'If-None-Match: *' --data-binary @- http://127.0.0.1:8147/.fs/Scratch/CondTest.md -w 'HTTP %{http_code}\n' -o /dev/null

# ③ 错误 If-Match:412,正文 Precondition Failed
printf 'y\n' | curl -s -X PUT -H 'Authorization: Bearer sbtoken-q1w2e3r4-demo' \
  -H 'If-Match: "sha256:deadbeef"' --data-binary @- http://127.0.0.1:8147/.fs/Scratch/CondTest.md -w 'HTTP %{http_code}\n'

实测回显(依次):

HTTP/1.1 200 OK
etag: "sha256:cb9f2c3bb6adf2e6defcb49828a19edc6cfab643ffbaee7451004f4f35dd180d"
HTTP 412
Precondition FailedHTTP 412

脚本写页面的安全姿势:先 If-None-Match: * 建,后改用 stat 拿到的 ETag 作 If-Match——并发写冲突时拿到 412 而不是丢数据。

元数据读取——X-Get-Meta: true 请求头让服务端省略正文、只给头(批量校验文件新鲜度用):

curl -s -H 'Authorization: Bearer sbtoken-q1w2e3r4-demo' -H 'X-Get-Meta: true' \
  -D - http://127.0.0.1:8147/.fs/Manual/Deploy-Notes.md | grep -iE '^(HTTP|x-|content-length)'

实测回显:

HTTP/1.1 200 OK
x-created: 1793169886273
x-last-modified: 1793169886273
x-content-length: 69
x-permission: rw
content-length: 0

服务端 Shell RPC——POST /.shell 在服务器上执行命令(认证必须开启;请求体字段是 cmd 与 args,字段名踩坑见「六、常见问题」Q3):

curl -s -X POST -H 'Authorization: Bearer sbtoken-q1w2e3r4-demo' \
  -H 'Content-Type: application/json' \
  -d '{"cmd":"echo","args":["hi-via-cmd-args"]}' http://127.0.0.1:8147/.shell

实测回显:

{"stdout":"hi-via-cmd-args\n","stderr":"","code":0}

账号列表——单空间模式不维护账号体系,返回调用者自身占位:

curl -s -H 'Authorization: Bearer sbtoken-q1w2e3r4-demo' http://127.0.0.1:8147/.accounts

实测回显:

[{"username":null,"me":true}]

其余端点(/.auth 登录/锁定行为、/.config 配置读取)分别在部署篇、配置篇已实测,本文不重复。

4.5 查询与模板实操

准备数据——两本带 frontmatter 标签的书(tags: [book] + rating 属性):

printf -- '---\ntags:\n- book\nrating: 5\n---\n# Alpha 架构笔记\n\nISBN 1234-5678。\n' | sb fs write 'Books/Alpha.md' --create
printf -- '---\ntags:\n- book\nrating: 3\n---\n# Beta 运维手册\n\nISBN 8901-2345。\n' | sb fs write 'Books/Beta.md' --create
sb eval 'system.reboot()' && sleep 6   # 外部新建页面后重建索引(Q2)

SLIQ 查询三连(sb query,--json 输出):

# ① 全部书名
sb query 'from tags.book select name' --json

# ② 过滤 + 排序:rating >= 4 按评分降序
sb query 'from t = tags.book where t.rating >= 4 order by t.rating desc select t.name' --json

# ③ 模板渲染:select 里直接套标准模板
sb query 'from p = index.pages() where p.name:startsWith("Books/") order by p.lastModified desc limit 5 select templates.pageItem(p)' --json

实测回显(依次):

["Books/Alpha","Books/Beta"]
["Books/Alpha"]
["* [[Books/Beta]]\n","* [[Books/Alpha]]\n"]

查询③证明了 select 子句可直接套 templates.pageItem(渲染成 * [[页面名]] 列表项)——这是官方推荐的集合渲染模式。

页面里的查询块——页面正文写 ${query[[…]]},浏览器打开时由客户端渲染成表格/列表:

printf '# Dashboard\n\n最近书籍:\n\n${query[[\n  from t = tags.book\n  order by t.rating desc\n  select templates.pageItem(t)\n]]}\n' | sb fs write 'Dashboard.md' --overwrite
sb fs read 'Dashboard.md'

实测回显(curl/sb 看到的是原始 Markdown 文本,查询块由客户端渲染):

# Dashboard

最近书籍:

${query[[
  from t = tags.book
  order by t.rating desc
  select templates.pageItem(t)
]]}

模板体系三层(官方 Template 文档口径):

层机制用途
内联模板template.new[==[…]==] 字符串 + ${expression} 占位Space Lua 里拼装动态内容
标准模板templates.pageItem/taskItem/itemItem/paragraphItem/tagItem/fullPageItem查询结果渲染(本文实测 pageItem)
页面模板页面打 #meta/template/page 标签新建页面的蓝图;打 #meta/template/slash 的页面定义斜杠命令

页面模板创建实测(打标签即成模板,无需注册):

printf -- '---\ntags:\n- meta/template/page\n---\n# 会议纪要模板\n\n## 参会人\n\n## 决议\n' | sb fs write 'Templates/Meeting.md' --create
sb fs read 'Templates/Meeting.md' | head -5

实测回显:

---
tags:
- meta/template/page
---
# 会议纪要模板

4.6 多端访问与并发

同一实例天然支持多端并发,本文全程实际并用了四类客户端:浏览器(会话 Cookie)、curl 脚本(Bearer 令牌)、sb CLI(Token)、宿主进程直改文件。它们操作的是同一份文件数据,所见即所得(4.1 的宿主直改附录行就是多端并发证据)。要点:

  • 认证三通道:Web 走 Cookie(POST /.auth),脚本走 Authorization: Bearer,sb 两者都支持——互不影响,令牌不过期烦恼为零;
  • 写冲突防护:条件写(4.4)保证多端同时写同一页面时不静默丢数据,冲突方拿 412 重读重写即可;
  • 变更推送:默认 SB_FS_WATCH=auto 下,外部程序改文件会在数秒内推送到已打开的浏览器客户端;NFS/SMB 场景改 poll 模式(配置篇 5.2);
  • 外部新建页面的索引延迟:运行时查询客户端不感知纯文件层新增,需 sb eval 'system.reboot()'(Q2 实录)。

4.7 备份与恢复

备份 = 打包数据目录。页面、配置页、认证文件全在其中;唯一建议排除的是 .chrome-data/(运行时无头浏览器的用户档案,纯缓存、重建实例自动再生)。实测对比:

cd /opt/silverbullet
du -sh data
tar -czf sb-backup-full.tar.gz -C /opt/silverbullet data
tar -czf sb-backup-exclude-chrome.tar.gz -C /opt/silverbullet --exclude='.chrome-data' data
ls -la sb-backup-full.tar.gz sb-backup-exclude-chrome.tar.gz | awk '{print $5, $9}'

实测回显:

3.9M	data
1939 sb-backup-exclude-chrome.tar.gz
1229483 sb-backup-full.tar.gz

排除 .chrome-data 后备份从约 1.2 MB 缩到 2 KB(本文 space 很小;页面多时差距更大)。全量备份的内容清单实测(节选,tar -tzf):

data/
data/Books/Beta.md
data/Books/Alpha.md
data/Scratch/CondTest.md
data/Dashboard.md
data/CONFIG.md
data/.silverbullet.auth.json
data/Manual/Deploy-Notes.md
data/index.md
data/Templates/Meeting.md
data/.chrome-data/…

恢复 = 解包 + 起服务。用排除版备份恢复到一个全新实例(容器 IP 直连、不占宿主端口)验证闭环:

mkdir -p /opt/silverbullet/restore-test && \
  tar -xzf sb-backup-exclude-chrome.tar.gz -C /opt/silverbullet/restore-test --strip-components=1
docker run -d --name silverbullet-2.12.1-restore-test \
  -v /opt/silverbullet/restore-test:/data \
  -e SB_FOLDER=/data -e SB_USER=admin:q1w2e3r4 -e SB_AUTH_TOKEN=sbtoken-restore-test \
  ghcr.io/silverbulletmd/silverbullet:2.12.1

读页与查询双验证:

RIP=$(docker inspect silverbullet-2.12.1-restore-test --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}')
curl -s -H 'Authorization: Bearer sbtoken-restore-test' "http://$RIP:3000/.fs/Books/Alpha.md" -w "\nHTTP %{http_code}\n"
sb --url "http://$RIP:3000" --token sbtoken-restore-test query 'from tags.book order by rating desc select name' --json

实测回显:

---
tags:
- book
rating: 5
---
# Alpha 架构笔记

ISBN 1234-5678。

HTTP 200
["Books/Alpha","Books/Beta"]

页面原样恢复、frontmatter 属性完好、查询索引重建后照常可用。认证文件 .silverbullet.auth.json 随备份走:恢复出的实例沿用原账号口令;若要换密码,改 SB_USER 环境变量重建实例即可(部署篇 4.5 的说明)。定时备份加一行 cron 即可:

# crontab -e:每天 02:30 备份(保留 14 天轮转请配合 find -mtime +14 -delete)
30 2 * * * tar -czf /opt/backup/sb-$(date +\%F).tar.gz -C /opt/silverbullet --exclude='.chrome-data' data

五、注意事项

  1. .chrome-data/ 在数据目录里:运行时 API 的无头浏览器档案随 space 落盘,备份/同步/rsync 时排除(4.7 有量化对比);
  2. Library/、Repositories/ 是虚拟只读页:服务端内置标准库(perm:"ro"),能读能查但磁盘上不存在,别在备份脚本里等它落盘,也别想改它——自定义要写进自己的页面或 CONFIG.md;
  3. 外部新建页面后查询为空:文件层与运行时索引两层可见性(Q2 实录),脚本批量导页后统一 sb eval 'system.reboot()';
  4. 条件写是并发安全的钥匙:多端/多脚本写同一页面务必带 If-Match,否则靠「最后写入赢」;
  5. 文件系统约束:大小写敏感的本地文件系统(ext4/xfs);NFS/SMB 需 poll 模式且官方不推荐;macOS/Windows 目录不要直接当 space;
  6. 升级节奏:CLI 与服务端是两支独立二进制,各自 upgrade;升级后浏览器连刷两次。

六、常见问题

Q1:sb search 子命令不存在 / POST /.search/query 返回 405? 版本差:官方文档已收录搜索 API(/.search/status、/.search/query、/.search/related)与 sb search/sb related 子命令,但 v2.12.1 稳定构建尚未包含。本文实测:sb search '部署手记' 报 error: unrecognized subcommand 'search';POST /.search/query 返回 405 Method Not Allowed(allow: GET,HEAD,GET 则落到 UI 壳页面)。2.12.1 上的替代:浏览器内搜索框(客户端索引)或 sb query 精确查;需要服务端全文搜索就升级到包含该特性的新构建。

Q2:sb query 明明页面已写入,查询却返回 {}? 外部程序(脚本/宿主直写)新建页面后,运行时查询客户端的索引没有感知——本文实测新建 Books 两页后三个查询全部返回 {}。执行 sb eval 'system.reboot()'(等数秒重建)后同样查询返回 ["Books/Alpha","Books/Beta"]。浏览器客户端不受影响(它自己维护同步)。

Q3:POST /.shell 一直报错? 请求体字段名是 cmd(字符串命令)+ args(参数数组)。本文踩坑实录:{"command":"echo","args":[…]} 报 Invalid JSON: missing field 'cmd';{"cmd":["echo","x"]} 报 expected a string;{"cmd":"echo hello"} 把整串当可执行文件名报 No such file or directory。正确形态 {"cmd":"echo","args":["hi"]} 才返回 {"stdout":"hi\n","stderr":"","code":0}。另外该端点等于把服务器 shell 交给令牌持有者,令牌务必保密。

Q4:sb describe <tag> 报 Unknown tag? describe 查的是 tag.define 注册的 schema,不是 frontmatter 里随手打的标签。实测:describe book(未定义 schema)→ Error: Unknown tag: book;describe glossary(CONFIG.md 里定义过)→ 返回 schema JSON。要给 book 配 schema,在 CONFIG.md 里 tag.define { name = "book", schema = { … } }。

Q5:二进制启动报 Address in use (os error 98)? 端口被占。本文实录:8151 被其他服务占用时启动失败于 failed to listen on 127.0.0.1:8151: Address in use (os error 98)。ss -ltn | grep :<port> 先查,换 -p 端口即可。

Q6:GET /.fs/… 不带头时返回的是 HTML 页面而不是文件/JSON? 这就是 X-Sync-Mode: true 头的用途:不带它,服务端把请求当浏览器导航处理,返回编辑器 UI 壳(实测 /.search/status 等路径)。curl 调 API 时养成带头习惯。

七、参考链接