Files
internet_use_control/AGENTS.md

9.8 KiB

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.

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.
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 (existing Mosquitto) + 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?