Phase 4: kids' Windows computer client (AUD-97)

Adds windows/, the Python client for the kids' Windows computers, and proves
both halves of the Phase 4 definition of done on the real box
(DESKTOP-TE2RHV2).

The client:
- subscribes, retained, to its own config on the 8883 TLS device plane with its
  own broker account (FR-3, FR-6);
- keeps the last valid config on disk and loads it before connecting, so a
  broker outage changes nothing (FR-14);
- terminates every process on the blocklist while the device is effectively
  locked, with the allowlist winning and the client's own tree plus critical
  system processes protected (FR-4, FR-16);
- publishes presence and daily per-app usage; the day's totals are held on disk
  and republished when the link returns (FR-7);
- shows an always-on-top warning window naming what is blocked, why and until
  when (FR-9).

install-scheduled-task.ps1 registers the logon Scheduled Task for the kid's
account with an interactive token, and grants that account Modify on the state
directory: a file created from an elevated shell inherits Administrators only,
and the limited task is otherwise refused with Access denied.

Evidence (windows/EVIDENCE.md, from the machine's own log): notepad.exe killed
inside a scheduled window (pid 16600, then 9724), the device online in the
Control Service with locked/until/reason, a telemetry row for the device
(app=690s), enforcement continuing with the broker unreachable
(192.168.20.13:8884), the warning window on the interactive desktop, and the
Scheduled Task registered at logon as New Comp.

Domains are deliberately not reported: Windows has no cheap per-process DNS
attribution, so the client sends "domains": [] and the gap is documented rather
than guessed.

54 unit tests, run with `make windows-test` (no broker, no Windows, no psutil).

AGENTS.md §11 is now 10 of 10.
This commit is contained in:
2026-10-14 15:00:22 +11:00
parent eb300b6701
commit 1275b1baaa
22 changed files with 2799 additions and 13 deletions

View File

@@ -5,13 +5,14 @@ Internet Use Control project (`SYSTEM_MAP.md`). It holds schedules, blocklists,
per-device state and usage, and serves the web dashboard. Only it publishes
device config.
**Project status: 9 of 10 phases done.** Phases 1–3 and 5–10 are complete (Phase 10,
this documentation set, included). **Phase 4 — the Windows computer client — is
deliberately deferred and is NOT built.** There is no Python computer client and
no Windows process blocking in this repository. The only enforcement client is
the kids' Android app in `android/`. See `AGENTS.md` §11.
**Project status: 10 of 10 phases done.** Phases 1–10 are complete, including
**Phase 4 — the kids' Windows computer client** in `windows/` (Python, MQTT
config, process blocking, telemetry, warning overlay, keep-last-state, installed
as a logon Scheduled Task). Evidence: `windows/EVIDENCE.md`. The enforcement
clients are the kids' Android app in `android/` and the Windows client in
`windows/`. See `AGENTS.md` §11.
This repository contains **Phases 1–3 and 5–10** (AGENTS.md §11):
This repository contains **Phases 1–10** (AGENTS.md §11):
- Go service with its own image (`Dockerfile`) and compose file.
- SQLite schema for the whole data model (§7), applied through versioned
@@ -539,6 +540,32 @@ cache in `.gocache/` (gitignored), so nothing in the repository ends up owned by
root. `make mqtt-verify` never touches the shared Mosquitto: it stages a scratch
broker on `127.0.0.1:18883` and removes it afterwards.
## Kids' Windows computer client (Phase 4)
`windows/` is the kids' computer client: Python, one process, installed as a
Scheduled Task at logon. It subscribes, retained, to its own config on the 8883
device plane, and while the device is locked it terminates any process on the
blocklist that is not on the allowlist (FR-3, FR-4).
- **Keep last state (FR-14).** The last valid config is cached on disk and loaded
before the first connect, so a broker outage changes nothing.
- **Presence (FR-6).** A retained `{"online": true}` status plus the broker's
Last Will `{"online": false}`; a force-stop or a power-off shows up as an
offline device.
- **Usage (FR-7).** Daily per-app seconds, sampled from the apps that own a
visible window, published as day totals on the device's telemetry topic.
- **Warning (FR-9).** An always-on-top Tkinter window naming what is blocked, why
and until when.
- **Domains are not reported.** Windows has no cheap per-process DNS
attribution, so the client sends `"domains": []` rather than a guess; there is
no Windows site filter in this phase.
`windows/EVIDENCE.md` holds the transcript of the Definition of Done on the real
machine: a listed process killed inside a scheduled window, the device online in
this service, a telemetry row for it, enforcement continuing with the broker
unreachable, and the Scheduled Task registered at logon. Run the client's tests
with `make windows-test`.
## Kids' Android client (Phase 5.1)
`android/` is the kids' phone app: a normal Gradle project with its own
@@ -575,6 +602,10 @@ emulator on `.27`; see `android/README.md` and `android/tools/emu-proof.sh`.
```
cmd/control/ service entry point (+ hash-password, mqtt-clients, rollup, keycode, alerts, usage)
windows/ kids' Windows computer client (Python; process block, overlay, keep-last-state)
webrestrict/ MQTT link, block rule, enforcer, telemetry, warning window, agent loop
install-scheduled-task.ps1 logon Scheduled Task (+ the state directory ACL)
EVIDENCE.md the Phase 4 proof transcript from the real machine
android/ kids' Android client (Gradle, Kotlin; Accessibility + overlay)
app/src/main/java/… Accessibility Service, overlay, block rule, MQTT, tamper report
tools/emu-proof.sh headless emulator proof (block overlay + tamper)
@@ -625,14 +656,15 @@ docs/vikunja-tasks.json copy of the Vikunja board tasks
## Non-goals (Phase 10)
- **Phase 4, the Windows computer client, is not built.** It is deliberate. The
Python client, process blocking, and the Windows overlay warning do not exist.
The project stands at **9 of 10** because of this single gap.
- **The Windows client does not filter sites.** Blocking is by process only. There
is no per-process DNS attribution on Windows without a second dependency or an
administrator-only channel, so the client publishes `"domains": []`; FR-18 site
filtering is satisfied on the phone only (`android/`). See `windows/README.md`.
- No grant button in the dashboard. Grant is on the admin API; the dashboard has
lock and unlock. Adding the button is a later UI change.
- No per-child or per-device keycode. One code, every device (§10 rule 3).
- No HTTP telemetry ingest. Telemetry arrives over MQTT only. The kids' phone
client exists (Phase 5). The kids' computer client does not (Phase 4, deferred).
client exists (Phase 5) and the kids' Windows client exists (Phase 4).
- No Alerts screen in the dashboard yet. The alert history is in `alerts` and is
readable with `control alerts`; the screen is a later UI change.
- The Hub is wired to this service: the running Hub on `.13:8788` routes