Spirefy Studio is one app built on a framework we give away. The framework, desktop/, is the reusable shell: a native window, the operating system’s WebView, the application chrome (menu bar, status bar, and layout), the bridge between web and host, the Zora plugin engine, capability gates, an activity log, and a record-and-playback test harness. The React UI is built into the framework and embedded at build time. Build your own plugin-based desktop app on it and your app gets the same shell, the same engine, the same plugin format, and the same WIT contracts.
One process, not two
Most desktop frameworks marshal IPC between a UI process and a backend process. Spirefy does not. The WebView and the native host live in the same process, and calls cross through direct WebView primitives (executeJS in, postMessage out, plus a DOCUMENT_START bootstrap injection for cold-start state). There is no second process, no JSON serialized across a process boundary, and no context switch on every interaction.
Spirefy Studio [native host process]
│
├── Frameless WebView
│ └── bootstrap injected before first paint (no flash)
│ └── application shell (part of the desktop framework)
│ ├── chrome: menu bar, status bar, layout
│ └── slots that host page and panel plugins
│
└── Zora plugin engine [Wasmtime, compiled in, with a disk cache]
├── instance pools (WASM to host to WASM reentrancy)
├── host functions grouped by concern:
│ model, HTTP, filesystem, profile, view updates, AI
└── plugins, all swappable
Native WebView, no bundled browser
The app uses the operating system’s own WebView rather than shipping a browser engine. A frameless window gives full control over the chrome (menu bar, traffic lights, drag regions), and DOCUMENT_START bootstrap injection paints the React tree with the right theme on the first frame.
| WebView | Platform | Notes |
|---|---|---|
| WKWebView | macOS | Native, GPU-accelerated |
| WebView2 | Windows 10+ | Edge / Chromium engine |
| WebKit2GTK 4.1 | Linux | GTK3 |
Profiles: plugins you can turn on and off
The Plugin Manager treats which plugins are loaded as user-controllable state. Load only what today’s task needs, save that as a profile, and switch profiles later or install something new from the marketplace. Profile switches are hot: plugins load and unload in seconds, taking their menu items and panels with them, while plugins untouched by the switch keep their state. The framework swaps in place rather than tearing down the React tree, so there is no window reload.
Embedding the engine in your own program
Zora is a Zig library today. Embedding from a non-Zig host through a stable C ABI is on the roadmap; the surface is small (engine init, plugin load, function call, host-function registration), and we want to lock it down before exposing it.
const zora = @import("zora");
pub fn main() !void {
var engine = try zora.pe.Engine.init(allocator, .{});
defer engine.deinit();
// Load plugins from a directory, instantiated lazily on first call.
_ = try engine.loadPluginsAndInstantiate(&.{ "plugins/my-plugin.zp" });
// Call a function on a loaded plugin.
const result = try engine.callPlugin("my-plugin", "process", payload);
defer allocator.free(result);
}
To be precise about status: building a plugin-based app on the Zig framework is what Spirefy Studio itself does, and the framework is the supported path today. A C ABI or static library for embedding the host inside a Rust, Go, or C program is on the roadmap, and would take a small C-ABI shim that is not built yet. The plugins are the polyglot part: WebAssembly written in Zig and Rust now, with more languages reachable as the toolchain matures.