5 Commits
Author SHA1 Message Date
timka123 d280101bce disallow to disable guard during focus
docker / image (push) Failing after 24s
lint / ruff (push) Failing after 28s
tests / backend (push) Successful in 37s
tests / desktop (push) Successful in 22s
tests / android-core (push) Successful in 49s
tests / web (push) Successful in 1m20s
android / apk (push) Successful in 8m49s
2026-10-28 09:50:58 +03:00
timka123 157a113c39 fix timers
lint / ruff (push) Failing after 15s
tests / desktop (push) Successful in 21s
tests / web (push) Successful in 46s
docker / image (push) Failing after 1m31s
tests / android-core (push) Successful in 1m5s
tests / backend (push) Successful in 2m33s
android / apk (push) Successful in 4m34s
2026-10-27 18:52:54 +03:00
timka123andClaude Opus 5 5facf41f10 blocker: an exception reads like a block, so make it behave like one
`youtube.com` on the block list catches `music.youtube.com` — a plain entry
covers its subtree. `youtube.com` on the *allow* list caught only itself,
because exceptions were glob-matched whatever they looked like and a plain name
has no metacharacters. The same text in two boxes of the same form meant a
subtree in one and a single hostname in the other, with nothing in the UI
saying so.

A plain exception now covers its subdomains; a masked one is glob-matched
exactly as before, which is still how you spare something narrower than a whole
subtree (`docs.*`, `*.docs.google.com`).

Three enforcers decide this and all three had to move together, or an exception
would work in the browser and not at the resolver: `blocker_service.is_allowed`,
`:core`'s `isAllowed`, and the desktop DNS stub's own allow check, which was
reusing the generic glob matcher. Parity tests on both sides, plus the two
sentences in the web UI and the README that promised the old behaviour.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-22 17:24:17 +03:00
timka123 50e9c68b2f android: the focus guard on the phone itself
The Guard screen shows the shared policy; two Android facilities enforce it.
An accessibility service reads the browser's URL bar (no /etc/hosts exists,
and the VPN slot belongs to the user's always-on VPN), sends blocked pages
home and records the attempt; UsageStatsManager spots blocked apps in front
and the service presses home — 'kill' cannot mean force-stop, and the KDoc
says so.

Masks mean what they mean on every client: a bare '*' in the expansion list
makes '*.site' cover every subdomain, and masks no hosts file can express
are matched as ordinary globs against every URL, so they work here even
where the desktop cannot route them. Offline, the kept blocker settings are
re-evaluated with :core's evaluatePolicy, so losing signal does not lose the
guard. Two limits are documented rather than papered over: in-app WebViews
are invisible to the URL-bar reader, and blocking degrades to reporting
without the service.

Core keeps the policy/matcher semantics testable on a plain JVM: 190 core
and 44 app tests. The screen's copy says junk labels are ignored, a '*' is
kept, and unroutable masks are enforced on this phone only.
2026-10-19 18:45:50 +03:00
timka123 1e55144291 some backend fixes and add android app 2026-10-19 14:52:20 +03:00