ZenNotes 2.51.0: the ecosystem boundary release, with a pacman updater fix, Cloud vault retirement, and Vim block edits
Released on September 16, 2026 as v2.51.0, at 8ff2cb86. The release branch v2.51.0 was fast-forwarded into main through PR #796, which carries PR #789 (ecosystem boundaries) and PR #795 (server cutover). Two reports came from @uNyanda and one from @vlcinsky; each is closed with a commit-linked comment. Channel and validation details are in the sections at the end.
✨ Features
- The command-line tool is
zn. The desktop's bundled command is nowzn, the same name as the standalone Go CLI and terminal app. Opening ZenNotes once upgrades an existing desktop-managedzenshortcut in place; your commands keep working. Settings > CLI shows what is installed and offers a reviewed Repair shortcut when a shortcut points at a moved Mac app or an old AppImage, showing the old target, the replacement and the backup path before it touches anything. Homebrew and manual installations stay managed by their own installer. This version still runs the bundled Node engine; the Go engine arrives in the next release once Go CLI v0.2.0 is published and pinned.ZENNOTES_CLI_ENGINE=legacykeeps the previous engine during the transition. - Note creation dates travel with the vault. Creation metadata is now app-owned, kept beside the note under
.zennotes/note-metadata/, and moved, renamed and deleted with it by the desktop, the MCP server and the CLI, so creation dates survive syncs, copies and filesystems that drop birth times. Cloud sync carries the metadata as vault content. A sidecar that links outside the vault is refused, and malformed metadata fails a save before the Markdown is touched. - Self-hosted offline shell installs again. The self-hosted web app registers its service worker from the bundled code instead of an inline script, which the server's Content Security Policy had been blocking silently. Reaches self-hosters with the server release that embeds this web build.
🐛 Fixes
.pacmanupdater: install shows through to relaunch (#790). On Arch the in-app update ran pacman through pkexec, and pkexec fell back to a textual agent on a terminal the app does not have, so nothing installed and Relaunch did nothing. The updater now has an installing phase, requires a graphical polkit agent, tells "you dismissed the dialog" (exit 126) from "no agent could run" (exit 127), and offers recovery from Details > About. The AUR package now conflicts with the oldZenNotespackage name so the two cannot coexist.- Deleted Cloud vault no longer shows as up to date (#791). A vault deleted on the server kept reporting successful syncs. Only the authenticated vault-level confirmation now retires the remote association; a missing item, revision or upload stays a sync error rather than being mistaken for a deletion. A retired vault is shown as such, auto-sync stops, and Cloud settings offer unlink.
- Vim visual-block edits apply to every row (#792). Block insert, append and change only changed the first row because CodeMirror kept a single selection. The Vim extension now enables multiple selections and the block highlighter paints the full rectangle even across short rows.
🧰 For contributors
- The ecosystem boundary refactor, PR #789: app-core, shared-domain, bridge-contract and shared-ui are consumed as packed packages with a published contract; the share viewer and the self-hosted web bundle ship as immutable, checksummed artifacts (
web-*,viewer-*releases); mobile and website consumers pin them. - The Go server moved to ZenNotes/znserver, PR #795.
apps/server, the Dockerfile and the Docker publisher left this repository;tooling/server-release.jsonpins the znserver release andtooling/scripts/server-binary.mjsdownloads and checksum-verifies it fordev:server,dev:web-stackand the browser harness.docker-compose.ymlruns the publishedadibhanna/zennotesimage. - #790: d1aa811b. #791: d7bdfa96, with
packages/shared-domain/src/cloud-vault-availability.tsas the single classifier. #792: 7f1ec2ae. CLI migration groundwork: b6c3b9da (terminal-release.json,tooling/scripts/terminal-artifact.mjs,docs/specs/desktop-cli-tui-migration.md). Service worker: 019c474b. Handoff docs: 4d4932f7. Version bump: 0964cee9. - CI follow-ups on the release branch: 5cd4eb5f (CodeQL: launcher and binary read through one handle), 8ff2cb86 (POSIX-only suites skip on Windows).
- Closure comments: #790, #791, #792, each linking the fixing commit with local verification steps.
In-app help (help.ts) documents the zn command, the terminal app and existing installations; the website docs mirror it (docs.blade.php CLI section, docs-sections.json zn tui entry).
Demos
No clips shipped with this release. Two of the three fixes need environments the recorder does not have (an Arch install with pkexec for #790, a Cloud account whose vault is deleted server-side for #791); the Vim block-edit clip for #792 can be recorded from the built app later and attached here and on the website entry.
Verification
- On the
v2.51.0branch: typecheck clean;test:runapp-core 2371 tests (210 files) and desktop 849 tests (58 files); terminal artifact and launcher node tests 12; web-dist lock 1; web artifact 5;npm run build7 of 7 tasks; the builtindex.htmlcarries no inline script and the bundle registerssw.js. - Self-hosted web shell over CDP (headless Chrome, Vite dev of this branch on 5180, the pinned znserver v2.50.4 binary on 7878 with an isolated vault and token): token sign-in and first-run skip, seeded note and task listed from the server, Cmd+P opens the note, typing saves exact UTF-8 bytes through the HTTP bridge, Tasks view shows the new task, zero browser errors.
- Packaged app from
npm run packon the branch (dist/mac-arm64/ZenNotes.app, 2.51.0, Developer ID signed): a CDP page target exists after launch with both stores isolated andZEN_PERF=1; first-run tour and Skip setup; seeded vault listed; Cmd+P opens a note; typing saves exact UTF-8 bytes; new note createsUntitled.mdand saves its body; Tasks view shows the open task; Settings opens and the CLI, Cloud, About, Vault and Editor tabs render; zero renderer errors, zero main-process error lines. - CodeQL on the release PR flagged two check-then-use sequences in the new terminal runtime; fixed by reading the launcher and binary through single handles (
Fix(cli): read the launcher and binary through one handle). The Windows build job then caught two suites that only run where POSIX shells exist (CLI shortcut repair, .pacman install); they skip on win32 now, matching the file's existing per-test skips.
Distribution channels
- Release workflow: 35147840800. Linux x64 and arm64 finished first with all five installer formats each plus both updater manifests, then Windows (installer, portable ZIP, blockmap, manifest), then macOS (Apple silicon and Intel DMGs, ZIPs, blockmaps, manifest) with Developer ID signing and notarization.
- AUR:
44f1359in the separatezennotes-binclone (zennotes-bin 2.51.0-1), mirrored on desktopmainas2d937d74. Downloaded tarball SHA-2564dac4ef574eb9bb2d510a9118951a978d5334416fc1a1970235eac75e7ed8c95matched GitHub's asset digest. - Nix:
nix-update.ymlrun 35149151215 computed the hashes and opened bot PR #798, lifted exactly ontomainas4e4a7d0eand closed.nix-build.yml35149433597 passed onmainfirst try. - nixpkgs: PR #561418 restacked onto current master as
zennotes-desktop: 2.45.0 -> 2.51.0(fork commit1b9f0c1f, body innixpkgs-pr-body.md). - Homebrew:
bcce447in the tap (zennotes 2.51.0), mirrored in the repo as53c7e107(on the 2.51.1 release branch, which fast-forwards intomain). The cask pins both DMG digests from the GitHub API (arm6494c62da00d885d2cc5605b8ef7943f73616b5acaeb54cb4f6bedab43064946b2, x64ffda8ae09bdf9e634329535ff98b92e70e0cb78f263139046104772330e6320c). - Website: the CLI docs, the terminal walkthrough and the Cloud creation-metadata allowlist went out first as website PR #26 (
389833b); the 2.51.0 releases entry followed asbe9a294(full suite 796 tests), deployed, live. - Boundary artifacts from the tagged commit: web-2.51.0-web.hd4bbb4979ae81a3e, core-2.51.0-core.h49d73b531d346192 (app-core plus bridge-contract and shared-domain
boundaries.h18d39d9887df8897), viewer-2.51.0-viewer.h98c5c0085c5fe6b4; each manifest names source8ff2cb86, clean, and every archive digest was verified before publication. - Server: ZenNotes/znserver v2.51.0 embeds the web artifact above; Docker
adibhanna/zennotes:2.51.0,2.51andlatestpublished from that tag by the server repository's publisher (multi-arch, labels name znserver2f3070c4). This repository pins that release intooling/server-release.json(395e9106). - Go CLI: ZenNotes/tui v0.2.0 (six archives, checksums verified); the desktop pins it in 2.51.1.
npm run verify:channels -- 2.51.0passed: Homebrew tap, AUR, Nix, and all seven website download redirects serve 2.51.0.
Release validation
Release PR #796's CI passed on macOS, Ubuntu, Ubuntu arm64 and Windows, plus CodeQL (after two file-system race findings in the new terminal runtime were fixed), the Arch package check and the production dependency audit, before the fast-forward. The release workflow passed every platform job. nix-build.yml 35149433597 rebuilt the desktop Nix package from the lifted hashes on main.
Local gates before the tag: typecheck, test:run (app-core 2371, desktop 849), the terminal, web-dist lock and web artifact node tests, npm run build, the mac app packaged from the branch (npm run pack, 2.51.0, Developer ID signed) exposing a CDP page target with both stores isolated and ZEN_PERF=1 and passing the desktop smoke, and the self-hosted web shell over CDP against the pinned server. The Windows runner caught two POSIX-only suites from the later work; they skip on win32 now.
The release was cut from a scratch worktree of the boundary PR head. Two lessons for the record: actions/checkout treats an abbreviated source_commit as a branch name (pass the full SHA to the artifact workflow), and zennotes-server --help starts the server, so never use it as a probe.