Looking for the case rather than the internals? Start with why Spirefy.
One native app, every feature a plugin
Spirefy Studio is one native process. The interface runs in the operating system’s own WebView, and the engine that runs every feature is Wasmtime, the Bytecode Alliance’s production WebAssembly engine, compiled straight into the app. There is no second process to marshal data to, and no browser engine shipped inside the download.
Most desktop frameworks split a UI process from a backend process and serialize JSON across that boundary on every interaction. Spirefy Studio does not. The WebView and the native host live in one process, and calls cross through direct WebView primitives.
IPC-based (Electron, Tauri, Wails):
UI process → serialize JSON → cross process boundary → native process
→ deserialize → run → serialize result → return → deserialize
Spirefy (same process):
WebView thread → executeJS / postMessage → native host functions
(bootstrap state injected before the first paint)
Because the engine is built in, every meaningful feature is a WebAssembly plugin: pages, importers, exporters, linters, tools, and the components they are made of. The app starts with a baseline set; the rest are marketplace plugins.
You decide which of them run. Only the plugins in your active set are live; the rest stay idle and do no work. Save a set as a profile, name it, and switch between profiles in place with no window reload, because the engine activates and deactivates only the difference. So you can keep a deep library of plugins installed and still run a focused app, bringing a fuller set online the moment a task calls for it.
One source tree cross-compiles to native binaries for every target, with no platform-specific branches.
| Platform | Versions | WebView |
|---|---|---|
| macOS | 11.0+ (Big Sur), Apple Silicon and Intel | WKWebView |
| Windows | 10+ | WebView2 (Edge / Chromium) |
| Linux | Kernel 5.4+ | WebKit2GTK 4.1 (GTK3) |
x86_64 and ARM64 are supported today, ARM64 including Apple Silicon, AWS Graviton, and Raspberry Pi 4 and 5 on a 64-bit OS. RISC-V is on the roadmap; the toolchain and Wasmtime both support it, and we have not validated it end to end yet.
On size: a default build links Wasmtime, the engine, the React interface, and on-device AI, so it is larger than a bare engine and far smaller than an app that ships its own Chromium. It lands in tens of megabytes, not the hundreds a bundled-Chromium app carries.
Extend it without forking it
A plugin binds to a typed interface, not to the application. You define a capability as a WIT interface (WebAssembly Interface Types), and any app that hosts that interface can load the plugin. Build a feature once, and it runs wherever the interface exists.
Traditional plugin systems:
├── Photoshop plugin → only works in Photoshop
├── VS Code extension → only works in VS Code
└── Same feature in three apps? Build it three times.
Interface-bound plugins:
WIT interface: "spirefy:image-processor@1.0"
(resize, crop, filter, export)
│ │ │
▼ ▼ ▼
Photo app Doc editor CLI tool
└──────────────┼──────────────┘
▼
One image-processor plugin
Works in every app that hosts the interface.
The interface itself is built this way. Spirefy Studio’s UI is a full React application, yet it is assembled from independent plugins that mount pages, panels, and components into named slots in the shell. The plugins never import one another. They coordinate through the shared model, where every change is an observable command batch any plugin can subscribe to, and through a shared event system. Add a plugin and its UI and menu items appear; remove it and they go with it, leaving the rest untouched. Composing a desktop interface from sandboxed plugins this way is unusual, and it is what keeps features decoupled as the app grows.
Every UI action crosses the bridge as a typed, timed call, which is also what lets Spirefy Studio record a session and replay it: the same recording serves as a live demo, a tutorial script, or an automated UI test.
Go deeper: the plugin model.
Sandboxed and signed
Every plugin runs in a WebAssembly sandbox with explicit capability grants. The manifest declares the network hosts and filesystem paths the plugin may touch, and the host enforces that at the boundary.
# plugin.yaml
resources:
network:
http: true
allowed_hosts:
- "api.example.com"
- "*.amazonaws.com"
filesystem:
- "/data/imports"
By construction, a plugin cannot reach the filesystem outside its granted paths, call a network host it did not declare, read the host’s environment, touch another plugin’s memory, run a system command, or crash the app. A plugin that reaches for a host it was not granted is denied at the host function, not trusted to behave. On POSIX the host also refuses to load a world-writable plugin file rather than silently correcting it.
Marketplace plugins are signed with ML-DSA-65 (NIST FIPS 204), the post-quantum signature scheme standardized in 2024, and the signature is verified before the plugin’s code is decompressed, so a tampered package never runs. Post-quantum rather than Ed25519 because a plugin signature needs a multi-year shelf life.
Go deeper: capabilities and signing.
One model for all your API specs
Most tools treat each spec as a separate file. Spirefy Studio imports them into one model: a single shape that holds OpenAPI 2 and 3, AsyncAPI, Arazzo, Postman, Insomnia, Bruno, and more at once, without forcing each one through another spec’s structure. Spirefy’s founder co-authored Arazzo, helped author RAML 1.0, and is active in the OpenAPI Initiative. The model is a superset of Arazzo: everything Arazzo expresses, plus conditions and loops, retry policies, per-step timeouts, custom WebAssembly logic steps, and UI metadata that Arazzo leaves out.
Import is broad. The baseline formats round-trip; writing the marketplace formats back out arrives format by format, as plugins rather than core changes.
| Format | Import | Export | Availability |
|---|---|---|---|
| OpenAPI 3.x | Yes | Yes | Baseline |
| OpenAPI 2.0 (Swagger) | Yes | Via 3.x | Baseline |
| AsyncAPI 3.x | Yes | Yes | Baseline |
| Arazzo 1.1 | Yes | Yes | Baseline |
| Postman v2.1 | Yes | No | Marketplace |
| Insomnia v4 | Yes | No | Marketplace |
| Bruno | Yes | No | Marketplace |
| HAR | Yes | No | Marketplace |
| cURL | Yes | No | Marketplace |
HTTP files (.http) | Yes | No | Marketplace |
| Hoppscotch | Yes | No | Marketplace |
| Thunder Client | Yes | No | Marketplace |
| RAML | Yes | No | Marketplace |
| API Blueprint | Yes | No | Marketplace |
| OpenAPI Overlay | Yes | n/a | Marketplace |
Baseline formats ship with the app. Marketplace formats are plugins you add inside the app. A format is not listed as supported until it is verified against the model.
Once your specs are in one model, you can hold them to a standard. A Spectral linter ships as a WebAssembly plugin, and the engine is pluggable, so your team’s rules are not tied to one vendor. The larger payoff works on that same model: turning a drifted estate (the same User described five ways across five services) into one agreed set of schemas each API references through OpenAPI external $ref. The model carries provenance, conflict-aware merge folds the duplicates into one canonical schema, and Spirefy Studio surfaces every conflict for a person to resolve rather than collapsing different schemas silently.
Go deeper: the unified model and command protocol.
Author plugins in your language
Plugins are WebAssembly, so you write them in a systems language that targets WASM and they run with no runtime overhead. Zig and Rust are the proven languages today: the built-in Spectral linter is a Rust plugin, and the rest are Zig. More WASM-target languages are reachable as the toolchain matures.
Your bindings come from your WIT interface, and you implement the functions it declares.
// Bindings come from `spirefy generate --lang=zig`.
// You implement the functions your WIT interface declares.
pub fn lint(model: Model) ![]Finding {
var findings = std.ArrayList(Finding).init(allocator);
for (model.operations) |op| {
if (op.summary == null) try findings.append(.{
.rule = "operation-summary",
.target = op.id,
});
}
return findings.toOwnedSlice();
}// Bindings come from `spirefy generate --lang=rust`.
// You implement the functions your WIT interface declares.
fn lint(model: Model) -> Result<Vec<Finding>, Error> {
let mut findings = Vec::new();
for op in model.operations {
if op.summary.is_none() {
findings.push(Finding {
rule: "operation-summary".into(),
target: op.id,
});
}
}
Ok(findings)
}The spirefy CLI scaffolds a project, builds it to WebAssembly, validates the exports against your WIT interface, then packages, signs, and verifies it. It also pairs with a model you already run: feed Spirefy’s context prompt to a local or hosted LLM, describe the plugin, and let it draft the source you build, so you are not tied to a paid coding assistant.
Go deeper: plugin authoring and the quickstart.
On-device AI
Spirefy Studio ships AI built in: an on-device model (llama.cpp, with CPU, Metal, or Vulkan acceleration) that runs entirely on your machine, so your APIs never leave it. Plugins reach it through a single host function, so a plugin asks for a completion without wiring to the model itself.
The model is tuned to read and edit the unified model directly, and it powers AI authoring: you describe a change in plain language and the app applies it as typed, reviewable commands. Because it is tuned to the unified model, its edits land correctly in that shape, which a general-purpose model cannot reliably do. Unlimited use is $5 a month, verified by a sign-in check.
Collaboration you can host yourself
Several people can work on the same model at once, and the runtime that makes it safe is open source: collab is its own MIT-licensed project, a Zig client and a small Go server, with no dependency on Studio.
Every edit, whether from you, an import dropping in thousands of operations, or the AI, already flows through one ordered writer. Collaboration extends that across the network. Each change becomes a small operation that is signed, encrypted, and sent to everyone in the session, where it merges into their copy of the model. There is no central server deciding the truth. Edits made at the same time converge to the same result on every machine, and a resent or reordered operation changes nothing.
Ad-hoc, you host a session and others on your network join directly, with no server and no account; the session key comes from the room code, so changes stay in the room. For a team that wants more, the Go server holds a persistent shared workspace, always on, with accounts and roles, forwarding only ciphertext it cannot read, so the model is there whether or not anyone is hosting. An organization can run that server on infrastructure it owns. The same-network sessions carry no added cost; the persistent workspace and the organization controls unlock on sign-in. It is one of our newer surfaces, developed in the open.
Build on it
The shell Spirefy Studio runs on is a framework we give away. desktop/ is a native window, the operating system’s WebView, the app chrome, the plugin engine, capability gates, an activity log, and a record-and-replay harness, with the React UI built in. Build your own plugin-based desktop app on it and you get the same engine, the same plugin format, and the same WIT contracts. The host is Zig today; embedding it inside a Rust, Go, or C program through a stable C ABI is on the roadmap, and we want to lock that surface down before exposing it.
The framework has a page of its own. Go deeper: embedding the engine.