Desktop browser for tweaking any site — inject scripts, swap resources, persist overrides.
Pouch turns any web page into a standalone desktop app on macOS and Windows, then lets you stack your own changes on top: drop in a userscript and it runs at document_start; save a tweaked copy of any served file and Pouch serves it back on the next reload. Cookies, origin, and session behavior match the live site exactly — you're not rewriting the page, you're layering on top of it.
- Run any web page as a standalone desktop app — one entry in a config file, no wrapper code to write.
- Open multiple pages at once; each gets its own window, all sharing the same cookies and storage.
- Inject your own JavaScript with Tampermonkey-style
@matchrules — runs before the page's own scripts. - Replace any served file (JS, CSS, image, HTML…) with a local copy, just by editing the file on disk.
- Tweaks survive reloads automatically — no DevTools session to keep open, no rebuild step.
- Skip the cache on chosen hosts or URL patterns (analytics, telemetry, CDNs) so they always hit the network.
- Configure startup URLs, window size, and ignore rules in a single TOML file with inline docs.
- DevTools available with F12. On macOS, three titlebar buttons add reveal-folder / reload-from-config / toggle-devtools shortcuts.
brew install --cask leaker/tap/pouchUpdating:
brew upgrade --cask pouchOn first launch Pouch asks to trust a local certificate so it can view and modify HTTPS traffic for the page you load. Approve once at the system prompt and you're done — there is no second prompt on later launches.
Scoop downloads Pouch through PowerShell as a portable zip — Windows does not flag it with SmartScreen.
scoop bucket add leaker https://github.com/leaker/scoop-bucket
scoop install pouchUpdating:
scoop update pouchScoop installs the portable pouch.exe. Pouch does not ship a Windows installer; scoop update pouch is the upgrade path.
For users without Homebrew or Scoop, grab the latest build from the releases page.
Pick one to install:
- macOS —
Pouch-<version>.dmg(universal binary, signed and notarized) - Windows portable —
Pouch-<version>.exe(single binary, double-click to run) orPouch-<version>.zip(binary plus samplehook.conf.tomlandinject/folder)
Ignore these — they're used by Pouch's built-in updater (macOS only):
Pouch-<version>.app.tar.gz+.sig(macOS update payload)latest.json(updater channel manifest)
On launch Pouch reads hook.conf.toml and opens every URL listed in startup_urls as its own window. The default config opens https://www.leelib.com — replace it with the site you actually want.
- macOS: if
startup_urlsis empty Pouch pops a native prompt asking for a URL. The first time you run Pouch you'll also get a one-shot system prompt asking to trust a local certificate (so Pouch can view and modify HTTPS traffic). - Windows: if
startup_urlsis empty Pouch logs a warning and waits — populate the file and relaunch.
Press F12 any time to open DevTools. On macOS, the right side of the title bar carries three shortcut buttons — reveal the Pouch data folder in Finder, reload from config (Cmd+R), toggle DevTools (F12) — and Cmd+N opens a "new window" prompt with a URL combobox that remembers recent destinations.
Starting with v2.1.0, Pouch stores its data in %APPDATA%\Pouch\ instead of next to pouch.exe. On first launch after the upgrade, Pouch automatically moves your hook.conf.toml, inject/, and overrides/ from the old portable location into %APPDATA%\Pouch\. The migration is one-shot — a marker file in the new directory prevents it from running again, so it's safe to keep upgrading.
Pouch reads a single TOML file on launch. On macOS, edit it and hit Reload (Cmd+R, or View → Reload from Config) to apply. On Windows, quit and relaunch to pick up changes.
| Platform | Path |
|---|---|
| macOS | ~/Library/Application Support/Pouch/hook.conf.toml |
| Windows | %APPDATA%\Pouch\hook.conf.toml |
A fully-commented default is seeded on first launch and never overwritten — open it in your editor and tweak in place.
startup_urls— URLs to open at launch. The first entry becomes the main window; each additional entry opens as its own extra window with shared cookies and storage.window_dimensions—"inherit"(default; restore last session's geometry),"default"(1280×960),"maximized","fullscreen", or{ width = 1600, height = 1000 }.ignore_urls— URL patterns Pouch passes through without caching. Useful for analytics endpoints, font CDNs, or anything you don't want sitting inoverrides/. Four match modes:suffix— host suffix including the apex ({ suffix = "gstatic.com" }matches bothgstatic.comandfonts.gstatic.com).wildcard— host glob;*matches one label and does not cross.({ wildcard = "*.google.com" }matchesfonts.google.combut notgoogle.com).url_wildcard— full-URL glob;*matches any characters including/. Anchored at both ends.url_regex— raw regex against the full URL; you control the anchors.
updater.auto_check— whether Pouch silently checks for updates ~5 seconds after launch and again every 24 hours. Defaulttrue. On macOS, setting it tofalsekeeps the "Check for Updates…" menu item available for an on-demand check; on Windows there is no manual entry, sofalsemeans no checks at all.
See the sample hook.conf.toml at the project root for inline documentation on every field, including how parse failures are handled.
Pouch checks GitHub for new releases about five seconds after launch and again every 24 hours while the app is running. The check is silent — you only see a prompt if a newer version is published.
You can disable both the post-startup check and the 24-hour re-check in hook.conf.toml:
[updater]
auto_check = falseThe Pouch menu hosts a Check for Updates… entry (right after About) for on-demand checks, and the entry remains available even with auto_check = false. When you accept the prompt, Pouch downloads the signed update and restarts. Your data in ~/Library/Application Support/Pouch/ is preserved across updates.
Pouch runs entirely in the background on Windows — there's no menu, no manual entry point. When the silent check finds a newer release you'll see a dialog automatically:
- Scoop install — the dialog offers a Copy command button that copies
scoop update pouchto your clipboard. Paste it in PowerShell to upgrade. - Portable (single
.exe/.zip) — the dialog offers an Open release page button that opens the GitHub releases page in your default browser, where you can download the newPouch-<version>.exeorPouch-<version>.zipand replace your existing copy.
Pouch never downloads or installs the update for you on Windows — this avoids broken installs from interrupted downloads and keeps your portable layout intact if you've placed pouch.exe somewhere specific.
Because there's no menu fallback on Windows, setting auto_check = false disables update checks entirely; in that case visit the releases page yourself to grab a newer build.
Pouch keeps everything you can tweak inside its data folder:
| Platform | Data folder |
|---|---|
| macOS | ~/Library/Application Support/Pouch/ |
| Windows | %APPDATA%\Pouch\ |
Use the title bar's reveal-folder button (or View → Reveal Pouch Folder, Cmd+Shift+O on macOS) to jump straight there.
Put a .js file anywhere under inject/ and declare which URLs it runs on with a Tampermonkey-style header. Pouch scans inject/ recursively at launch, so feel free to organize by host, by feature, or however you like — what decides the match is the @match line, not the folder name. Replace example.com below with the site you want to customize.
// ==UserScript==
// @name tweak example
// @match https://www.example.com/*
// ==/UserScript==
(function () {
console.log('[tweak] running on', location.host);
// your tweaks here
})();Rules:
@match— required. Glob form by default (*matches any characters, crossing/); prefix withregex:to switch to a real regex (@match regex:^https://(api|cdn)\.example\.com/). Multiple@matchlines are allowed; any one matching triggers the rule.@name— optional label that shows up in startup logs.- No
@matchline → file is skipped at scan time with a warning. Comment out every@matchto disable a script temporarily without deleting it. - Scripts run at document_start, before the page's own JavaScript, in their own try/catch — a thrown error in one script never breaks the others. The dispatcher is present in all frames, but each frame only runs scripts whose
@matchrules match that frame's ownlocation.href. - A bare
@match *is the top-frame startup fallback only. It does not run in iframes; add an explicit iframe URL glob or regex when you want a script to run inside a frame. - SPA route changes do not re-trigger scripts. Hook the History API yourself if you need that.
A safer default than @match * is @match https://*, which skips about:blank and data: frames.
The first time Pouch fetches a resource it stashes the response under overrides/<host>/<path>, with a small .meta.json sidecar holding the original headers. On the next load Pouch serves the file straight from disk — no network request.
overrides/
cdn.example.com/
static/
bundle.js
bundle.js.meta.json
site.css
site.css.meta.json
URLs with a query string get a hashed suffix in the filename: ?v=1 makes bundle.js land as bundle.js.__qs_<8-char-hash>__.js. Different queries map to different files so a stale signed URL never replaces the live one. The trailing .js (or .css, .png, ...) is repeated so your editor still picks the right syntax highlighting.
To customize a response, edit the file on disk and reload. To fall back to the live response, delete the file (and its .meta.json) — Pouch will refetch and re-cache on the next load.
Hosts and URL patterns listed in ignore_urls are fetched but never written to overrides/, which is the right setting for analytics endpoints and other things you don't want to freeze.
brew uninstall --cask pouch # remove the app only
brew uninstall --cask --zap pouch # remove the app AND your dataWarning:
--zappermanently deletes your inject scripts, overrides cache, andhook.conf.toml— everything under~/Library/Application Support/Pouch/plus related WebView caches. Back up yourinject/folder first if you want to keep your work. Drop--zapto keep your data and reinstall later.
The trusted certificate stays in your Keychain — open Keychain Access, search for "Pouch", and delete the entry manually if you want it gone.
- Scoop:
scoop uninstall pouch. - Portable: delete
pouch.exe(and the surrounding folder if you extracted the zip).
In all cases, your data in %APPDATA%\Pouch\ is left in place. Delete that folder manually if you want a clean slate.
Drag Pouch.app to the Trash. Remove ~/Library/Application Support/Pouch/ if you want to wipe your tweaks too.
Requires the Rust toolchain and the Tauri v2 prerequisites for your platform.
git clone https://github.com/leaker/pouch
cd pouch
cargo dev # development with hot-reload
cargo start # run the release binary without bundling
cargo build-app # release binary into src-tauri/target/release/
cargo bundle # host-arch bundle (macOS .dmg; quick local check)
cargo bundle-universal # universal macOS .dmg, matches CI's release artifactSee .cargo/config.toml for the full alias list.
MIT.