Architecture

Egg is two native processes on top of a platform-supplied web engine, all coordinating through a single SQLite database. Knowing what each piece is, and who ships it, explains a lot about why some things behave the way they do.

Two processes, one database. egg.exe on Windows or Egg.app on macOS hosts the user-visible chrome and the tab webviews. egg-daemon hosts the agent harness and the persistent services (feed polling, page monitors, file watches, sync). They run as siblings, not parent and child. Closing the browser does not stop the daemon, and the agent keeps running with the window shut. Both processes connect to egg.db in SQLite WAL mode so the gateway can write while the browser reads.

The web engine is not Egg’s. On Windows, every tab is a WebView2 instance hosted by Microsoft’s evergreen runtime, which wraps Chromium: Blink for layout, V8 for JS, Skia for raster, Viz and cc for compositing. On macOS, every tab is a WKWebView, which wraps WebKit: WebCore for layout, JavaScriptCore for JS, CoreAnimation for compositing. Egg is the same Tauri/Wry shell on both platforms, but the engine underneath is not the same. The two engines have different CDP coverage, different extension support, and different rendering quirks. That difference matters whenever you write automation that needs to behave the same on both.

Tauri and Wry sit between Egg and the engine. Tauri is the Rust application framework. Wry is its webview adapter: on Windows it talks to WebView2, on macOS it talks to WKWebView. Egg uses Tauri 2’s multi-webview model with one webview for the React shell and a separate child webview per browser tab.

YOUR MACHINE egg / Egg.app Tauri shell + Wry React UI Browser API server WebView host (per-tab) egg-daemon (gateway) persistent service Agent harness Feed poller, monitors, sync Gateway API server HTTP / WS Web engine one webview per tab Windows: WebView2 → Chromium Blink · V8 · Skia · Viz/cc macOS: WKWebView → WebKit WebCore · JSC · CoreAnimation egg.db: SQLite (WAL mode) Web feeds RSS, HN, GitHub, &c. used by gateway Chrome Web Store .crx + updates (Win only) used by browser Egg Cloud sync relay, proxy APIs used by gateway

Platform differences

The shell, the daemon, and the database are identical on Windows and macOS. What differs is everything that touches the engine, the OS, or the per-platform extension model. The differences worth knowing up front:

AreaWindowsmacOS
EngineWebView2 (Chromium / Blink / V8)WKWebView (WebKit / WebCore / JSC)
Engine updatesMicrosoft Edge Update, evergreen, independent of EggTied to macOS releases; system update only
CDP coverageFull Chrome DevTools Protocol via WebView2Limited to what WKWebView exposes; some methods unavailable
Chrome extensionsSupported. .crx from the Chrome Web Store, full v3 manifestNot supported. WKWebView has no Chrome-extension loader
System tray + boot startTray icon, daemon registers for boot startMenu-bar item, login items via launchd
Discovery file path%APPDATA%\com.eggbrowser.desktop\~/Library/Application Support/com.eggbrowser.desktop/

For automation that needs to be cross-platform, prefer the micro-command surface over raw CDP. Micro-commands are implemented to behave the same on both engines wherever possible; raw CDP is only as portable as WKWebView lets it be.

Ownership & updates

Three different vendors ship pieces of what runs on your machine. Knowing who updates what explains why some things change between Egg releases without an Egg update, and why Mac and Windows sometimes diverge on web-platform behavior.

ComponentShips fromUpdated by
egg, Egg.app, egg-daemonEggEgg auto-update channel. The two binaries always update together; an old browser does not run against a new daemon, and vice versa.
WebView2 RuntimeMicrosoftMicrosoft Edge Update, evergreen channel. The Chromium version (Blink, V8, etc.) underneath floats with whichever channel WebView2 is on for that OS, usually a few weeks behind upstream Chrome.
WKWebViewApplemacOS system updates. The WebKit version is tied to the macOS release; you can’t update one without the other.
Chrome extensionsChrome Web StoreEgg fetches updates from the Web Store’s update endpoint on a periodic schedule, the same way Chrome itself does. Force-update available from the Extensions page.

Privacy & tracking protections

Egg ships with a set of privacy protections turned on by default, before the user touches a setting. They sit in front of every navigation and every page load, not as a per-site opt-in.

A built-in ad blocker is layered on top of the above. It runs on every tab unless the domain is in the per-user exemption list, and pulls filter-list updates on a periodic schedule.

Network stack & request interception

