Blog
How Koru is built: a native Rust app, not Electron
By Steven Liechti, founder of Koru · Published
Short answer
Koru is a native desktop app written in Rust, not Electron. winit opens the window, vello draws every pixel on the GPU, parley lays out the text, and AccessKit publishes the screen to assistive technology. Workspaces are Loro CRDT documents in a local database, and iroh syncs your devices directly.
I build Koru, a free, open-source (MPL-2.0), local-first workspace for notes, docs and files. If you're looking for a native Obsidian alternative, the first question is fair: is it another website in a window? It isn't. Here is the engineering answer: what Koru is built on, why, how the pieces fit, and what it costs.
Everything here can be checked in the public repository, koruworks/mono. Versions are the ones pinned in its Cargo.toml on October 20, 2026. Koru is pre-1.0, so details move.
Why build a notes app in Rust?
Two reasons. The first is the product: a notes app holds years of writing, so I wanted it to open straight from your own disk, draw with the GPU and own its data format. Rust gives that control without a garbage collector or a runtime between you and the operating system, with memory safety the compiler checks.
The second is how software is written now. I believe Rust is the future of agentic programming. More and more code is written together with coding agents, and Rust's type system and borrow checker turn a large class of mistakes into compile errors before anything runs. A strict compiler is a reviewer that never gets tired, for people and agents alike. The repository's AGENTS.md is written for both.
Koru is one Cargo workspace on the 2024 edition, more than 170 packages across apps/* and crates/*, with the toolchain pinned in rust-toolchain.toml.
What is Koru built with?
The short version, with the versions Cargo.toml pins:
| Layer | Crate | What it does in Koru |
|---|---|---|
| Windows and input | winit 0.30 | Opens native windows and delivers keyboard, mouse, scale factor and redraw events. Draws nothing. |
| Rendering | vello 0.8, on wgpu | Draws every pixel with GPU compute shaders, over DirectX 12, Vulkan or Metal. |
| Text layout | parley 0.11 | Shaping, font fallback, bidirectional text and line breaking. Hands positioned glyphs to vello. |
| Accessibility | accesskit 0.24, accesskit_winit 0.32 | Publishes a tree of what's on screen to the platform accessibility API. Wired up on Windows today. |
| Terminal tabs | alacritty_terminal 0.26.0 | Alacritty's terminal state machine, behind Koru's own types. |
| Documents | loro 1.13.9 | The CRDT every workspace is stored as: a movable tree of nodes, with rich text. |
| Local database | rusqlite 0.37, bundled SQLite | Stores the Loro snapshots and updates, the list of workspaces and the search index. |
| Device sync | iroh 1.1 | Direct device-to-device connections over QUIC, with a relay when two devices cannot reach each other. |
| AI tools | koru-mcp | A local MCP server, so an MCP client can read and edit your workspace. |
How does Koru draw its window without a browser engine?
winit gives Koru a window and a stream of events, and nothing else: no widgets, no text, no canvas. Every window is created hidden and shown only once its first frame has been presented, so you don't see an empty window flash up.
vello fills the window. Most 2D renderers rasterize on the CPU; vello does it in compute shaders. Each frame, Koru builds a scene, a flat list of drawing commands, and the GPU works out every pixel in parallel, including anti-aliasing, curves and clipping.
parley sits between your text and vello. Drawing text means shaping (which glyph each character becomes), font fallback, bidirectional ordering, line breaking and cluster boundaries. parley does all of that and returns positioned glyphs, which vello draws directly. The two crates share their font and color types through peniko, so there's no conversion step. Koru turns on parley's complex-scripts feature because two of the eight languages it ships, Japanese and Chinese, need dictionary-based line breaking.
One exception is worth stating. The in-app support chat is a web page, shown in the system's own web view (WebView2 on Windows) through the wry crate. It's the only part of the app that links a web engine. The editor, the page tree and the tabs are all drawn by Koru.
If Koru draws everything itself, how do screen readers work?
This is the honest cost of drawing your own interface. A browser engine derives an accessibility tree from the page's markup; Koru has to build its own, and assistive technology sees exactly what Koru publishes and nothing else.
AccessKit is how Koru publishes it. The accesskit crate is the schema: roles, actions and a tree of nodes. accesskit_winit is the adapter that turns that tree into the platform's accessibility API, which on Windows is UI Automation. parley 0.11 ships its own AccessKit projection of a text layout, so the mapping between byte offsets and the characters a screen reader announces comes from the library that laid the text out, not from a second implementation in Koru.
Today the adapter is wired up on Windows. The macOS side still has to be verified.
How do terminal tabs work?
Koru opens terminal sessions as tabs beside your documents, and coding agents such as Claude Code or Codex run in them. Terminal emulation is hard to get right, so Koru doesn't write its own. It uses alacritty_terminal, the terminal state machine from the Alacritty project, pinned to exactly 0.26.0 because Koru tests against its Unicode width table.
It lives behind a small crate, koru-terminal-emulator, and Alacritty's types never cross that boundary: a caller can only ask for a window of rows or a page of scrollback. The pseudo-terminal comes from portable-pty on macOS and from Koru's own adapter on Windows, which starts the shell suspended inside a Job object.
Where does Koru keep your notes?
In a local database on your device. Each workspace is a Loro document. Loro is a CRDT, a conflict-free replicated data type: it stores operations with enough identity to order them, and any two copies that have seen the same operations compute the same state, whatever order they arrived in. Koru uses Loro's movable tree for the page hierarchy, rich text with marks for each node's text, and maps for attributes. The movable tree decided the library: moving the same subtree to two places at once is the hard case in any outliner, and Loro resolves it, including refusing to create a cycle.
The documents are stored with rusqlite, on a bundled SQLite. A catalog database lists your workspaces, and each workspace has its own database with its Loro snapshots, checkpoints and incremental updates. Attachments are files named by their content hash. Writes use synchronous = FULL, so an acknowledged write is on the disk. Migrations only move forward, and a database newer than the app is refused, never rewritten. One process, the Store, is the only component that writes this state.
Loro is pinned to an exact version, because a routine lockfile update once pulled in a release that changed undo semantics.
If you only use one device, Koru never exchanges an operation, and the snapshot is simply the file format. The option to sync costs a single-device user nothing.
How do devices sync without a server?
With iroh. When you link a second device, the two connect directly over QUIC through iroh and exchange their changes. When they can't reach each other directly, a relay forwards the traffic. Koru's relay is a thin wrapper around iroh's own relay server, and it's in the same repository. Device-to-device sync is free.
Koru Cloud, the optional paid cloud sync at $9.99/month for 100 GB, is a separate hosted service. It is proprietary, its server code is not in the repository, and a build from source has it switched off. The app works fully without it.
How is this different from an Electron app?
Electron, in its own words, embeds Chromium and Node.js into its binary, so a team can keep one JavaScript, HTML and CSS codebase and ship it on several platforms without native development experience. That's a real advantage, and it's why so many good apps use it. Obsidian is one: its help pages call Electron the framework on which Obsidian is built.
The differences are architectural:
- Rendering: an Electron app's interface goes through Chromium's layout and rendering engine. Koru draws its own with vello on the GPU.
- Text: Electron gets shaping, fallback and line breaking from Chromium. Koru gets them from parley.
- Accessibility: Chromium derives an accessibility tree from the page. Koru builds its own and publishes it through AccessKit.
- Code: an Electron app runs JavaScript on Chromium and Node.js. Koru compiles to native code, with no JavaScript runtime in the editor.
- Extending it: web technology makes plugins and themes natural. Koru has no plugin system.
I'm not going to put numbers on startup time or memory here. I haven't published measurements that compare the two fairly, and a benchmark I can't back up isn't worth printing. What I can say is what the architecture does: your workspace opens straight from your own disk, with nothing to load from a server first.
The costs run the other way, and they're real. Drawing your own interface means owning text input, scrolling, focus and accessibility, which a browser hands you. vello has no software fallback, so Koru needs a GPU with DirectX 12, Vulkan or Metal. And the first build from source takes several minutes, much of it wgpu and its shader compiler, naga. I think it's the right trade for an app you keep open all day, but it is a trade.
Can I build Koru myself?
Yes. The repository is public under MPL-2.0. You need Rust through rustup (plus the MSVC build tools on Windows), sccache, which isn't optional because the repository sets it as the compiler wrapper, a GPU with DirectX 12, Vulkan or Metal, and Python 3 for the repository's checks.
- Clone koruworks/mono.
- On Windows, run
koru.bat setup. It sets up whatever is missing and lists what it could not do. - Run
koru.bat run. The first compile takes several minutes; later builds are incremental.
On macOS, ./koru.sh setup and ./koru.sh run do the same. Every commit is signed off under the Developer Certificate of Origin.
If you're here for a native Obsidian alternative, see how Koru compares with Obsidian, or download Koru for Windows or macOS. It's free, with no account. The code is at github.com/koruworks/mono, and I post about the build on X as @liechti_dev.
Frequently asked questions
Is Koru built with Electron?
No. Koru is a native desktop app written in Rust. It draws its own interface with vello on the GPU and lays out text with parley. The only web view is the in-app support chat, which uses the system's web view through wry.
Is Koru open source?
Yes. The Koru app is open source under MPL-2.0, at github.com/koruworks/mono. Koru Cloud, the optional paid cloud sync, is proprietary and not part of the repository.
