145 lines
4.8 KiB
Markdown
145 lines
4.8 KiB
Markdown
# 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<br/>(browser)"]
|
|
AA["Admin app<br/>(your phone)"]
|
|
HUB["Hub<br/>(peer)"]
|
|
CS["Control Service<br/>Go + SQLite<br/>on .13"]
|
|
MQ["Broker<br/>Mosquitto on .13"]
|
|
KP["Kids' phone app<br/>(Kotlin)"]
|
|
KC["Kids' computer client<br/>(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.
|