A Chrome extension that catches muscle-memory navigation. Sites on your list show you your own reason instead of loading — plus one click to somewhere you’d rather be, and an easy way through when you actually mean it.
Load src/ as an unpacked extension, add a domain in its settings page, then
navigate to that domain. You get the reminder page rather than the site. Bypass
lasts 5 minutes and dies with the browser.
Unit tests cover the pure logic: node --test tests/unit/*.js.
chrome://extensionssrc/ directory — not the repository rootThe settings page opens automatically on first install.
Numbered criteria correspond to the success criteria in
knowledge/plans/STEER_CLEAR_MVP.md.
| # | Check | Expected |
|---|---|---|
| 1 | Add example.com, visit https://example.com |
Reminder page appears |
| 2 | Visit https://example.com/some/path?q=1 |
Full URL shown on the reminder page |
| 3 | Leave the site’s target blank | “Go where I meant to” uses the global default |
| 4 | Set a per-site target | Button uses that instead |
| 5 | Set a per-site note | That note is shown, not the global one |
| 6 | With subdomains on, visit www.example.com |
Reminder page appears |
| 7 | With subdomains off, visit www.example.com |
Site loads normally |
| 8 | Click “Continue anyway” | Site loads |
| 9 | Wait 5 minutes, revisit | Reminder page returns |
| 10 | Bypass, restart Chrome, revisit | Reminder page returns |
| 11 | Restart Chrome | Settings still present |
| 12 | Untick a site’s checkbox | Site loads normally |
Debugging: on chrome://extensions, click service worker under Steer
Clear to open its console. All log lines are prefixed [steer-clear].
To inspect the live rules, run this in that console:
chrome.declarativeNetRequest.getDynamicRules().then(console.table)
node --test tests/unit/*.js
90 tests covering pattern normalization, regex generation, subdomain matching, lookalike-domain rejection, rule construction, bypass filtering, and global-versus-per-site resolution. No dependencies; Node 20+.
The Chrome-dependent surfaces (service worker, options page, interstitial) are verified manually via the table above — they need a real browser.
Navigation to a blocked site
│
▼
declarativeNetRequest regex rule
│ regexSubstitution: blocked.html?from=\0
▼
blocked.html reads the config from chrome.storage.sync
│
├── per-site target ──► falls back to global target
├── per-site note ──► falls back to global note
│
└── renders: your reason, [Go where I meant to], [Continue anyway]
Why regex rules rather than the simpler domain condition: declarativeNetRequest
cannot inject the originally-requested URL into a redirect through
queryTransform — addOrReplaceParams takes static values only. Preserving the
source URL requires regexFilter paired with regexSubstitution, where \0
expands to the whole matched URL.
Rule ownership: the service worker is the only writer of dynamic rules. The options page and interstitial mutate storage or send messages; the worker observes and rebuilds. Rules are therefore always derivable from storage, so a full rebuild is always safe and cannot drift.
steer-clear/
├── src/ # Load THIS directory as the unpacked extension
│ ├── manifest.json
│ ├── constants.js
│ ├── background/
│ │ ├── service_worker.js # Owns dynamic rules + bypass lifecycle
│ │ ├── rules.js # Pure config -> rules (unit-tested)
│ │ └── config.js # Storage read/write/validate
│ ├── blocked/ # The interstitial
│ ├── options/ # Settings UI
│ └── icons/ # PLACEHOLDER — see icons/README.md
├── tools/
│ ├── package.sh # Builds the Web Store upload zip
│ └── screenshots.mjs # Regenerates store/screenshots/ at 1280×800
├── tests/unit/
├── store/ # Web Store submission materials
│ ├── LISTING.md
│ └── PRIVACY_POLICY.md
└── knowledge/plans/
See store/LISTING.md for copy-paste listing text, permission justifications,
and data-use disclosures. Outstanding items requiring you:
knowledge/setup/SUBMISSION_STEPS.md to submitExpect closer-than-average review: declarativeNetRequest with redirect is the
permission set traffic-hijacking extensions use. The justifications in
store/LISTING.md are written to address that directly.