返回资讯中心
外部精选
软件工程
#开发者实时流

Reeve – A native PWA launcher in Rust to replace Electron

Reeve is a native PWA launcher for Windows. It replaces heavyweight Electron bundles and Chromium-per-app patterns with a single shared WebView2 runtime, kernel-enforced memory caps, per-app data isolation, and a capabi…

Hacker News 最新marcelnazare0502026/10/27 · 今天30 分钟阅读

以下正文同步自 Hacker News 最新,版权归原站所有,已转换为易读排版。

Reeve is a native PWA launcher for Windows. It replaces heavyweight Electron bundles and Chromium-per-app patterns with a single shared WebView2 runtime, kernel-enforced memory caps, per-app data isolation, and a capability-gated bridge that gives web apps native superpowers without sacrificing the sandbox.

Every Electron app ships its own Chromium. Open five apps, and you are running five browser process trees. The RAM cost is enormous, startup is slow, and none of those bundled browsers can share a renderer or GPU process with each other.

Electron model: Reeve model: ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ Chromium │ │ Renderer │ │ Renderer │ │ Renderer │ │ + App A │ │ App A │ │ App B │ │ App C │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ ┌──────────┐ ▲ ▲ ▲ │ Chromium │ └──────────┴──────────┘ │ + App B │ shared WebView2 └──────────┘ browser process ┌──────────┐ │ Chromium │ ┌──────────────────────┐ │ + App C │ │ Job Object (caps) │ └──────────┘ └──────────────────────┘

Five Electron apps: five full Chromium trees. Five Reeve apps: one browser process, five isolated renderers, one resource budget, five isolated storage profiles.

  1. Extreme resource efficiency. Every app runs inside a single shared WebView2 environment. One browser process, one GPU process, N isolated renderers. The launcher itself is a lightweight Rust process — no bundled Chromium, no Node runtime.

  2. Per-app data isolation. Every app runs in its own WebView2 profile. Separate cookies, localStorage, IndexedDB, and Service Worker caches. Logging out of Slack doesn't log you out of Notion. The launcher grid's "Clear Data" button is now genuinely scoped to a single app.

  3. A native bridge with real capabilities. Web apps request file-system access, native notifications, and other desktop features through a capability-gated IPC bridge. Every call is validated against a per-app manifest — capability check, scope check, origin check — before the native function executes. Nothing is granted by default.

  4. A managed dashboard. Live memory, editable permission grants, per-app kill switch, and deep telemetry. The savings are visible; the control is real.

For users. Old laptops feel new again. Battery life extends because you are not running five browser engines. Memory is capped at the kernel level, so one runaway app cannot starve the others. Apps can't see each other's cookies. When something misbehaves, the dashboard shows it and stops it in one click.

For developers. Build one PWA that works everywhere. Get desktop-grade capabilities through a predictable, documented bridge — without bundling Chromium, without learning a new framework, and without writing your own session store.

┌─────────────────────────────────────────────────────────┐ │ Reeve supervisor (Rust process) │ │ ├─ App catalog (SQLite) │ │ ├─ Permission store + capability checks │ │ ├─ Job Object manager (kernel memory caps) │ │ ├─ Popup classifier + system-browser routing │ │ ├─ PWA manifest ingestion (icons, names) │ │ ├─ Native Win32 context menu dispatcher │ │ ├─ Launcher WebView (dogfoods the same environment) │ │ └─ Dashboard WebView (same environment, live stats) │ └──────────────────────┬──────────────────────────────────┘ │ one shared WebContext ┌──────────────────────▼──────────────────────────────────┐ │ WebView2 browser process (shared) │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │Renderer │ │Renderer │ │Renderer │ │ │ │Linear │ │Notion │ │Figma │ │ │ │Profile A │ │Profile B │ │Profile C │ ← isolated │ │ └──────────┘ └──────────┘ └──────────┘ │ └─────────────────────────────────────────────────────────┘ │ ┌──────────────────────▼──────────────────────────────────┐ │ Windows Job Object │ │ · ProcessMemoryLimit (per-renderer cap) │ │ · JobMemoryLimit (total cap) │ │ · KillOnJobClose (no orphaned renderers) │ └─────────────────────────────────────────────────────────┘

