name: using-silverbullet description: Manages notes in SilverBullet/SB via the CLI and Space Lua. Use when the user mentions notes, SilverBullet, pages, journal, tasks in their space, or asks to search/read/write/edit notes. license: GPL-3.0-or-later metadata: author: Amolith amolith@secluded.site
SilverBullet is a self-hosted Markdown note-taking app with a Lua scripting layer. Do not write top-level # Titles; SilverBullet puts the page title at the top of the page and Title\n\n# Title is redundant.
Prefer the high-level CLI
sb talks to a running SilverBullet instance through the Runtime API. Prefer get, describe, and query before dropping into Lua. Use sb <command> -h for full flags and edge cases.
Most useful commands:
sb space ls,sb space add,sb space rm <name>β manage saved space connections. Use-s <name>to select a space when more than one is configured.sb getβ list indexed tag names in the space.sb describe [tag]β show query syntax and live schemas, e.g.sb describe pageorsb describe task.sb get <tag> [ref]β list indexed objects or fetch one object by ref. For pages, the ref is the page name.sb query '<sliq-expression>'β run Space Lua Integrated Query; this wraps the argument inquery[[...]].sb eval '<expression>'β evaluate one Space Lua expression and print its return value.sb script [code],sb script -f file.lua, or stdin β run multi-statement Space Lua. The positional argument is inline code, not a filename.sb logs,sb logs -n 20,sb logs -fβ inspect or follow headless client logs.
Use --json for parseable output, -o jsonl for line-oriented pipelines, and --text for human-readable output. --text is an output format, not βpage textβ.
Common operations
# List recent page metadata
sb get page --sort-by lastModified:desc --limit 10 --select name,lastModified
# Find pages by name/prefix metadata
sb get page --where name:contains=meeting --select name,lastModified
sb get page --where name:startsWith=Journal/ --select name,lastModified
# Fetch one page metadata object. This does NOT return the Markdown body.
sb get page "Journal/2026-06-07" --json
# Read the Markdown body of a page
sb eval 'space.readPage("Journal/2026-06-07")'
# Full-text search; returns matches with excerpts, scores, and offsets
sb eval 'silversearch.search("search terms", {silent=true})'
# Tasks: prefer get filters for routine list/filter/sort work
sb get task --where done=false --sort-by priority:desc --limit 20
sb get task --where due:lte=2026-06-30 --where done=false
# Query when get cannot express the relationship
sb query 'from l = index.tag "link" where l.toPage == "TargetPage" select l.fromPage'
sb query 'from p = index.tag "page" order by p.lastModified desc limit 10 select p.name'
# Write/create only when asked to change the space
sb eval 'space.writePage("PageName", "Content without a duplicate top heading")'
# Delete only when explicitly asked
sb eval 'space.deletePage("PageName")'
sb get supports --where, -l/--selector, --sort-by, --limit, --offset, and --select; run sb get -h rather than memorising every filter operator.
Editing pages safely
string.gsub treats the search string as a Lua pattern, not plain text. Characters like -, ., and % are magic and can silently break replacements. Use a helper that returns only the replacement string:
local function plainReplace(str, old, new, n)
old = old:gsub("[%^%$%(%)%%%.%[%]%*%+%-%?]", "%%%0")
new = new:gsub("%%", "%%%%")
local replaced
if n == nil then
replaced = str:gsub(old, new)
else
replaced = str:gsub(old, new, n)
end
return replaced
end
Use sb script for multi-step edits:
sb script <<'EOF'
local function plainReplace(str, old, new, n)
old = old:gsub("[%^%$%(%)%%%.%[%]%*%+%-%?]", "%%%0")
new = new:gsub("%%", "%%%%")
local replaced
if n == nil then
replaced = str:gsub(old, new)
else
replaced = str:gsub(old, new, n)
end
return replaced
end
local text = space.readPage("PageName")
text = plainReplace(text, "old literal text", "new literal text", 1)
space.writePage("PageName", text)
return "done"
EOF
For multi-line Markdown literals, prefer [==[...]==] over [[...]] so wiki links and templates do not terminate the string accidentally. If the content itself contains ]==], use a normal quoted string with \n escapes or edit existing content with plainReplace.
Space Lua tripwires
- Space Lua APIs are camelCase:
space.readPage, notspace.read_page. - Use
return;print()output is swallowed. - Do not chain
:gsub()calls.gsubreturns(string, count), so reassign each step. - Do not use
queryas a variable or parameter name; it can parse as SLIQ syntax. - The standard Lua
utf8library is unavailable. - In SLIQ, use explicit binding:
from p = .... - Use current command names:
evalandscript. Oldlua/lua-scriptaliases are hidden compatibility shims.
SilverBullet docs
The official docs space serves raw Markdown pages. Fetch docs directly when you need detail on Space Lua, SLIQ, or the CLI:
curl -fsSL 'https://silverbullet.md/.fs/CLI.md'
curl -fsSL 'https://silverbullet.md/.fs/Space%20Lua.md'
URL-encode page names (%20 for spaces, %2F for /). If sb is unavailable, read https://silverbullet.md/.fs/CLI.md yourself and link the user to https://silverbullet.md/CLI.