What it is
A working macOS web browser built from scratch in Python (PyObjC + WKWebView) — its own NSWindow and its own WebKit renderer process, not a shell around Chrome. Private by construction rather than by settings: an ephemeral WKWebsiteDataStore so history, cookies and cache exist only in RAM, a tracker/ad filter, anti-fingerprinting (UA, screen size and timezone spoofed), WebRTC and geolocation blocked, referrers stripped, HTTPS-only, stealth mode, one-click New Identity, and auto-burn of everything in memory on exit. Ships an ephemeral CONNECT proxy + DoH resolver that is strictly per-app — it never reads or writes the macOS system proxy, so Chrome and the rest of the system keep working whether the in-app proxy is on or off. (An earlier build did set the system proxy; that is what silently broke Chrome for other apps. That code is gone.) Packaged as a double-clickable .app with a universal Mach-O launcher and a CLI.
Version history
Straight from the project’s own card data — what each version added.
Native macOS browser: ephemeral WKWebView data store (no history/cookies/cache on disk), tracker filter, anti-fingerprinting, leak surfaces closed, stealth mode, New Identity, auto-burn on exit, per-app CONNECT proxy + DoH, universal Mach-O launcher, CLI, and the start-page privacy shield. Fixed: system-proxy leak that broke Chrome, fatal port binds, and the macOS 26 shell-launcher crash.
Files on the site
By the numbers
Measured from the real working folder ~/Library/WebKit — not a claim, a count.
Mostly: —
What broke, and what I’d change
Not written yet. This is the part only I can write — what actually went wrong, and what I’d do differently. It lives in builds/reflections/isaac-browser.md (see _TEMPLATE.md); as soon as that file has words in it, this section fills in on the next build.
Everything else on this page — the spec, the version history, the file counts — is generated from the project’s own build notes and files, so nothing here is guesswork.
Build spec
The project’s own build notes, rendered. Open the raw .md ↗
Show the spec
Isaac Browser — build notes
A real macOS web browser, written from scratch in Python. PyObjC + WKWebView. Not a shell around Chrome and not a mockup — it creates its own NSWindow, its own WKWebView, and gets its own WebKit renderer process (WebContent / GPU / Networking XPC children), exactly like Safari does.
The goal was a browser that is private by construction rather than by settings: nothing to forget to enable, nothing left behind to clean up.
Privacy architecture
- Ephemeral data store. The
WKWebViewis created with a non-persistent - Auto-burn on exit. On quit, all website data types are removed through the
- Tracker filtering. In-page requests to a known tracker/ad domain list are
- Anti-fingerprinting. User-Agent, screen dimensions and timezone are spoofed
- Leak surfaces closed. WebRTC and geolocation are blocked, referrers are
- Stealth mode + New Identity. One click discards the current session's
WKWebsiteDataStore, so history, cookies, localStorage and the HTTP cache exist only in RAM. Nothing is ever written to ~/Library/WebKit or the disk.
data store API — a clean teardown rather than just letting the process die.
refused before they go out.
so the browser does not present a unique, trackable signature.
stripped, and navigation is HTTPS-only.
in-memory state and regenerates the fingerprint.
The proxy: per-app, never system-wide
The app ships an ephemeral CONNECT proxy on 127.0.0.1:8557 plus a DoH resolver, and it is a deliberate design rule that it routes only the browser's own traffic and never touches the macOS system proxy.
An earlier version did the convenient thing and called:
networksetup -setwebproxy <service> 127.0.0.1 8557
networksetup -setsecurewebproxy <service> 127.0.0.1 8557
Those calls are system-wide. Every other app — Chrome included — got silently repointed at the browser's local proxy, and on quit the teardown ran -setwebproxystate off, which disabled the machine's proxy entirely, including a VPN's. From the user's side: Chrome appeared broken for no visible reason, and it was this browser doing it.
All system-proxy mutation is now deleted; the outbound path uses a per-app opener that never inherits the OS proxy configuration. The proxy toggle affects this app and nothing else.
Packaging: four things that stop a Python app opening on macOS 26
- macOS 26 rejects a shell script as
CFBundleExecutable. A bash launcher - A fatal port bind killed the app on every launch. The proxy called
- Editing an app after signing invalidates it. Changing
Info.plistor the - Builds need no admin. Everything is assembled into
~/Applications, which
makes the bundle look valid and then silently does nothing on double-click. The fix is a compiled Mach-O universal launcher (arm64 + x86_64) that satisfies the loader and then execs the Python framework binary.
bind() with no guard, so a stale process holding 8557 raised OSError: [Errno 48] and took the whole app down before a window appeared. Both the proxy and the local API server are now non-fatal on a busy port, and an already-running check runs first to stop duplicate instances.
icon leaves a stale _CodeSignature and Gatekeeper reports "plist or signature have been modified". Re-sign ad-hoc after every change.
Launchpad and Spotlight both index — no sudo, no password prompt.
Search without an API key
HTML-scraping DuckDuckGo and Google returns 429s and JS challenges. The browser instead queries the DuckDuckGo Instant Answer API and the MediaWiki search API, with a Bing HTML pass as a last resort — keyless, reliable, and not bot-blocked.
Verification
- Window opens on double-click;
WebContent/GPU/NetworkingXPC children - Search returns real results (10–12 per query).
networksetup -getwebproxystaysEnabled: Nowith the app running and when- The app, its launcher and the bundle all pass
codesign --verify.
confirm a page genuinely rendered.
the in-app proxy is toggled on.