sux #
A firefox plugin to filter shit out of your bsky feed
Mostly, it was just an excuse to fuck with jev.
What follows is pure Kimi-lish, because I don't feel like doing better at the moment. It'll be rewritten at some point in the next week or two, probably after the userscript-based rewrite. The neuralese is pretty complete tho.
A feed filter for bsky.app (Firefox, Zen Twilight, and other Firefox-based browsers). Every post Bluesky's web app retrieves is judged by Jev (TypeSafe's System One model); posts that don't pass your rules are hidden.
Rules (set in the sidebar) #
- Two words — Jev decides whether a post is more word 1 or word 2. A post is shown when Jev puts at least 50% probability on word 1.
- One word + direction — Jev answers "is this post
${word}?" with a probability. Choose are mostly (shown when > 50%) or are NOT (shown when < 50%). Exactly 50% is hidden either way. - Both rules on — by default a post must pass all enabled rules (AND); switch to any (OR) in the sidebar.
- The Filtering on checkbox fully disables the extension with one press. When off, the page is left completely untouched.
Posts stay hidden until judged (usually well under a second, slower under rate limits). Verdicts are cached by rule set + post text, so reposts and re-scrolls cost nothing. Image-only posts with no alt text are shown — there is nothing for Jev to judge.
Setup #
- Get a TypeSafe API key (https://typesafe.ai).
- Build:
npx web-ext build→web-ext-artifacts/sux_jev_feed_filter-0.1.0.zip. - Install (persists across restarts): quit the browser, copy the zip to
<profile>/extensions/@sux.xpi, and putuser_pref("xpinstall.signatures.required", false);in auser.jsfile in the profile dir (unsigned sideloads need it). Restart — Firefox registers the drop-in on startup. - Open bsky.app, then the sux sidebar. If Firefox shows the Grant site access banner, click it (Firefox treats host permissions as opt-in) and reload bsky.app tabs.
- Paste your API key, press Test, enable a rule, flip Filtering on.
To update after code changes: rebuild the zip, copy it over @sux.xpi, restart.
Posts are read from the rendered feed DOM (permalink anchors + item text), not
from network interception — bsky.app's API client uses a captured fetch
reference that page-world wrappers cannot see, so DOM extraction is the
reliable channel. A page-world interceptor still runs as a supplement.
Costs #
One Jev API call per unique post text per rule configuration. The sidebar shows judged / hidden / waiting counts and the last API error.
Notes & limitations #
- The sidebar is styled with the Everforest dark palette by default and adopts
your Firefox/Zen browser-theme colors automatically when the theme exposes
them via the theme API (
theme.js); it re-applies live when the theme changes. - The mapping from rendered feed items to posts uses Bluesky's permalink anchors
(
/profile/<author>/post/<rkey>), not CSS class names, but markup changes on bsky.app can still break it — if the feed stops filtering after an app update, this is the first place to look (content.js). - Quote-posts and reply-parents inside an item are not judged separately; the main post's verdict governs the whole item.
- Only feed surfaces are intercepted: timeline, author feeds, custom feeds, search, and threads — not notifications.
Development #
node --test test/ # unit tests (no key needed)
source typesafe_api_key.secret.sh && node scripts/live-check.mjs # live API check
npx web-ext lint # manifest/API lint
Layout: shared.js (pure decision logic, unit-tested), interceptor.js
(page-world fetch hook), background.js (queue + Jev client + cache),
content.js (DOM), sidebar.* (settings UI).