# Internet Use Control — system map **Date:** 2026-10-10 **Purpose:** name every piece once, clearly. No more "Go service" or "admin app" guessing. --- ## The pieces | Name | What it is | Where it runs | Language | |---|---|---|---| | **Control Service** | The brain **and** the source of truth. Holds schedules, blocklists, per-device state, usage. Serves the web dashboard. | `.13`, Docker | Go + SQLite | | **Web Dashboard** | Your setup screen in a browser. Schedules, blocklists, devices, usage. **This is the desktop admin.** | Served by the Control Service | HTMX | | **Broker** | The message bus. Pushes state to devices. Collects their reports. | `.13`, Mosquitto (already running) | — | | **Kids' phone app** | Enforces limits on a kid's phone. Shows what is blocked, and for how long. | Kids' phones | Kotlin | | **Kids' computer client** | Same job on a kid's laptop or desktop. | Kids' computers | Python | | **Admin app** | Your override and approval surface. | **Your phone** | Kotlin | | **Hub** | A **peer**. Asks questions only. Never controls. | `.13` | separate project | **There is no separate desktop admin app.** The dashboard in a browser *is* the desktop admin. If you want a native desktop app later, that is a new decision. --- ## Where the truth lives **SQLite inside the Control Service, on `.13`.** Not MQTT, not the devices, not the Hub. The broker is only a **delivery pipe**. It is fast and it remembers the last message, but it is not the database. --- ## Who writes what | Action | First goes to | Then | |---|---|---| | Change a schedule | Control Service (HTTP) | saved in SQLite → published to MQTT | | Lock or unlock now | Control Service (HTTP) | same | | You enter the device keycode | Control Service (HTTP) | same | | Device reports usage | Broker (MQTT) | Control Service writes it to SQLite | | Hub asks "screen time today?" | Control Service (HTTP) | reads SQLite, answers | **Only the Control Service publishes config to devices.** Everything else writes to the Control Service over HTTP. That is the answer to "who publishes config": **the Control Service, and only the Control Service.** One writer. One truth. **"Config"** means the desired state for **one device**: locked or not, until when, the allowed list, the blocked list. --- ## Data flow ```mermaid flowchart LR WD["Web Dashboard
(browser)"] AA["Admin app
(your phone)"] HUB["Hub
(peer)"] CS["Control Service
Go + SQLite
on .13"] MQ["Broker
Mosquitto on .13"] KP["Kids' phone app
(Kotlin)"] KC["Kids' computer client
(Python)"] WD -->|HTTP| CS AA -->|HTTP| CS HUB -->|HTTP /ask| CS CS -->|publish state| MQ MQ -->|deliver state| KP MQ -->|deliver state| KC KP -->|status + usage| MQ KC -->|status + usage| MQ MQ -->|reports| CS ``` --- ## Config carries per device Each device has its own state. That is how "Harry's phone off, desktop on" works: ``` restrict/harry_phone/config {"locked": true, "until": "21:00", "allow": ["spotify"]} restrict/harry_desktop/config {"locked": false} ``` The Control Service publishes each message, **retained**, so a device that reconnects gets its current state at once. --- ## Fail state, and where the device is The device must decide what to do when it **cannot reach the broker**. It also needs to know whether it is **at home or away**, because the two cases differ. | Situation | How the device knows | Behaviour | |---|---|---| | **At home, broker down** | On the home Wi-Fi, broker unreachable | **Keep last state** | | **Away from home** | Not on the home Wi-Fi | **Keep last state** | | **Long outage** | No contact for N hours | **Keep last state** | **One rule: keep last state.** An outage changes nothing. The child cannot escape by going offline, and a server restart does not lock them out. The device **caches its last config on itself**, so the rule always has something to follow. ### Overrides (two) 1. **On the device** — **one admin keycode, shared by every device**. For a broken phone, a laptop, or when you are away. Stored hashed. Changeable in the Control Service. 2. **Admin app** — remote lock, unlock, or grant time. ### Honest limit A child can turn off Wi-Fi and use mobile data to look "away". Accepted. The goal is **reasonable friction**, not a fortress. --- ## Kids' app interface A simple screen on the kid's device: - **What is blocked** right now. - **How long** until it lifts. - **Why** (schedule, limit reached). - An **alert** when a block starts. --- ## Decisions (2026-10-10) 1. **No mobile profile.** Away from home behaves the same as at home: keep last state. 2. **One admin keycode**, shared by every device. 3. **Keep last state indefinitely.** The admin keycode overrides it. These are settled. Do not re-open them unless something changes.