Every renderer is attached to the Job Object via ICoreWebView2Environment13::GetProcessInfos, rescanned every 3 seconds. Memory limits are enforced by the Windows kernel. WebView2 sandboxes its own renderers into a private job, so some AssignProcessToJobObject calls return ERROR_ACCESS_DENIED — expected and handled gracefully; the job-wide cap on the browser process covers the tree.

Every app is additionally created with with_profile_name(app.id), which isolates its cookies, localStorage, IndexedDB, and caches into a per-app profile directory while remaining in the same shared browser process.

Windows 10 or 11 (WebView2 Runtime pre-installed on all modern builds)

Rust 1.75+ (stable toolchain)

Visual Studio Build Tools with the C++ workload (MSVC linker)

GNU Make — ships with Git for Windows and MSYS2; install via choco install make or scoop install make if missing

Verify WebView2 is present:

Get-ItemProperty -Path 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients{F1E7E9DA-3F7D-4A15-A8D0-1C3A9F0F6C6C}' -Name pv -ErrorAction SilentlyContinue

If it returns nothing, install the Evergreen WebView2 Runtime.

Reeve is a workspace with two binaries: reeve (launcher) and benchmark (harness).

git clone https://github.com/MarcelNazare/Reeve.git cd Reeve cargo check cargo run # launches the launcher cargo run --bin benchmark -- --help # launches the benchmark harness

Or with the bundled Makefile:

make # build make run # build + run launcher with debug logging make release # optimized build (LTO, stripped)

Make on Windows: run make from Git Bash or MSYS2, not cmd.exe. The run target sets RUST_LOG with POSIX shell syntax.

%APPDATA%\Reeve\Reeve\data
├── catalog.db # apps, permissions, sessions ├── icons\ # cached PWA icons └── webview\ # shared WebView2 user-data folder ├── Default\ # shared fallback profile └── <app-id>\ # per-app isolated profiles

Each app's localStorage, IndexedDB, cookies, and Service Worker caches live under webview<app-id>. Clear Data wipes only that app's directory.

Paste a URL into the top search bar and press Add. Reeve immediately records the app, then spawns a background thread that:

You can also seed apps programmatically in src/main.rs. Seeding is idempotent and will not create duplicates.

Reeve injects a prelude script into every launched app, exposing a Promise-based SDK on the window object. You no longer need to manage postMessage or request_id routing.

// Read a file (requires fs.read permission) try { const { content } = await window.reeve.fs.read("C:\Users\Public\notes.txt"); console.log(content); } catch (err) { console.error("Access denied:", err.message); }

// Send a native notification (requires notify permission) await window.reeve.notify("Build Complete", "Deployment finished in 3.2s.");

// Open a native file picker (requires fs.pick permission) const { paths } = await window.reeve.fs.pick();

Every call passes through three gates:

Additionally, the bridge applies a 500ms rate limit per app to prevent one compromised PWA from spamming native dialogs.

Click Dashboard in the launcher header. A second window opens, sharing the same WebView2 environment.

Right-click any app in the launcher grid. A native Win32 menu appears with:

The menu is dispatched on a dedicated thread. While it's open, Reeve's event loop continues polling memory, updating the dashboard, and tracking focus — the UI never freezes.

The harness in src/bin/benchmark.rs runs configurations in isolation.

Initial benchmarks showed a massive t_stable delay of 41.1 seconds for Reeve, compared to 9.2 seconds for Chrome tabs.

By isolating the WebView2 initialization process, we discovered the bottleneck is GPU process and DirectComposition initialization. WebView2 blocks the host application while it negotiates with your graphics driver and builds its initial DirectComposition surface.

When running with software rendering (WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS="--disable-gpu"), cold-start memory stabilization drops from 41.1s to 3.3s — over 10x faster than Chrome.

The tradeoff: Software rendering makes heavy canvas apps (Figma, Google Maps) unusable. Reeve ships with --disable-features=CalculateNativeWinOcclusion to mitigate Windows window-throttling, keeping hardware acceleration intact.

