this post sux

Files
README.md

sux #

A firefox plugin to filter shit out of your bsky feed

Mostly, it was just an excuse to fuck with jev.

sux reads every post bsky.app shows you, asks Jev (TypeSafe's System One model) whether it passes your rules, and hides the ones that don't. Works in Firefox and Firefox-based browsers (Zen, etc.). There's also a userscript build for Tampermonkey/Violentmonkey in any browser.

Rules #

Set in the sux sidebar. Each section takes any number of clauses.

  • Keep posts that lean toward… — two words. A post is kept when Jev puts at least 50% on the first word over the second.
  • Keep posts that… — one word plus a direction: are mostly word (kept above 50%) or are NOT word (kept below 50%).
  • Hide posts like these — paste a few posts you don't want to see (3–5 is plenty). Jev hides posts of the same kind. Name the rule if you like; the name shows up when peeking.
  • AND / OR — by default a post must pass every enabled clause; switch to any to keep a post that passes at least one.
  • Filtering on — one switch turns the whole thing off. Off means the page is left untouched.

Peek and correct #

Turn on Peek hidden posts and hidden posts collapse to a one-line bar saying which rule hid them, instead of vanishing. For Hide posts like these rules the bar has two buttons:

  • not this — the post was hidden by mistake; it's remembered as a counter-example and the feed re-judges.
  • more like this — add the post to the rule's examples.

Install #

  1. Get a TypeSafe API key: https://typesafe.ai
  2. Get the zip: build it (see Development) or grab it from a release.
  3. sux isn't signed by Mozilla yet, so it needs a browser that allows unsigned add-ons — Firefox Developer Edition, Nightly, Zen, and most other forks (release Firefox won't load it):
    • Put user_pref("xpinstall.signatures.required", false); in a user.js file in your profile directory.
    • Quit the browser, copy the zip to <profile>/extensions/@sux.xpi, start it again. It stays installed across restarts.
  4. Open bsky.app, then the sux sidebar. If you see Grant site access, click it and reload your bsky.app tabs (Firefox makes site access opt-in).
  5. Paste the API key, press Test, add a rule, flip Filtering on.

To update: copy the new zip over @sux.xpi and restart.

How it behaves #

  • Posts stay hidden until judged — usually well under a second, slower when the API rate-limits.
  • Scores are cached per rule clause + post text, so reposts and scrolling back cost nothing, and editing one clause only re-asks about that clause. Flipping a direction or AND/OR costs no calls at all.
  • After a rule change the feed keeps its current verdicts on screen until the new ones arrive, posts nearest the middle of the screen first.
  • Image-only posts with no alt text are shown; there's nothing to judge.
  • If the API fails, posts are shown (fail open). The sidebar shows the last error and judged / hidden / waiting counts.
  • Quote-posts and reply-parents inside a feed item aren't judged separately; the main post decides the whole item.
  • Runs on the timeline, profile feeds, custom feeds, search, and threads — not notifications.

Cost and privacy #

Posts are sent to Jev three at a time, one API call per batch, and only for clauses not already cached. The text (and image alt text) of each post in your feed, plus your rules and their example posts, are sent to TypeSafe. Your API key and settings stay in browser storage.

Limitations #

  • Posts are found via Bluesky's permalink anchors in the rendered page (content.js), not CSS class names, but a bsky.app redesign can still break it. If filtering stops after an app update, look there first.
  • The sidebar uses the Everforest dark palette, or your browser theme's colors when the theme exposes them.

Development #

node --test test/                    # unit tests (no key needed)
npx web-ext lint                     # manifest/API lint
source typesafe_api_key.secret.sh && node scripts/live-check.mjs   # live API check

Build (--ignore-files is required; without it the zip bundles the bench data and the userscript):

npx web-ext build --overwrite-dest --ignore-files bench userscript test scripts docs web-ext-artifacts "*.secret.sh" README.md

Output: web-ext-artifacts/sux_jev_feed_filter-<version>.zip.

LOCAL_MODEL in shared.js is a build switch for an in-browser embedding model (local scoring, Jev only for unsure posts). It's off: no runtime ships yet, and the related UI is hidden. Background in bench/.