Pre-alpha — the extension is published, the desktop app builds from source. Don't make it your only safeguard yet.
How it works

Seven layers, one goal.

A request is blocked if any layer matches, so a gap in one is caught by the next. The layers are deliberately redundant — each catches what the one before it was never designed to see. Everything is deterministic, hostname-based where it can be, and runs entirely on your device. No page scoring, no guessing about you.

01

Curated domain blacklist

A hand-pruned list of 385,597 domains, sharded across three JSON files and loaded into the service worker at startup. Matching is exact-and-parent: block a domain and every subdomain goes with it.

Pruned, not padded

Cut 29.3% from 545,762 entries — every domain the keyword engine already catches was removed, leaving the domains with no machine-detectable stem. The solid wall.

Signed updates

The list refreshes between releases over an Ed25519-signed manifest, verified before use. A bad list can never brick your browsing.

Adult TLDs blocked outright

Whole top-level domains — .xxx .porn .adult .sex .sexy — never resolve at all.

02

Multilingual keyword engine

Runs on the lowercased hostname even when the blacklist doesn't match, catching sites no list has reached yet. Around 600 unambiguous stems across 41 languages, plus anime/3D, fetish and leak slang, and adult-gaming terms.

Smart, not blunt

Ambiguous roots are guarded by a trap-word whitelist — sex is excused inside "essex", anal inside "analytics". One rule governs the whole engine: a blocked legitimate site is worse than a missed porn site.

Evasion-aware

Leetspeak is normalised (p0rn → porn) and Cyrillic, Greek, Coptic and full-width look-alikes are folded to Latin, so a Cyrillic "о" in pоrn.com still matches.

Native-script detection

Vendored RFC-3492 punycode decoding covers Arabic, Chinese, Cyrillic, Japanese, Korean, Greek, Hebrew and Bengali terms — not just Latin-script ones.

03

Graylist V2 — per-item filtering

Mixed platforms like Reddit, X and Pixiv can't be whole-site blocked without removing legitimate use, and can't be left alone. So Oath Light reads each platform's own per-item NSFW label and removes flagged items before they render. Ground truth rather than heuristics — and it survives redesigns, because the underlying API fields stay stable.

35 platforms covered

24 via JSON/API interception, 10 via server-rendered DOM filtering, plus Discord's age-restricted channels and servers. Mainstream AI platforms keep working while their NSFW search dies.

Reads the real labels

over_18 · possibly_sensitive · xRestrict · sensitive · contentRating · contains_adult_content

04

SafeSearch & search enforcement

SafeSearch is forced on Google, Bing, DuckDuckGo and Yahoo by URL parameter with the toggle UI hidden, and YouTube Restricted Mode is applied by header rule. Both are permanent — neither has a switch anywhere in the product. Explicit search queries are blocked too, including a keyword filter on Reddit and Patreon search paths.

05

Bypass & evasion defense

The common workarounds are anticipated rather than discovered later. Translation and archive wrappers are unwrapped and the real target re-checked recursively; known proxies are blocked; raw public-IP navigation is stopped.

Wrappers unwrapped

translate.google, web.archive.org and proxies like croxyproxy, 12ft.io and archive.today are all caught.

Sensible allowlist

Around 110 mainstream domains are never blocked, and loopback and private IP ranges stay exempt.

06

System DNS filtering

A local filtering resolver takes over the system DNS, so the same blacklist and keyword engine apply to every application — not just browsers carrying the extension.

No DoH escape hatch

DNS-over-HTTPS is closed by policy where browsers expose it, and well-known DoH endpoints are blocked in the resolver itself.

Always on, and self-healing

It needs admin once to point the adapters at itself, then keeps retrying until it gets it. If the resolver ever stops answering, real DNS is restored immediately and the filter restarts itself.

07

On-device AI screen protection

Optional, and the last line for content no URL can describe. An ONNX ensemble — SigLIP Image-Guard plus NudeNet — runs entirely locally. No frame ever leaves the device.

Persistence, not one frame

It escalates only when a detection holds across frames, never on single-frame confidence — so a scroll past something doesn't trip it.

Two possible actions

A dwell-gated overlay, and opening your own redirect. Reporting a false positive shortens the pause — it can never make the filter catch less.

The friction & watchdog system

On instantly. Off only after a wait.

A commitment made in a strong moment has to be binding in a weak one. That asymmetry is the whole design: every protection turns on the instant you ask for it, and turning one off files a request that waits 24 hours.

This is not a claim of unbreakable security. An administrator account is the known-weakest surface and it's stated plainly in the repo's SECURITY.md rather than papered over. What the friction does is make impulsive removal cost more time than an urge lasts.

No off switch on the core

The uninstall guard, SafeSearch, YouTube Restricted Mode, the extension requirement and the system DNS filter cannot be turned off — not with the master password, not by waiting it out, not by editing the settings file. The floor is re-applied on every load.

Delayed weakening, everywhere else

For everything that is a choice — the AI monitor, lockdown, Serious Mode, a blocked app, a custom block — turning it off files a 24-hour request. Uninstall additionally requires typing a random 12-word phrase minted when the request was filed.

Clock-tamper immunity

Timers use a monotonic anchor. Rolling the system clock forward freezes the countdown and writes the event to the log instead of skipping ahead.

Lockdown mode

Allowlist-only browsing during your vulnerable hours or on demand, with a frozen variant that can only be waited out.

Dual-process watchdog

Two cooperating processes — the app and a hidden guardian — watch each other, with double autostart registration (a Run key plus a logon scheduled task), each self-healing the other. Both companions are embedded in the app binary and rewritten if they don't match on launch.

Second keyholder, if you want one

An optional Argon2-hashed master password, settable by a trusted person so you can't self-unlock. An optional trusted contact is notified on discrete events only — never browsing history, never screenshots.

Under the hood

How the pieces talk to each other.

extension/

Manifest V3 · JavaScript

A service worker, declarativeNetRequest rules and content scripts, plus a MAIN-world interceptor that strips NSFW items out of in-page JSON before render. No build step.

desktop-app/

Tauri 2 · Rust

The filtering DNS resolver, the AI screen monitor, friction, the guardian process, and the recovery layer — a Rust core with a web UI.

bridge

Native messaging

An authenticated channel between the extension and the desktop service, over localhost only. Every check and every message stays on your machine.

The full pipeline is documented in the repo's ARCHITECTURE.md, and the tamper-resistance model in HARDENING.md.