252 lines
11 KiB
Markdown
252 lines
11 KiB
Markdown
# Internet Use Control
|
|
|
|
> Build spec for Paperclip. Follow the phases in section 11 in order. Do not skip a
|
|
> phase's Definition of Done. When something is unclear, ask Sam — do not guess.
|
|
>
|
|
> **Read `SYSTEM_MAP.md` and `TRANSPORT_DESIGN.md` in this folder first.** They are
|
|
> the component picture and the MQTT design. This file is the build spec.
|
|
|
|
---
|
|
|
|
## 1. Purpose and outcome
|
|
|
|
A system that limits the kids' screen use on their own devices — Android phones and
|
|
Windows computers. It applies schedules, blocklists and allowlists on the device,
|
|
collects usage, and lets Sam override.
|
|
|
|
**Outcome (the single success statement):**
|
|
> Harry's phone and Finn's computer follow their schedule without anyone touching
|
|
> them, Sam can see their usage, and Sam can change a limit from his phone in
|
|
> seconds.
|
|
|
|
## 2. Scope
|
|
|
|
**In scope**
|
|
- Per-device weekly schedules.
|
|
- A blocklist of apps (Android package, Windows process) and sites (hostname).
|
|
- An allowlist during a block (for example, Spotify at night, school sites).
|
|
- Usage telemetry per device, app, and domain.
|
|
- Push state to devices; collect presence and telemetry.
|
|
- On-device warnings and a status view for the kid.
|
|
- An admin override (keycode on device; remote from the admin app).
|
|
- Alerts to Sam when a limit is hit.
|
|
|
|
**Out of scope**
|
|
- Anything house-wide. **No router change. No Pi-hole. No DNS server change.**
|
|
- The kids' Android **client** is this project. The **admin app** is not — it lives
|
|
in `kotlin_phone_app_admin`.
|
|
- Chores, quests, or earning screen time. Removed by decision. Do not build it.
|
|
|
|
## 2a. Prerequisites (verify before Phase 1)
|
|
|
|
Check these first. Two are missing and will stop you later if ignored.
|
|
|
|
| Need | Status / note |
|
|
|---|---|
|
|
| **Go toolchain** | Check `go version` on `.13`. Install if missing. |
|
|
| **Docker + compose** | Present on `.13`. Run as user `sam` (in the `docker` group, no sudo). |
|
|
| **MQTT broker** | The **Mosquitto container on `.13:1883`** (running). Use per-device credentials + topic ACLs. Do **not** use Home Assistant's broker on `.30`. |
|
|
| **Android toolchain** | **MISSING on `.13`** — no `java`, `gradle`, `kotlinc`, `adb`, or `sdkmanager`. Needed from **Phase 5**. Decide: install on `.13`, or build the Android client on `.51`. |
|
|
| **Windows test machine** | Needed from **Phase 4** to prove process blocking. |
|
|
| **The phone** | `sams-pixel-8a` is on the tailnet (`100.101.49.17`). ADB wireless can reach it. |
|
|
| **Apprise** | Running on `.35:8210`. |
|
|
| **The Hub** | Live on `.13:8788`. `/ask` is read-only. Contract: `central_ai_llm_master/docs/ask-contract.md`. |
|
|
| **Admin app** | Separate project: `kotlin_phone_app_admin`. It **calls this service**. Do not build it here. |
|
|
|
|
**Do not start Phase 5 until the Android toolchain question is answered.**
|
|
|
|
## 3. Users and roles
|
|
|
|
| User | Can do |
|
|
|---|---|
|
|
| **Sam** | Everything. Session login to the dashboard. Uses the admin app to override. |
|
|
| **Jo** | Read-only (optional, later). |
|
|
| **Harry / Finn** | See their own status on their device. Cannot change rules. |
|
|
|
|
## 4. Functional requirements
|
|
|
|
| ID | Requirement |
|
|
|---|---|
|
|
| FR-1 | Register devices: name each one, assign it to a child, choose its platform. |
|
|
| FR-2 | Weekly schedules: independent on/off windows per weekday, per device. |
|
|
| FR-3 | Blocklist: apps (Android package name, Windows process name) and sites (hostname). |
|
|
| FR-4 | Allowlist: things that stay available during a block (Spotify, school sites). |
|
|
| FR-5 | Publish each device's desired state to MQTT, **retained**, on its own topic. |
|
|
| FR-6 | Presence: know if a device is online, using the broker's Last Will and Testament. |
|
|
| FR-7 | Telemetry: receive usage by device, app, and domain, and store a rolling log. |
|
|
| FR-8 | Web dashboard: devices, schedules, blocklists, allowlists, and usage. |
|
|
| FR-9 | Kids' app view: what is blocked now, until when, why, and an alert when a block starts. |
|
|
| FR-10 | Override: a keycode on the device, and remote control from the admin app. |
|
|
| FR-11 | Admin app API: lock, unlock, grant time, and query state. **Another project consumes this** (`kotlin_phone_app_admin`) — keep the shape small and stable, and version it. |
|
|
| FR-12 | Hub API: `GET /capabilities`, `POST /ask` (read-only), `POST /notify`. |
|
|
| FR-13 | Alerts via Apprise when a limit is hit or a device goes offline too long. |
|
|
| FR-14 | Fail safe: when a device cannot reach the broker, it keeps its last state. |
|
|
| FR-15 | Audit log: every state change, who made it, and when. |
|
|
|
|
## 5. Screens and UX flow
|
|
|
|
**Screens (web dashboard)**
|
|
1. **Login** — session form.
|
|
2. **Dashboard** — every device as a card: name, child, platform, status dot, current state, time left.
|
|
3. **Schedule editor** — a weekly grid per device.
|
|
4. **Blocklist / allowlist editor** — one list, tagged by platform.
|
|
5. **Usage** — per device: top apps, top domains, by day and week.
|
|
6. **Alerts** — recent limit hits and warnings.
|
|
|
|
**Kids' device view (in the kids' client app)**
|
|
- What is blocked now.
|
|
- How long until it lifts.
|
|
- Why (schedule, limit reached).
|
|
- An alert when a block starts.
|
|
|
|
**Main flow**
|
|
```
|
|
Sam opens dashboard → sees Harry's phone "locked, 42 min left"
|
|
→ grants 30 min → the phone unlocks within one second
|
|
```
|
|
|
|
## 6. Look and feel
|
|
|
|
Reuse the Family Home Lab design system.
|
|
|
|
- Source: `/home/sam/Docker/Containers/family-home-lab/DESIGN.md`
|
|
- Tokens: `/home/sam/Docker/Containers/family-home-lab/portal/static/tokens.css`
|
|
- Copy `tokens.css` into this project.
|
|
|
|
Canvas `#f6f5f4`, one action blue `#0075de`, Inter, 8px grid, pill CTAs, hairline
|
|
borders. Include dark mode. Works on a phone browser. Touch targets ≥44px.
|
|
|
|
## 7. Data model
|
|
|
|
SQLite inside the Control Service. Suggested tables:
|
|
|
|
| Table | Holds |
|
|
|---|---|
|
|
| `devices` | id, name, child, platform, topic, online, last-seen |
|
|
| `schedules` | device id, weekday, start, end |
|
|
| `blocklist` | device id (or group), kind (`app`, `site`), value |
|
|
| `allowlist` | device id (or group), kind, value |
|
|
| `state` | device id, locked, until, allow (JSON), updated-at |
|
|
| `telemetry` | device id, app, domain, seconds, day |
|
|
| `overrides` | device id, actor, action, until, created-at |
|
|
| `audit` | actor, action, device id, detail, created-at |
|
|
|
|
Retention: telemetry rolls up daily and is kept long. The audit log is append-only
|
|
and size-capped.
|
|
|
|
## 8. Integrations
|
|
|
|
| System | Use |
|
|
|---|---|
|
|
| **MQTT broker** | The **existing Mosquitto on `.13`**. Per-device credentials and topic ACLs. |
|
|
| **Apprise** | Alerts to Sam. Running on `.35:8210`. |
|
|
| **Home Assistant** | Optional: publish device state with MQTT discovery, for dashboards. |
|
|
| **Hub** | `POST /ask` (read-only), `POST /notify`, `GET /capabilities`. |
|
|
| **Admin app** | Separate project. Calls this service's API directly. |
|
|
|
|
The Hub interface is frozen in `central_ai_llm_master/docs/ask-contract.md`.
|
|
|
|
**Control never goes through the Hub.** The admin app calls this service directly.
|
|
|
|
## 9. Technology (locked)
|
|
|
|
| Item | Value |
|
|
|---|---|
|
|
| Backend | **Go** |
|
|
| UI | **HTMX** + server-rendered HTML |
|
|
| Database | **SQLite** |
|
|
| Transport | **MQTT** on the existing Mosquitto container at `.13:1883` (per-device creds + ACLs) + HTTP for setup and queries |
|
|
| Design | Family Home Lab `tokens.css` |
|
|
| Host | `.13`, Docker |
|
|
| Container | **This project's own** image and compose file. |
|
|
| Port | Allocate one (suggest **8791**). Record it once chosen. |
|
|
| URL | `webrestrict.lab.audasmedia.com.au` via Caddy on `.35` |
|
|
| Auth | **Separate session login.** |
|
|
| Kids' phone client | **Kotlin**, Android Device Owner |
|
|
| Kids' computer client | **Python** (`psutil`, `pystray`) |
|
|
|
|
## 10. Rules and constraints
|
|
|
|
1. **Enforcement is device-side and kids-only.** No router, no Pi-hole, no
|
|
house-wide change.
|
|
2. **Fail safe is "keep last state".** The device caches its last config.
|
|
3. **One admin keycode**, shared by every device.
|
|
4. **No mobile profile.** Away from home behaves the same as at home.
|
|
5. Only the **Control Service** publishes config. Everything else writes to it over
|
|
HTTP.
|
|
6. MQTT uses TLS and per-device credentials with topic ACLs. A device may read only
|
|
its own config and write only its own status.
|
|
7. The kids' app never changes its own rules. It only obeys and reports.
|
|
8. Models go through **OmniRoute**. Secrets come from `environment.d`.
|
|
9. `/ask` is read-only.
|
|
10. **Never hard-delete** a device or its history.
|
|
|
|
## 11. Build order
|
|
|
|
**Phase 1 — Skeleton**
|
|
Go service, SQLite, config, session login, Dockerfile, compose, Caddy route.
|
|
*DoD:* the dashboard runs on `.13`, login works, `/health` returns ok.
|
|
|
|
**Phase 2 — Devices and state**
|
|
Device registry, dashboard cards, manual lock/unlock in the UI, state in SQLite.
|
|
*DoD:* a device can be added and its state changed from the dashboard.
|
|
|
|
**Phase 3 — MQTT**
|
|
Publish retained state per device. Subscribe to status and telemetry. Presence via LWT.
|
|
*DoD:* a test subscriber receives the state, and a dropped subscriber shows offline.
|
|
|
|
**Phase 4 — Computer client**
|
|
Python client: subscribe to config, apply process blocking, report telemetry, overlay
|
|
warning, keep last state.
|
|
*DoD:* a real Windows machine blocks a listed process during a scheduled window.
|
|
|
|
**Phase 5 — Phone client**
|
|
Kotlin Device Owner app: suspend listed packages, overlay, status view, telemetry.
|
|
*DoD:* a real Android phone suspends a listed app during a window.
|
|
|
|
**Phase 6 — Schedules, blocklists, allowlists**
|
|
Editors in the dashboard. Allowlist honoured during a block.
|
|
*DoD:* Spotify stays available while other apps are blocked.
|
|
|
|
**Phase 7 — Telemetry and usage**
|
|
Store and display usage by device, app, and domain.
|
|
*DoD:* the dashboard shows a week of real usage for one device.
|
|
|
|
**Phase 8 — Overrides**
|
|
Device keycode and the admin app API (lock, unlock, grant).
|
|
*DoD:* the admin app locks a device and it locks in under one second.
|
|
|
|
**Phase 9 — Hub and alerts**
|
|
`/capabilities`, `/ask`, `/notify`, and Apprise alerts.
|
|
*DoD:* the Hub asks "how long have the kids been on screens today?" and gets a correct answer.
|
|
|
|
**Phase 10 — Documentation**
|
|
*DoD:* project-docs run; Gitea, Obsidian, Outline, Vikunja, and the map are updated.
|
|
|
|
## 12. Acceptance criteria
|
|
|
|
- A real device follows its weekly schedule without intervention.
|
|
- A listed app is blocked on the real phone and computer.
|
|
- Spotify stays available during a block, when allowlisted.
|
|
- The admin app locks a device in under one second.
|
|
- Usage is visible per device for at least a week.
|
|
- The Hub answers a screen-time question correctly.
|
|
- When the broker is unreachable, the device keeps its last state.
|
|
- Everything runs in its own container on its own port, behind Caddy.
|
|
|
|
## 13. Decisions and remaining questions
|
|
|
|
Resolved:
|
|
1. **Device-side only**, kids only. No router, no Pi-hole.
|
|
2. **MQTT** with retained messages and LWT presence. `TRANSPORT_DESIGN.md`.
|
|
3. **Keep last state.** One admin keycode. No mobile profile.
|
|
4. **Go** for the service. **Kotlin** for the kids' phone client. **Python** for the computer client.
|
|
5. **Admin app** is a separate project (`kotlin_phone_app_admin`); it calls this service.
|
|
6. **Chores to unlock time** removed. Not in scope.
|
|
|
|
Remaining:
|
|
1. Pass-through apps during a lock: confirm the list (Spotify, school sites).
|
|
2. Remote shutdown or sleep for a kid's computer, or blocking only?
|
|
3. Windows client: a service, or a scheduled task?
|
|
4. Telemetry retention: how long to keep daily rollups?
|