From 856b176ce73c95e809129c8e1ce82bcedd7cdf47 Mon Sep 17 00:00:00 2001 From: Zef Hemel Date: Thu, 5 Mar 2026 08:12:19 +0100 Subject: [PATCH 01/11] Fixes #1864 by making ordering stable --- client/space_lua.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/space_lua.ts b/client/space_lua.ts index 3bf831b2..86747dd5 100644 --- a/client/space_lua.ts +++ b/client/space_lua.ts @@ -34,6 +34,9 @@ export class SpaceLuaEnvironment { expr: parseExpressionString("script.priority or 0"), desc: true, nulls: "first", + }, { + expr: parseExpressionString("script.ref"), + desc: false, }], } as LuaCollectionQuery, ); From e1daa7204d78185cc739ad09ec2288de5e6b9012 Mon Sep 17 00:00:00 2001 From: Zef Hemel Date: Thu, 5 Mar 2026 08:20:43 +0100 Subject: [PATCH 02/11] CHANGELOG and Space Overview updates reflecting new space-lua ordering --- libraries/Library/Std/Pages/Space Overview.md | 4 +- website/CHANGELOG.md | 54 +++++++++---------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/libraries/Library/Std/Pages/Space Overview.md b/libraries/Library/Std/Pages/Space Overview.md index bf8eadcb..70f45928 100644 --- a/libraries/Library/Std/Pages/Space Overview.md +++ b/libraries/Library/Std/Pages/Space Overview.md @@ -16,8 +16,8 @@ ${query[[ # Active Space Lua These are all Space Lua scripts and the order in which they are loaded: ${template.each(query[[ - from index.tag "space-lua" - order by _.priority desc + from script = index.tag "space-lua" + order by (script.priority or 0) desc, script.ref ]], template.new[==[ * [[${ref}]] (priority: ${priority or "default"}) ]==])} diff --git a/website/CHANGELOG.md b/website/CHANGELOG.md index 85484e7d..fbb56f8d 100644 --- a/website/CHANGELOG.md +++ b/website/CHANGELOG.md @@ -3,33 +3,6 @@ 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. -* 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/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 -* [[Space Lua/Lua Integrated Query]] improvements (courtesy of [Matouš Jan Fialka](https://github.com/mjf)): - * [[Space Lua/Lua Integrated Query/Grouping|group by]] and `having` clauses with [[Space Lua/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 (also courtesy 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 - * [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` -* Lua APIs: - * [[API/table#table.select(table, keys...)]] (non-standard in Lua) API, convenient to use in [[Space Lua/Lua Integrated Query]] `select` clauses, see example in docs. * 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` @@ -51,6 +24,33 @@ Whenever a commit is pushed to the `main` branch, within ~5 minutes, it will be * 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/Lua Integrated Query]] improvements (courtesy of [Matouš Jan Fialka](https://github.com/mjf)): + * [[Space Lua/Lua Integrated Query/Grouping|group by]] and `having` clauses with [[Space Lua/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/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/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. From e14d0a3dfd16331640bc7730a89351314a74f863 Mon Sep 17 00:00:00 2001 From: Zef Hemel Date: Thu, 5 Mar 2026 08:23:12 +0100 Subject: [PATCH 03/11] Mildly reduce hostility in contribution guide --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 52ab065b..6a34f597 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ So you're interested in helping out? That's great! -First of all, please have a look at our [LLM use policy](https://silverbullet.md/LLM%20Use) (TL;DR: no). All good? Let's proceed. +First of all, please have a look at our [LLM use policy](https://silverbullet.md/LLM%20Use). All good? Let's proceed. ## Issuing PRs Before issuing a PR, please run a few commands: From c299a651987d24b36166404c23e3035ab679b933 Mon Sep 17 00:00:00 2001 From: Zef Hemel Date: Thu, 5 Mar 2026 13:14:29 +0100 Subject: [PATCH 04/11] Updates to HTTP API docs --- website/HTTP API.md | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/website/HTTP API.md b/website/HTTP API.md index d18a43aa..3d6de696 100644 --- a/website/HTTP API.md +++ b/website/HTTP API.md @@ -2,11 +2,19 @@ The server API is relatively small. The client primarily communicates with the s All API requests from the client will always set the `X-Sync-Mode` request header set to `true`. The server _will_ use this fact to distinguish between requests coming from the client and regular e.g. `GET` requests from the browser (through navigation) and redirect appropriately (for instance to the UI URL associated with a specific `.md` file). +# Authentication +When authentication is enabled, most endpoints require a valid session cookie (JWT) or a Bearer token. The following paths are excluded from auth: `/service_worker.js`, `/.client/*`, `/.auth`, `/.ping`. + +* `GET /.auth`: Serves the login page. Returns 403 if authentication is not enabled. +* `POST /.auth`: Authenticates a user. Expects form fields: `username`, `password`, `rememberMe` (optional), `from` (optional redirect path). Returns JSON `{"status":"ok","redirect":"..."}` on success or `{"status":"error","error":"..."}` on failure. Implements lockout after repeated failed attempts. +* `GET /.logout`: Clears authentication cookies and redirects to `/.auth`. +* **Bearer token**: When [[Install/Configuration#Authentication|SB_AUTH_TOKEN]] is set, requests can authenticate via the `Authorization: Bearer ` header instead of cookies. + # File system The space file system is exposed under the `/.fs` prefix: * `GET /.fs` will return a full listing of all files in your space including metadata like when the file was last modified, as well as permissions in JSON format. This is primarily used for sync purposes with the client. -* `GET /.fs/*`: _Reads_ and returns the content of the file at the given path. This means that if you `GET /.fs/index.md` you will receive the content of your `index` page. If the optional `X-Get-Meta` _request header_ is set, the server does not _need to_ return the body of the file (but it can). The `GET` _response_ will have a few additional SB-specific headers: +* `GET /.fs/*`: _Reads_ and returns the content of the file at the given path. This means that if you `GET /.fs/index.md` you will receive the content of your `index` page. If the optional `X-Get-Meta` _request header_ is set, the server does not _need to_ return the body of the file (but it can). The `GET` _response_ will have a few additional SB-specific headers: * (optional) `X-Last-Modified` the last modified time of the file as a UNIX timestamp in ms since the epoch (as coming from `Data.now()`). This timestamp _has_ to match the `lastModified` listed for this file in `/.fs` otherwise syncing issues may occur. When this header is missing, frequent polling-based sync will be disabled for this file. * (optional) `X-Created` the created time of the file as a UNIX timestamp in ms since the epoch (as coming from `Data.now()`). * (optional) `X-Permission`: either `rw` or `ro` which will change whether the editor opens in read-only or edit mode. When missing, `ro` is assumed. @@ -15,16 +23,25 @@ The space file system is exposed under the `/.fs` prefix: * `DELETE /.fs/*`: Again the same, except this will _delete_ the given file. # RPC -Some functionality is exposed as RPC calls +Some functionality is exposed as RPC-style calls -* `GET /.ping`: Returns 200 if the server is available -* `POST /.shell`: Run a shell command on the server side and return the result -* `* /.proxy/`: Proxy a HTTP request (to avoid CORS issues) +* `GET /.ping`: Returns 200 with body `OK` if the server is available. +* `POST /.shell`: Run a shell command on the server side and return the result. +* `POST /.logs`: Accepts a JSON array of log entry objects with fields `source`, `level`, `message`, and `timestamp`. The server logs these with the client's IP. Returns `OK`. +* `* /.proxy//path`: Proxy an HTTP request to avoid CORS issues. + * Blocked in read-only mode (returns 405). + * The URI does not include a scheme — it is auto-detected: `http://` is used for `localhost`, `127.0.0.1`, bare IP addresses, and `host.docker.internal`; `https://` is used for everything else. + * Request headers prefixed with `X-Proxy-Header-` have the prefix stripped and are forwarded to the upstream server. + * Upstream response headers are returned with an `x-proxy-header-` prefix. + * The `x-proxy-status-code` header contains the actual upstream HTTP status code; the proxy response itself always returns 200 to avoid interference. # Client * `GET /.config`: Retrieve client configuration, JSON with the following keys: * `readOnly`: Run the client in read-only mode * `spaceFolderPath`: Path of the space (used to prefix client database names to support switching space folders) * `indexPage`: name of the index page + * `logPush`: whether the client should push logs to the server via `/.logs` + * `enableClientEncryption`: true when authentication is enabled, offering client-side encryption as an option * `GET /.client/*`: Retrieve files implementing the client +* `GET /.client/manifest.json`: Dynamically generated PWA manifest containing the space name and description (not served from the static bundle). * `GET /*` (every other path) will serve the HTML of the SilverBullet UI From 92d4807d52729161b7ba6fa7482ce93fc31c9f17 Mon Sep 17 00:00:00 2001 From: Zef Hemel Date: Thu, 5 Mar 2026 14:14:44 +0100 Subject: [PATCH 05/11] 2.5.0 --- deno.json | 2 +- version.ts | 2 +- website/CHANGELOG.md | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/deno.json b/deno.json index 02d45ede..7101ae34 100644 --- a/deno.json +++ b/deno.json @@ -1,6 +1,6 @@ { "name": "@silverbulletmd/silverbullet", - "version": "2.4.1", + "version": "2.5.0", "tasks": { "build": "make clean && deno run -A build_plugs_libraries.ts && deno run -A build_client.ts", "build-production": "deno task build --production", diff --git a/version.ts b/version.ts index 84bf009b..cb86341e 100644 --- a/version.ts +++ b/version.ts @@ -1 +1 @@ -export const version = "2.4.1"; +export const version = "2.5.0"; diff --git a/website/CHANGELOG.md b/website/CHANGELOG.md index fbb56f8d..e1047f31 100644 --- a/website/CHANGELOG.md +++ b/website/CHANGELOG.md @@ -3,6 +3,9 @@ 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. +* Nothing new yet + +## 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` From 0768884b7ca4928b86ab93a97651ba9ea94ac680 Mon Sep 17 00:00:00 2001 From: Zef Hemel Date: Thu, 5 Mar 2026 14:44:47 +0100 Subject: [PATCH 06/11] Log SilverBullet version on server boot --- server/cmd/server.go | 5 ++++- silverbullet.go | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/server/cmd/server.go b/server/cmd/server.go index 6ec76e28..ba29fbaa 100644 --- a/server/cmd/server.go +++ b/server/cmd/server.go @@ -231,7 +231,7 @@ func ensureIndexAndConfig(rootSpaceConfig *server.SpaceConfig) { } } -func ServerCommand(bundledFiles fs.FS) *cobra.Command { +func ServerCommand(bundledFiles fs.FS, versionFileText string) *cobra.Command { var hostname string var port int var c = &cobra.Command{ @@ -239,6 +239,9 @@ func ServerCommand(bundledFiles fs.FS) *cobra.Command { Short: "Run the Silverbullet server", Args: cobra.MinimumNArgs(0), Run: func(cmd *cobra.Command, args []string) { + if version, err := server.ParseVersionFromTypeScript(versionFileText); err == nil { + log.Printf("Starting SilverBullet version %s", version) + } serverConfig := buildConfig(bundledFiles, args) if port != 3000 { serverConfig.Port = port diff --git a/silverbullet.go b/silverbullet.go index 1ba641fb..a0fac44a 100644 --- a/silverbullet.go +++ b/silverbullet.go @@ -11,7 +11,7 @@ import ( var VersionFileText string func main() { - c := cmd.ServerCommand(client_bundle.BundledFiles) + c := cmd.ServerCommand(client_bundle.BundledFiles, VersionFileText) c.AddCommand(cmd.VersionCommand(VersionFileText), cmd.UpgradeCommand(), cmd.UpgradeEdgeCommand()) c.Execute() } From 17418fd7ec72438fe7a463442c44818ea4d821e5 Mon Sep 17 00:00:00 2001 From: Zef Hemel Date: Thu, 5 Mar 2026 15:07:25 +0100 Subject: [PATCH 07/11] Pull reported client version number from system.getVersion() --- plugs/editor/help.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugs/editor/help.ts b/plugs/editor/help.ts index 80c4deaa..aec99011 100644 --- a/plugs/editor/help.ts +++ b/plugs/editor/help.ts @@ -1,8 +1,7 @@ -import { editor } from "@silverbulletmd/silverbullet/syscalls"; -import { publicVersion } from "../../public_version.ts"; +import { editor, system } from "@silverbulletmd/silverbullet/syscalls"; export async function versionCommand() { await editor.flashNotification( - `You are currently running SilverBullet ${publicVersion}`, + `You are currently running SilverBullet ${await system.getVersion()}`, ); } From e9266d467e1edd183f1d9f70b280b03ae250027d Mon Sep 17 00:00:00 2001 From: Zef Hemel Date: Thu, 5 Mar 2026 15:28:35 +0100 Subject: [PATCH 08/11] (Debug) bundle timestamps --- server/cmd/server.go | 1 + 1 file changed, 1 insertion(+) diff --git a/server/cmd/server.go b/server/cmd/server.go index ba29fbaa..bd32c0f8 100644 --- a/server/cmd/server.go +++ b/server/cmd/server.go @@ -175,6 +175,7 @@ func buildConfig(bundledFiles fs.FS, args []string) *server.ServerConfig { bundlePathDate = stat.ModTime() } } + log.Printf("Determined bundle path change date: %s, %d", bundlePathDate, bundlePathDate.UnixMilli()) serverConfig.ClientBundle = server.NewReadOnlyFallthroughSpacePrimitives(bundledFiles, "client", bundlePathDate, nil) rootSpaceConfig.SpacePrimitives = server.NewReadOnlyFallthroughSpacePrimitives(bundledFiles, "base_fs", bundlePathDate, spacePrimitives) From 19483e8687c88ce07835f5b9fe9489709a26e3f0 Mon Sep 17 00:00:00 2001 From: Zef Hemel Date: Thu, 5 Mar 2026 16:03:25 +0100 Subject: [PATCH 09/11] Attempting to determine the build time in a more reliable way --- .github/workflows/edge.yml | 25 +------------------------ .github/workflows/server.yml | 25 +------------------------ Makefile | 18 ++++++++++++++---- server/cmd/server.go | 20 ++++++++++++-------- silverbullet.go | 5 ++++- 5 files changed, 32 insertions(+), 61 deletions(-) diff --git a/.github/workflows/edge.yml b/.github/workflows/edge.yml index 7e9d427e..5b15c2a6 100644 --- a/.github/workflows/edge.yml +++ b/.github/workflows/edge.yml @@ -23,30 +23,7 @@ jobs: deno task build-production deno task build-plug-compile - name: Build Go binaries for multiple platforms - run: | - # Linux ARM64 - GOOS=linux GOARCH=arm64 go build -o silverbullet . - zip silverbullet-server-linux-aarch64.zip silverbullet - - # Linux x86_64 - GOOS=linux GOARCH=amd64 go build -o silverbullet . - zip silverbullet-server-linux-x86_64.zip silverbullet - - # macOS ARM64 - GOOS=darwin GOARCH=arm64 go build -o silverbullet . - zip silverbullet-server-darwin-aarch64.zip silverbullet - - # macOS x86_64 - GOOS=darwin GOARCH=amd64 go build -o silverbullet . - zip silverbullet-server-darwin-x86_64.zip silverbullet - - # Windows x86_64 - GOOS=windows GOARCH=amd64 go build -o silverbullet.exe . - zip silverbullet-server-windows-x86_64.zip silverbullet.exe - - # FreeBSD x86_64 - GOOS=freebsd GOARCH=amd64 go build -o silverbullet . - zip silverbullet-server-freebsd-x86_64.zip silverbullet + run: make build-server-releases - name: Update edge release uses: softprops/action-gh-release@v1 diff --git a/.github/workflows/server.yml b/.github/workflows/server.yml index 5b2fc36f..0c3bcdce 100644 --- a/.github/workflows/server.yml +++ b/.github/workflows/server.yml @@ -23,30 +23,7 @@ jobs: deno task build-production deno task build-plug-compile - name: Build Go binaries for multiple platforms - run: | - # Linux ARM64 - GOOS=linux GOARCH=arm64 go build -o silverbullet . - zip silverbullet-server-linux-aarch64.zip silverbullet - - # Linux x86_64 - GOOS=linux GOARCH=amd64 go build -o silverbullet . - zip silverbullet-server-linux-x86_64.zip silverbullet - - # macOS ARM64 - GOOS=darwin GOARCH=arm64 go build -o silverbullet . - zip silverbullet-server-darwin-aarch64.zip silverbullet - - # macOS x86_64 - GOOS=darwin GOARCH=amd64 go build -o silverbullet . - zip silverbullet-server-darwin-x86_64.zip silverbullet - - # Windows x86_64 - GOOS=windows GOARCH=amd64 go build -o silverbullet.exe . - zip silverbullet-server-windows-x86_64.zip silverbullet.exe - - # FreeBSD x86_64 - GOOS=freebsd GOARCH=amd64 go build -o silverbullet . - zip silverbullet-server-freebsd-x86_64.zip silverbullet + run: make build-server-releases - name: Release uses: softprops/action-gh-release@v1 env: diff --git a/Makefile b/Makefile index cb029597..3838903e 100644 --- a/Makefile +++ b/Makefile @@ -1,21 +1,31 @@ +LDFLAGS = -X main.buildTime=$$(date -u +%Y-%m-%dT%H:%M:%SZ) + build: # Build client deno task build # Build plug-compile deno task build-plug-compile # Build server - go build + go build -ldflags "$(LDFLAGS)" build-for-docker: build - GOOS=linux GOARCH=arm64 go build -o silverbullet-arm64 . - GOOS=linux GOARCH=amd64 go build -o silverbullet-amd64 . + GOOS=linux GOARCH=arm64 go build -ldflags "$(LDFLAGS)" -o silverbullet-arm64 . + GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o silverbullet-amd64 . docker: build-for-docker docker buildx build --platform linux/arm64,linux/amd64 --push . +build-server-releases: + GOOS=linux GOARCH=arm64 go build -ldflags "$(LDFLAGS)" -o silverbullet . && zip silverbullet-server-linux-aarch64.zip silverbullet + GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o silverbullet . && zip silverbullet-server-linux-x86_64.zip silverbullet + GOOS=darwin GOARCH=arm64 go build -ldflags "$(LDFLAGS)" -o silverbullet . && zip silverbullet-server-darwin-aarch64.zip silverbullet + GOOS=darwin GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o silverbullet . && zip silverbullet-server-darwin-x86_64.zip silverbullet + GOOS=windows GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o silverbullet.exe . && zip silverbullet-server-windows-x86_64.zip silverbullet.exe + GOOS=freebsd GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o silverbullet . && zip silverbullet-server-freebsd-x86_64.zip silverbullet + clean: rm -rf client_bundle/{base_fs,client} dist public_version.ts - rm -f silverbullet silverbullet-arm64 silverbullet-amd64 + rm -f silverbullet silverbullet-arm64 silverbullet-amd64 silverbullet.exe silverbullet-server-*.zip check: # Frontend type check diff --git a/server/cmd/server.go b/server/cmd/server.go index bd32c0f8..27d15936 100644 --- a/server/cmd/server.go +++ b/server/cmd/server.go @@ -17,7 +17,7 @@ import ( "github.com/spf13/cobra" ) -func buildConfig(bundledFiles fs.FS, args []string) *server.ServerConfig { +func buildConfig(bundledFiles fs.FS, args []string, buildTime string) *server.ServerConfig { serverConfig := &server.ServerConfig{ BindHost: "127.0.0.1", Port: 3000, @@ -168,13 +168,17 @@ func buildConfig(bundledFiles fs.FS, args []string) *server.ServerConfig { } } - // Extract the last modified time from the main binary, best effort - bundlePathDate := time.Now() - if executablePath, err := os.Executable(); err == nil { - if stat, err := os.Stat(executablePath); err == nil { - bundlePathDate = stat.ModTime() + // Determine bundle timestamp from build-time injected value, fall back to current time + var bundlePathDate time.Time + if buildTime != "" { + if t, err := time.Parse(time.RFC3339, buildTime); err == nil { + bundlePathDate = t } } + if bundlePathDate.IsZero() { + log.Println("Couldn't determine build time of binary, falling back to current time stamp") + bundlePathDate = time.Now() + } log.Printf("Determined bundle path change date: %s, %d", bundlePathDate, bundlePathDate.UnixMilli()) serverConfig.ClientBundle = server.NewReadOnlyFallthroughSpacePrimitives(bundledFiles, "client", bundlePathDate, nil) @@ -232,7 +236,7 @@ func ensureIndexAndConfig(rootSpaceConfig *server.SpaceConfig) { } } -func ServerCommand(bundledFiles fs.FS, versionFileText string) *cobra.Command { +func ServerCommand(bundledFiles fs.FS, versionFileText string, buildTime string) *cobra.Command { var hostname string var port int var c = &cobra.Command{ @@ -243,7 +247,7 @@ func ServerCommand(bundledFiles fs.FS, versionFileText string) *cobra.Command { if version, err := server.ParseVersionFromTypeScript(versionFileText); err == nil { log.Printf("Starting SilverBullet version %s", version) } - serverConfig := buildConfig(bundledFiles, args) + serverConfig := buildConfig(bundledFiles, args, buildTime) if port != 3000 { serverConfig.Port = port } diff --git a/silverbullet.go b/silverbullet.go index a0fac44a..c128c55b 100644 --- a/silverbullet.go +++ b/silverbullet.go @@ -10,8 +10,11 @@ import ( //go:embed public_version.ts var VersionFileText string +// Set via LDFLAGS +var buildTime string + func main() { - c := cmd.ServerCommand(client_bundle.BundledFiles, VersionFileText) + c := cmd.ServerCommand(client_bundle.BundledFiles, VersionFileText, buildTime) c.AddCommand(cmd.VersionCommand(VersionFileText), cmd.UpgradeCommand(), cmd.UpgradeEdgeCommand()) c.Execute() } From 4b66106c2056af43ec1e6044ce3652a59b0b8625 Mon Sep 17 00:00:00 2001 From: Zef Hemel Date: Thu, 5 Mar 2026 16:11:32 +0100 Subject: [PATCH 10/11] Reduce logging in common cases --- server/cmd/server.go | 1 - 1 file changed, 1 deletion(-) diff --git a/server/cmd/server.go b/server/cmd/server.go index 27d15936..f665688b 100644 --- a/server/cmd/server.go +++ b/server/cmd/server.go @@ -179,7 +179,6 @@ func buildConfig(bundledFiles fs.FS, args []string, buildTime string) *server.Se log.Println("Couldn't determine build time of binary, falling back to current time stamp") bundlePathDate = time.Now() } - log.Printf("Determined bundle path change date: %s, %d", bundlePathDate, bundlePathDate.UnixMilli()) serverConfig.ClientBundle = server.NewReadOnlyFallthroughSpacePrimitives(bundledFiles, "client", bundlePathDate, nil) rootSpaceConfig.SpacePrimitives = server.NewReadOnlyFallthroughSpacePrimitives(bundledFiles, "base_fs", bundlePathDate, spacePrimitives) From 91ecfcbd605cfca50159cc5843cd66b380f51700 Mon Sep 17 00:00:00 2001 From: Zef Hemel Date: Thu, 5 Mar 2026 16:12:49 +0100 Subject: [PATCH 11/11] 2.5.1 --- deno.json | 2 +- version.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/deno.json b/deno.json index 7101ae34..d4498562 100644 --- a/deno.json +++ b/deno.json @@ -1,6 +1,6 @@ { "name": "@silverbulletmd/silverbullet", - "version": "2.5.0", + "version": "2.5.1", "tasks": { "build": "make clean && deno run -A build_plugs_libraries.ts && deno run -A build_client.ts", "build-production": "deno task build --production", diff --git a/version.ts b/version.ts index cb86341e..d2f8a9cd 100644 --- a/version.ts +++ b/version.ts @@ -1 +1 @@ -export const version = "2.5.0"; +export const version = "2.5.1";