N4darae/anti-mage: scoring anti-detect browsers by making them disagree with themselves
For detecting anti-detect browsers and spoofed profiles through runtime coherence analysis.
At a glance
- What is it?
- A Go server that collects 34 browser-side measurements, runs 23 coherence readings over them, and returns one score, one determination and one sentence. It ships no signature list, and it caps at 90 because nothing is ever certain.
- Who is it for?
- Adopt it if you already collect browser telemetry and want a second opinion that does not depend on a vendor keeping a signature feed current, and if you can read the reference tables before trusting the number. Do not adopt it if you need a pass or fail gate, if you cannot run JavaScript in the context you are judging, or if you need coverage of the 7 reference entries that are still unverified.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 9 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 16, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
What anti-mage is for, and who ends up running it
The repository states the problem in its first line: anti-detect browsers. These are browsers built to present a profile that does not match the machine underneath, and the usual way to catch them is a list of known tools. N4darae/anti-mage takes the other route. It never asks which tool produced an environment. It asks whether the browser's own surfaces agree with each other and with the platform the browser claims to be, and it returns a number between 0 and 90.
The audience is narrow and technical. You need a Go 1.24 toolchain, a page you can load in the browser you want to examine, and the ability to read Go source, because the README says the source is the specification. This is not a hosted API and there is no dashboard. The person who benefits is the one doing fraud or bot analysis who is tired of maintaining a fingerprint signature list that goes stale the week after it is written.
The mechanism: 34 measurements in, 23 readings out
The browser-side collector takes 34 measurements. Those feed 23 independent readings, each of which looks at one place where a browser's surfaces have to agree. The examples in the README are concrete: installed fonts against the claimed platform, screen geometry against CSS, time zone against measured offsets, two serialisations of one drawing surface, hardware decoders against the device named, and both graphics interfaces against one device.
Native accessor integrity is the densest of them. It is four separate readings of every accessor a spoofer has to touch: how the function serialises, whether three different enumerators of its own keys agree, whether the property still sits on the interface prototype object where the interface definition puts it, and whether calling it with the wrong receiver throws the TypeError the specification mandates. That last one is the interesting test. A hook that returns a plausible value but forgets the error path is caught by the specification, not by a signature.
Each reading returns one of five determinations: consistent, contradiction, instrumented, inconclusive, unverified. The score is built by class rather than by count. A surface that reports its own modification weighs least, a plain disagreement weighs more, and a disagreement that nothing but a deliberate change produces weighs most. Each further body of evidence adds less than the one before it, and the result is rounded onto a step of ten so that no single reading can be isolated by watching the last digit. The constants live in internal/scan/band.go.
Why unverified is the determination that matters
A reading abstains when the probe was not collected, when the browser does not expose the feature, when the page was not served to a secure context and the feature is gated there, when nothing was named to compare against, or when the reference table it would have read has not been verified against a system the author has actually observed. The README is explicit that a browser is never scored for lacking a feature.
That design decision has a cost, and the repository admits it. 7 of the 22 reference entries are not yet marked verified, which means the readings that would have used them abstained on all 100 samples in the published results. So the numbers in that table are a floor. Verifying those entries can only push the anti-detect column up, never down, because the scoring has a property the README says is covered by tests: only evidence raises the score, so withdrawing a reading can never raise it.
The second property is subtler. A self-declared hook explains its own downstream damage. When an accessor reports that it has been modified and a later reading contradicts itself in a way that accessor accounts for, the finding is downgraded from a lie to a declared modification. That is why Brave's median sits at 10 in the published results rather than in the anti-detect range: Brave modifies surfaces it says it modifies, which the project records as the lightest class of finding. The README states plainly that the scores are not a ranking of the tools measured.
Installing anti-mage and running a first scan
There is nothing to install. The README gives a single clone-and-run line, and go.mod confirms the module has no external dependencies beyond the Go standard library. Go 1.24 or newer is required.
git clone https://github.com/N4darae/anti-mage && cd anti-mage && go run .After that command the server prints one loopback URL, 127.0.0.1:8787 by default. Open that URL in the browser you want to examine and press the button on the page. The page measures itself and posts the result back to the same server.
Three flags are documented. -addr changes the address, which must be loopback; both the address you ask for and the socket that actually gets bound are checked, so the server cannot be talked onto a public interface. -web serves the page from disk instead of the copy compiled into the binary. If you touch the collector in web/, that flag is how you avoid a rebuild cycle. -dump writes every payload posted to it, for replay through tools/debugscan later.
The HTTP surface is two calls. GET /api/bootstrap (also answers POST, aliased at /bootstrap.json) returns the inputs the server chose for one scan, including a 128-bit nonce from crypto/rand and six font family names. The client measures itself against those inputs and posts what it measured. If you are wiring this into an existing pipeline rather than using the bundled page, that bootstrap-then-post pair is the whole integration.
The server hardening, and where the design stops helping
The server binds loopback only, caps a scan body at 1 MiB, sets header, read, write and idle timeouts, recovers from a panic in a handler, and sends nosniff, no-referrer and no-store on everything it serves. That is a reasonable floor for a tool that accepts attacker-controlled JSON, and the 1 MiB cap is the constraint you will hit first if you extend the collector: more probes mean a bigger payload, and the cap is not documented as configurable.
The real limitation is the one the README states itself. The score tops out at 90, and nothing in the project will ever tell you an environment is certainly modified. If your workflow needs a binary verdict, anti-mage does not give you one, and rounding onto a step of ten deliberately destroys the resolution you would need to build one.
There is a second boundary the README does not address. Every reading depends on JavaScript executing in the browser under examination and on the page being served to a secure context, because the README lists that as one of the abstain conditions. A browser that blocks the collector, or a target you can only observe from outside, produces no score at all. The README does not document rollback, retry or partial-scan handling either. And the published results are 100 samples per browser on Windows only; the repository gives no equivalent data for macOS, Linux, Android or iOS, so treat cross-platform behaviour as unmeasured.
How anti-mage differs from client-side fingerprint libraries
The obvious alternative is a fingerprinting library such as FingerprintJS or CreepJS, which computes a stable identifier or a headless-detection score from browser signals. The difference is what the output means. A fingerprint library answers whether two visits look like the same machine. Anti-mage answers whether one environment is internally coherent, and it does so without a database of known tools, which is the property the README leans on hardest: there is no signature list, no known-tool database, and nothing to keep updated.
That choice buys durability and costs coverage. A signature-based detector can name the tool once it has seen it; anti-mage can only say that the environment contradicted itself, and its own README says it needs two things the browser said to disagree. A spoofer that patches one surface cleanly and consistently, with correct error paths and correct prototype placement, produces nothing for this project to weigh. The reference tables are the compensating mechanism, and 7 of the 22 entries are still unverified, so the compensating mechanism is not finished.
Editorial conclusion
Adopt it if you already collect browser telemetry and want a second opinion that does not depend on a vendor keeping a signature feed current, and if you can read the reference tables before trusting the number. Do not adopt it if you need a pass or fail gate, if you cannot run JavaScript in the context you are judging, or if you need coverage of the 7 reference entries that are still unverified. Verify three things first: whether the 23 readings cover the surfaces your traffic actually spoofs, whether the score bands in internal/scan/band.go match the thresholds your process uses, and whether the shipped reference tables have been checked against systems you have observed, because an unverified table makes its reading abstain rather than guess.
Frequently asked questions
What is N4darae/anti-mage?
It is a non-profit Go project that scores how coherent a browser environment is, together with the reference data the scoring reads. A client asks for a score and gets back one determination, one number, and one sentence saying what the two mean.
How do you use N4darae/anti-mage?
Clone the repository and run go run . with Go 1.24 or newer, then open the loopback URL it prints (127.0.0.1:8787 by default) in the browser you want to examine and press the button. The page measures itself and posts the payload back to the same server.
What is an Anti-Mage in the context of this project?
The name refers to a detector aimed at anti-detect browsers, the tools built to present a profile that does not match the machine underneath. The README frames the project as a way to catch them by making a browser disagree with itself rather than by matching a signature.
Who is N4darae/anti-mage for?
The README describes it as a non-profit Go project for people tired of anti-detect browsers, and the published results are aimed at anyone who wants to know whether a browser environment contradicts itself. Running it requires a Go 1.24 toolchain and a browser you can load the page in.
Who is N4darae/anti-mage a counter to?
The README names anti-detect browsers as the target, listing AdsPower, CloakBrowser, NSTBrowser and Camoufox in its results table. It states that the scores are not a ranking of those tools, only a record of how much each environment contradicted itself.
Community notes