Browstack turns your own Chrome browsing history into a private weekly digest. Everything runs locally; your browsing data never leaves your machine. Because the project is open source, its security rests entirely on design that is safe even though an attacker can read every line of this code — not on anything being secret.
This document states honestly what Browstack protects, what it does not, and how to report a problem.
Please report security issues privately to howie@protico.io. Do not open a public GitHub issue with a working proof-of-concept — every user runs a resident local server, so a public exploit puts the whole user base at risk before a fix ships. We'll acknowledge and work with you on a coordinated disclosure.
-
Same-user code is out of scope (by design). Any program running as your macOS user account can read
data/browstack.db(your history) and any local secret, directly from disk. A local infostealer with your UID is not something a local tool can defend against. Keep your machine free of malware. -
The archive link's security equals your email account's security. The weekly email contains a link with a capability token (
?k=…) that opens your local archive. Anyone who can read that email can open the archive on your machine. The token lives in your inbox (and Google's link-scanner logs / synced devices). Treat it like an account credential. Rotate it any time with:npm run token:rotate # old email links stop working; the next issue carries a fresh one -
On a multi-user Mac, security depends on file permissions. Browstack tightens
data/,out/, andassets/covers/to0700and the database and logs to0600on every run, so other local accounts cannot read your history. If you loosen these permissions, other users on the same Mac can read your data. -
The archive adds a local attack surface that pure ingest did not have. A readable HTTP endpoint on
127.0.0.1:8787now serves history-derived pages. It is defended by: loopback-only bind, an exactHostallowlist (anti-DNS-rebinding), a 256-bit capability token, and a strictContent-Security-Policy. These controls must not be weakened. The email link only works on the same Mac while the receiver is running — it is a dead link on a phone or any other device, by design.
Changes to security-critical files (src/server.ts, src/shared/settings.ts,
src/fetch/extract.ts, .gitignore, and the archive modules) require a code-owner
review, and CI asserts the invariants below. Please do not "simplify" past them:
- The server binds
127.0.0.1only — never0.0.0.0or a configurable address. - The
Hostcheck is an exact allowlist — neverincludes/startsWith/regex. - The capability token is CSPRNG-only, compared in constant time, and fails closed when absent — never a hardcoded/default/derived value, never minted in a request handler.
- The CSP has no
script-srcand nounsafe-eval;default-src 'none'stays. POST /capturerequiresContent-Type: application/json.jsdomparses hostile page HTML with inert defaults — neverrunScriptsorresources: "usable".
The resident receiver (com.browstack.serve) is launched by launchd with a pinned
Node path. A Node upgrade (nvm/Homebrew) can invalidate that path and silently stop
the receiver — email links then fail to connect and captured reading is queued (and
eventually dropped past 300 items). The daily heartbeat probes /health and warns
via Notification Center if the receiver is down. If links stop working, re-run
npm run schedule:weekly.