Egg sits on top of WebView2 (Win) and WKWebView (Mac) for the actual TCP/TLS/HTTP layer. The engine handles connection pooling, HTTP/2 and HTTP/3 negotiation, certificate validation, DNS, and proxy resolution. Egg interposes on top of that layer rather than reimplementing it.

Three places Egg modifies the request lifecycle by default:

  1. Pre-navigation URL rewrites. Tracking-parameter stripping and HTTP-to-HTTPS upgrade run before the engine receives the navigation. The address bar shows the cleaned URL; the engine never sees the original.
  2. Per-tab request interception. The automation surface exposes network route micro-commands and the CDP Network.* domain on Windows for fine-grained mock, abort, or modify behavior. These rules are scoped to a specific tab and cleared on close.
  3. Ad blocking. The ad-block module installs request-blocking patterns at the engine level on every tab. Patterns load from filter lists and refresh periodically; per-domain exemptions skip blocking for tabs on that origin.

System-level proxies are honored. Egg does not bring its own proxy resolver; if the OS or a Chromium --proxy-server flag (forwarded to WebView2) sets a proxy, that is what the engine uses for both browser tabs and the gateway daemon’s outbound HTTP.

Chrome extensions

On Windows, Egg installs Chrome extensions by downloading the .crx from the Chrome Web Store, extracting it into a per-extension folder under the user profile, and registering it with WebView2 so the manifest, content scripts, background service worker, and DOM-level APIs run the same way they would in Chrome. The full extension API surface that WebView2 implements is available, including webRequest, declarativeNetRequest, content scripts, manifest v3 service workers, alarms, and storage. Updates are pulled from the Web Store on a polling schedule and applied at next page load.

This path doesn’t exist on macOS. WKWebView has no equivalent extension loader; Apple’s Safari Web Extension model is a different format with a different distribution channel and isn’t supported here. Cross-platform automation that needs to work on both Mac and Windows should use the CDP surface or micro-commands rather than relying on a Chrome extension.

Service Workers & PWA

Progressive Web Apps

Service workers are an engine feature, not something Egg implements. They run inside whatever WebView2 or WKWebView provides, and any site that registers a service worker behaves the way it would in Chrome or Safari respectively. Egg’s automation surface can suspend service workers per-tab over CDP when an agent or scraper needs to bypass cached responses for a fresh fetch, and the “clear site data” control in Settings drops registered service workers along with cookies and storage. Chrome extensions that ship a manifest v3 background service worker on Windows are hosted by the same engine path, so their lifecycle is the platform’s, not Egg’s.

PWA support is intentionally minimal. The --app=<url> launch flag opens a chromeless window pointed at a URL, the way Chrome’s app mode does, and that is the main PWA-adjacent affordance Egg provides today. There is no install-app prompt driven by a site’s web app manifest, no desktop-shortcut creation, and no separate installed-apps catalog. Web Push is effectively off as well: by default Egg denies notification permission at the privacy layer, so a site that tries to subscribe never gets the user-facing prompt. Service workers themselves still install and run on regular pages; what is missing is the install ceremony, the push channel, and any manifest-driven naming or iconography.

WebAssembly

WebAssembly

WebAssembly runs inside the engine, not in any Egg-supplied runtime. Whatever each engine ships is what sites get when they instantiate a WASM module: WASM 1.0 across the board, SIMD and threads where the engine supports them, JS interop, structured-clone across worker boundaries. The level of support tracks the bundled Chromium version on Windows (V8) and the bundled WebKit version on macOS (JavaScriptCore), so a feature lands in Egg whenever it lands in the upstream engine.

Egg does not add a WASM sandbox of its own. There is no Egg-side WASI host, no extension-style WASM module loader, and no plugin surface for shipping a .wasm to the daemon. The only WebAssembly that runs is web-page WASM, executed by the engine in the same context as the rest of the page’s JS. We intend to keep parity with whatever each engine supports going forward, and will look at first-class WASM surfaces if the underlying platforms grow useful primitives for them.

Tabs & sessions

A tab is its own native webview attached as a child of the browser window. On Windows it is a WebView2 instance; on macOS it is a WKWebView. The React shell hosts the tab strip, address bar, and overlays in the parent window; page content lives in the child webview.

Tab IDs are stable across restarts. Several built-in features depend on this: thumbnails persist per tab so a reopened browser shows the previous session’s previews without recapture, session restore reattaches state to the same id, and per-tab automation rules can be re-applied cleanly.

Windows & incognito

Incognito

