One goggle instead of six that cannot be combined. Brave applies exactly one goggle per search, so everything that is supposed to act at the same time has to live in the same file.
The rules are written for a German-language, Linux and homelab flavoured search profile. Take them as a starting point, not as a truth.
| Part | File | Maintenance |
|---|---|---|
| Own rules (boost/downrank/discard) | rules.goggle |
by hand |
| Exceptions from the upstream lists | allowlist.txt |
by hand |
| AI content and AI images, ~1,180 domains | upstream | automatic |
| StackOverflow and GitHub copycats, ~190 domains | upstream | automatic |
| Result | neverperfect.goggle |
generated, do not edit |
State of the last build: 1,503 instructions, 46 KB. Brave allows 100,000 instructions and 2 MB, so there is a lot of room left.
Python 3.9 or newer, standard library only. No dependencies, no virtual environment.
-
Create a GitHub repository (public) and put this folder into it. A private Gitea will not work: Brave only accepts
github.com,gist.github.comandgitlab.com, and the file has to be reachable from outside. A secret gist is enough if you do not want a public repository. -
Build:
python3 build.py git add -A && git commit -m "chore: update goggle" && git push
python3 build.py --offlinebuilds from the cached copies undercache/instead of refetching the upstream lists. -
Submit the raw URL to Brave at https://search.brave.com/goggles/create
https://raw.githubusercontent.com/NeverPerfect/brave-goggle/main/neverperfect.goggle -
Select it afterwards at https://search.brave.com/goggles
python3 -m unittest -vThe tests cover the parsers, the allowlist and the limit check in build.py.
The same command runs in CI as the check job. CI deliberately does not build:
a build fetches the upstream lists, and an outage there is not a reason to fail
a pull request.
Important: after every change the URL has to be submitted to Brave again.
Brave caches the file and does not poll. A git push on its own changes
nothing.
Effect order: discard > boost=10..1 > downrank=1..10.
discard is final. A $boost can not undo a $discard. That is why the
exception list lives in allowlist.txt and takes effect while generating, not
at query time. When a domain has to leave an upstream list, it belongs there,
not into rules.goggle as a boost.
downrank instead of discard everywhere false positives are likely, for
example in the affiliate patterns, which also catch a review published by c't.
The result does not disappear then, it just moves further down.
-
No understanding of text. Goggles match on the URL only.
$intitle,$incontentand$indescriptiondo appear in Brave's quickstart, but are marked as planned there and are not usable. "No AI text" is therefore only implementable as a domain blocklist, not semantically. New slop domains slip through until somebody reports them. -
Image search probably not. Brave's documentation names web results plus the discussions, news and videos clusters. Images are not mentioned. The AI image rules (Freepik, Adobe Stock, Vecteezy, Civitai, Midjourney) are in there; whether they act in image search has to be tried out.
-
Only one goggle at a time, and the choice applies per search, not permanently. A bookmark helps:
https://search.brave.com/search?q=%s&goggles_id=<your-url>added in Firefox as a search engine with a keyword. -
Privacy note from Brave: a goggle that only one person uses works as an identifier through its goggle URL. Brave's search queries are not personalised, but the note stands like that in their README. If that bothers you, set
! public: trueso that more people use it.
- Not a curated blocklist of its own. The blocked domains come from upstream lists. What is maintained here is the own ruleset and the exceptions, nothing else.
- Not a content filter. See above: URL matching only.
- Not a browser extension and not a proxy. It works in Brave Search and nowhere else.
- No automatic submission to Brave. There is no API for it, step 3 stays manual.
"Neutral" cannot be measured, so rules.goggle says something else instead:
what gets boosted are news agencies without an opinion desk, public
authorities, statutory texts, statistics offices and specialist literature, so
sources that others cite. What gets downranked is tabloid press and how-to
portals. That list is a proposal, not a truth. Change it.
The AllSides rating that "News from the Left/Right" is based on is purely US-American and does not apply to German media.
- Brave Goggles quickstart: https://github.com/brave/goggles-quickstart
- HUGE AI Blocklist: https://github.com/laylavish/uBlockOrigin-HUGE-AI-Blocklist
- Copycats removal (Goggles 101): https://github.com/brave/goggles-quickstart/blob/main/goggles/copycats_removal.goggle
Not used: Super SEO Spam Suppressor.
409,000 domains, which blows past Brave's limit by a factor of four. In
build.py that source is set to False and the reason stands next to it.
MIT, see LICENSE. Copyright (c) 2026 Stephan Fröhlich.
That covers what is written here: build.py, rules.goggle, allowlist.txt
and the documentation. Fork it, change it, use it, no conditions beyond keeping
the copyright notice.
build.py fetches the upstream lists at build time; they are not committed
here. The generated neverperfect.goggle does contain their domains, so their
terms travel with any build you publish.
| List | Licence |
|---|---|
| HUGE AI Blocklist | CC0-1.0, public domain, no conditions |
| Copycats removal (Brave, Goggles 101) | none stated, see below |
| Super SEO Spam Suppressor | WTFPL, not used here |
brave/goggles-quickstart carries no licence file. Its CONTRIBUTING.md calls
the repository educational material meant to be used, which is an intention and
not a grant. A bare list of domains is thin material for a copyright claim, and
the goggle itself is published by Brave as public: true for anyone to apply,
so redistributing it in a build is the intended use rather than a stretch of
one. Whoever needs certainty asks Brave or disables that source in
build.py.