zbugs as a native desktop app in Rust, on the Zero Rust client
(rust/zero-client) and the Rune UI
framework. macOS first; it also builds and renders on Linux (CI checks
both), and Windows is next.
- The schema, queries, and mutators are
zbugs-model's, shared with the GPUI app. src/sync.rsruns the client on a thread of its own, on tokio, with thetokio-transport,tls, andsqlitefeatures ofzero-client: the client is single-threaded and Rune owns the main thread without an async executor. The UI sends it commands, and it sends back each query's rows, read as the generated row types, through Rune'sAppHandle::update.src/view.rshas the screens, withrune-kit's controls: the project and the filters (status, sort, creator, assignee, and labels, as the web app's list params), the issue list (virtual rows, paged as it scrolls, with a dot on issues changed since you looked, and Select to delete several; j and k step through it, as on the web, and so do the arrow keys), the issue with its Markdown, comments, and reactions, its assignee, visibility, notifications, and labels, and sheets to create, edit, and delete an issue, edit and delete comments, create a label, log in, and set the servers.src/images.rsfetches avatars and the images in issues and comments (ureq, over rustls) for Rune's image cache to decode, as the web app shows them.src/login.rslogs in through GitHub in the browser: the API server redirects back to a listener the app opens on 127.0.0.1 for that one login (a loopback redirect, RFC 8252 §7.3).src/settings.rskeeps the servers and the selected issue in~/Library/Application Support/zbugs-rune/settings.json; the replica is SQLite, beside it inreplica/.src/token.rskeeps the login token in the Keychain (servicedev.rocicorp.zbugs-rune, accountjwt), like zbugs-swift: the Credential Manager on Windows, the Secret Service on Linux. Where there's no store, it's atokenfile (mode 0600) beside the settings. An expired token is deleted at launch, and Log Out deletes it.
Rune is pre-release and pinned to a rev of its dev branch. It needs Rust
1.90 or later, so the app is a Cargo workspace of its own with Rune's
toolchain (rust-toolchain.toml), apart from rust/, which builds on 1.88.
Start zbugs' backend, as for zbugs-swift (it needs Node 24, psql, and
Postgres with wal_level = logical):
ZBUGS_PG=postgresql://user:password@127.0.0.1:6434/postgres \
node apps/zbugs-swift/server/server.tsTo keep its data across restarts (the database, the replica, its keys,
and so the token it prints), add ZBUGS_KEEP=1 and a working directory.
The backend doesn't create the directory, so make it first:
mkdir -p ~/.zbugs-backend
ZBUGS_KEEP=1 ZBUGS_DIR=~/.zbugs-backend \
ZBUGS_PG=postgresql://user:password@127.0.0.1:6434/postgres \
node apps/zbugs-swift/server/server.tsIt prints a token for a crew user and ready ws://127.0.0.1:4848 http://127.0.0.1:5173, the servers the app uses by default. Then:
cd apps/zbugs-rune
cargo run --releaseLog in from the account menu over the issue list with the printed token, or
through GitHub when the API server has a GitHub app (zbugs' own backend);
Servers…, in the same menu, points the app elsewhere. ZBUGS_TOKEN,
ZBUGS_CACHE_URL, and ZBUGS_API_URL in the environment override the
saved ones for that launch, and ZBUGS_RUNE_DIR
moves the settings, the replica, and the token (as a file, out of the
Keychain), so a test leaves the real login alone.
On macOS, cargo run runs the app as an app: bundle.sh, Cargo's runner
there (.cargo/config.toml), makes ZBugs Rune.app beside the executable
(target/release here) and runs the app from inside it, so the Dock and the
app switcher show its icon. The environment, the arguments, and the output
are as before. The bundle stays, to open from Finder or keep in the Dock.
The icon is compiled with Xcode's actool, which needs Xcode 26 or later.
The icon (AppIcon.icon, an Icon Composer file) is zbugs-swift's with
Rune's logo as a badge: its pixel ᚱ, from Rune's repository (MIT,
© 2026 Anomaly).
Like every Rune app, it renders headless too:
cargo run -- --snap zbugs.png --size 1180x760 (and --tree, --script;
see Rune's README).