Egg supports multiple browser windows. Each window has its own tab strip, address bar, and active tab; tabs and tab groups are window-scoped. Windows share state at the daemon level so the agent, feed polling, monitors, sync, and saved data behave the same regardless of how many windows are open. Profiles are window-scoped (see Profiles & multi-account) so different windows can hold different logged-in personas.

A new window opens with Cmd+N (Ctrl+N on Windows). A new incognito window opens with Cmd+Shift+N (Ctrl+Shift+N).

Incognito. Each tab in an incognito window runs the engine in private mode: WebView2 InPrivate on Windows, a non-persistent data store on macOS. No cookies, cache, history, or local storage is written to disk for those tabs, and closing the window discards everything they accumulated. Browsing history, download metadata, and session restore all skip incognito tabs.

A handful of background workflows that run for normal tabs do not fire for incognito ones. Sync key-exchange and inbound transfer do not run in an incognito window since there is nothing on that side to sync. Cookie probes against the user’s logged-in services and import-from-other-browser pulls are also suppressed, on the principle that an incognito window should not touch the user’s persistent identity.

The incognito window uses a forced dark theme and a distinct title-bar label so it stays visually separable from a normal window at a glance.

Profiles & multi-account

Egg supports multiple browser profiles. Each profile is an isolated bundle of cookies, saved passwords, history, and per-site preferences with its own identity from the engine’s perspective. Switching profiles for a tab swaps the underlying cookie jar and storage partition; sites have no way to see across the boundary.

A profile is bound to a window, not a tab. Opening a new window can pick a different profile from the manager, and tabs created in that window inherit it. There is also a separate flavor of profile used by the agent harness when it acts on the user’s behalf, isolated from the user’s logged-in personas. It can optionally start with a copy of selected credentials and cookies from the default profile so the agent does not have to re-log into shared services.

Automation that targets a specific profile picks the right window first; the browser API’s tab routes are scoped per-window and the profile follows. Cross-profile workflows (for example, log in as Persona A, then read mail as Persona B) work by opening tabs in different windows.

Downloads & file safety

Downloads run through a layered set of checks before the file is handed to the user’s downloads folder. Egg adds these on top of whatever the engine and OS already do.

  1. Filename sanitization. Path-traversal characters, reserved Windows names, control characters, and trailing whitespace or dots are removed. Long names are truncated. The sanitized name is what the UI shows so it always matches the on-disk name.
  2. Mark-of-the-Web / quarantine. On Windows, WebView2 writes the Zone.Identifier alternate-data-stream so SmartScreen and Office’s Protected View kick in for executables and documents. On macOS, WKWebView sets the com.apple.quarantine extended attribute so Gatekeeper prompts on first open. Egg has a fallback that applies the right marker explicitly if the engine path did not.
  3. Content-type taxonomy. Files are classified by MIME plus extension into categories (executable, archive, document, media, other). The classification drives the UI warning level and decides whether the file is held in a staging area until the user explicitly opens it.

Server-reputation lookups against known-bad-host databases are on the roadmap, not shipped yet.

Password manager

Egg ships its own password manager. Saved credentials are stored locally and are never written to disk in cleartext. Each password is encrypted with AES-256-GCM under a vault key derived from the user’s master password using Argon2. The vault key lives in memory only while the vault is unlocked and is cleared on lock. The master password itself is never stored; only a verifier value sits on disk, so the daemon can check whether a typed master password is correct without holding the key when the vault is locked.

Autofill in a tab matches saved credentials to the current page’s origin, with URL variants tried so that https, http, and bare-domain forms share entries instead of fragmenting into duplicates. Imports from Chrome, Edge, and other Chromium-based browsers fold into the same vault on first run. Exports require the master password to be passed via the EGG_VAULT_PASSWORD environment variable, never a flag, so it does not leak to process listings or shell history. Passwords are one of the continuous sync categories: each entry rides inside the AES-GCM envelope described below, and the receiving device re-encrypts the value under its own vault key before storing, so cleartext exists only briefly inside each device’s process and never on the relay.

Passkeys & WebAuthn

Passkey

Egg ships its own WebAuthn (Level 2) authenticator. Most browsers delegate WebAuthn to the OS (Windows Hello, Touch ID, system passkey vault). Egg implements the authenticator inside the daemon and lets the user choose between two modes per credential.

The default mode is synced. The per-credential private key rides the existing multi-device sync rails, so the same passkey works on every paired device. Useful for accounts the user logs into from multiple machines.

The other mode is device-bound. The private key never leaves the device that created it. The device-bound mode will move to the OS secure element (Secure Enclave on macOS, TPM on Windows) in a future update. Useful for accounts where the user wants per-device credentials, or for relying parties that enforce device-bound AAGUIDs.

