Files
internet_use_control/README.md
Sam Rolfe 7c88d3f52b Phase 2: device registry, dashboard cards, manual lock/unlock, audit
- Device registry: add/edit name, child and platform, persisted in devices.
- Stable per-device topic derived from name + id, unchanged by renames.
- Desired state persisted in state; timed locks expire and are audited.
- Dashboard cards: name, child, platform, status dot, state and time left.
- Manual lock/unlock forms with CSRF; every change appends to audit (FR-15).
- Append-only activity list on the dashboard. No hard deletes (rule 10).
- Store and handler tests; version bumped to 0.2.0-phase2.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-13 14:18:02 +11:00

5.5 KiB

Internet Use Control — Control Service

The Control Service is the brain and the single source of truth for the 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.

This repository currently contains Phase 2 — Devices and state (AGENTS.md §11), on top of the Phase 1 skeleton:

  • Go service with its own image (Dockerfile) and compose file.
  • SQLite schema for the whole data model (§7), applied through versioned migrations.
  • Configuration from the process environment (environment.d); no secrets in the repo.
  • A separate session login for Sam.
  • GET /health.
  • HTMX + server-rendered dashboard styled with the Family Home Lab design system (tokens.css copied in).
  • Device registry: add and edit a device, assign it to a child, choose its platform. Persisted in devices.
  • Dashboard cards: name, child, platform, status, current state, time left.
  • Manual lock/unlock from the dashboard, persisted in state and written to the append-only audit log (FR-15).

Enforcement, MQTT, schedules, blocklists and the clients arrive in later phases. Nothing here touches the router, Pi-hole, or DNS.

Quick start (on .13)

cd /home/sam/paseo/projects/internet_use_control

# 1. Create the env file from the template and set real values.
cp environment.d/control-service.env.example environment.d/control-service.env
$EDITOR environment.d/control-service.env

# 2. Build and start. Own image, own container, port 8791.
docker compose up -d --build

# 3. Prove it.
curl -s http://127.0.0.1:8791/health
# {"status":"ok","service":"control-service","version":"0.2.0-phase2",...}

Open http://127.0.0.1:8791/ in a browser. It redirects to /login. Sign in with CONTROL_ADMIN_USERNAME and CONTROL_ADMIN_PASSWORD.

Configuration

All configuration is environment variables. Docker Compose loads them from environment.d/control-service.env (gitignored). The variables can also be exported from ~/.config/environment.d/10-secrets.conf instead; the service reads only the process environment.

Variable Default Purpose
CONTROL_ADDR :8791 Listen address
CONTROL_DB_PATH /data/control.db SQLite file
CONTROL_SESSION_SECRET — (required) Signs the session cookie, ≥ 16 chars
CONTROL_SESSION_TTL 12h Session lifetime
CONTROL_COOKIE_SECURE true Secure cookie flag; keep true behind HTTPS
CONTROL_ADMIN_USERNAME sam Dashboard login
CONTROL_ADMIN_PASSWORD — Plaintext password (option A)
CONTROL_ADMIN_PASSWORD_HASH — bcrypt hash (option B, preferred)
CONTROL_APP_ENV production Label shown in the footer
CONTROL_LOG_LEVEL info debug/info/warn/error

Generate a bcrypt hash without host Go:

docker run --rm --entrypoint /app/control webrestrict/control:0.2.0-phase2 \
  hash-password 'your-password'

Endpoints (Phase 2)

Method Path Notes
GET /health JSON. status: ok, database check. No auth.
GET /login Session login form.
POST /login Verifies credentials, sets a signed session cookie.
POST /logout Clears the session.
GET / Dashboard: service status, device cards, recent activity.
GET /partials/status HTMX fragment, polled every 10 s.
GET /devices/new Add-device form.
POST /devices Creates a device.
GET /devices/{id}/edit Edit-device form.
POST /devices/{id} Updates name, child, platform.
POST /devices/{id}/lock Locks now; optional minutes (0 = until unlocked).
POST /devices/{id}/unlock Unlocks now.

Every form POST needs the csrf field from the session cookie. State changes append to the audit table. Devices are never hard-deleted (§10 rule 10).

Development

There is no host Go toolchain on .13; use the cached builder image.

make test          # go test ./... in golang:1.25-alpine
make vet           # go vet ./...
make build         # build the container image
make up            # docker compose up -d --build
make logs          # follow container logs

Layout

cmd/control/            service entry point (+ hash-password helper)
internal/config/        environment configuration
internal/store/         SQLite open, migrations, audit, devices + state
internal/store/migrations/001_init.sql
internal/auth/          signed-cookie session login + CSRF
internal/web/           HTTP handlers, templates, static assets
  devices.go            device registry + manual lock/unlock handlers
  templates/            login, dashboard, device form, HTMX partials
  static/               tokens.css (Family Home Lab), app.css, htmx, Inter fonts
Dockerfile              own image (multi-stage, CGO-free)
docker-compose.yml      own container, port 8791
environment.d/          control-service.env.example (real file gitignored)
deploy/caddy/           Caddy vhost for webrestrict.lab.audasmedia.com.au

Caddy

deploy/caddy/webrestrict.caddy holds the vhost for webrestrict.lab.audasmedia.com.au. The terminating Caddy runs on .35 (sam-Ubuntu1); see that file for the two proxy patterns and the apply commands.

Non-goals (Phase 2)

  • No MQTT publishing (Phase 3).
  • No schedules, blocklists or allowlists (Phase 6).
  • No telemetry or usage screens (Phase 7).
  • No admin API (Phase 8).
  • No Hub contact (Phase 9).