### ADR Important [[Architecture]] decisions for SilverBullet are captured as [Architecture Decision Records](https://adr.github.io/) (template: [[^Library/Page Templates/ADR]]). See [[Health]] for records due for review. # Stats ${query[[ from p = index.pages("adr") group by p.status order by key select { Status = key, Count = #group } ]]} # Active decisions ${query[[ from p = index.pages("adr") where p.status ~= "superseded" and p.status ~= "deprecated" order by p.status, p.name select { ADR = "[[" .. p.name .. "]]", Owner = p.owner, Decided = p.date } ]]} # Superseded & deprecated ${query[[ from p = index.contentPages("adr") where p.status == "superseded" or p.status == "deprecated" order by p.name select { Status = p.status, ADR = "[[" .. p.name .. "]]", ["Superseded by"] = p.supersededBy, Owner = p.owner, Decided = p.date } ]]} # Connections Dynamically generated from ADR defined relationships. Dotted edges mark supersession. Click a node to open the ADR. ${mermaid.diagram(mermaid.relationGraph { pages = query[[from index.pages("adr")]], direction = "TD", })} # Decision history ${mermaid.diagram(mermaid.timeline{ pages = query[[from index.pages("adr")]], title = "ADR decision history" })} # By status ${mermaid.diagram(mermaid.distribution{ pages = query[[from index.pages("adr")]], by = "status", title = "ADRs by status" })} --- ### API --- references: - client/space_lua/stdlib.ts - client/plugos/syscalls/index.ts - plug-api/syscalls.ts --- This describes the APIs available in [[Space Lua]]: # Lua Standard Library * [[API/global]] * [[API/math]] * [[API/os]] * [[API/string]] * [[API/table]] # Space Lua APIs * [[API/command]] * [[API/dom]] * [[API/encoding]] * [[API/http]] * [[API/js]] * [[API/jsonschema]] * [[API/mq]] * [[API/net]] * [[API/slashCommand]] * [[API/spacelua]] * [[API/syntax]] * [[API/tag]] * [[API/taskState]] * [[API/template]] * [[API/widget]] # Syscall APIs * [[API/asset]] * [[API/clientStore]] * [[API/codeWidget]] * [[API/config]] * [[API/datastore]] * [[API/editor]] * [[API/event]] * [[API/index]] * [[API/language]] * [[API/lua]] * [[API/markdown]] * [[API/service]] * [[API/shell]] * [[API/space]] * [[API/sync]] * [[API/system]] * [[API/yaml]] --- ### Aggregator Pages --- description: A thin page whose body is primarily drived through queries that collects items from across your space. tags: glossary guide --- An aggregator page is a thin page whose body is primarily drived through queries that collects items from across your space. Instead of hand-maintaining a list of all open questions, all ADRs, or all team members, you tag each item on its natural home page and let a single query page assemble the collection automatically. Add an item anywhere in your space, tag it, and the aggregator picks it up automatically. # The pattern Three moving parts combine to make an aggregator: **1. A tag** you apply to items on their natural home page, for example, `#open-question` on any page that tracks an open question. **2. An aggregator page** whose body is a single [[Space Lua/Integrated Query|SLIQ]] query that pulls everything carrying that tag: ```lua ${query[[ from t = index.contentPages("open-question") order by t.name select templates.pageItem(t) ]]} ``` The query lives inside `${...}` and renders as a live widget in [[Live Preview]]. Because the prose body explains the pattern, the page retains value even outside SilverBullet. **3. A `tagPage` mapping** in [[CONFIG]] so clicking the tag in the editor jumps straight to the aggregator overview: ```lua tag.define { name = "open-question", tagPage = "Open Questions", } ``` See [[API/tag#tag.define(spec)]] for the full list of options `tag.define` accepts. # Why aggregators beat hand-maintained lists - **Zero maintenance** — to add an item, tag it on its home page; the aggregator updates itself on the next index cycle. - **No drift** — the list is always exactly what the index says it is; typos or deletions surface instantly. - **Single source of truth** — the item's data lives once, on the item's own page; the aggregator is just a view. # Recipe To set up a new collection: 1. **Pick a tag** — choose a short, lowercase, hyphenated name (`meeting-note`, `open-question`, `decision`). 2. **Write the aggregator page** — create a page (e.g. `Open Questions`) whose body is the SLIQ query above, substituting your tag. Use `index.contentPages("tag")` to filter out [[Meta Page|Meta Pages]]; use `index.objects("tag")` if you also want items from meta pages. 3. **Register `tagPage` in [[CONFIG]]** — add a `tag.define` block so clicking the tag navigates to your aggregator. Place `tag.define` calls in `CONFIG` so they run during the index phase. 4. **Link the overview from your index or catalog page** — add a `[[Open Questions]]` link wherever people would naturally look for the collection. # Examples in this manual This docs space already uses the pattern for two collections: - **[[ADR]]** — aggregates all pages tagged `adr`; the `tag.define` in [[CONFIG]] maps `tagPage = "ADR"`. - **[[Architecture]]** — aggregates all pages tagged `component`; mapped via `tagPage = "Architecture"` in [[CONFIG]]. Open either page to see a live query-driven aggregator with no hand-maintained list. --- ### Anything Picker --- references: - client/components/anything_picker.tsx - plugs/editor/complete.ts --- The anything picker can be used to open any page, including [[Meta Page]] and pages hidden with [[Page Decorations]]. Otherwise it’s equivalent to the [[Page Picker]]. To open, run the command ${widgets.commandButton("Navigate: Anything Picker")} using this button or [[Command Palette]]. Alternatively cycle from any other Picker by typing `^`. --- ### Architecture #development This page describes the big-picture view of SilverBullet, assembled from its [[#Components]]. Each component has its own page describing how it relates to the others, the diagram below is generated on-the-fly from the meta data in those pages. # Top-level Architecture ```mermaid flowchart TB subgraph n1 ["Client"] n3("Datastore") click n3 call __sbNav("Architecture/Datastore") n4("Editor") click n4 call __sbNav("Architecture/Editor") n5("Events") click n5 call __sbNav("Architecture/Events") n7("Plugs") click n7 call __sbNav("Architecture/Plugs") n11("Services") click n11 call __sbNav("Architecture/Services") n13("Space Lua") click n13 call __sbNav("Architecture/Space Lua") n15("Syscalls") click n15 call __sbNav("Architecture/Syscalls") end subgraph n9 ["Server"] n6("File System API") click n6 call __sbNav("Architecture/File System API") n8("Runtime Manager") click n8 call __sbNav("Architecture/Runtime Manager") n12("Space Files") click n12 call __sbNav("Architecture/Space Files") end subgraph n10 ["Service Worker"] n2("Client Bundle Cache") click n2 call __sbNav("Architecture/Client Bundle Cache") n14("Synced Files") click n14 call __sbNav("Architecture/Synced Files") end n1 -->|"connectsTo"| n10 n4 -->|"connectsTo"| n15 n6 -->|"consumes"| n12 n7 -->|"connectsTo"| n15 n8 -->|"consumes"| n12 n10 -->|"connectsTo"| n9 n13 -->|"connectsTo"| n15 n15 -->|"connectsTo"| n11 n15 -->|"connectsTo"| n5 n15 -->|"connectsTo"| n3 ``` # The three layers * [[Architecture/Client]]: one instance per browser tab; runs 90%+ of the logic ([[Architecture/Editor|editor]], [[Architecture/Space Lua|Space Lua]], [[Architecture/Plugs|plugs]], [[Architecture/Syscalls|syscalls]], [[Architecture/Datastore|datastore]]). * [[Architecture/Service Worker]]: one instance per browser; offline cache + [[Sync]]. * [[Architecture/Server]]: — authentication, serving the client, and the file [[HTTP API]], otherwise a dumb file store. # Components Every box in the diagram is a page tagged `component`: * [[Architecture/Client]] * [[Architecture/Client Bundle Cache]] * [[Architecture/Datastore]] * [[Architecture/Editor]] * [[Architecture/Events]] * [[Architecture/File System API]] * [[Architecture/Plugs]] * [[Architecture/Runtime Manager]] * [[Architecture/Server]] * [[Architecture/Service Worker]] * [[Architecture/Services]] * [[Architecture/Space Files]] * [[Architecture/Space Lua]] * [[Architecture/Synced Files]] * [[Architecture/Syscalls]] --- ### Aspiring Pages --- description: An aspiring page is a page that does not yet exist, but is already linked to. tags: glossary references: - plugs/index/relation.ts --- An aspiring page is a [[Page|page]] that does not yet exist, but is already linked to. Aspiring pages appear in the [[Page Picker]] (with a `Create page` hint) as well as in auto complete when creating [[Link]]. # Finding dangling links Every `[[link]]` to a non-existent page produces an `aspiring-page` object in the [[Object Index]], so you can query them to audit broken or forward-references: ${query[[ from t = index.aspiringPages() where not string.startsWith(t.page, "Library/") select { target = t.name, linkedFrom = t.page } ]]} In this query, `t.name` is the link **target** (the page that does not yet exist); `t.page` is **where the link lives** (the source page). Filter on `t.page` to scope results to your own content. Triage guidance: - **Real typo**: fix the link on the source page. - **Intentional placeholder**: you mean to write the page eventually, then leave it. Aspiring pages double as a "to-write" backlog and appear in the page picker as a reminder. - **Library/meta target**: not yours to fix, those links are maintained by the library. --- ### Attribute --- description: A key-value pair attached to a page or object, used for querying and filtering. tags: glossary references: - client/codemirror/attribute.ts - plugs/index/attribute.ts --- Attribute syntax can contribute additional [[Metadata]] to various [[Object|Objects]], including: * Pages (although the preferred way is [[Frontmatter]]) * Items (bulleted and numbered list items) * [[Task|Tasks]] * Paragraphs ## Syntax The syntax is as follows: ``` [attributeName: value] ``` For Obsidian/LogSeq compatibility, you can also double the colon like this: `[attributeName:: value]` Attribute names need to be alpha-numeric. Values are interpreted as [[YAML]] values. So here are some examples of valid attribute definitions: * string: [attribute1: sup] * number: [attribute2: 10] * array: [attribute3: [sup, yo]] Multiple attributes can be attached to a single entity, e.g. like so: * Some item [attribute1: sup][attribute2: 22] ## Scope Depending on where these attributes appear, they attach to different things. For instance, this attaches an attribute to a page: [pageAttribute: hello] However, usually, [[Frontmatter]] is used for this purpose instead. Example query: ${query[[ from index.pages() where _.name == editor.getCurrentPage() select {name=_.name, pageAttribute=_.pageAttribute} ]]} This attaches an attribute to an item: * Item [itemAttribute: hello] #specialitem Example query: ${query[[ from index.items("specialitem") where itemAttribute == "hello" select { name = _.name, itemAttribute = _.itemAttribute } ]]} This attaches an attribute to a task: * [ ] Task with an attribute, I’m so cool [taskAttribute: hello] Example query: ${query[[ from index.tasks() where taskAttribute == "hello" select { name = _.name, taskAttribute = _.taskAttribute } ]]} --- ### Authelia How to set up SilverBullet with Authelia For SilverBullet to work as intended, some files will need to be excluded from your authentication method of choice. These files always need to be accessible, for example for offline or [[PWA]] support. The files are the following: - The web manifest - The app icon - The service worker These files can be whitelisted by adjusting your Authelia configuration to something like this: ```yaml access_control: default_policy: deny rules: - domain: silverbullet.yourdomain.com resources: - '/\.client/.*$' - '/service_worker\.js$' policy: bypass - domain: yourdomain.com policy: two_factor ``` Please adjust this to fit your specific needs. The important part is that the files are associated with `policy: bypass`. --- ### Authentication Proxy In case you would like to run SilverBullet behind an authentication proxy (such as Authelia, Authentik or the ones integrated with Cloudflare Zero Trust or Pangolin) there is one key configuration tweak you need to make: **You must exclude a few paths from authentication**. If you don’t do this a lot of PWA functionality may not work and SilverBullet may break in unexpected ways. Doing this is perfectly safe, it just gives browsers unauthorized access to some client code, not any of your content. Add **rules to allow unauthenticated access** for the following paths: * `/service_worker.js` * `/.client/*` This is perfectly safe, they are just static (code) files. --- ### Authentication --- tags: getting-started references: - bin/silverbullet/src/config.rs - bin/silverbullet/src/server.rs - server/src/multi/users.rs - server/src/multi/access.rs --- How you authenticate depends on how the server is running (see [[Space Manager#Boot modes]]): * **Accounts (the default).** A fresh install manages people through named accounts in `users.json` and controls who can reach each space. This is the recommended setup — see [[#Accounts]]. * **Single-space mode.** One folder served as one space, authenticated by a single set of environment-variable credentials — see [[#Single-space mode]]. * **No authentication.** A single-space server with no credentials set is open to anyone who can reach it. # Accounts When the server runs in the default [[Space Manager|multi-space]] mode, authentication is account-based: * Every person has an **account** (username + password). * Each [[Space]] is either **public** (no login) or requires login, and lists the **members** allowed in. Admins can reach every space and the admin UI. * Accounts, spaces, and access are all managed in the `/.spaces` surface, which every account can open (admins additionally get the Users tab and space create/edit screens). * When no space is bound to `/`, the server root provides an account-facing index of the spaces available to the current user. # Single-space mode [[Space Manager#Single-space mode|Single-space mode]] serves one folder as one space, authenticated the classic way: a single set of credentials set via the `SB_USER` environment variable in `username:password` form. ## Enabling authentication Set `SB_USER` when starting the server. For the [[Install/Binary]]: ```shell SB_USER=pete:1234 ./silverbullet my-space ``` For [[Install/Docker]]: ```shell docker run -e SB_USER=pete:1234 ... ``` This allows `pete` to log in with password `1234`. When authentication is enabled, SilverBullet shows a login page on first access. # API For programmatic access via the [[HTTP API]], you can use bearer token authentication. In single-space mode, this token is configured with an environment variable, see [[Install/Configuration]]. In multi-space mode, new API tokens can be issued via the [[Space Manager]] UI. # Authentication proxies Alternatively, or in addition, you can use an [[Authentication Proxy]] to delegate authentication to an external system (like Authelia, Authentik, or a reverse proxy's built-in auth). This is common in more complex self-hosted setups. In accounts mode, pair a proxy with **public** spaces so the proxy owns identity; in single-space mode, put the proxy in front of an open server. For all authentication-related configuration options, see [[Install/Configuration#Authentication]]. --- ### Baked Sections --- references: - client/baked_sections/bake.ts - client/baked_sections/regions.ts --- A baked section is a piece of dynamic content whose rendered output has been written *into* the page as plain [[Markdown]], wrapped in HTML comments so SilverBullet can keep it up to date while other tools just see the result. # Why SilverBullet’s most powerful features (`${...}` [[Space Lua]] expressions, [[Space Lua/Integrated Query|queries]], and [[API/widget|widgets]]) are computed live in the editor. That is wonderful inside SilverBullet, but the underlying [[Markdown]] only contains the *source* of the computation. Open such a page on GitHub, in another markdown editor, and instead of a neat table you get to see the raw expressions. This limits your options in further processing markdown (e.g. using some publish tool) or to keep it tool independent. # How baking works A baked section keeps the expression *and* its rendered output side by side, using HTML comments as delimiters: | name | | ---- | | Baked Sections | | Publishing | The two `` markers are ordinary HTML comments, so **every markdown renderer ignores them** — GitHub, Obsidian, a plain viewer. What they render is the body in between: a real markdown table. Inside SilverBullet the markers are shown as subtle, grayed-out comments, and the expression in the opening marker is the source of truth used to refresh the body. In short: the page renders correctly *everywhere*, and stays editable and re-runnable inside SilverBullet. # Using baked sections There are three actions: - **Bake button** — block `${...}` widgets (tables, lists, diagrams) show a _Bake_ button in their hover toolbar, next to Copy. Click it to turn that live directive into a baked section in place. - `Baked Sections: Update` (`Ctrl-Shift-b` / `Cmd-Shift-b`): re-evaluates *every* baked section on the current page and rewrites each body with its latest output. Run this whenever the underlying data changed and you want the baked output refreshed. - `Baked Sections: Unbake Section At Cursor` — put your cursor inside a baked section and run this to turn it back into a live `${...}` directive. Handy when you want to edit the expression with [[Live Preview]] again, bake it back when you’re done. Baking is **static and manual**: a baked section shows the output from the last time it was baked. It does not refresh by itself, re-run `Baked Sections: Update` to bring it current. # Who is this for? * **Sharing or publishing outside SilverBullet** ([[Share]], [[Export]]) — bake your dynamic pages so a published site, a GitHub repo, or a static export shows real tables and content instead of directive source. * **Docs-as-code / shared repositories** — when teammates browse `.md` files on GitHub or in another editor, baked sections render natively for them, while you keep the live query in SilverBullet. If your content never leaves SilverBullet, you don’t need baking, live `${...}` directives are simpler and less noisy. Baking is for when the *markdown itself* has to look right somewhere else or processed further. # Good to know * **Block-level only.** Baked sections wrap block content (a table, a list, a [[API/widget|widget]] such as a Mermaid diagram) on their own lines. Inline values in the middle of a sentence aren’t baked. * **Needs a markdown renderer.** Anything with a markdown form bakes cleanly (values, query tables, widgets that expose markdown — including diagrams that emit a fenced ` ```mermaid ` blocks). A widget that can *only* render HTML has nothing portable to write, so it’s skipped (and the Bake button doesn’t appear on it). * **The markers are just comments.** If a baked body somehow contains the literal closing marker, it’s automatically escaped so updating stays reliable. --- ### Best Practices #guide Let’s be clear upfront: your space is your own, you can structure it however you like. That said, over the years a few “best practices” of how to structure your space have emerged. If you don’t have strong preferences coming in, consider following them. # Flat name space While SilverBullet will dynamically create [[Folder|Folders]] when you use slashes (`/`) in your page names, the typical SilverBullet user tends to use this feature lightly. Organization is achieved through incrementally adding [[Frontmatter]] and [[Markdown/Hashtags]], rather than investing in a very structured folder structure upfront. This is also why, by default, SilverBullet does not ship with the classic file tree — it would be very boring to look at. The advantage of this approach is multi-fold: 1. Reduces “decision fatigue”: you create a new page, where should you put it? Answer: top level. 2. Whereas a page can only ever live in a single folder, it can be tagged with an unlimited number of hashtags. This gives more flexibility. As a result, this means that most content pages tend to live at the “top level” (see this space as an example). A few exceptions: * `Inbox/` for quick notes * `Journal/` for journal entries * `Library/` for [[Library|libraries and customizations]] These make sense to split out, and keep contained in their own folders, away from other areas of your space. # Frontmatter and tags When adding a [[Frontmatter]] section to a page, it becomes cleaner to move any [[Markdown/Hashtags]] you previously put in your page to the `tags` attribute: ```yaml --- role: Data analyst tags: sometag anothertag --- ``` # Page conventions * **Title-Case page names**: name a page as you’d write its title in prose: `Customer Persona`, not `customer-persona`. See [[Names]] for additional rules and hard constraints. * **No top-of-page H1**: the page name is already the title, don’t restate it as an `# H1`. * **First line is a one-sentence summary**: the opening body line defines or summarises the page, this is what link previews and catalogs quote. * **Absolute wiki links**: links are paths from the space root: `[[Folder/Page]]`. This way, links stay valid no matter where the linking page lives or moves. See [[Link]]. # Querying your space SilverBullet's [[Object Index]] is the engine behind every live query: * Prefer `index.contentPages()` over `tags.page` for page lists and lint sweeps — it filters out [[Meta Page|Meta Pages]] (pages tagged `meta` or `meta/*`) so Library and configuration pages do not pollute your results. See [[Object Index]]. * The index is **asynchronous** — after editing a page, expect a few seconds before queries reflect the change. Re-run before drawing conclusions. See [[Object Index]]. * For cross-cutting collections (all open questions, all ADRs), use the **aggregator page + `tagPage`** pattern instead of hand-maintained lists. See [[Aggregator Pages]]. * Find broken or forward-references via the `aspiring-page` tag. See [[Aspiring Pages]]. # Authoring Space Lua When developing on a `space-lua` based feature, follow the edit, reload, check (browser) logs, verify loop: edit a script, run `System: Reload` (Ctrl-Alt-r) to re-execute all definitions, then **check the console logs**. A successful reload does not mean the script is healthy, Lua errors surface in logs, not always as a visible reload failure. For a programmatic “reboot to ready” call that also drains the index queue, see [[API/system#system.reboot()]]. --- ### Browser SilverBullet is a web application. You access it via any modern browser, and it leverages a lot of recent technological advances built into modern browsers, such as service workers (for offline capabilities), IndexedDB (for storing and querying data) and cryptography APIs. _Because_ it is a browser-based web application, you can access it from any device with a modern web browser, this includes desktops, laptops, phones and tablets. _While_ it’s a browser-based web application, many browsers (such as Chromium-based and Safari-based ones) allow you to install it as a [[PWA]], giving it an icon on your home screen, or icon in your dock, and removing any unnecessary browser Chrome, making it visually indistinguishable from a “native” desktop app: ![[pwa-screenshot.png]] --- ### CHANGELOG An attempt at documenting the changes/new features introduced in each release. ## Edge Whenever a commit is pushed to the `main` branch, within ~5 minutes, it will be released as a docker image with the `:v2` tag, and a binary in the [edge release](https://github.com/silverbulletmd/silverbullet/releases/tag/edge). If you want to live on the bleeding edge of SilverBullet goodness (or regression) this is where to do it. * **Live external edits**: changes made to pages by other programs (or users, but don’t rely on this for real-time collaboration) now show up in an open pages almost instantly, instead of waiting for the next sync. The client applies them to the open page as minimal, cursor-preserving edits land in the undo history, so `Cmd/Ctrl-z` reverts an external edit like any other. * This relies on active file-system, watching which can be configured server-wide with the new `SB_FS_WATCH` environment variable (`auto` (default) / `poll` / `off`): set `poll` when the space lives on a network mount (NFS/SMB) where changes made may not result in FS events. * **Inline comments** ([[Comment]]): any HTML comment (``) is now a parsed and rendered as a note. * [[Space Manager|Multi-space]] mode: the [[Runtime API]] (`runtimeApi`) is now **on by default** for new and existing spaces, instead of off. It only actually runs when the server found a Chrome or Chromium install at startup and only booted upon first use of the API. * [[Space Manager]]: saving an edited space now returns to the space list, instead of staying on the edit form with a brief “✓ Saved” confirmation. Creating a space still lands on the new space's own screen. * Fix: the [[Runtime API]]’s headless Chrome crashed and restarted every few seconds on the `-runtime-api` docker image, spamming the server log (and the host’s console with core dumps) and leaving the API only intermittently available. Chromium 150 fails to initialize its GPU stack inside a container, which was fatal to the whole browser because it was launched with `--in-process-gpu`. ([#2078](https://github.com/silverbulletmd/silverbullet/issues/2078)) * Fix: the docker image ignored `PUID`/`PGID` and space folder ownership, running as `root` and creating root-owned files * Fix: the `:latest` docker tag pointed at the ~1.2GB Chromium-bearing `-runtime-api` image instead of the ~90MB base image ([#1994](https://github.com/silverbulletmd/silverbullet/issues/1994)) * Building from source is a little cheaper: the `zip` dependency (used only to unpack our own release archives during self-upgrade) no longer pulls in the zstd, lzma, bzip2 and xz decoders. That drops 18 crates — three of which compile bundled C — for ~25 CPU-seconds off a clean build, and three fewer things to keep working across the cross-compile targets. * Fix: the FreeBSD **server** binary is being built and released again * Fix: [[Space Manager|multi-space]] mode silently ignored `SB_REMEMBER_ME_HOURS`, `SB_LOCKOUT_TIME`, and `SB_LOCKOUT_LIMIT`, hardcoding “remember me” sessions to 7 days and lockout to 10 attempts per minute. All three now apply there too — server-wide, like the session itself — matching what [[Install/Configuration]] documents. * Multi-space servers now share a single headless Chrome across all spaces instead of launching one browser per space. * Fix: the Runtime API failed to start when authentication was enabled. * Fixes around casing in page/file names: * Renaming a page or folder to a different casing of the same name now works on case-insensitive filesystems (macOS, Windows) * Renames are now rejected when the new name differs only in casing from an existing page or document, so spaces stay portable between case-sensitive and case-insensitive hosts. * On case-insensitive filesystems, writing a file whose folder differs only in casing from an existing one now re-cases that folder to match — so writing `notes/foo` when the disk holds `Notes/` renames the folder, changing the reported path of every page inside it. ## 2.10.0 * [[Space Manager]]: multi-space hosting with multiple accounts is here. A fresh install pointed at an empty folder opens a browser-based first-run **setup wizard** that creates an admin account and your first space, then serves it in place with no restart. One server can host any number of [[Space|spaces]], each bound to a URL prefix or hostname. * [[Baked Sections]]: bake `${...}` Lua expressions and widgets into HTML-comment-delimited markdown (`` … ``). * Space Lua: **code complete now shows documentation** (where available), all available via [[API/spacelua]] reflection APIs. * Backend and CLI have been ported to Rust ([see background on this](https://no.silverbullet.plus/tech-stacks)), both should be behavior preserving (that is: you shouldn’t really notice): * The server backend (previously written in Go) has now been replaced by an adapted version of [SilverBullet+](https://silverbullet.plus/)’s backend written in Rust, more unifying those code bases. * CLI client reimplemented/back-ported to Rust as well. * This means the project is now all TypeScript + Rust. * [[Frontmatter]] in the editor now has configurable folding: by default long frontmatter blocks fold automatically, and `frontmatterFolding` options let you disable auto-folding, always fold frontmatter, or change the line threshold. A subtle right-side marker folds or unfolds the whole block, and folded frontmatter previews any `tags` value as tag chips. This is configurable via the [[Configuration Manager]] as well. * [[Page Picker]]: type `$` to switch to [[Markdown/Anchor|anchor]] navigation * Pulling the "this was experimental card" for the CLI: removed the `sb get` command and the `/.runtime/objects/*` REST API, including their dedicated client-side query bridge. Use `sb query`, `sb eval`, or `sb script` for indexed-object access. This added too much complexity and another query language. * Fix: `SB_SHELL_BACKEND=local` disabled shell command execution instead of enabling it. * Fix: a `range` (or `pos`) attribute in your own content could overwrite the source offsets an indexed object uses to point back at its place in the page. * Fix: only the first entry of a frontmatter list of wiki links formed a [[Object/relation]] (e.g. an `authors:` list of `[[...]]` items), so every entry after the first was missing from [[Object Graph]], linked mentions and rename refactoring. * Fix: writing to a read-only path (anything served from the bundled library, a `SB_READ_ONLY` server) returned a 500, which clients could not tell apart from a temporary server fault — so a syncing client retried it forever. Read-only refusals now return 403, and the sync engine records the path and stops re-attempting it until the local file changes. This most often bit spaces holding a stale copy of a `Library/Std` page that a later release had dropped from the bundle. * Fix: major typing/navigation slowdown on pages with many internal links in large spaces. * Fix: the service worker precached client assets *through* the browser's HTTP cache, so a stale client could be copied into its cache and then served as though it were the current build — leaving a "A new version of SilverBullet client is available." notification that no reload could clear (only a hard reload, which bypasses the service worker, showed the real client; the next normal reload brought the notification back). Precaching now bypasses the HTTP cache. * The server now sets `Cache-Control` on client assets, which matters if you run SilverBullet behind a CDN. * Lua: Space Lua comments are now parsed and retained in the AST instead of being stripped before parsing (part of the enabler for code complete with documentation). * Fix: first-ever load of an authenticated space no longer shows a spurious "Could not process config and no cached copy, please connect to the Internet" alert before redirecting to the login page (the login redirect aborted the remaining boot requests, which were misread as being offline). * Fix: frontmatter link live preview now follows the editor's regular markdown preview behavior: raw YAML syntax stays visible when markdown syntax rendering is enabled, and only the link currently being edited is revealed in clean mode. * Fix: write-mode commands (those requiring read-write, e.g. the baking commands) are now hidden in the command palette and their keybindings disabled on **per-page** read-only pages (`perm: ro`), not just in fully read-only spaces. * Runtime API: the embedded headless-Chrome runtime now logs its lifecycle (when it launches on first use, when it becomes ready, and on crash/restart), and forwards the headless page‘s `console.*` output to the server log by default (disable with `SB_CHROME_LOG_CONSOLE=0` see [[Install/Configuration]]). * New [[API/codeWidget]] Lua API: register a renderer for a fenced code block language from Lua (e.g. ` ``` /* Detailed source-code truncated for AI context efficiency. */ ``` ` fences. * Fix: [[Page Template]]s are now fixed (required specifying of `suggestedName` before), now also supports objects as frontmatter * Removed: tapping the top bar (page name / action buttons area) no longer scrolls the editor to the top. * Fix: page decoration prefixes now consistently appear in query-driven lists * Fix: Safari/WebKit no longer renders block lua widget (queries, etc.) borders at the wrong position on first paint (previously required hovering or resizing the window to clean up). * Fix: page navigation should now glitch less. * Fix: browser back/forward now restores the scroll position you last had on each page. ## 2.7.0 * [[Configuration Manager]]: new UI for editing configuration, accessed via the `Configuration: Open` command (`Ctrl/Cmd-,`) and `Configuration: Key Bindings` commands. This is a work in progress, but should already be a big improvement over the old ways. It currently supports: * Changing (common) configuration options * Key binding management (oh my!) * A Library manager, superseding the old Library Manager UI (which now has been removed) * [[Runtime API|CLI]] renamed from `silverbullet-cli` to `sb`, in addition: * renamed `lua` → `eval` * `luascript` → `script` * a new `describe` command that describes SLIQ and lists tags with defined schemas. * New (experimental) [[Markdown/Anchor]] syntax (`$name`): a stable, space-globally unique name for (almost) any [[Object]], referenced from links via `[[$name]]`. * Rebrand: “Lua Integrated Query” (LIQ) is now called [[Space Lua/Integrated Query|Space Lua Integrated Query]] (_SLIQ!_) (as coined by Matouš Jan Fialka) * API extensions for [[API/config]]: `config.define` now propagates schema `default` values. New `config.defineCategory` / `config.getCategories` APIs, plus UI annotations for the configuration manager. The `ui.order` schema annotation and `config.defineCategory`'s `order` field have been renamed to `priority` and now sort *descending* (higher = appears earlier), matching the rest of SilverBullet's `priority` conventions. * Server no longer generates a default `CONFIG.md` in empty spaces, this page is now auto created by the configuration manager when required. * The legacy `plug-manager` has now been removed (superseded by the Library manager part of the Configuration Manager UI) * [[Plugs/Development]] (now with new docs!) gains an optional `build:` section in manifests, running `esbuild`, `sass`, or `copy` transforms before asset bundling — enables plugs to ship bundled TSX/SCSS UIs. * Keyboard shortcut for `Navigate: Home` changed to `Ctrl-Shift-h`/`Cmd-Shift-h` * Action buttons: new `command` attribute for `actionButton.define`. When using this instead of a `run` callback, keyboard bindings will appear in the tooltip. * Docker: removed `VOLUME` declaration from the Dockerfile (it gave a false sense of persistence `/space` must be explicitly mounted, as documented). This also fixed the silverbullet-website repo. * Fix: [[Sync]] now falls through to local data on browser-native network errors instead of returning 503; previously synced spaces serve locally immediately after a service worker restart. * Fix: navigation no longer blocks while the initial index is still running. * Fix: rich text paste only worked on the second try * Fix: indexing blew up with malformed bullet list items * Fix: regression where aspiring pages were not deleted once the page was created. * Fix: auto complete of meta pages was broken * Fix: page rename failed when the page contains external URL links. * Fix: too-tall mini editor in various pickers on Safari. * Security fix: auth cookies now set stricter security flags (HttpOnly, Secure, SameSite); auth config file corruption no longer fails silently. * Potentially **breaking** CSS change for theme authors: `.sb-notifications` has moved in the DOM (notifications now portal to `document.body`). * New [[API/system]] syscalls `system.loadPlug` / `system.unloadPlug` for per-path plug (re)loading. * New [[API/editor]] syscall `editor.focus` for explicitly focusing the editor. * Configuration Manager: Key Bindings tab now says "Filter commands" instead of "Search commands". * More sensible fallback values for config options before the initial index has populated defaults. * Lint: the `name` attribute uniqueness check is now limited to `#meta/library` pages. * [[Runtime API]]: better debug output when the headless Chrome instance fails to boot. * Fix: more robust markdown tree traversal in the face of invalid markdown trees. * Fix: [outline operation edge cases](https://github.com/silverbulletmd/silverbullet/issues/1936). * Fix: button text wrapping. * Fix: Runtime API fixed for users using PUID and PGUID users (by [Luminiferous348](https://github.com/Luminiferous348)). * Fix: symlinks inside the space directory are no longer accidentally removed when cleaning up empty parent directories after a file delete. * Fix: slash commands now resolve the syntax node ending at the cursor, so they no longer get incorrectly suppressed adjacent to comment blocks or links. * Fix: [Service Worker is now built without `import` statements](https://github.com/silverbulletmd/silverbullet/pull/1949) so it loads on Firefox versions before 147 (by [Carlos Fdez. Llamas](https://github.com/sirikon)). ## 2.6.1 * **Technical: Deno → Node.js migration**: The TypeScript/client codebase has been migrated from Deno to Node.js, now using vitest for tests. * Bundle size optimization: chunked builds with ESBuild, JIT loading of larger modules (vim, syntax modes). * **[[Runtime API]]** and accompanying [[CLI]] (==Experimental==): programmatically interact with a (remote) SilverBullet server over via `silverbullet-cli` or a [[Runtime API|HTTP API]]: evaluate Lua expressions, run scripts, and retrieve console logs. Powered by a headless Chrome instance running the full SilverBullet client via CDP, so all results reflect live client state. * New query shortcuts: `index.contentPages()` (pages excluding meta pages) and `index.metaPages()` (only meta pages) for convenient querying without manual tag filtering. * [[Outlines]] commands have been thoroughly reworked. Should now be more robust and better tested outline move/indent operations. New features: * Now also works with numbered items (and renumbers them) * Now works with headers (moves around entire sections) * Now works with paragraphs * When ending an list item with a `:`, and pressing _Enter_, the next item will be indented one level * Markdown support enhancements: * [[Markdown/Footnotes]]: both reference-style (`[^1]`) and inline (`^[text]`) footnotes with syntax highlighting, live preview on hover, reference completion, and invalid reference linting. * [[Live Preview]] for HTML tags * [Custom markdown syntax extensions](https://github.com/silverbulletmd/silverbullet/pull/1881) (==Experimental==): define custom inline syntax via [[API/syntax]] that gets parsed, highlighted, and rendered in live preview. * [[Space Lua]] enhancements: * Performance: Lua interpreter hot-path optimizations, tree traversal and page index optimizations. * Performance: `LuaTable` internals tuned for faster Lua execution. * [[Space Lua/Integrated Query]] improvements (courtesy of [Matouš Jan Fialka](https://github.com/mjf)): * [Unified field list syntax](https://github.com/silverbulletmd/silverbullet/pull/1909) for `from`, `select`, and `group by` clauses, enabling multi-source cross-joins * [Implicit single group](https://github.com/silverbulletmd/silverbullet/pull/1907) for aggregates without `group by` * `offset` clause support * Intra-aggregate `order by` support * [13 new aggregate functions](https://github.com/silverbulletmd/silverbullet/pull/1891) (`product`, `string_agg`, `yaml_agg`, `json_agg`, `bit_and`, `bit_or`, `bit_xor`, `bool_and`, `bool_or`, `stddev_pop`, `stddev_samp`, `var_pop`, `var_samp`), `aggregate.alias` API for custom aliases, and `index.aggregates` queryable collection * Client upgrade notification: if the server is updated but the client version doesn't match, a notification will appear instructing the user to reload. * The [[^Library/Std/Widgets/Widgets#Table of contents]] widget is now **collapsible**, defaults to open (by [Dobli](https://github.com/Dobli)). * [Improved Lua widget rendering](https://github.com/silverbulletmd/silverbullet/pull/1876) (by [Matouš Jan Fialka](https://github.com/mjf)): `${...}` expressions now render scalars, arrays, records, and arrays-of-tables with better HTML and markdown output. * [[Task]] `Task: Clean Completed` now handles more scenarios, and leaves a cleaner outline. * [Panels now use Shadow DOM elements instead of iframes](https://github.com/silverbulletmd/silverbullet/pull/1819) (by [onespaceman](https://github.com/onespaceman)). * `editor.flashNotification` now supports an optional third argument with `timeout` (use `0` for persistent notifications) and `actions` (buttons with callbacks). * Fix: [table cell alignment for missing and misaligned cells](https://github.com/silverbulletmd/silverbullet/pull/1873) * Fix: [handle tagged floats before plain object check in `renderCellContent`](https://github.com/silverbulletmd/silverbullet/pull/1876) (by [Matouš Jan Fialka](https://github.com/mjf)). * Fix: document file opening with URL prefix. * Fix: autofocus on authentication page. * Fix: mini editor regressions. * Mobile: "lock" button to toggle read-only, useful for navigating without accidentally editing. * Action Button enhancements: * `standalone` option: only show certain action buttons in standalone/PWA mode (e.g. forward/back navigation buttons) * Support for opting out action buttons from the mobile dropdown menu * Fix: "No such header #boot" errors in PWA mode. * Fix: Edit buttons now work correctly for Lua expressions and code widgets whose bodies appear multiple times in the same page. * Fix: [attribute rendering](https://github.com/silverbulletmd/silverbullet/pull/1880). * Fix: [Markdown table rendering](https://github.com/silverbulletmd/silverbullet/pull/1879) and removal of deprecated command button remnants. * [[Task]] improvements (by [Matouš Jan Fialka](https://github.com/mjf)): * [Dropdown picker for custom task states](https://github.com/silverbulletmd/silverbullet/pull/1900) with autocomplete and per-state CSS styling via `data-task-state` attribute * [Improved task widget](https://github.com/silverbulletmd/silverbullet/pull/1905): toggle dropdown on re-click, narrowed decoration range for better cursor behavior * [[API/shell#shell.run(cmd, args, stdin?)]]: `shell.run` now accepts an optional `stdin` parameter (by [Brett Anthoine](https://github.com/banthoine)). * Sync: further edge case fixes (timestamp/content-length mismatch, service worker activation). * Subtle **breaking** change: `template.each` now returns an empty string on empty results instead of `nil`. * Fix: only `#meta` and `#meta/` prefixed tags are now considered meta tags, not e.g. `#metabase`. * Fix: TOC rendering when headers contain a numbered list item. * Fix: edge case where the markdown link regex could go into infinite loop (links with escaped square brackets). * Fix: unwrap multi-return values in PropertyAccess and method calls (by [Brett Anthoine](https://github.com/banthoine)). * Fix: allow colons in `SB_USER` password (by [Joshua Brunner](https://github.com/joshuabrunner)). * Fix: mobile tasks caret jumping and empty widgets on navigation. * Mobile: home button moved outside of the dropdown menu; hamburger menu style tweaks. * Fix: extended task state widget click behavior and rendering in widgets. * Fix: safer handling of default template files in non-empty spaces. * Frontmatter `tags:` key now has autocomplete support. * [[API/tag#tag.define(spec)]]: new `tagPage` attribute to customize where clicking a tag navigates to (defaults to `tag:name`). * Fix: 32-bit ARM Docker builds. * Fix: reduce visual bouncing when navigating between pages. * Fix: encode URLs with dots in path names on WebKit/Safari (fixes opening e.g. PDF files). * [[Vim]] mode: [allow passing arguments to vim ex commands](https://github.com/silverbulletmd/silverbullet/pull/1924) (by [Felix Riedel](https://github.com/felixr)). * Tweaked default `index` and `CONFIG` pages for new installations. * Fix: tag stripping for page templates. * Fix: widget glitching — widget HTML content is no longer cached (only heights), reducing storage usage and WebKit rendering issues. ## 2.5.0 * Changed keyboard bindings (sorry!). CodeMirror no longer directly allows `Alt-` and `Alt-` [[Keyboard Shortcuts]], meaning I had to **remap a few key bindings**. It’s basically a mission impossible to pick great ones, but here are the new defaults: * `Quick note` is now bound to both `Ctrl-q q` (type `Ctrl-q` first, then hit `q` again) and `Ctrl-q Ctrl-q` (hit `Ctrl-q` twice) * `Navigate: Home` is now bound to `Ctrl-g h` * `Text: Marker` is now bound to `Ctrl-Alt-m` * [[Outlines]] commands generally now use a `Mod-.` (`Cmd-.` on mac, `Ctrl-.` on Linux/Windows) prefix: * `Outline: Move Right`: `Mod-. l` * `Outline: Move Left`: `Mod-. h` * `Outline: Move Up`: `Alt-ArrowUp` still works, but now also adds `Mod-. k` for consistency * `Outline: Move Down`: `Alt-ArrowDow` still works, but now also adds `Mod-. j` for consistency * `Outline: Toggle Fold`: `Mod-. Mod-.` * `Outline: * Fold` (other fold commands): keyboard disabled, readd yourself if you need them (see [[Keyboard Shortcuts]]) * `Task: Cycle State`: `Mod-. t` * `Page: Rename` keyboard shortcut removed * `Page: Rename Linked Page` keyboard shortcut removed * `Sync: Space` keyboard shortcut removed * As documented in [[Keyboard Shortcuts]], it is now possible to specify _multiple_ keyboard shortcuts to a commands. * [[Sync]] reliability work: * Better indication whether your page is synced to the server: “Dirty state” (slightly tinted color of page name) is now aligned with actual synced-to-server state _unless_ the editor clearly indicates it is in offline mode (yellow top bar). * Sync snapshots are now persisted after every file sync, reducing (and hopefully eliminating) edge cases where the sync engine is killed mid-sync (for whatever reason) and the snapshot becomes of sync with “reality”. * The index status progress indicator (blue circle) should now be more reliably reflect the actual indexing status. * HTTP status codes >= 500 are now treated as offline (better offline detection). * [[Space Lua/Integrated Query]] improvements (courtesy of [Matouš Jan Fialka](https://github.com/mjf)): * [[Space Lua/Integrated Query/Grouping|group by]] and `having` clauses with [[Space Lua/Integrated Query/Aggregating|aggregator]] support * `filter(where )` clause for per-row aggregate filtering * `nulls first`/`nulls last` in `order by` * Null/missing query cells now render as empty * [[Space Lua]] engine general improvements (most courtesey of [Matouš Jan Fialka](https://github.com/mjf)): * [Native Lua pattern matching engine](https://github.com/silverbulletmd/silverbullet/pull/1838) (replacing previous implementation) * [Support for `` attribute and __close metamethod](https://github.com/silverbulletmd/silverbullet/commit/9419cdcd9be61908330e1dce68a9156dbb911d23) * [Better arithmetic error messages](https://github.com/silverbulletmd/silverbullet/commit/5a20a5f8f476a98172609e80c799cd1d83765585) * [Refactor of control flow (performance)](https://github.com/silverbulletmd/silverbullet/commit/e5b4c8feb22a44cb4b22b3a77f9f2ed21dd09297) * [Improved numeric type semantics](https://github.com/silverbulletmd/silverbullet/pull/1803) * Implement `string.pack`, `string.unpack` and `string.packsize` * Implement `math.random`, `math.randomseed`, `math.tointeger`, `math.frexp` and `math.ldexp` * Implement `table.move`; align `table.pack` and `table.unpack` with Lua semantics * [[API/table#table.select(table, keys...)]] (non-standard in Lua) API, convenient to use in [[Space Lua/Integrated Query]] `select` clauses, see example in docs. * [Extend `os` module](https://github.com/silverbulletmd/silverbullet/pull/1836) * Add `_VERSION` environment variable * `tostring()` now respects `__tostring` metamethod; `#` operator now respects `__len` metamethod * Fix: `table.sort` comparator, `string.gsub` table replacement, `math.modf` return types, number formatting in `..` and `table.concat` * **Load order** of scripts is now well defined: `order by (script.priority or 0) desc, script.ref` * New _experimental_ API: [[API/tag#tag.define(spec)]], see linked page for docs and example uses. Brings back ability to define 📅 deadlines for tasks (see example). Another part of this is [[Schema]] support for [[Tag|tags]]. When a schema is defined for a tag, you get: * [[Frontmatter]] **attribute completion and linting** (in-editor error indicators) for attributes defined as part of the tag’s schema. * [[Space Lua/Integrated Query]] **attribute code completion** _if_ you use the `from v = index.tag(“bla”)` style syntax (so explicitly bind your iterator variable). * Item-level linting (highlights the object in-line in case of validation errors). * Tag schema updates: * `pos` (present in link, item and some other tags) is now _deprecated_, use `range` instead * `range` is a tuple of two numbers: _from_ and _to_ (e.g. `{0, 10}`) identify where the object appears in the page * Editor improvements: * New `Page: Create Under Cursor` command, useful to pre-create an aspiring page link. Put your cursor in a wiki link to a non-existing page, and hit `Cmd-Shift-Enter` (`Ctrl-Shift-Enter`) to create it (empty) without navigating there. * [[Linked Mention|Linked Mentions]] now list full page path rather than abbreviated version. * Hide vertical scrollbar overflow for long page names. * Upload file: prompt user before replacing files and no-clobber behavior for paste uploads (by [Oliver Marriott](https://github.com/rktjmp)). * Trim user input from prompts where appropriate (by [rktjmp](https://github.com/rktjmp)). * Consider empty string as invalid path (by [rktjmp](https://github.com/rktjmp)). * Styling changes: * Attribute names and values ([key: value] notation) now get different CSS classes in the editor: `sb-attribute-name` for names and `sb-attribute-value` for values. * The `diff` [[Markdown/Fenced Code Block]] language now uses colors to indicate additions and removals (by [Lajos Papp](https://github.com/silverbulletmd/silverbullet/pull/1807)). * Configuration: * New `shortWikiLinks` config (defaulting to `true`) that decides whether a wiki link should be rendered in its short form (rendering just the last segment, e.g. `Person/John` would show as `John`). To always render the full name, put `config.set(“shortWikiLinks”, false)` in your [[CONFIG]]. * [[Authentication]]: how long “remember me” works is now configurable (by [Metin Yazici](https://github.com/silverbulletmd/silverbullet/pull/1796)) via [[Install/Configuration]] and more reliably persisted. * Library Manager: SilverBullet now navigates to library page after installing one. * Now excluding `.plug.js` and `.js.map` files from the document list. * Fix: bring back [[Virtual Pages]]. ## 2.4.0 * Indexer rework (note: upgrading will start a full space reindex automatically): * Performance: up to 2x faster * Internal refactor, actually adding at least (rudimentary) unit tests now (imagine!) * `item` and `task` now also index (wiki) links and inherited (wiki) links (links appearing in parent nodes), as [requested here](https://community.silverbullet.md/t/coming-from-logseq-outlines-and-linked-mentions/290) under `links` and `ilinks`. Updated the "Linked Tasks" widget now to rely on `ilinks`. * Rewrote snippet text for links (used in [[Linked Mention|Linked Mentions]]) to be more contextual, now also includes child bullet items, see [community discussion](https://community.silverbullet.md/t/coming-from-logseq-outlines-and-linked-mentions/290). * For consistency with items, `task` `refs` now point to the item’s position resulting in a slight positional shift, if you have code relying on this, you may have to adjust it. * Disabled indexing all paragraph text by default, this caused significant indexing overhead. [See discussion](https://community.silverbullet.md/t/who-is-using-paragraph-for-queries/3686). To re-enable: `config.set("index.paragraph.all", true)` * Better link support in frontmatter (by [Tomasz Gorochowik](https://github.com/silverbulletmd/silverbullet/pull/1711)) * The `page:index` event now also receives a `text` and `meta` attributes. * [[Transclusions]] improvements: * Now have an “eye” button to navigate to the transcluded location * Transclusions now only live preview when the cursor is outside of them (as with other pieces of markup) * Transclusions now properly support headers * Items and tasks are now transcluded with their children (based on `@pos` notation) (this is mostly helpful when used in queries) * Page/document/meta picker tweaks: * Upgraded the [Fuse.js](https://www.fusejs.io) library and tuned the ranking parameters, hopefully leading to better results. * Meta picker now more consistent with page picker * You can now use `Alt-space` to complete a folder matching the first result — try it and let me know how this works for you in practice. * **Built-in full-text search has been removed** from the main distribution, this has now been moved to [a separate repo](https://github.com/silverbulletmd/basic-search) (installable via the library manager). Rationale: full text indexing is expensive and the search results were quite bad. Recommendation: install [Silversearch](https://github.com/MrMugame/silversearch) as an alternative. * [[Task|Tasks]]: * `taskstate` objects are no more. Custom task states should now be defined using the [[API/taskState]] API. * **Removed:** deadline syntax (legacy syntax from v1) for tasks, please use attributes instead (e.g. `[deadline: "2026-01-01"]`). * New APIs: * [[API/space#space.readFileWithMeta(name)]] * [[API/space#space.readPageWithMeta(name)]] * [[API/space#space.readRef(ref)]] * [[API/taskState#taskState.define(def)]] (see “Tasks” above) * New commands: * `Navigate: Copy Ref To Current Position` * `Navigate: Copy Link To Current Position` * Lua: * [SLIQ fix](https://github.com/silverbulletmd/silverbullet/issues/1705) * [Ctrl-click](https://github.com/silverbulletmd/silverbullet/pull/1713) navigate to definition on non-Mac operating systems * Support for `` in Lua (by [Matouš Jan Fialka](https://github.com/silverbulletmd/silverbullet/pull/1715)) * Production builds now include sourcemaps for easier debugging in browser DevTools. If you don't want to serve sourcemaps publicly, you can block `*.js.map` files at your reverse proxy level (see [[TLS#Blocking sourcemaps]]). * Should now **deal better with authentication layers** (Cloudflare Zero Trust, Authelia, Pangolin) * [Sync errors](https://github.com/silverbulletmd/silverbullet/issues/1720) now propagate better to the UI * Document editors now fixed in Safari (by [MrMugame](https://github.com/silverbulletmd/silverbullet/pull/1710)) * `%` now supported in [page names](https://github.com/silverbulletmd/silverbullet/issues/1694) * Lua widgets “flapping” should now be less ## 2.3.0 This release (re)introduces [[Share]], formalizes [[Library]], and introduces in initial version of the Library Manager, a type of package manager for SilverBullet. It also progresses on Lua 5.4 compatibility. Here’s what’s new: * [[Share]]: a new mechanism to push content to external places and pull external content in (also used as the foundation of [[Library]]). This partially replaces many [[Export]] use cases. Export will be more for one-off use cases. * [[Library]]: are now a more “real” thing, and can be distributed via the Library Manager and curated with [[Repository]]. For instructions on how to build your own libraries, see [[Library/Development]]. Eventually, this mechanism will succeed the `plugs` configuration and `Plugs: Update` mechanism. Plug authors can already start to update their plugs to get ready, usually all that needs to be done is to add a `PLUG.md` file to their repository: [example](https://github.com/silverbulletmd/silverbullet-mermaid/blob/main/PLUG.md). * [[Service]]: a new mechanism used behind the scenes to power [[Share]], but also [[Export]] and likely other features in the future. Built on top of [[Event]]. * [[URI]] are now a more formalized and centralized mechanism, used by [[Share]] and likely other features in the future. * Removed “Import” support, succeeded by [[Share]]. * [[Tag Picker]]: to quickly navigate to tag pages * Space Lua improvements (courtesy of Matouš Jan Fialka): * Support for `goto` (yes, I said I’d never add it, but Matouš did anyway) * Significant [performance leaps](https://github.com/silverbulletmd/silverbullet/pull/1666) * Support [\t](https://github.com/silverbulletmd/silverbullet/pull/1698) in strings * More of an in-your-face error when you’re not using [[TLS]] and you should, with instructions how to fix it: even though using plain HTTP was never a supported configuration, it hard-broke in 2.2.1 * Plugs are now loaded from anywhere in the space, as long as they end with `.plug.js` (so no longer need to be in `_plug`, in fact all shipped core plugs are now mounted under `Library/Std/Plugs`) * Automatically follow system dark mode (by [Lelouch He](https://github.com/silverbulletmd/silverbullet/pull/1696)) * Fix Youtube embes (by [Rodolfo Souza](https://github.com/silverbulletmd/silverbullet/pull/1672)) Upgrade notes: * If you have third-party plugs installed and intend to reinstall them as Libraries: be sure to delete the old versions first. You can do so by cleaning out your `_plug` folder right on the file system, or use the document picker, filter on `plug.js` and delete every single document that’s in the `_plug` folder that way. * If you somehow end up in a state where SilverBullet doesn’t load properly, have a look at [[Troubleshooting]] for hints on what to try to fix it. ## 2.2.0 This is a dot release primarily because due to changes in how IndexedDB databases are named, a fully resync and reindex of your space will happen on all your devices. I’m sorry for the inconvenience, we try to limit how often this is required. If you’d like to clean up unnecessary databases afterwards you can run the `Client: Clean` command (once) afterwards. * [[Client Encryption]]: when using a untrusted device (e.g. a public computer), enable this option when logging in (only supported with built-in [[Authentication]]) to encrypt all locally stored data (at a performance penalty). * Lua fixes, making [[Space Lua]] more compatible with Lua 5.4 (most courtesy of of Matouš Jan Fialka): * [Fix length (`#` operator) features](https://github.com/silverbulletmd/silverbullet/pull/1637) * [Add `rawget` and `rawequal`](https://github.com/silverbulletmd/silverbullet/pull/1647) * [Allow `..` to also concatenate strings and numbers](https://github.com/silverbulletmd/silverbullet/pull/1648) * [Make truthiness more Lua compatible](https://github.com/silverbulletmd/silverbullet/pull/1644) * [Align arithmetic model with standard Lua](https://github.com/silverbulletmd/silverbullet/pull/1611) * [Add `huge` constant and `type` to `math.*` API](https://github.com/silverbulletmd/silverbullet/pull/1632) * [Add `load` function](https://github.com/silverbulletmd/silverbullet/pull/1631) * [Support %u in os.date](https://github.com/silverbulletmd/silverbullet/issues/1598) * [Pass on status code differently when using `http.request`](https://github.com/silverbulletmd/silverbullet/issues/1608) * More video embeds in standard library (courtesy of Andy Costanza): * [Vimeo](https://github.com/silverbulletmd/silverbullet/pull/1616) * [Peertube](https://github.com/silverbulletmd/silverbullet/pull/1612) * New `widget.htmlBlock` and `widget.markdownBlock` convenience APIs for creating block widgets (that take the full width of the screen rather than being inlined) * The [[^Library/Std/APIs/DOM]] API now supports embedded widgets and markdown * The markdown renderer now renders ${"`inline code`"} as a `code` tag with `.sb-code` class * Atomic upgrades with `silverbullet update` and `silverbullet update-edge` (by [Mihai Maruseac](https://github.com/silverbulletmd/silverbullet/pull/1634)) * Added `Client : Clean` command that deletes all redundant IndexedDB databases (ones the client is not using) * Very basic Prometheus metrics (see [[Install/Configuration#Metrics]]) * Fix: bottom search bar dark mode styling (by [numan](https://github.com/silverbulletmd/silverbullet/pull/1614)) * Fix: navigation with auto links (by [MrMugame](https://github.com/silverbulletmd/silverbullet/pull/1607)) * Fix: `SB_USER` now works with `SB_URL_PREFIX` ## 2.1.8 * New [[^Library/Std/APIs/Virtual Page]] API, internally used by: * [[^Library/Std/Infrastructure/Tag Page]] * Some fixes in `tonumber` handling * Default table renderer now renders `ref` attributes as links, so they’re clickable: ${query[[from index.tag "page" limit 3 select {ref=ref, lastModified=lastModified}]]} * Fix: render TOC correctly when header itself contains a link (by [Oleksandr Kuvshynov](https://github.com/silverbulletmd/silverbullet/pull/1597)) * Fix: read-only pages are now _never_ saved back to your space (could happen, e.g. with tasks on tag pages) * Fix: Table of Contents widget works again * Fix: Poor behavior when multiple pages are delete in sequence (by [Oleksandr Kuvshynov](https://github.com/silverbulletmd/silverbullet/pull/1599)) * Lua: * Setting a table value to `nil` now deletes it as a key, so it no longer appears in `table.keys` ## 2.1.7 * Restructure of the `Library/Std` library, added some more (self) documentation. See [[^Library/Std]] as an entry point. * Re-added `page:saved` event that was removed in 2.1 * When the clipboard API is used in Safari, will now give a proper error (Safari restriction) by ([Noah Stanley](https://github.com/silverbulletmd/silverbullet/pull/1575)) * Full text search results now show full page path * Space folders now support symlinks again (regression from 2.1) * Lua: fixes in arithmetic model (by [Matouš Jan Fialka](https://github.com/silverbulletmd/silverbullet/pull/1587)) * Lua: Removed unary plus from grammar (wasn’t actually supported) (by [Matouš Jan Fialka](https://github.com/silverbulletmd/silverbullet/pull/1585)) * Bugfix: auth and service worker caching fixes * Bugfix: docker health check failed when SB_URL_PREFIX was used * Bugfix: infinite item index loop in obscure cases ## 2.1.4 - 2.1.6 * Fixed broken auth in Safari * Renamed the inconstently named `index.search.enable` to `index.search.enabled` * Last opened (for pages) and last run (for commands) timestamps are now kept persistently between client loads (and tabs) * Fixed docker user and group creation issues * Removed `file:deleted` triggering when checking for `getFileMeta` (causing an infinite indexing loop in SilverSearch) * Server: HTTP Gzip compression is now enabled for sensible content types * Nicer syntax to query tag objects from the index: `tags.page` becomes an alias for `index.tag "page"` (implemented here: [[^Library/Std/APIs/Tag]]) * Hidden files and folders (starting with `.`) are no longer synced, as well as files without a file extension (those were not support anyway) ## 2.1.2 This is a major architectural overhaul compared to 2.0. Please — as always — make sure you regularly backup your space files to avoid any data loss. Nothing should happen, but better be safe than sorry! * All new server written in Go (previously written using Deno). Uses significantly less memory and is significantly smaller in size. * Docker base image is now based on Alpine (previously Ubuntu), further reducing memory and disk space usage. * Significant engine re-architecture: see [[Architecture]] and [[Sync]], now lives in the service worker and parallelizes sync. Once upgrading a full resync will need to happen. Documents are no longer synced by default (you can enable this via config, see [[Sync]]). * More configuration options for what to index (see [[^Library/Std/Config]] under the `index` section) for the purpose of reducing local storage size and needless CPU waste. Some useful ones: * `config.set("index.search.enabled", false)` to disable [[Full Text Search]] entirely (saves on processing and storage if you don’t use it) * `config.set("index.paragraph.all", false)` to disable indexing all (untagged) paragraphs. This is also somewhat wasteful if you don’t query these. * Disable ability to rename pages in read-only mode (by [Jelenkee](https://github.com/silverbulletmd/silverbullet/pull/1509)) * Improved docker build + health check (by [Zef](https://github.com/silverbulletmd/silverbullet/issues/1515)) * Added `templates.tagItem` template (by [Andy Costanza](https://github.com/silverbulletmd/silverbullet/commit/6d4f964a6e2a4f7dae04aa7558defcaa9f1f1a86)) * Support links in table queryable objects (by [Alex Dobin](https://github.com/silverbulletmd/silverbullet/commit/f5aef74a87bc92c133968a37f992fe0c2b25ccf4)) * Refactor of document editors (by [MrMugame](https://github.com/silverbulletmd/silverbullet/commit/4706be29e6a155bdd4c3aa7508a0383496d77369)) * Command to toggle markdown syntax rendering (by [aphymi](https://github.com/silverbulletmd/silverbullet/commit/6914d4bc319781b4dc2b0d657bee77db405af2bf)) * Fix transclusions not being indexed as links (by [MrMugame](https://github.com/silverbulletmd/silverbullet/pull/1539)) * Render links inside frontmatter code as clickable anchors (by [Andy Constanza](https://github.com/silverbulletmd/silverbullet/pull/1552)) * New `SB_LOG_PUSH` option asking clients to push their browser JS logs to the server so they’re visible there. * Hot reloading plugs has been disabled because it caused some nasty race condition. To explicitly reload plugs without restarting the client, use the `Plugs: Reload` command. ## 2.0.0 * We’re now live! For previous versions, see [the v1 CHANGELOG](https://v1.silverbullet.md/CHANGELOG) --- ### CLI --- tags: maturity/experimental references: - bin/sb/src/cli.rs - bin/sb/src/commands/mod.rs - bin/sb/src/main.rs --- > **note** This is **not** the server > `sb` is the optional **CLI client**, it talks to an already-running SilverBullet instance over HTTP. If you’re looking for the actual server binary, that’s [[Install/Binary]] (`silverbullet`), not this. You do not need `sb` to use SilverBullet. The SilverBullet CLI is a companion command-line tool for interacting with a running SilverBullet instance from your terminal. It communicates with the server via the [[Runtime API]], letting you evaluate Lua expressions, run scripts, open an interactive REPL, tail logs, and more — without touching a browser. # Installation The CLI binary (`sb`) is available alongside the server binary on the [GitHub releases page](https://github.com/silverbulletmd/silverbullet/releases) (or the [edge](https://github.com/silverbulletmd/silverbullet/releases/tag/edge) build). Download the version matching your platform. Once installed, it can self-update: ```bash sb upgrade # latest stable release sb upgrade-edge # latest edge (main branch) build ``` # Configuring spaces Before using the CLI, connect it to a SilverBullet instance. The CLI stores space configurations in `~/.config/silverbullet/config.json` (respects `XDG_CONFIG_HOME`). ## Adding a space ```bash sb space add ``` This interactive wizard will prompt for: 1. A **name** for the space (alphanumeric and hyphens) 2. The **URL** of your SilverBullet server 3. **Authentication** method ([[Install/Configuration#Authentication|token]], username/password, or none) ## Listing and removing spaces ```bash sb space list sb space remove ``` # Global flags These flags are available on all commands that connect to a space: | Flag | Description | |---|---| | `-s, --space ` | Select a space by name (auto-selected if only one is configured) | | `--url ` | Connect directly to a URL (bypasses space config) | | `--token ` | Authentication token (use with `--url`) | | `-t, --timeout ` | Request timeout (default: 30) | # Commands Note that the first call may take a few seconds, since the [[Runtime API]] will spin up a headless Chromium instance and need to perform an initial index for the first call. ## `lua ` Evaluate a single Lua expression and print the result. ```bash sb lua "1 + 1" # => 2 sb lua "editor.getCurrentPage()" # => "index" ``` ## `lua-script [file]` Execute a multi-line Lua script from a file or stdin. ```bash sb lua-script myscript.lua # Or pipe from stdin: echo 'local x = 40; return x + 2' | sb lua-script ``` ## `repl` Open an interactive Lua REPL with multi-line support. ```bash sb repl ``` Special commands inside the REPL: * `.exit` or `Ctrl-D` — exit * `.script` / `.end` — enter/exit multi-line script mode * `.timeout ` — change request timeout The REPL automatically detects incomplete expressions (unclosed brackets, blocks) and waits for more input. ## `logs` Show console logs from the headless browser client. ```bash sb logs # last 100 entries sb logs -n 20 # last 20 entries sb logs -f # follow (tail) mode ``` | Flag | Description | |---|---| | `-n, --lines ` | Number of entries (default: 100) | | `-f, --follow` | Continuously stream new log entries | ## `screenshot [file]` Capture a PNG screenshot of the headless client viewport. ```bash sb screenshot page.png # save to file sb screenshot > page.png # or pipe to stdout ``` ## `version` Print the installed CLI version. ## `upgrade` / `upgrade-edge` Self-update the CLI binary to the latest stable or edge release. # Authentication The CLI supports three authentication methods, configured per-space during `space add`: * **Token** — sends an `Authorization: Bearer ` header. Use this with `SB_AUTH_TOKEN` on a [[Space Manager#Single-space mode|single-space]] server, or with a per-account [[Space Manager#API tokens|API token]] on an accounts-based server. * **Password** — authenticates via `POST /.auth` (username/password), then uses the returned session cookie. Use this with `SB_USER` on a single-space server, or with an account username/password on a space you’re a member of. * **None** — no authentication (for local or trusted-network setups, or public spaces). Credentials are encrypted at rest using AES-256-GCM with PBKDF2 key derivation. # Examples Query your space for recent pages: ```bash sb lua 'query[[from tags.page order by lastModified desc limit 5 select name]]' ``` Run a script that lists all tasks: ```bash echo 'return query[[from tags.task where not done select ref, name]]' | sb lua-script ``` Tail logs while debugging: ```bash sb logs -f ``` --- ### CONFIG This is where you configure SilverBullet to your liking. See [[^Library/Std/Config]] for a full list of configuration options. # Site configuration ```space-lua actionButton.define { icon = "activity", description = "What's new", priority = 2.8, run = function() editor.navigate "CHANGELOG" end } actionButton.define { icon = "message-circle", description = "Community", priority = 2.7, run = function() editor.openUrl "https://community.silverbullet.md" end } actionButton.define { icon = "github", description = "Github", priority = 2.6, run = function() editor.openUrl "https://github.com/silverbulletmd/silverbullet" end } actionButton.define { icon = "heart", description = "Funding", priority = 2.5, run = function() editor.navigate "Funding" end } actionButton.define { icon = "search", description = "Search", priority = 1.5, run = function() editor.invokeCommand("Silversearch: Search") end } ``` # Custom tag definitions (further detailed in [[API/tag#Use cases]]) ``` /* Detailed source-code truncated for AI context efficiency. */ ``` --- ### Client Encryption --- tags: maturity/beta references: - client/data/encrypted_kv_primitives.ts - client/data/indexeddb_kv_primitives.ts - client/data/kv_primitives.ts --- By default SilverBullet keeps a copy of all your files as well as the index, unencrypted in your browser’s IndexedDB. As long as you control your device and other people do not have access to it, this should be perfectly safe. Browsers do not allow access to these databases from other websites and domains. However, if you are accessing SilverBullet from an “untrusted” device, such as a public computer, this is not a great option, because even after closing your browser all content remains stored on the device unencrypted. You can use commands like `Client: Wipe` and `Client: Logout` to remove data locally, but there’s a chance you will forget at some point. > **note** Note > Client encryption right now is only available for users of SilverBullet’s native [[Authentication]] system. This is what SilverBullet’s **client encryption** is for. When you authenticate using SilverBullet’s [[Authentication]] system, you will have a checkbox “Enable client encryption”. When checked, _all_ your content kept on the client will be encrypted (both your synced files and index). Since no encryption keys are written to disk at any stage, closing your browser or even closing all SilverBullet tabs will flush the key. While content remains on the device, it cannot be viewed without the encryption key derived from your username and password, leaving it useless to malicious actors. This safety does come at a cost: 1. **Performance:** due to constant encryption and decryption SilverBullet will be slower. 2. **Convenience:** the client-side encryption key (derived from your username and password) will only ever be kept in memory, which means that if you close all your SilverBullet browser tabs and windows, the key will be flushed and you will have to login again. # Details Some technical details on how client encryption works. Note that this is _client_ encryption only, not end-to-end encryption. Data on the server is only encrypted if your space folder is kept on some encrypted volume on the server side. _Transfer_ of data from the client to server is always encrypted via TLS, whether you use client encryption or not. On the client, all of SilverBullet’s local data storage is built on a small key-value based [abstract interface](https://github.com/silverbulletmd/silverbullet/blob/main/client/data/kv_primitives.ts). By default this interface is implemented to [directly](https://github.com/silverbulletmd/silverbullet/blob/main/client/data/indexeddb_kv_primitives.ts) communicate with your browser’s [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API). On top of this we build the sync engine and all our database indexing features. When you enable client encryption, we put a layer in-between: the encryption layer, which based on a cryptographic key will encrypt and decrypt both keys and values on the fly. A strong 256-bit cryptographic key is derived (using _PBKDF2_) on the client from your username/password combo entered upon login. This key is kept in the service worker for SilverBullet clients to obtain so that the user is not required to constant log in when refreshing a tab, or opening new SilverBullet tabs and windows. On an account-managed multi-space server, every prefix uses the server's shared encryption salt. Prefixes have separate service-worker scopes, so a newly opened space asks the server's other same-origin SilverBullet workers for the in-memory key. As long as another unlocked space remains open, moving between spaces does not require entering the password again; once every worker has discarded the key, the login page is required to unlock client storage again even if the server session cookie is still valid. Since we need deterministic and stable encryption for data store keys, we use _AES-CTR_ with a fixed counter. For values we use _AES-GCM_ with randomized ivs. --- ### Command Palette --- description: The searchable popup for running commands. tags: glossary references: - client/components/command_palette.tsx --- The Command Palette is used to explore SilverBullet’s numerous [[Command]] as well as execute them. The UI and its operation is largely the same as the [[Page Picker]]‘s, with a few differences: * If a keyboard shortcut is configured for the given command, it is listed along the command name to the right. * The ordering is decided based on two factors: * The last time the command was invoked via the [[Command Palette]] in this client session. * The `priority` configured for the command (in the plug, or via [[^Library/Std/Config]] under `shortcuts`). --- ### Command --- description: A named action you can invoke from the command palette or bind to a key. tags: glossary references: - libraries/Library/Std/APIs/Command.md - client/plugos/hooks/command.ts --- Commands define actions that SilverBullet can perform. They range from simple edit commands, such as `Text: Bold`, but may be more elaborate such as `Page: Rename`. SilverBullet ships with a lot of commands built in, but custom ones can also be defined using [[Space Lua]] and [[Plugs]]. All commands available commands appear in the [[Command Palette]] but may have key bindings as well (these key bindings appear in the [[Command Palette]] and are configurable in [[Configuration Manager]]). You can create custom command using the [[^Library/Std/APIs/Command]] API. --- ### Comment --- description: A note-to-self or @-addressed HTML-comment thread attached to a piece of text, indexed as a queryable object. tags: glossary maturity/experimental references: - plug-api/lib/comments.ts - plugs/index/comment.ts - plugs/editor/comments.ts - client/codemirror/comment_widget.ts --- Comments let you — and, when you want, your collaborators — leave notes inside a page without touching the rendered content. They are plain [[Markdown]] HTML comments under the hood, so the convention works everywhere and are technically valid markdown. # Grammar Any HTML comment is considered a note unless it’s a [[Baked Sections]] marker. The simplest example is just bare text (Alt-click to see the underlying code): Sign a note to record when, and optionally who wrote it with the `— author, date` suffix. The separator may be `-`, `--`, `–` or `—` when hand-typed: An optional **first line** `re: "quoted anchor"` anchors the note to a snippet of the surrounding text (straight or curly quotes both work). It shows up as the quoted context in the card: Addressing a note to someone turns it into a routing mechanism (that can be used for querying): * **`@who:`** addresses a message to `who`. * **Replies** are just more lines in the same block, each addressed to whoever should answer next. A reply can itself be unaddressed, if there’s no one left to hand it to. * **Resolving** a thread means deletes the comment block altogether. There's no separate "resolved" state to track — gone is resolved. A full back-and-forth looks like: # Widget A conforming comment block renders as a card: the quoted (`re:`) text (if any) with an `@addressee` when the message is addressed, or just its text when it is not. A _Reply_ button appears when comments are addressed with `@addressee`, the button appends a new line addressed to whoever should answer next (inferred from the last message). The _Resolve_ button simply removes the comment entirely. # Commands * `Comment: Add` (`Ctrl-Alt-c`/`Cmd-Alt-c`) inserts a new comment block after the current paragraph. With an active selection, the selected text becomes the `re: "..."` quote. * Slash command: `/comment` inserts a comment. # Configuration * `comments.author`: your signature identity. Set it so messages you write get signed automatically. Also editable from the Configuration Manager's Comments category. ```lua config.set("comments.author", "pete") ``` Leaving `comments.author` unset is fine — comments you add just carry a date-only signature instead of a named one. # Query Every conforming comment is indexed as an object tagged `comment`, queryable like any other [[Object]] via [[Space Lua/Integrated Query]]. `index.comments()` is the idiomatic named collection: * `page`: the page the comment lives on * `range`: `[from, to]` character offsets of the comment block * `quote?`: the anchored text, if the block had a `re: "..."` line * `thread`: the list of messages, each `{ addressee?, text, author?, date? }` * `addressees`: the distinct set of everyone addressed in the thread (empty for a purely unaddressed note) * `waitingOn?`: the addressee of the *last* message, if it's addressed — whose turn it is to respond. Absent for a note with no addressee. * `lastDate?`: the date of the last signed message, if any Example: ${query[[ from c = index.comments() select table.select(c, "ref", "thread") ]]} --- ### Completion --- references: - plugs/editor/complete.ts - plugs/index/complete.ts --- SilverBullet provides context-aware autocomplete to help you write faster. Completions are triggered automatically or via keyboard shortcuts as you type. # Page link completion Type `[[` to trigger page name completion. SilverBullet searches across all pages in your space and offers matching suggestions. Select one to insert a [[Link]] to that page. If no page with that name exists, a link is still created — navigating to it will create the page (these are tracked as [[Aspiring Pages]]). # Tag completion Type `#` to trigger tag completion. SilverBullet suggests existing tags from across your space. This helps maintain consistent tagging — no more typos creating duplicate tags. # Emoji completion Type `:` followed by a keyword to search for emoji. For example, `:rocket` offers the rocket emoji. Press Enter to insert it. # Frontmatter key completion Inside a [[Frontmatter]] block, SilverBullet suggests attribute keys that are already used elsewhere in your space. This helps keep your metadata schema consistent. # Slash commands Type `/` at the beginning of a line (or after a space) to trigger [[Slash Command]] completion. Slash commands can insert templates, perform actions, or trigger custom behavior defined in [[Space Lua]]. # Lua code completion Inside `space-lua` fenced code blocks, SilverBullet provides code completion for: * Global functions and variables * API namespaces (`editor.`, `space.`, `index.`, etc.) * Table fields and methods * Function signatures and one-line documentation when the API provides metadata; selecting a documented function reveals its complete source-backed API documentation in an adjacent information panel Ctrl-click or Cmd-click a Lua-defined function to navigate to its definition. # Custom completions You can extend the completion system by subscribing to the `editor:complete` event via [[Space Lua]]. Your handler receives the current cursor context and can return additional completion items. See also: [[API/event]] --- ### Configuration Manager --- references: - plugs/configuration-manager/* --- A built-in UI for editing your space's configuration without having to hand-edit [[Space Lua]] blocks in [[CONFIG]]. Launch it via the `Configuration: Open` command (`Cmd-,` or `Ctrl-,`). # Sections ## Configuration Lists all configuration options registered via [[API/config#config.define(key, schema)]], grouped by category. For each option you can edit its value according to its schema — scalars (string, number, boolean), enums (drop-downs), passwords (masked input), and structured objects all have appropriate editors. Defaults come from the schema, so leaving a field untouched means to use the default. ## Keyboard Shortcuts Lists every [[Command]] in the system and lets you rebind its shortcut. Features: * A **chord recorder**: click the record button, then press the key combination you want — including multi-stroke chords like `Ctrl-q q`. * Multiple **alternate bindings** per command. ## Libraries Manage installed [[Library|Libraries]]. # How configuration is kept The Configuration Manager writes your edits into a managed `space-lua` block inside your [[CONFIG]] page. Simple hand edits to the managed block generally survive a round-trip through the UI, but the authoritative format is whatever the Configuration Manager writes. Anything you add _outside_ that block is left alone. --- ### Data Sovereignty #value Data Sovereignty (in our definition) is the principle that your data ought to be controlled by _you_. _You_ ought to have control over your own data, how it is accessed, processed and used. SilverBullet supports data sovereignty by taking the approach of [[Self Hosted]], combined with [[Open Source]], [[Zero Tracking]], and leveraging the [[Browser]] (rather than apps) for universal access. --- ### Development --- tags: development references: - Makefile - build/build_client.ts - build/build_plugs.ts --- SilverBullet development can happen at various level. At its most basic level, you can do a lot using [[Space Lua]]. If this does not give enough power, you can upgrade to [[Plugs]]. If _that_ doesn’t suffice, you can contribute to SilverBullet’s core (see below). # Stack SilverBullet’s client is written in [TypeScript](https://www.typescriptlang.org/) and built on top of the excellent [CodeMirror 6](https://codemirror.net/) editor component. Additional UI is built using [Preact](https://preactjs.com/). [ES Build](https://esbuild.github.io) is used to build the frontend. [[Plugs]] are also written in TypeScript. The SilverBullet server is written in [Rust](https://www.rust-lang.org/) (a Cargo workspace). # Code structure * `client/`: The SilverBullet client, implemented with TypeScript * `server/`: The SilverBullet server library (Rust): HTTP router, handlers, auth, runtime seam * `server-common/`: Shared Rust crate (space primitives, shared types) * `server-runtime-chrome/`: Headless-Chrome runtime backend (Rust) * `bin/silverbullet/`: The standalone server binary (Rust) * `bin/sb/`: The `sb` command-line client (Rust) * `plugs`: Set of built-in plugs that are distributed with SilverBullet * `libraries`: A set of libraries (space scripts, page templates, slash templates) distributed with SilverBullet * `plug-api/`: Useful APIs for use in plugs * `lib/`: Useful libraries to be used in plugs * `syscalls/`: TypeScript wrappers around syscalls * `types/`: Various (client) types that can be references from plugs * `bin/plug-compile.ts`: the plug compiler * `scripts/`: Useful scripts * `docs/`: silverbullet.md website content # Development Requirements: * [Node.js](https://nodejs.org/) 24.13 or newer (see `.nvmrc`) * [Rust](https://www.rust-lang.org/tools/install) (stable, via `rustup`) * Make Install dependencies once: ```shell make setup ``` ## Server vs. client SilverBullet has two halves you rebuild **independently** — knowing which one you changed saves time: * The **server** (Rust: `server/`, `server-common/`, `server-runtime-chrome/`, `bin/silverbullet/`) is a compiled binary. * The **client** (TypeScript: `client/`) is built by ESBuild into `client_bundle/`, which the server serves. Run the server in development with `cargo run`. A **debug** build serves the client bundle **live from `client_bundle/` on disk** (a release build embeds it). Use `SB_DISABLE_SERVICE_WORKER=1` so the service worker doesn't cache stale assets: ```shell SB_DISABLE_SERVICE_WORKER=1 cargo run -p silverbullet -- ``` To pass arguments like `-p` or `-L`, put them after `--`: ```shell SB_DISABLE_SERVICE_WORKER=1 cargo run -p silverbullet -- -L 0.0.0.0 ``` **When you change the server** (any Rust code): rebuild **and restart** it — stop the process and re-run `cargo run` (it recompiles). A running server does *not* pick up source changes. **When you change only the client** (TypeScript in `client/`): you do **not** need to restart the server. Rebuild just the client and reload the page in your browser — the debug server serves the new bundle from disk: ```shell npm run build:client # rebuild only the client; then reload the page ``` (For plugs, use `npm run build:plugs`; `npm run build` does both.) To build a self-contained **release** binary (with the client bundle embedded) and run it: ```shell make build-rs # -> target/release/silverbullet ./target/release/silverbullet ``` ### Useful development tasks ```shell # Clean all generated files make clean # Typecheck and lint all code make check # Format all code make fmt # Run all tests make test ``` ### Docker Multi-arch (amd64 + arm64 + arm/v7) Docker images are published to Docker Hub and the GitHub Container Registry. The **edge** channel is rebuilt on every push to `main`; **stable** images come from git tags: * `zefhemel/silverbullet:v2` (edge) / `:latest` + `:X.Y.Z` (stable) — the server (Alpine, static musl binary) * `…:v2-runtime-api` (edge) / `:latest-runtime-api` + `:X.Y.Z-runtime-api` (stable) — the same, plus Chromium for the server-side Lua runtime (`/.runtime/*`) Both images are mirrored to `ghcr.io/silverbulletmd/silverbullet` under the same tags. To run one: ```shell docker run -p 3000:3000 -v :/space zefhemel/silverbullet:v2 ``` These are built by `.github/workflows/ci.yml`, which cross-compiles the binary natively (`cargo build --target` with installed musl cross-toolchains) and copies it into a small Alpine image. --- ### Document Editor --- references: - client/document_editor.ts - client/document_editor_js.ts --- Document editors allow you to not only view but also edit files other than markdown. By itself they are just [[Plugs|plugs]] which hook into Silverbullet and provide an editor based on the extension of the file. Silverbullet includes an image viewer by itself to view most image files (`jpg`, `png`, ...). If you are missing an editor and want to build your own look at [[#Development]]. # Usage To get started, you first need to install a plug that provides a document editor (for examples see [[#Available editors]]). Once installed and loaded, you can open the document navigator using `Cmd-o` (Mac) or `Ctrl-o` (Windows, Linux), which works similarly to the page navigator. The only difference is that files which you can open will have their extension highlighted in blue, files for which no document editor is loaded are gray. # Available editors Currently only two major document editors exist (As well as the built-in image viewer). Both are installable through the [[Configuration Manager#Libraries|Library Manager]]. - [PDF viewer](https://github.com/MrMugame/silverbullet-pdf/) - [Excalidraw](https://github.com/LogeshG5/silverbullet-excalidraw) # Development First the editor is defined inside the plug manifest. The `editor` field is used to specify the file extensions your editor can handle. ```yaml name: txteditor functions: TXTEditor: path: ./editor.ts:editor editor: ["txt"] ``` The function provided should look like this. The html tag will be directly inserted into an iframe as the `srcdoc`. This means you can also use the `` or `` tags. ```typescript export async function editor(): Promise<{ html: string }> { return { html: "

