Detection layers
How Haven decides that something is gambling. Two engines do this work: a domain matcher that answers one binary question about a hostname, and a risk engine that combines many independent signals about a page into a graded verdict.
Both are pure, Android-free Kotlin, which is why they are covered by the bulk of the project's unit tests.
This page describes the structure of the detection. The tuned constants — score weights, thresholds, timing windows, and the exact contents of the fence lists — are deliberately not published. Publishing them would be publishing a recipe for building a page that scores just under the line.
The domain matcher#
Given a hostname, the matcher returns a decision and the rule that produced it. It evaluates in order of precision, cheapest and most specific first, and the first hit wins:
- Allowlist — an explicit allow always wins over everything below it.
- Encrypted-DNS resolvers — known DNS-over-HTTPS/TLS endpoints, so filtering cannot be sidestepped by moving lookups off the system resolver.
- Exact domain and subdomain — a hash-set probe over the hostname and each of its parent domains, so one rule for
example.comalso coverswww.example.comand every other subdomain. - Wildcard patterns — a small glob layer for a handful of curated patterns.
- Keyword heuristics — in two tiers. Strong keywords are gambling-specific enough to match anywhere inside a domain label. Weak keywords match only as a whole word, with digits and hyphens treated as word boundaries, plus curated start/end rules so that a brand-like construction is caught while ordinary words containing the same letters are not.
- Fuzzy and phonetic matching — on a de-obfuscated form of the hostname, restricted to longer strong keywords. This collapses misspellings and leetspeak evasion onto the real term. A fuzzy hit and a phonetic hit are marked differently in the log so the user can see why something matched.
Before any of this, the hostname is normalised: lowercased, trailing dots removed, and homoglyphs folded, so visually-identical Unicode look-alikes cannot be used to slip past an exact rule.
The active rule set is an immutable snapshot swapped atomically. A blocklist update therefore takes effect immediately, without restarting the VPN and without ever blocking the DNS hot path.
The risk engine#
The domain matcher answers "is this hostname on a list?". The risk engine answers "does this page behave like a gambling site?", which is what catches domains no list knows yet.
Its design rule: no layer can block on its own. Each signal source only contributes points; a central engine sums them and maps the total onto a verdict. A site that changes one thing — a class name, a keyword, a logo — cannot silently slip past the way it could past a single-method detector.
The signal sources#
| Source | What it looks at |
|---|---|
| Domain | Whether the hostname itself already carries risk |
| Text | Bonus-lure language on the visible page, across roughly a dozen languages |
| Prediction market | The trading interface of a prediction/event-betting market |
| Provider | Names of well-known casino game providers appearing together |
| Licence | Gambling-licence and responsible-gambling boilerplate |
| Bonus | Strong bonus/promotion wording |
| CTA | Density and phrasing of call-to-action controls |
| Structural | Shape of the page: card grids, image-to-text ratio, link density |
| DOM similarity | Structural similarity to known gambling page templates (inactive — see below) |
Adding a source is a registration change and nothing else; the engine itself never needs editing.
The anchor rule#
A verdict of BLOCK requires at least one anchor — a gambling-specific signal, not a pile of structural or layout heuristics. An encyclopedia article about roulette, or a busy product grid on a webshop, can look structurally similar to a casino lobby. Without an anchor the strongest verdict such a page can reach is a warning, which by itself never interrupts anything.
Warn, then confirm#
The engine produces three verdicts: allow, warn, block.
- A lone warning never acts. It arms the host. A second, independent scan that also warns can escalate — but only if an anchor is present.
- A block still needs two separate positive scans of the same page before Haven intervenes, so a half-loaded frame captured mid-navigation cannot get a legitimate site blocked.
Page loads produce a burst of accessibility events and then go quiet, so Haven schedules its own follow-up look rather than relying on the page to emit another event.
The address bar is not evidence#
The browser's address bar is stripped of its text before the page is scored, while staying in the tree so the structural fingerprint is unaffected.
The reason is concrete: the address bar contains user input, or whatever someone else's link put there. If its text counted as page content, then a link containing gambling words would be enough to get any domain scored, warned, escalated and permanently written into the user's filters. A link is far too cheap a way to do that to any site.
Fences the heuristics may not climb#
Certain categories of host — search engines and very large user-content platforms — can never be learned by the heuristic layer, even on a positive score. A search results page quotes whatever was searched for, and because a learned rule is a suffix rule, one false catch on one video would take away an entire platform, permanently.
On these hosts Haven still shows the pause and still logs it. Only the permanent rule is withheld. The specific list is not published here.
Inactive by design#
Several model-driven sources (screenshot classification, logo recognition, screenshot embedding similarity, DOM-template similarity) are defined as interfaces with no-op implementations. They report themselves as unavailable and contribute nothing, and the engine skips them.
They are shipped inert on purpose: each needs a trained model or a large crawled corpus that is not present on the device. Rather than implying a capability that is not there, the seam is real and the implementation is honestly empty.
Transparency#
Every block Haven makes is explainable. The block log records the host, the layer that caught it and the specific matched rule or the reasons the risk engine gave. A heuristically-learned site keeps its evidence attached so the user can inspect it and overturn it — see Self-learning and personal filters.