# SilverBullet 2.5 released

**URL:** https://community.silverbullet.md/t/silverbullet-2-5-released/3943
**Category:** Announcements
**Created:** [March 5, 2026, 1:26pm UTC](https://community.silverbullet.md/t/silverbullet-2-5-released/3943 "2026-03-05T13:26:23Z")
**Posts on this page:** 15
**Page:** 1

<div class="post-metadata">

### Author: ![zef](https://community.silverbullet.md/user_avatar/community.silverbullet.md/zef/32/7_2.png) [@zef](https://community.silverbullet.md/u/zef)
#### Post date: [March 5, 2026, 1:26pm UTC](https://community.silverbullet.md/t/silverbullet-2-5-released/3943/1 "2026-03-05T13:26:23Z")

</div>

SilverBullet 2.5.0 brings a number of keyboard binding changes (due to CodeMirror dropping support for `Alt-<letter>` shortcuts, sorry about this...), significant sync reliability improvements ensuring your local state stays in sync with the server, and powerful new Lua Integrated Query features including group by and aggregator support. The Space Lua engine also saw significant improvements with a port of Lua's native pattern matching library, `<close>` attribute support, and various performance and compatibility improvements.

# Changes

- Changed keyboard bindings (sorry!). CodeMirror no longer directly allows `Alt-<letter>` and `Alt-<special-character>` [Keyboard Shortcuts](https://silverbullet.md/Keyboard%20Shortcuts), 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](https://silverbullet.md/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](https://silverbullet.md/Keyboard%20Shortcuts))
    - `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](https://silverbullet.md/Keyboard%20Shortcuts), it is now possible to specify _multiple_ keyboard shortcuts to a commands.

- [Sync](https://silverbullet.md/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).

- [Lua Integrated Query](https://silverbullet.md/Space%20Lua/Lua%20Integrated%20Query) improvements (courtesy of [Matouš Jan Fialka](https://github.com/mjf)):
  - [group by](https://silverbullet.md/Space%20Lua/Lua%20Integrated%20Query/Grouping) and `having` clauses with [aggregator](https://silverbullet.md/Space%20Lua/Lua%20Integrated%20Query/Aggregating) support
  - `filter(where <cond>)` clause for per-row aggregate filtering
  - `nulls first`/`nulls last` in `order by`
  - Null/missing query cells now render as empty

- [Space Lua](https://silverbullet.md/Space%20Lua) 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 `<close>` 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
  - [table.select(table, keys...)](https://silverbullet.md/API/table#table.select(table,%20keys...)) (non-standard in Lua) API, convenient to use in [Lua Integrated Query](https://silverbullet.md/Space%20Lua/Lua%20Integrated%20Query) `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: [tag.define(spec)](https://silverbullet.md/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](https://silverbullet.md/Schema) support for [tags](https://silverbullet.md/Tag). When a schema is defined for a tag, you get:
  - [Frontmatter](https://silverbullet.md/Frontmatter) **attribute completion and linting** (in-editor error indicators) for attributes defined as part of the tag's schema.
  - [Lua Integrated Query](https://silverbullet.md/Space%20Lua/Lua%20Integrated%20Query) **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 Mentions](https://silverbullet.md/Linked%20Mention) 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` [Fenced Code Block](https://silverbullet.md/Markdown/Fenced%20Code%20Block) 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](https://silverbullet.md/CONFIG).
  - [Authentication](https://silverbullet.md/Authentication): how long "remember me" works is now configurable (by [Metin Yazici](https://github.com/silverbulletmd/silverbullet/pull/1796)) via [Configuration](https://silverbullet.md/Install/Configuration) and more reliably persisted.

- [Library Manager](https://silverbullet.md/Library%20Manager): SilverBullet now navigates to library page after installing one.
- FreeBSD buids!
- Now excluding `.plug.js` and `.js.map` files from the document list.
- Fix: bring back [Virtual Pages](https://silverbullet.md/Virtual%20Pages).

# Upgrading

- For docker-based install, pull the new image, stop the container and start a new one. For binary-based installs run `silverbullet --upgrade` and restart.
- After upgrading the server, make sure you reload all your SilverBullet tabs a few times, just to make sure the cache is flushed.

# Dedication

As mentioned earlier, I decided to reduce my regular day job to fewer days and spend this time on SilverBullet. This allows for deeper focus and more ambitious work. If you like this, consider [sponsoring](https://silverbullet.md/Funding) to make this a little bit more financially viable for me.

---

<div class="post-metadata">

### Author: ![zef](https://community.silverbullet.md/user_avatar/community.silverbullet.md/zef/32/7_2.png) [@zef](https://community.silverbullet.md/u/zef)
#### Post date: [March 5, 2026, 3:15pm UTC](https://community.silverbullet.md/t/silverbullet-2-5-released/3943/2 "2026-03-05T15:15:59Z")

</div>

Quick patch release with 2.5.1: there was a latent caching bug in some environments, that resulted in built-in plugs and libraries not being updated (somehow they were stuck with an old timestamp). Should be fixed after updating to 2.5.1.

---

<div class="post-metadata">

### Author: ![malys](https://community.silverbullet.md/user_avatar/community.silverbullet.md/malys/32/840_2.png) [@malys](https://community.silverbullet.md/u/malys)
#### Post date: [March 6, 2026, 8:56am UTC](https://community.silverbullet.md/t/silverbullet-2-5-released/3943/3 "2026-03-06T08:56:03Z")

</div>

My plugin MdTableSheet is not working anymore after 2.5.1 migration. Is there a breaking change list somewhere? (something related to table management or signature of function changes?)

---

<div class="post-metadata">

### Author: ![cladv](https://community.silverbullet.md/letter_avatar_proxy/v4/letter/c/4bbf92/32.png) [@cladv](https://community.silverbullet.md/u/cladv)
#### Post date: [March 6, 2026, 10:54am UTC](https://community.silverbullet.md/t/silverbullet-2-5-released/3943/4 "2026-03-06T10:54:51Z")

</div>

Thank you Zef and dev team. Group by is a tremendous feature.

Regarding to my issues after upgrade, the only that I notice was with a function (pixel\_tracker\_map\_section) from the thepaperpilot CBT library. Changing from `100%` to `100%%` solved the issue.

```space-lua
--from this:
local offset_expr = string.format("calc(100% - (%d * 1lh) + var(--gap))", week_index)
--to this:
local offset_expr = string.format("calc(100%% - (%d * 1lh) + var(--gap))", week_index)

```

---

<div class="post-metadata">

### Author: ![zef](https://community.silverbullet.md/user_avatar/community.silverbullet.md/zef/32/7_2.png) [@zef](https://community.silverbullet.md/u/zef)
#### Post date: [March 6, 2026, 12:12pm UTC](https://community.silverbullet.md/t/silverbullet-2-5-released/3943/5 "2026-03-06T12:12:38Z")

</div>

There's a list in the announcement, although nothing _should_ be breaking. Since on the Lua end some things have been reimplemented to be more Lua compatible, it's possible that somewhere your code depends on non-standard or not valid previous behavior?

---

<div class="post-metadata">

### Author: ![zef](https://community.silverbullet.md/user_avatar/community.silverbullet.md/zef/32/7_2.png) [@zef](https://community.silverbullet.md/u/zef)
#### Post date: [March 6, 2026, 12:13pm UTC](https://community.silverbullet.md/t/silverbullet-2-5-released/3943/6 "2026-03-06T12:13:54Z")

</div>

Right, this an example of what I just mentioned. In `string.format` `%` has special meaning, to insert an actual `%` you have to use `%%`. This was always the case, but previously _not_ doing this also (sometimes) worked.

---

<div class="post-metadata">

### Author: ![zef](https://community.silverbullet.md/user_avatar/community.silverbullet.md/zef/32/7_2.png) [@zef](https://community.silverbullet.md/u/zef)
#### Post date: [March 6, 2026, 12:27pm UTC](https://community.silverbullet.md/t/silverbullet-2-5-released/3943/7 "2026-03-06T12:27:45Z")

</div>

Just released 2.5.2:

- Fixed table indexer shifting columns when cells are empty
- Fixed transclusions not working in widgets
- Fixed renaming page with `)` in the name

---

<div class="post-metadata">

### Author: ![malys](https://community.silverbullet.md/user_avatar/community.silverbullet.md/malys/32/840_2.png) [@malys](https://community.silverbullet.md/u/malys)
#### Post date: [March 6, 2026, 4:22pm UTC](https://community.silverbullet.md/t/silverbullet-2-5-released/3943/8 "2026-03-06T16:22:52Z")

</div>

before  
${string.find("space-lua","space-lua")} =\> 1  
After:2.5.0  
${string.find("space-lua","space-lua")} =\> nil  
${string.find("space-lua","space")} =\> 1

My breaking change or regression?

---

<div class="post-metadata">

### Author: ![marcoswur](https://community.silverbullet.md/letter_avatar_proxy/v4/letter/m/a87d85/32.png) [@marcoswur](https://community.silverbullet.md/u/marcoswur)
#### Post date: [March 6, 2026, 4:29pm UTC](https://community.silverbullet.md/t/silverbullet-2-5-released/3943/9 "2026-03-06T16:29:39Z")

</div>

This is the case with format, gsub, find, ...  
Use % with special caracteres.  
${string.find("space-lua","space%-lua")} =\> 1

---

<div class="post-metadata">

### Author: ![malys](https://community.silverbullet.md/user_avatar/community.silverbullet.md/malys/32/840_2.png) [@malys](https://community.silverbullet.md/u/malys)
#### Post date: [March 6, 2026, 4:32pm UTC](https://community.silverbullet.md/t/silverbullet-2-5-released/3943/10 "2026-03-06T16:32:01Z")

</div>

it worked fine before and in my case, every parameter are variables . It will be a nightmare to escape everything.  
Is there a method to auto escape strings?

On another issue: "Error: invalid use of '%' in replacement string".  
I'm sure that i will detect more regression. Is there a list of "breaking change" for space-lua language in 2.5.x?

---

<div class="post-metadata">

### Author: ![marcoswur](https://community.silverbullet.md/letter_avatar_proxy/v4/letter/m/a87d85/32.png) [@marcoswur](https://community.silverbullet.md/u/marcoswur)
#### Post date: [March 6, 2026, 5:13pm UTC](https://community.silverbullet.md/t/silverbullet-2-5-released/3943/11 "2026-03-06T17:13:44Z")

</div>

You can even override the Lua functions...  
But this is the Lua default.  
I changed all the occurrences I had. The main problem was with dates (2026-03-06).  
Below is one possibility...

```space-lua
--priority 99
sfind = string.find
string.find = function(s,p)
  local p = string.gsub(p,'%-','%%-')
  return sfind(s,p)
end

```

Another option...  
The 4th parameter defines an exact search, escaping special characters.

> string.find("space-lua","space-lua",1,true) =\> 1

---

<div class="post-metadata">

### Author: ![malys](https://community.silverbullet.md/user_avatar/community.silverbullet.md/malys/32/840_2.png) [@malys](https://community.silverbullet.md/u/malys)
#### Post date: [March 6, 2026, 5:29pm UTC](https://community.silverbullet.md/t/silverbullet-2-5-released/3943/12 "2026-03-06T17:29:19Z")

</div>

Thanks. It's an idea.  
Step by step, I will identify the full impact of this new behavior.  
In function, I will rewrite or eliminate some of scripts to not have to migrate them too often.

---

<div class="post-metadata">

### Author: ![marcoswur](https://community.silverbullet.md/letter_avatar_proxy/v4/letter/m/a87d85/32.png) [@marcoswur](https://community.silverbullet.md/u/marcoswur)
#### Post date: [March 6, 2026, 5:40pm UTC](https://community.silverbullet.md/t/silverbullet-2-5-released/3943/13 "2026-03-06T17:40:16Z")

</div>

Remember... these are the special characters:  
( ) . % + - \* ? [ ^ $  
I think these are the Lua functions impacted:  
find, gmatch, match, gsub  
format

---

<div class="post-metadata">

### Author: ![zef](https://community.silverbullet.md/user_avatar/community.silverbullet.md/zef/32/7_2.png) [@zef](https://community.silverbullet.md/u/zef)
#### Post date: [March 6, 2026, 5:47pm UTC](https://community.silverbullet.md/t/silverbullet-2-5-released/3943/14 "2026-03-06T17:47:31Z")

</div>

Cc @mjf for awareness

---

<div class="post-metadata">

### Author: ![mjf](https://community.silverbullet.md/user_avatar/community.silverbullet.md/mjf/32/654_2.png) [@mjf](https://community.silverbullet.md/u/mjf)
#### Post date: [March 6, 2026, 7:08pm UTC](https://community.silverbullet.md/t/silverbullet-2-5-released/3943/15 "2026-03-06T19:08:06Z")

</div>

Acked.
