- Go service with its own multi-stage Dockerfile and docker-compose.yml; runs as container webrestrict-control on port 8791. - SQLite schema for the full AGENTS.md §7 data model via versioned migrations (PRAGMA user_version); audit log is append-only. - Configuration from the process environment; the real env file is gitignored and a template is committed. No secrets in the repo. - Separate session login (signed HMAC cookie + CSRF) for Sam. - GET /health; HTMX + server-rendered dashboard styled with the Family Home Lab tokens.css (copied) and Inter fonts; dark mode; phone-friendly. - Caddy vhost for webrestrict.lab.audasmedia.com.au applied on .35 (Let's Encrypt cert issued); route recorded in deploy/caddy/webrestrict.caddy. - Unit tests for config, auth and store. Co-Authored-By: Paperclip <noreply@paperclip.ing>
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 1 — Skeleton (AGENTS.md §11):
- 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.csscopied in).
Enforcement, MQTT, devices, schedules 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.1.0-phase1",...}
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.1.0-phase1 \
hash-password 'your-password'
Endpoints (Phase 1)
| 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. Redirects to /login when not signed in. |
GET |
/partials/status |
HTMX fragment, polled every 10 s. |
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
internal/store/migrations/001_init.sql
internal/auth/ signed-cookie session login + CSRF
internal/web/ HTTP handlers, templates, static assets
templates/ login, dashboard, 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 1)
- No MQTT publishing (Phase 3).
- No device registry or lock/unlock UI (Phase 2).
- No admin API (Phase 8).
- No Hub contact (Phase 9).