---
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 page` or `sb 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 in `query[[...]]`.
- `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

```bash
# 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:

```lua
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:

```bash
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`, not `space.read_page`.
- Use `return`; `print()` output is swallowed.
- Do not chain `:gsub()` calls. `gsub` returns `(string, count)`, so reassign each step.
- Do not use `query` as a variable or parameter name; it can parse as SLIQ syntax.
- The standard Lua `utf8` library is unavailable.
- In SLIQ, use explicit binding: `from p = ...`.
- Use current command names: `eval` and `script`. Old `lua`/`lua-script` aliases 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:

```bash
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`.