Each mode advertises its own AAGUID, so a relying party that builds an allowlist can distinguish the two. The authenticator follows WebAuthn §5.1.3 origin validation: a credential bound to an RP id is only usable on origins matching that id or its sub-domains. User-presence and user-verification flags are set per the platform’s biometric or passcode prompt at the time of the ceremony.

Multi-device sync

If the user pairs two or more devices, a curated set of categories stays in step across them without an Egg server ever seeing the plaintext. Pairing is point-to-point; the cloud is a dumb relay.

What syncs. Continuously: bookmarks, browser passwords, passkeys, app settings, firewall rules. Deletes propagate alongside inserts and updates via per-table SQLite triggers that record into a tombstone log. One-time on first pairing (opt-in by category): browse history, open tabs, cookies and sessions. The one-time channel is for setting up a second machine; it does not run continuously after the initial copy.

When it syncs. Event-driven, not scheduled. A SQLite update_hook on each synced table fires the moment a write commits, signals an in-process notify, and a debouncer coalesces bursty edits (~750 ms) into a single push round per active peer. There is no polling loop. A device that is offline buffers nothing special: the next time it comes online, the relay still has any inbound batches waiting and the next local write triggers an outbound.

How it’s protected. Pairing is bootstrapped by a 6-character invite code the user types from one device into the other. The code is single-use and short-lived; it is not the long-term secret. Behind the code, both devices run an X25519 ECDH and derive a shared AES-256-GCM key with HKDF, mixing the invite code into the key derivation so a passive observer at the relay cannot substitute their own keys. The derived key is what stays on each device and protects every subsequent push. After pairing completes, every push is an encrypted envelope POSTed to the relay, which routes it to the recipient by partner pseudonym. The relay sees ciphertext sizes and timing, never bookmark URLs, password vaults, or settings values. A per-direction replay counter on each side rejects duplicates and reorders.

For the full protocol, recovery semantics, and the threat model, see Gateway › Cross-device sync.

Browser ↔ Gateway boundary

Egg runs as two processes: the browser shell (Tauri) and the Gateway daemon (egg-daemon). The boundary between them is load-bearing: the browser shell never makes routing decisions, never holds API keys, and never makes outbound HTTP calls to LLM or media providers. The Gateway daemon is the single place where capability resolution, tier gating, credential storage, and outbound provider HTTP live.

Browser shell owns. The JavaScript surface (Egglet SDK, window.* built-in AI, agent harness frontend), webview management, tab chrome, UI routing, and the in-process work that is intrinsically Tauri-shaped. Everything visible to a JavaScript caller.

Gateway daemon owns. The Platform Capability Registry (single writer), the capability_usage daily-quota counter (single writer), the llm_providers credential vault (raw API keys never leave this process), and the /api/ai/* endpoints that handle every text completion, stream, embedding, transcription, image generation, TTS, vision, and video-generation call across the platform.

The architectural property follows: an Egglet that calls ctx.ai.text.chat.complete, a web page that calls window.Summarizer.summarize, and an agent skill that asks for a vision verification all land at the same daemon endpoint, all consume the same per-tier daily quota, all read keys from the same vault. Adding a new provider is one place. Fixing a provider-side bug is one place. Tier-blocking a user without a paid plan is one place.

Process lifecycle reinforces the split. The daemon is long-lived and survives the browser window closing; always-on work (background thinking, sync push loops, schedulers) belongs there. The browser shell comes and goes with the user’s session.

API surfaces

There are four ways to drive a tab. They overlap in implementation but exist for different audiences. Pick the one that matches your tooling, not the one that’s “closest to the metal”. They are parallel entry points, not a stack you have to descend.

SurfaceWho it’s for
Raw CDPClients that already speak the Chrome DevTools Protocol: Playwright, Puppeteer, custom CDP scripts. Direct access to any method or event the engine exposes. Works fully on Windows (WebView2 forwards CDP); macOS coverage is limited to what WKWebView supports.
Micro-commandsScript writers and HTTP clients. ~120 stable named operations covering navigation, interaction, find/query, waits, network, storage, emulation, capture, state, tabs. Cross-platform.
Agent toolsThe LLM in the agent harness. 15 grouped tools designed for an observation/action loop: browser_observe, browser_act, browser_find, and so on. Each dispatches to one or more micro-commands.
SkillsUsers composing repeatable workflows. Folder-based SKILL.md files that bundle prompts, examples, and chained agent-tool calls into a reusable unit.