Skip to content

Architecture Overview

This page is the mental model for how an Electrobun app is built, what’s inside it, and what happens when it runs. Nothing here is required reading to ship an app — but when you want to know why the framework is shaped the way it is, this is the place.

The short version: Hutch does everything at build time and ships nothing; your main process owns the app at runtime; webviews render UI in isolation and talk to the main process over bridges; and distribution is a small self-extractor plus verified transactional updates served from static storage.

Build-time architecture

Hutch is the only build orchestrator. When you run a build, it:

  1. Loads tasks and any optional exact Electrobun or external package-manager override from hutch.config.ts.
  2. Loads the app, build, and distribution description from electrobun.config.ts.
  3. Verifies the selected platform core archive in ~/.hutch and refreshes the generated project .hutch/devkit SDK projection.
  4. Bundles TypeScript views using dependencies already installed by Hutch’s built-in resolver or the project’s explicitly selected external manager.
  5. Bundles a Cottontail or Bun main process, or compiles a Zig, Rust, Go, or Odin main process.
  6. Assembles the platform application bundle.
  7. For canary and stable builds, creates the self-extracting wrapper, compressed update archive, metadata, optional patch, and platform installer.
  8. Applies configured code signing and notarization.

Hutch and its compiler toolchains are not shipped inside the finished app — what your users download contains only what the app needs to run.

Hutch’s built-in npm-compatible resolver is the JavaScript default: hutch install reads package.json, writes hutch.lock, and hutch pm exec runs an already-installed binary from the nearest project’s node_modules/.bin. The top-level hutch.config.ts packageManager selection may instead name npm, Bun, pnpm, Yarn, or a custom executable; in that explicit mode, hutch install and hutch pm ... delegate to the selected tool. Cargo, Go modules, and other language managers keep their normal ownership.

Runtime architecture

Every Electrobun application contains:

  • a small platform launcher
  • the selected main-process runtime or native executable
  • Electrobun Core and the platform native wrapper
  • bundled application code and view assets
  • optional CEF and WGPU libraries
  • updater metadata and patch tooling

At runtime there’s a clear division of authority. The main process owns application state and privileged native objects; it opens windows, views, menus, trays, and GPU surfaces through Electrobun Core. Each webview is a separate native browsing context that communicates with the main process only through Electrobun’s event and RPC bridges — page JavaScript never holds privileged APIs directly. The renderer controls its exact operating-system process model.

Main-thread model

Native GUI frameworks require their event loop on the process main thread, and Electrobun respects that instead of fighting it. Cottontail and Bun run your application JavaScript on a worker while Electrobun Core owns the main-thread event loop. Native SDKs expose the same arrangement directly: application code initializes the core, starts its work on another thread where needed, and enters runMainThread.

Application resources

The exact platform layout differs, but the resource model is consistent:

Resources/
|-- app/
| |-- bun/ # Bundled JavaScript main process, when selected
| `-- views/ # Bundled and copied webview assets
`-- version.json # Installed version and update metadata

Use the views:// URL scheme to load files under Resources/app/views — it resolves to the right place on every platform, so you never construct filesystem paths to your own assets.

On macOS, executables and dynamic libraries live under Contents/MacOS, assets under Contents/Resources, and optional frameworks under Contents/Frameworks. Windows and Linux bundles use equivalent bin and Resources directories.

Webview engines

System views are the default, which is a big part of why Electrobun apps stay small — the browser engine is already on the user’s machine:

  • macOS: WKWebView
  • Windows: WebView2
  • Linux: WebKitGTK 4.1

When a pinned Chromium engine matters more than download size, bundle CEF. macOS and Windows applications can select the renderer per view. A Linux process uses its CEF wrapper when CEF is bundled and GTK WebKit otherwise.

Two custom elements extend what a webview can hold. <electrobun-webview> creates a separately composited native browser view — a real independent browsing context, not an iframe. <electrobun-wgpu> places a native GPU surface in the same layout. Electrobun synchronizes their bounds, visibility, masking, and input behavior with the host document, so they behave like ordinary elements on the page.

Distribution wrapper

A canary or stable release wraps the full runnable app in a small native self-extractor whose payload is a Zstandard-compressed archive. On first launch, the wrapper verifies and extracts the application to its managed location, then starts the inner app — from the user’s perspective, the app just opens.

Hutch emits a DMG on macOS, a Setup ZIP on Windows, and a self-extracting setup archive on Linux. See Bundling and Distribution for the artifact layout.

Updates

The installed app compares its local hash with hosted update metadata, follows available hash-named patches or downloads the named compressed archive. A native post-exit helper then validates the bundle identity and replaces the app with rollback protection before relaunching it. The same transaction is used on Windows, macOS, and Linux; Windows uses Task Scheduler only to run the helper after locked processes exit.

There’s no update server to run — static object storage is sufficient. The application decides when to check, download, install, and relaunch through the Updater API.

Development output

Dev builds skip the distribution artifacts and route main-process, webview, and native diagnostics to the invoking terminal. System webviews and CEF both forward browser console output in development mode, so console.log in a view shows up next to your main-process logs.