Note: These numbers predate per-app profile isolation. With partitioned cookies enabled, expect a small additional initialization cost per profile on first launch. Re-benchmarking is on the roadmap.

Click to view the raw benchmark output

Reeve benchmark

Test apps: https://linear.app, https://notion.so, https://figma.com, https://app.slack.com/client, https://open.spotify.com

Cold start

Scenario t_stable (proxy) t_ready (exact)
reeve 41100 ms 12460 ms
chrome-tabs 9200 ms —

Steady state

Scenario Peak memory Steady memory Peak processes
reeve 2575.5 MB 2165.5 MB 18
chrome-tabs 2212.2 MB 2195.3 MB 24

What this tells us

Steady-state memory is at parity. Reeve (2165 MB) and Chrome (2195 MB) use essentially the same amount of RAM for these five heavy apps. The "80% less RAM" claim holds against Electron, but not against Chrome tabs.

Process count is lower. Reeve achieves parity with 18 processes vs Chrome's 24.

GPU initialization is the cold-start tax. WebView2's synchronous GPU setup adds ~30 seconds to the memory stabilization time on this hardware.

Project Layout

reeve/ ├── Cargo.toml ├── Makefile ├── build.rs # winres: embeds assets/reeve.ico ├── README.md ├── LICENSE # MIT ├── assets/ │ ├── reeve.ico # multi-resolution Windows icon │ └── reeve-product-snapshot.png ├── migrations/ │ └── 001_init.sql └── src/ ├── main.rs # entry point for the launcher (default-run) ├── catalog.rs # SQLite: apps, permissions, sessions ├── environment.rs # shared WebView2 data directory ├── governor.rs # Job Object, memory queries, process snapshots ├── bridge.rs # capability checks + native dispatch + rate limiting ├── instance.rs # app lifecycle state machine ├── manifest.rs # PWA manifest ingestion (fetch, parse, download icon) ├── ui.rs # launcher, dashboard, IPC, popups, search, native menu └── bin/ └── benchmark.rs # entry point for the benchmark harness

Roadmap

Milestone Status

Shared environment, one browser process ✅

Job Object memory caps, renderer attachment ✅

Capability-gated bridge (v1 capabilities) ✅

PSL origin gate + rate limiting ✅

Promise-based developer SDK ✅

App management (uninstall, clear data) ✅

Partitioned cookies (per-app profile) ✅

Native Win32 context menu (thread-safe) ✅

Profile name validation + fallback logging ✅

Cold-start benchmark with isolated profiles ⏳

Owner-draw context menu (theme consistency) ⏳

macOS target (WKWebView, soft memory limits) ⏳

Linux target (WebKitGTK, cgroups v2) ⏳

Security Model

The bridge is the attack surface. Design principles:

Symptom Fix

with_profile_name not found Bump wry = "0.56" (feature shipped in 0.56.0, not 0.55).

HWND type mismatch windows 0.61 requires HWND(*mut c_void), not HWND(isize). Cast with hwnd as *mut core::ffi::c_void.

TPM_RETURNCMD | TPM_NONOTIFY type mismatch Extract the inner u32 with .0 — the flag type is a newtype in windows 0.61.

cargo run could not determine which binary to run Ensure default-run = "reeve" is in Cargo.toml under [package], or use cargo run --bin reeve.

Logout in one app affects another Check the console for profile name rejected — the app likely fell back to the shared profile.

FOREIGN KEY constraint failed The latest catalog.rs cascades deletions on startup. Run once to heal the DB.

Blank icons Ensure you're on v0.6.0+ which reads icons as Base64 data URLs.

Context menu freezes the UI Ensure the menu is spawned on a dedicated thread (std::thread::spawn), not called inline.

couldn't read migrations/001_init.sql Confirm migrations/001_init.sql exists at the project root.

couldn't read assets/reeve.ico Confirm assets/reeve.ico exists; build.rs requires it.

Dashboard shows 0 B memory No apps are open

正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。

打开原文
去社区讨论 →