Document Editor example

" } } ``` To communicate with silverbullet events/messages are used. You can subscribe to an event inside a script tag as follows ```javascript window.silverbullet.addEventLister("file-open", (event) => { console.log("Got an event:", event); }); ``` SB will dispatch the following events: - `file-open`: A file was just navigated to, the document editor needs to open it. The details contain the meta and data: `{ data: Uint8Array, meta: DocumentMeta }` - `request-save`: Silverbullet is requesting a save, you should send a `file-saved` event as soon as possible - `focus`: You should focus the editor if possible (i.e. highlight cursor). If you don't know how to handle this `window.focus()` is a good bet. To send events/messages like the `file-saved` message, you can use the `sendMessage` function. ```javascript window.silverbullet.sendMessage("file-saved", { data: new TextEncoder().encode("We saved a text file") }) ``` If a the document changed and a save is necessary you can send the `file-changed` message. Silverbullet will request a save automatically. You can also send events from the outside using the `editor.sendMessage` syscall. If you want to communicate the other way around you can call syscalls using `window.silverbullet.syscall(name: string, ...args: any[])`. --- ### Document --- description: A non-page file (such as an image, PDF, or other attachment) stored in your space. tags: glossary --- While SilverBullet is aimed at primarily text-based content, life can not fully be represented in text always. Therefore, SilverBullet supports documents. Documents, like [[Page]] ultimately are — once again — just files on disk. Using plugs, documents of specific file types can also be viewed and edited. # Uploading To create a document, you have a few options: * Use the ${widgets.commandButton("Upload: File")} command (especially useful on mobile devices) * Drag & drop files or images onto a page * Copy & paste files or images onto a page All options will prompt you for a file name to use to store the document, and then include the document as an embedded image (if it was an image) or link to the file. # Managment Using `Cmd-o`/`Ctrl-o` you can open the document navigator, which will display all documents in your Silverbullet space. You can either: - Delete or rename documents for which no [[Document Editor]] is available (indicated by the grey hint) - Open and view documents for which a [[Document Editor]] is available (indicated by the blue hint) # Linking Documents can be linked to in two ways: * Via the regular link syntax: `[link text](document.pdf)`. URLs here are relative to the page, so on a page named `MyFolder/Hello`, `[link text](document.pdf)` would refer to a document stored in `MyFolder/document.pdf`. * Via the wiki link syntax: `[[document.pdf]]`. These paths are absolute and relative to your space’s root, just like regular page links. That is: on a page `MyFolder/Hello` a document link `[[document.pdf]]` would link to the file `document.pdf` in the space’s root folder. # Embedding Media can also be embedded using the [[#Linking]] syntax, but prefixed with an `!`: Images, videos, audio and PDFs are currently supported. * `` * `![[image.png]]` These follow the same relative/absolute path rules as links described before. ## Media resizing In addition, media can be _sized_ using the following syntax: * Specifying only a width: `` or `![[image.png|300]]` * Specifying only a height: `` or `![[image.png|x300]]` * Specifying both width and height: `` or `![[image.png|300x300]]` --- ### Editor By design, the vast majority of the SilverBullet UI is taken up by the editor. This is where the magic happens. This is where you write in [[Markdown]]. --- ### End User Programming An excellent [essay from Ink & Switch](https://www.inkandswitch.com/end-user-programming/) introduces **end-user programming** as follows: > Today’s software apps are like appliances: we can only use the capabilities exactly as programmed by the developer. What if we, and all computer users, could reach in and modify our favorite apps? Or even create new apps on the fly according to our needs in the moment? > > This is end-user programming, a vision for empowered computing pursued by bright-eyed computer science visionaries. Its rich history reaches back to the 1960s with programming environments like Smalltalk and Logo. Notable successes since then include Unix, the spreadsheet, Hypercard, and HTML. And today, newcomers like Zapier, Coda, and Siri Shortcuts are trying their own approaches to automation and dynamic modeling. SilverBullet is working towards supporting more end-user programming features. Some of them currently available include: * [[Space Lua]] * [[Object]] * [[Metadata]] --- ### Event --- description: A named signal that plugs and Lua scripts can listen to and react to. tags: glossary references: - client/plugos/event.ts - plug-api/types/event.ts - plug-api/syscalls/event.ts --- SilverBullet has its own event bus that allows different parts of the system to communicate. Events are the foundation for much of SilverBullet's extensibility — features like [[Service]], [[Virtual Pages]], widgets, and custom indexing are all built on top of events. # Subscribing to events Use `event.listen` to subscribe to an event: ```lua event.listen { name = "editor:pageLoaded", run = function(e) print("Loaded page: " .. e.data.name) end } ``` The `run` callback receives an event object with a `data` field containing event-specific information. To see what data an event provides, add a `print` call and check the [[Log|logs]]. # Dispatching events You can dispatch your own custom events: ```lua event.dispatch("my-custom-event", {message = "Hello!"}) ``` Other scripts can then listen for `my-custom-event`. # Built-in events Here is a list of built-in events triggered by SilverBullet's core: ## Editor events * `editor:init`: Editor has initialized * `editor:pageLoaded`: A page has been loaded in the editor * `editor:pageReloaded`: A page was reloaded (e.g. after being changed on disk) * `editor:pageSaving`: A page is about to be saved * `editor:pageSaved`: A page has been saved * `editor:pageCreating`: A page is being created (can return content — used by [[Virtual Pages]]) * `editor:pageModified`: A change was made to the document (fires in real-time) * `editor:documentSaving`: A document (non-page file) is about to be saved * `editor:documentSaved`: A document was saved * `editor:modeswitch`: Toggled between [[Vim]] mode and normal mode * `editor:fold`: Code was folded in the editor * `editor:unfold`: Code was unfolded in the editor ## Interaction events * `page:click`: User clicked a location on the page * `editor:complete`: Editor completion triggered — return completion results to extend [[Completion]] * `slash:complete`: Slash completion triggered — return completion results * `editor:lint`: Lint request — return errors to show in the editor ## System events * `page:index`: A page has changed and needs to be indexed (used by [[Object]] indexing) * `plugs:loaded`: Plugs were loaded * `cron:secondPassed`: One second has passed (useful for implementing periodic behavior) ## Widget events * `hooks:renderTopWidgets`: Top widgets requested to render — return widgets to display above the page content * `hooks:renderBottomWidgets`: Bottom widgets requested to render — return widgets to display below the page content # All subscribed events Here’s a dynamically generated list of events that this SilverBullet instance has subscribed to, to give a sense of what’s there: ${query[[ from event.listEvents() where not _:startsWith("service:") order by _ ]]} See [[API/event]] for the full API reference. ---