π Visit Sheaf Browser
Every web developer's debugging toolchain is four browser extensions in a trench coat: modify headers, format JSON, edit cookies, mock requests. Each install wants access to every site you visit, and Manifest V3 has been quietly narrowing what the interesting ones are allowed to do.
Sheaf Browser takes a different route: move the tools below the extension sandbox entirely. It's a small open-source browser (Electron, MIT) where the four tools are native plugins in the browser's main process β the layer that actually owns the network.
Here's the what, why, who, when, where, and how β plus the security model, which is the real story.
What
Four tools, first-class, written from scratch:
| Tool | Does |
|---|---|
| Letterhead | Add / replace / remove request and response headers; substring, glob (example.com/*) or regex scoping; profiles swap rule sets; the icon pulses while a rule is live on the current page |
| Folio | Every JSON response becomes a searchable, collapsible tree with copy-path on any key; sheaf://folio is a paste-anything scratchpad |
| Imprint | Read and edit cookies including HttpOnly, plus localStorage / sessionStorage |
| Mailroom | Stub, redirect, block or delay any request; record the session; export HAR |
Plus the browser around them: tabs, private windows, ranked omnibox, bookmarks, downloads, find-in-page, docked resizable DevTools, and device simulation (viewport + DPR + UA) that works while DevTools is open.
Why
Three facts, one conclusion:
-
MV3's
declarativeNetRequestis a static rule table with hard limits β designed so ad blockers can be audited, and consequently so debugging tools can't really exist. -
HttpOnlycookies are invisible to extension JavaScript by definition β but not to the browser itself. The flag restricts page JS, not the embedder. -
Electron doesn't implement
declarativeNetRequestat all. An MV3 header extension in any Electron app installs, renders, and silently does nothing β so bundling existing extensions was never an option.
If you already keep a second browser for dev work (you do), the conclusion follows: build the second browser properly.
Who
For: developers, QA, and support engineers debugging web apps daily; security teams that prefer one inspectable MIT codebase over four third-party extensions with total site access.
Not for: daily driving. No Widevine, no sync, no password manager, no Web Store β deliberate non-goals. Mixing your personal sessions back into the dev browser would recreate the problem it solves.
When
Reach for it when you need to: send an Authorization or feature-flag header to one origin; read an API response as a tree; edit an HttpOnly session cookie in place; stub a flaky endpoint or delay a request to reproduce a race; export a HAR for a ticket; test a mobile viewport with DevTools open.
Where β one layer down
Sheaf browser capabilities all come from a single placement decision: the tools run in the browser's main process, below the extension sandbox. session.webRequest sees every request and response with no rule ceilings and no permission grants. The same asymmetry repeats everywhere β hard or impossible as an extension, straightforward as the embedder.
How
flowchart TB
subgraph main["Main process"]
TM["TabManager"]
PH["PluginHost"]
SQL["SQLite repos"]
PROT["sheaf:// & sheaf-stub://"]
end
subgraph window["One BrowserWindow"]
CHROME["Chrome renderer (React)"]
TAB["Tab = WebContentsView, one per tab"]
DT["DevTools view"]
SPL["Splitter view"]
end
OVL["Omnibox overlay window"]
CHROME -- IPC --> TM
TM --> TAB & DT & SPL
PH -- "session.webRequest" --> TAB
The compositing constraint. Each tab is a WebContentsView β a native Chromium view composited over the React-rendered chrome. Native views ignore DOM z-index, so nothing in the DOM can appear above page content β ever. Every overlay is therefore native too: the omnibox dropdown is a separate frameless window, DevTools is its own docked view, and the drag-to-resize splitter is a third thin view. Find your platform's compositing boundary in week one; it designs your component architecture for you.
The single-listener rule. Electron permits one webRequest listener per event per session β a second registration silently replaces the first. All plugins therefore register typed handlers with one PluginHost, which owns the real listeners: header handlers fold over the header map in order, the first mock rule to answer a request wins, and the host attaches per session so private windows get the same rules over a throwaway in-memory session.
Mocking without a proxy. Stubs are served from an internal sheaf-stub:// protocol; block/delay/redirect act directly in the pipeline; a completion-time recorder exports HAR.
sequenceDiagram
participant Page
participant PH as PluginHost
participant SP as sheaf-stub://
Page->>PH: GET /api/flaky-endpoint
PH->>PH: mock rule matches (stub)
PH-->>Page: redirect β sheaf-stub://β¦
Page->>SP: fetch
SP-->>Page: user-defined status / headers / body
Shared rule matching. Rule types and the URL matcher are one shared module compiled into both the UI and the network layer β the "rule is live here" pulse uses the same matcher that enforces the rule, so the interface can't lie about what the pipeline will do.
Security
The part that justifies the whole project:
-
Permission model, deleted. No "access to all sites" grants β the tools are the browser. Runtime dependency tree: two MIT packages (
better-sqlite3,adm-zip); neither touches the network. -
Isolation. One renderer process per tab; context isolation on; no Node in page contexts; typed preload-mediated IPC;
javascript:URLs in the omnibox are never executed. -
Zero own traffic β verified. No telemetry, no update pings, no third-party APIs. Favicons are captured locally at bookmark time; the new-tab greeting is clock-driven; spellcheck is macOS-native only.
npm run verifylaunches the built app, records every request across a full scripted feature pass, and fails on anything unexpected β after first proving the recorder captures real traffic against a local echo server. Observers must earn trust before their silence means anything. - Provenance. A clean-room rule in CONTRIBUTING keeps GPL/AGPL implementations away from this MIT tree β contributors don't read competing sources while building a feature.
- Distribution honesty. Unsigned builds, with the Gatekeeper/SmartScreen friction documented per platform; private vulnerability reporting via SECURITY.md; Electron's Chromium CVE stream treated as a standing upgrade obligation.
Limits, stated plainly
No DRM, no sync, partial Chrome-extension compatibility (DevTools-panel extensions mostly work; MV3 network extensions can't work in any Electron app), and throttling/touch emulation blocked on a CDP-vs-DevTools conflict β all on the record in the README.
Try it
Grab a build from Releases, or:
git clone https://github.com/rajeshkumaravel/sheaf-browser
cd sheaf-browser && npm install && npm run dev
npm run verify runs the same verification suite CI runs β including the network-silence check.
If the architecture interests you, β star the repo β https://github.com/rajeshkumaravel/sheaf-browser. Issues for header cases and rule syntax that annoy you, Discussions for arguing with a trade-off, PRs for the open problems β JWT decoder plugin, CDP throttling, shareable team rule sets. CONTRIBUTING.md (clean-room rule included) has the details.
And thatβs it!
I hope you found this article useful, and as always, thanks for reading!
Happy Coding!
RK


Top comments (0)