A Chrome extension that uses TypeSafe Jev to reduce feed noise without hiding ordinary organic content. A post being promotional is not treated as a reason to filter it. Filtering is reversible, the rules are explicit, and the API key is entered inside the extension.
Personal-use preview. No terminal, bridge, pairing code or external server is required.
Extract the release into a permanent folder. Open chrome://extensions, enable Developer mode, choose Load unpacked, and select the extension directory containing manifest.json.
In FeedGate, enter a direct TypeSafe API key, grant cloud-analysis consent, and click Connect & start with All six supported feeds preselected. Chrome asks once for the selected origins. The extension tests one fictional post with the actual API before enabling the selected feeds. Refresh existing tabs, especially after removing an older FeedGate build.
The ready-made extension does not need Node, Python, npm, a local service, or an installer executable. Chrome 120 or later is declared in the manifest. Managed browsers may prohibit unpacked extensions; do not work around administrator policy.
For a short step-by-step walkthrough, open START_HERE.html, START_HERE.md or the Polish START_TUTAJ_PL.md. Upgrading from 2.x: read docs/MIGRATION.md; the update workflow is explained in docs/UPDATES.md.
Version 3.3 adds time-limited topic mutes, conservative duplicate grouping and honest API-usage controls, and fixes three author-list edges. No existing category or threshold changes meaning:
- Mute topic… from the status panel: pick a post, edit the topic name, choose the scope (this site or all supported platforms) and 24 hours / 7 days / until removed. Matching is semantic through the same Jev request — a passing mention stays visible, Keep this post wins, Undo is offered, and an expired rule stops hiding posts by itself. Manage everything in Settings → Muted topics.
- Grouping repeated posts (optional, off by default). The first copy stays visible; later near-duplicates fold behind a "more similar posts" bar that expands in place. Similarity is computed locally with a bounded per-tab memory, no provider request is spent, and everything returns when you turn it off.
- API usage panel and thrift mode. Review → API usage shows the daily used/remaining budget (reset at 00:00 UTC, shown in local time), attempts, analyses, cache hits, errors, cancellations and tokens; cost appears only from your own configured rates and is labelled an estimate, not a provider bill. Thrift mode waits until a post is visible and scrolling has settled before calling the model; local author, verified-ad and cosmetic rules still act immediately.
- Author-list fixes. Concurrent mute/allow actions can no longer drop one another, Undo restores the exact earlier state and refuses to overwrite a newer decision, and LinkedIn/Facebook/Instagram matching now uses the canonical profile link instead of the display name (name-only posts never match a saved list).
- Policy
policy-3.3.0, rubricfeed-v2.2.0, settings schema 6, diagnostics schema 8. New optional features stay off for existing users after migration.
Version 3.2 makes updates survivable and changes no model, rubric or threshold:
- A stable extension identity. The manifest carries a bundled public key, so the extension id no longer depends on the folder path. Load 3.2.0 once and the encrypted vault, settings and local lists survive future in-place updates and folder moves.
- Updates panel in Settings (
06 Updates). It compares the loaded build with the packagedbuild.jsonon disk and says when the files in the folder are newer than the running extension, with the in-place steps (copy files over the same folder, Reload fromchrome://extensions, never Remove) and a button that opens the extensions page. - Sealed key backup. Export and import move only the encrypted vault blob, never the plaintext key; unlock it with its passphrase on the new install.
- Diagnostics schema 7. Exports gain an
updatesection (running, on-disk and build id). One-time note: earlier builds derived the id from the folder, so the first 3.2.0 load starts a new identity — enter the key once more or import a backup, and updates stop bothering you afterwards.
Version 3.1 adds two user controls and changes no model, rubric or threshold:
- Mute author. Every filter card offers Mute author beside Allow author. Muting hides that author's posts on that platform deterministically — no provider request, rule
author_muted, sourceauthor— and the list is local, capped, mutually exclusive with the allow list and fully revertible from Settings → Muted authors. A post you explicitly kept still wins over the mute list, and cosmetic rules are unaffected. - A clearer exception rule. The custom instruction is now a first-class My exception rule (optional) panel with a live character counter and a status badge, plus plain fine print: it is a hint the model may ignore, it is sent with every cloud request even when the
Your exclusion rulecategory is Off (which costs tokens), and secrets must never go there. Diagnostics still report only a boolean, never the text. - Author controls in the status panel (3.1.1). The expanded panel now has Mute author… and Allow author… beside Pick element: click a post, see a preview naming the author, save with Undo in the toast, or cancel with Escape with nothing saved. Both directions reuse the same deterministic local lists as the filter cards.
- Policy
policy-3.1.0, settings schema 5, diagnostics schema 6. One new precedence row (author_muted) before all semantic branches; the settings schema gainsblockedAuthorsand migrates 3.0.x settings automatically; diagnostics addmutedAuthorCount(a count only). Runtime state stays version 5, so a 3.0.x session still restores its cache and counters.
Version 3.0 keeps the 2.1.1 filtering semantics and adds local control, safety and coverage:
- A status panel with real modes. The on-page status starts as a small pill, expands into controls, or can be hidden completely. Hiding the panel never pauses filtering, the choice survives restarts, and a hidden panel is not recreated unless you change the setting. Errors surface in the popup instead.
- Complete recovery for any page. Restore this site saves a pause first in the trusted worker, then asks open tabs to remove their effects with a bounded timeout. If a tab never responds, the pause still holds and you get an explicit Reload this tab with filtering paused option. Restarting the browser cannot resurrect hidden content.
- Transactional element picker. Picking an element now previews before anything is saved: scope, match count and what would hide. Structural targets (page roots, navigation, the main content column, multi-item containers, FeedGate's own UI) and match sets that would hide most of the page are rejected. Commit re-validates the live node, and every commit offers a 15-second Undo plus a persistent Undo in the popup.
- Managed cosmetic filters. Rules are real records with stable ids, a status (
active,quarantined,disabled,deleted), a concise reason and per-domain revision checks. The options page lists them with Enable, Disable, Edit, Delete, Undo deletion, bulk disable and confirmed bulk delete. Selectors stay local and never appear in diagnostic exports. - Legacy rules cannot hide anything at startup. Cosmetic CSS from earlier versions becomes quarantined with the reason
legacy_unvalidated. A rule is re-checked against the current safety rules and only activates after a real matching element is observed. - Loop containment. One controller per document, coalesced scheduling, unchanged decisions are no-ops, self-authored mutations are ignored for extraction and model work, and a bounded circuit breaker quarantines a rule that keeps rewriting the page.
- Wykop (beta). Public Główna/Wykopalisko lists, Mikroblog entries, tag streams and profile/detail pages are supported through the same pipeline. Link cards and entries stay distinct, comments and replies are excluded, and Wykop's informational "Wykop Poleca" note or old promotion text can never trigger local ad hiding.
- Honest feed registry. One registry drives selection, permissions, registration and diagnostics. The feeds list shows desired, permitted and effective states with plain reasons, and Enable all supported feeds requests the missing origins in a single logical prompt.
- Diagnostics schema 5. Exports separate semantic, cosmetic and UI metrics, include a build fingerprint and per-site states, and prove renderer health with heartbeats instead of assuming silence means zero activity.
Version 2.0 already separated promotion from hiding rules, but its diagnostics showed that one ambiguous category still did most of the work, and the rubric sent roughly a thousand input tokens per classified post. Version 2.1 is precision-first:
- Promotion is never enough. High promotional intent alone keeps the post visible. An unwanted-advertising estimate collapses a post only when feed-spam signals are also high and both usefulness estimates are low.
- Clearer categories. The policy now names Unwanted advertising, Feed spam, Manipulative engagement bait, Low-signal filler and Risk or scam-like content as separate estimates. "Promotion" is no longer a filtering category and no longer looks inherently bad in the interface.
- Explicit protection indicators. Useful information (>= 0.65) or Original contribution (>= 0.70) keeps a post visible ahead of the advertising, bait and low-signal rules. Only risk, an explicit custom exclusion or overwhelmingly strong spam evidence (>= 0.98) can override protection.
- Smaller requests. The shared instruction and the eight question texts were rewritten without changing their meaning, cutting the instruction payload by about 38% (roughly 2,900 to 1,800 characters). The per-post request now targets a few hundred input tokens instead of about a thousand.
- Rule names in evidence. Every decision records an explicit rule such as
unwanted_ad_low_value,feed_spam,useful_or_original_protectionorpromotion_signal_alone, alongside the scores, protection flag, sufficiency estimate, cache status, model and latency. - No stale semantics. The rubric id is now
feed-v2.1.0and the cache namespace changed. Cached 2.0 judgments and 2.0 thresholds are ignored, and the old promotion threshold is deliberately not mapped onto the new unwanted-advertising category.
Balanced, Aggressive, Observe Only and Custom presets expose the same deterministic rules. Opened profiles normally receive gentler semantic filtering. Visible quotes are separated from the main author's words, and truncated posts are treated more cautiously. No author reputation is inferred, no account is automatically blacklisted, and no attempt is made to prove that someone is a bot.
The 2.1.1 repair removes the remaining ways an ordinary post could be hidden by local ad detection:
- Structured ad evidence. Local ad detection now returns a structured evidence object (
verified,ambiguousornone) with a marker kind, selector id, ownership, rendered-label flag, evidence kind and scope. Only a verified rendered or platform-metadata label owned by the outer post can collapse it locally. - No guessing from neighbouring markup. Impression or placement tracking wrappers, words like "Sponsored" in post text, names, links, media alt text or unrelated accessibility labels, labels hidden in templates, and badges that belong to a quoted or adjacent card can no longer mark the outer post.
- Legacy flags are downgraded. Boolean ad flags stored by 2.0 or 2.1.0 are migrated to
ambiguous, so they continue through the semantic path instead of collapsing locally. - Truthful reasons.
promotion_signal_aloneis reported only when no other noise estimate (feed spam, bait, risk) is elevated. Mixed below-threshold signals are reported asbelow_noise_thresholds. - Marginally useful content keeps its conflict branch. Protection eligibility now starts at
useful >= 0.65ororiginal >= 0.70(an engineering choice), and every decision recordsprotectionEligibleseparately from the appliedprotectedflag. - Separate version ids. The release, semantic rubric, policy, ad detector, extraction and diagnostics schema are versioned independently, and the diagnostics export includes a build fingerprint. Open tabs working with an outdated content script show a reload prompt instead of mixing schemas.
- Reddit title duplication fixed. The post title is no longer prepended twice.
The policy thresholds were not lowered, Low-signal filler remains off by default, and the sufficiency guard is unchanged.
Show temporarily restores a post. Keep this post stores a local hash-based exception for that particular visible post. Allow author and Mute author are deliberate, platform-specific lists (mutually exclusive). Alt + Shift + F reveals or refilters the current page. Explicit cosmetic rules and a small element picker remain available for local cosmetic filtering.
The on-page status panel and Diagnostics screen separate detection, pending work, returned judgments, local cosmetic actions and filtering. Export diagnostics produces a structured JSON file without keys, vault ciphertext, post text, authors, custom rule text, raw cosmetic selectors, stored post hashes or full browsing URLs. Recovery steps are documented in docs/RECOVERY.md.
Enabled feed
-> isolated content script
-> visible text and quote extraction
-> extension service worker
-> explicit local rule / user exception
-> score cache and request queue
-> direct TypeSafe HTTPS request
-> strict Noul response validation
-> deterministic precision-first policy
-> reversible DOM update
The fixed endpoint is https://api.typesafe.ai/v1/systemone; the default model is jev-latest. There are eight independent Noul questions (unwanted advertising, feed spam, manipulative bait, low-signal filler, useful information, original contribution, risk and evidence sufficiency) and an optional ninth when a custom question is present. Question probabilities are not bot identity, legal conclusions, or verified truth. See the policy specification.
X and Reddit are the primary adapters. LinkedIn, Facebook, Instagram and Wykop (beta) are secondary adapters, disabled until the user grants their optional permissions. Adapter tests use representative HTML fixtures based on served public markup, not current signed-in feeds. Wykop ad detection is deliberately conservative: no verified placement marker exists today, so Wykop placements are reported as ambiguous instead of locally hidden. See Wykop support.
The default key lives in trusted chrome.storage.session and is not returned to content scripts. Browser restart, disabling or reloading the extension can clear that session. The optional persistent vault uses AES-256-GCM and PBKDF2-SHA256 with 600,000 iterations, a random 16-byte salt and a random 12-byte nonce. Unlocking requires the user's passphrase after a browser restart. No passphrase is stored. A sealed key backup can be exported and imported (ciphertext only) when moving to another install of the same identity; see docs/UPDATES.md.
A password field or an encrypted file is not protection against malware or a compromised extension. Use a dedicated, revocable provider key. Saving a session-only key replaces any previously saved vault. This build does not use storage.sync.
Cloud opt-in allows visible extracted post text, visible quote text, accessible media text, platform ad markers and linked domains to reach TypeSafe. No screenshots, image/video files, cookies, browser history or passwords are requested. Private-route and editor guards are best effort, not a guarantee that all sensitive material can be recognised. Read Privacy and Security.
The packaged evidence records 164 passing Node tests, 66 passing Chromium DOM/UI scenarios, an 8-check unpacked-extension smoke test and 9 release integrity checks. Unit tests execute the actual policy, HTTP client, vault and worker source; worker tests replace Chrome APIs and HTTP with controlled implementations. Browser tests execute the actual DOM and UI code with mocked Chrome messaging, page location and Jev responses. The smoke test loads the real extension/ folder in a local Chromium profile.
The suites include the 25 precision-first regression cases, the 2.1.1 ad-evidence contracts, the v3 control/safety cases (HUD modes, recovery without a responsive content script, permission and site states, transactional picker guards, rule management and revisions, loop containment, Wykop fixtures), the 3.1 author-mute and exception-panel cases, the 3.2 update and identity cases (stable id from the manifest key, build.json comparison, sealed vault-backup validation and round trip, the Updates panel), the 3.3 cases (atomic author-list updates, exact-state Undo including stale conflicts, stable author identity, topic mute with expiry and accidental-mention handling, duplicate grouping, thrift deferral and the usage counters), plus three real privacy-safe diagnostic exports used only to replay recorded counts.
No real TypeSafe key was used for this release and no signed-in feed was accessed or measured. The unpacked extension was loaded from extension/ in a local Chromium profile by the smoke test, but managed installation remains blocked in the build environment, so live-provider classification accuracy stays unmeasured. No administrator policies were changed. These are implementation regression tests, not a measured improvement in live classification accuracy or an independent security audit.
The local test feed contains fictional text and does not ship mock model scores. Once key and consent are enabled, it uses actual Jev requests. Test report explains the remaining manual checks. Optional live evaluation provides labelled fictional examples and an explicit opt-in developer runner.
JavaScript modules and browser APIs are used without runtime third-party dependencies. Node 22 or later is needed only for development:
npm run check
npm test
npm run build
npm run release:checkBrowser fixtures additionally require Python, the pinned tooling in tests/requirements.txt and Chromium:
python -m pip install -r tests/requirements.txt
python -m playwright install chromium
npm run test:e2eSet CHROMIUM_PATH to a permitted Chromium executable when necessary. Never disable managed policies to run these tests. The included GitHub Actions workflow declares Windows and Linux Node checks and Linux DOM tests. It was not run on GitHub as part of this local build.
src/ contains executable source, static/ contains markup and assets, and extension/ is the ready-to-load result. Rebuilding simply copies local files. scripts/release-check.mjs checks source/output agreement, manifest references and the absence of a local bridge.
FeedGate changes the appearance of supported feeds. It does not block network requests, trackers, video advertisements or every advertisement on the internet. It does not analyse image/video pixels, open landing pages, expand hidden text, crawl profiles or train an account reputation model. Existing content blockers can remain installed.
The UI may leave a post visible because the key is locked, consent is absent, text is insufficient, a queue or budget limit was reached, an API request failed, or no policy rule matched. Inspect Diagnostics instead of interpreting every visible post as a successful semantic judgment. A hidden status panel never means the engine is paused, and a disabled cosmetic rule or a recovery pause is reported explicitly.
GPL-3.0-or-later. See LICENSE and THIRD_PARTY_NOTICES.md. This code continues the earlier direct-API FeedGate implementation and replaces the bridge workflow and v1 policy. uBlock Origin informed the separation of DOM watching, cosmetic rules and user controls; its code, engine, lists and branding are not bundled. This is not an official uBlock or TypeSafe product.
