A lightweight website blocker that enforces "focus mode" in the browser, using a Bloom Filter for O(k) domain membership checks instead of a linear list lookup.
This was built as a practical implementation of Bloom filters for a Data Structures & Algorithms course. A companion project applying the same data structure to a different problem — network-level packet filtering for a game server — is linked at the bottom of this README.
A naive blocker checks every visited domain against a blocklist stored as an array or hash set. As the list grows, so does lookup cost (or memory). This project swaps that lookup for a Bloom filter: a probabilistic set membership structure that answers "definitely not blocked" or "probably blocked" in constant time, using a fraction of the memory a full domain set would need — at the cost of a small, tunable false-positive rate.
Because a browser extension's service worker can't reliably read local files or run native C extensions (like the mmh3 hashing library), the system is split across two cooperating processes:
┌─────────────────────────────┐ ┌──────────────────────────────┐
│ BROWSER │ │ LOCAL MACHINE │
│ │ │ │
│ background.js │ │ server.py │
│ • Chrome extension │ │ • Flask backend │
│ (Manifest V3) │ HTTP │ • Owns the "real" Bloom │
│ • Watches navigation via │◄──────►│ filter (bloom.py) │
│ webNavigation API │ request │ • Loads blocklist from │
│ • Queries the local server │ │ blocked_urls.txt │
│ • Injects blocking overlay │ │ • Answers /check/<url> │
└─────────────────────────────┘ └──────────────────────────────┘
│ ▲
└──────────── GET /check/<hostname> ───────┘
The two processes don't share memory — the extension calls the server over localhost for every navigation, and the server is the single source of truth for what's blocked.
| File | Runs On | Role |
|---|---|---|
bloom.py |
Local machine (Python) | Core Bloom filter ADT (create_bloom_filter, add_item, check_item) plus a standalone test harness |
server.py |
Local machine (Python/Flask) | Initializes the Bloom filter on startup and exposes it over HTTP via /check/<url>, with CORS enabled for the extension |
background.js |
Browser (Chrome extension) | Service worker that hooks navigation, queries the local server, and injects the blocking overlay |
manifest.json |
Browser (Chrome extension) | Manifest V3 config declaring permissions (webNavigation, scripting) and registering the service worker |
blocked_urls.txt |
Shared file | The blocklist — read by server.py on startup to build the Bloom filter |
- Python 3.8+ and pip
- Google Chrome (or any Chromium-based browser supporting Manifest V3)
pip install flask flask-cors mmh3 bitarrayEdit blocked_urls.txt — one domain per line, no http://, no www.:
facebook.com
instagram.com
tiktok.com
If this file doesn't exist yet, server.py will create it automatically with a default sample list on first run.
python server.pyYou should see:
=== Initializing Python Backend ===
[*] Blocklist loaded from blocked_urls.txt
* Running on http://127.0.0.1:5000
Leave this running — the extension needs it active on localhost:5000 to check URLs.
Want to see the Bloom filter tested standalone (without the server or extension)? Run:
python bloom.pyThis runs a small built-in test suite that checks sample IPs and URLs against two independent filters.
- Open Chrome and go to
chrome://extensions. - Enable Developer mode (toggle in the top-right corner).
- Click Load unpacked.
- Select this repository's folder (the one containing
manifest.json). - "Digital Detox Blocker" should now appear in your extensions list and be active.
With the backend running and the extension loaded:
- Navigate to a site not in the blocklist (e.g.
wikipedia.org) — it should load normally. - Navigate to a site that is in the blocklist (e.g.
instagram.com) — a full-screen "Get Back To Work" overlay should appear, hiding the page. - Click the Redirect to Canvas button on the overlay to confirm the redirect works.
- Add a new domain to
blocked_urls.txt, restartserver.py, and confirm it's now blocked too. - Watch the terminal running
server.py— every check logs a line like[SERVER] Checked instagram.com - Blocked: True, useful for confirming requests are actually reaching the backend.
To stop enforcing focus mode:
- Stop the
server.pyprocess (Ctrl+C). With the server down, the extension fails open — sites load normally rather than being blocked. - Optionally, disable or remove the extension from
chrome://extensions.
- Two independent Bloom filter implementations.
bloom.py(Python, usingmmh3) is the source of truth, queried through the Flask server.background.jsalso contains a JavaScript reimplementation (using a custom hash function in place ofmmh3, since native C extensions aren't available in a browser service worker) — but it's currently initialized without being used for the actual blocking decision, since the real check happens via the server. This is a candidate for cleanup in a future iteration. - No deletions. Bloom filters don't natively support removing an item without a more complex structure (e.g. a counting Bloom filter), so removing a domain currently requires editing
blocked_urls.txtand restarting the server to rebuild the filter. - False positives are possible, false negatives are not. With
fp_rate=0.05, a small percentage of legitimate domains could — in rare cases — be mistakenly flagged, but a domain that's actually in the blocklist will never slip through. - Server dependency. The extension's blocking decision depends entirely on the local Flask server running — if it's stopped, the extension fails open rather than blocking.
- Hostname-level only. The filter currently blocks by hostname, not by full path or subdomain-specific rules.
This is one of two Bloom filter implementations built for the same DSA coursework. The second project, Network Security Filter, applies the same underlying data structure to network-level packet filtering — intercepting inbound traffic on a game server VM using iptables + NetfilterQueue and checking source IPs against a Bloom filter to allow or drop connections:
| Name | Contribution |
|---|---|
| Abbas Dar | Worked on the Network Security Filter including the VM setup |
| Muhammad Saad Masood | Worked on the Digital Detox including the custom browser extension setup |
| Syed Aayan Rizvi | Coordinated with both the teammates and worked on documentations and presentations |