From 0e4e2c1ca7e619ab68ad47138f59e2feb556a300 Mon Sep 17 00:00:00 2001 From: adlee-was-taken Date: Sun, 5 Apr 2026 09:29:57 -0400 Subject: [PATCH] docs: add implementation plan for project documentation 18 tasks covering README, home user guide, operator guide, architecture overview + conventions, and 12 per-subsystem reference docs. Each task is grounded in reading real source to avoid invented facts. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../plans/2026-04-05-project-documentation.md | 1720 +++++++++++++++++ 1 file changed, 1720 insertions(+) create mode 100644 docs/superpowers/plans/2026-04-05-project-documentation.md diff --git a/docs/superpowers/plans/2026-04-05-project-documentation.md b/docs/superpowers/plans/2026-04-05-project-documentation.md new file mode 100644 index 0000000..ce29d48 --- /dev/null +++ b/docs/superpowers/plans/2026-04-05-project-documentation.md @@ -0,0 +1,1720 @@ +# Project Documentation Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Create a polished top-level `README.md`, a linear home user guide, a reference operator guide, and a split architectural reference (overview + conventions + 12 per-subsystem files) for Vigilar. + +**Architecture:** Approach 3 (Hybrid) from the spec — monolithic linear guides for users, split reference docs for contributors. No tooling added, no code changes, no image assets. Plain Markdown organized as if MkDocs-ready. + +**Tech Stack:** Markdown only. No build step. All claims must be grounded in the actual code under `/home/alee/Sources/vigilar/` at write-time. + +**Spec:** `docs/superpowers/specs/2026-04-05-project-documentation-design.md` — read it before starting. + +--- + +## Universal rules for every task in this plan + +1. **Do not guess.** Every file path, command, TOML key, MQTT topic, DB table, CLI subcommand, blueprint route, and function/file reference must be verified against the actual repo before writing. If you cannot verify something, omit it — or, for subsystem docs, write `"no publishers found at time of writing"` (or equivalent). +2. **Do not create code, scripts, unit files, or images.** This effort is docs-only. The only exception is creating the `docs/architecture/` and `docs/architecture/subsystems/` directories implicitly by writing files into them. +3. **Do not modify `docs/camera-hardware-guide.md` or anything under `docs/superpowers/`** other than this plan file. +4. **Commit after each task.** One commit per task unless a task says otherwise. +5. **Tone:** plain, direct, no marketing fluff, no emoji, no exclamation marks. Match the tone of `CLAUDE.md` and `scripts/backup.sh` comments. +6. **Line wrap:** keep prose at ~100 columns for readability in terminals and diffs. +7. **Relative links:** docs inside `docs/` link to each other with relative paths (e.g. `architecture/overview.md`). The top-level README links use `docs/...` paths. + +--- + +## File Structure + +Files to be created in this plan (17 total): + +``` +README.md # new +docs/home-user-guide.md # new +docs/operator-guide.md # new +docs/architecture/overview.md # new +docs/architecture/conventions.md # new +docs/architecture/subsystems/camera.md # new +docs/architecture/subsystems/detection.md # new +docs/architecture/subsystems/events.md # new +docs/architecture/subsystems/alerts.md # new +docs/architecture/subsystems/sensors.md # new +docs/architecture/subsystems/ups.md # new +docs/architecture/subsystems/storage.md # new +docs/architecture/subsystems/highlights.md # new +docs/architecture/subsystems/presence.md # new +docs/architecture/subsystems/pets.md # new +docs/architecture/subsystems/health.md # new +docs/architecture/subsystems/web.md # new +``` + +Tasks are ordered so that later tasks can reference and link to earlier ones. + +--- + +## Task 1: Architecture overview + +**Files:** +- Create: `docs/architecture/overview.md` + +- [ ] **Step 1: Gather grounding facts by reading source** + +Read the following files and take notes. Do not write prose yet. + +```bash +ls vigilar/ +ls vigilar/camera vigilar/events vigilar/alerts vigilar/storage vigilar/web/blueprints +``` + +Read these files in full: +- `CLAUDE.md` +- `config/vigilar.toml` +- `vigilar/camera/worker.py` +- `vigilar/camera/motion.py` +- `vigilar/camera/recorder.py` +- `vigilar/camera/ring_buffer.py` +- `vigilar/events/processor.py` +- `vigilar/events/rules.py` +- `vigilar/storage/db.py` +- `vigilar/storage/schema.py` +- `vigilar/storage/encryption.py` +- `vigilar/web/__init__.py` +- `systemd/vigilar.service` +- `systemd/vigilar-mosquitto.conf` + +From these, write down on scratchpad: +- The exact MQTT topic format used (look for `mqtt.publish` / `client.publish` calls and topic string literals). +- The actual table names in `vigilar/storage/schema.py`. +- The file+function name where RTSP frames are captured. +- The file+function name where motion is detected. +- The file+function name where a recording is started. +- The file+function name where an event row is written. +- The file+function name where a push notification is sent. +- Whether the process model is actually `multiprocessing.Process` (check `vigilar/cli/cmd_start.py` or wherever subsystems are spawned). + +If any of these cannot be found, note them as "unverified" and they will be omitted from the doc. + +- [ ] **Step 2: Create `docs/architecture/overview.md`** + +Write the file with these sections, in this order. Each section's content must reflect what you actually found in Step 1, not generic text. + +```markdown +# Vigilar Architecture Overview + +This document explains how Vigilar is put together for someone reading the +codebase for the first time. It is short on purpose — per-subsystem details +live under `subsystems/`. + +## Design principles + +- **Offline-first.** No external calls in the critical path. Cloud integrations, + if any, are opt-in and off the hot path. +- **Subsystem isolation.** Each subsystem runs in its own process. A crash in + one subsystem cannot take down another. +- **Loose coupling via MQTT.** Subsystems do not call each other directly. + They publish and subscribe to a local Mosquitto broker on + `127.0.0.1:1883`. +- **SQLite (WAL) is the single durable store.** Access goes through + SQLAlchemy Core expressions, not ORM mapped classes. +- **Adaptive cost.** Cameras idle at 2 FPS and jump to 30 FPS on motion, with + a 5-second ring buffer so the moment leading up to the trigger is kept. +- **Configuration is typed.** `config/vigilar.toml` is loaded and validated + by Pydantic v2. Secrets are never inline — they are file paths under + `/etc/vigilar/secrets/`. + +## Process topology + + + +``` + ┌────────────────┐ + │ vigilar start │ (supervisor) + └───────┬────────┘ + │ spawns + ┌────────┬────────┬──────┼──────┬────────┬────────┐ + ▼ ▼ ▼ ▼ ▼ ▼ ▼ + camera sensors events ups alerts health web + ↑ ↑ ↑ ↑ ↑ ↑ ↑ + └─────────┴────────┴──────┴──────┴───────┴────────┘ + MQTT (127.0.0.1:1883) +``` + +## The MQTT bus + +- Broker: Mosquitto, bound to loopback only. Config: + `systemd/vigilar-mosquitto.conf`. +- Topic convention: `` — quote one or two + real topics from the codebase. +- Why MQTT rather than an in-process queue: crash isolation, introspection + with `mosquitto_sub`, and the option to move subsystems to separate hosts + later without changing the wire format. + +## Data flow: from motion to phone notification + +Numbered sequence. Each step names the real file and function where the +work happens. + +1. `vigilar/camera/worker.py:` — RTSP capture at 2 FPS. +2. `vigilar/camera/motion.py:` — OpenCV MOG2 detects movement and + publishes ``. +3. `vigilar/camera/recorder.py:` — flips to 30 FPS, flushes the ring + buffer, begins writing a `.vge` file. +4. `vigilar/events/processor.py:` — inserts a row into `` and + publishes ``. +5. `vigilar/highlights/...` — scores the event. +6. `vigilar/alerts/...` — sends a VAPID web-push notification. +7. Web UI — updates the timeline (mechanism: ). + +## Storage layout + +- `vigilar.db` (SQLite, WAL mode). Tables: . +- Recordings: `.vge` files, AES-256-GCM. Key at + `/etc/vigilar/secrets/storage.key`. Losing the key means losing the + recordings. +- HLS: rolling segments under `[system] hls_dir` (default + `/var/vigilar/hls`). +- Backups: DB + `/etc/vigilar` tarball at `[system]`-adjacent path (see + `scripts/backup.sh`). + +## Configuration and secrets + +- `config/vigilar.toml` is the only configuration file the app reads. +- It is validated by Pydantic v2 at startup — invalid config stops the + process before any subsystem spawns. +- Secrets never live in the TOML. They live as files under + `/etc/vigilar/secrets/` and the TOML only references paths. +- The arm PIN and admin password are hashed; comparisons are + constant-time. + +## The web tier + +- Flask with Blueprints, one per feature area + (`vigilar/web/blueprints/*.py`). +- Jinja2 templates, Bootstrap 5 dark theme. +- Live view: `hls.js` grid for bandwidth efficiency, MJPEG single view for + low latency. +- PWA with VAPID push — no Firebase, no Google Cloud Messaging. + +## What is NOT in the critical path + +- Remote access (`[remote]` section) — optional, bandwidth-shaped. +- Email alerts (`[alerts.email]`) — optional. +- Any cloud service — never. + +## Where to go next + +- Conventions: `conventions.md` +- Per-subsystem details: `subsystems/` +``` + +Replace every `` with the real value from Step 1. If a fact +cannot be verified, delete the bullet or sentence entirely rather than +guessing. + +- [ ] **Step 3: Verify all claims against source** + +For every file path referenced in the overview, run: + +```bash +ls +``` + +For every topic string quoted, run: + +``` +(use Grep tool, NOT bash grep) +Grep for the topic string across vigilar/ — must return at least one hit. +``` + +For every table name quoted, confirm it appears in `vigilar/storage/schema.py`. + +Fix any that fail. + +- [ ] **Step 4: Commit** + +```bash +git add docs/architecture/overview.md +git commit -m "docs: add architecture overview" +``` + +--- + +## Task 2: Architecture conventions + +**Files:** +- Create: `docs/architecture/conventions.md` + +- [ ] **Step 1: Re-read `CLAUDE.md` and `vigilar/constants.py`** + +```bash +ls vigilar/constants.py # confirm it exists +``` + +Read `vigilar/constants.py` in full and note the `StrEnum` style actually +used. + +- [ ] **Step 2: Create `docs/architecture/conventions.md`** + +Target length ~400 words. Write the file: + +```markdown +# Coding Conventions + +These are the rules a contributor needs in their head before touching +Vigilar. They are distilled from `CLAUDE.md` and from the patterns already +in the codebase. + +## Language and style + +- Python 3.11+. +- Ruff for linting. Line length 100. +- Type hints on all public functions. Internal helpers may omit them when + obvious. +- No docstrings unless the logic is non-obvious. Let short, well-named + functions speak for themselves. + +## String constants + +All string constants that are referenced in more than one place live in +`vigilar/constants.py` as `StrEnum` members. This includes MQTT topics, +event kinds, alert channels, and anything else that would otherwise be a +magic string. If you are about to write `"motion_detected"` in two places, +add it to the enum instead. + +## Database access + +- SQLite in WAL mode. +- SQLAlchemy Core expressions only — no mapped classes, no ORM session. +- Schema lives in `vigilar/storage/schema.py`. Queries live in + `vigilar/storage/queries.py`. +- New tables go in `schema.py`. New query helpers go in `queries.py`. Do + not scatter ad-hoc SQL across subsystem code. + +## Processes and the MQTT bus + +- Each subsystem runs in its own `multiprocessing.Process`. +- Subsystems communicate only through the local MQTT broker. They do not + share memory or call each other's functions across process boundaries. +- Topic naming: //`>. +- If you are tempted to reach into another subsystem directly, publish a + topic instead. + +## Configuration + +- `config/vigilar.toml` is the only config file the app reads at runtime. +- It is validated by Pydantic v2. Add new fields to the Pydantic models, + not just to the TOML. +- Secrets are file paths. Never put a key, password, or token directly in + the TOML. + +## Testing + +- `pytest` from the repo root. +- Tests live under `tests/`. + +## Committing + +- `ruff check vigilar/` must pass. +- `pytest` must pass. +- One logical change per commit. Commit often. +``` + +Replace `` with the actual +topic convention observed. + +- [ ] **Step 3: Verify** + +```bash +ls vigilar/constants.py vigilar/storage/schema.py vigilar/storage/queries.py +``` + +All three must exist. If `queries.py` does not exist, remove the reference +to it from the doc. + +- [ ] **Step 4: Commit** + +```bash +git add docs/architecture/conventions.md +git commit -m "docs: add coding conventions reference" +``` + +--- + +## Task 3: Subsystem doc template and the `camera` subsystem + +The next 12 tasks (3–14) all follow the same template. Task 3 is written +in full. Tasks 4–14 reuse the same template verbatim and only change which +subsystem is being documented. + +**Files:** +- Create: `docs/architecture/subsystems/camera.md` + +- [ ] **Step 1: Enumerate the subsystem's files** + +```bash +ls vigilar/camera/ +``` + +Read every `.py` file in the directory. For each one, note in 1 sentence +what it is responsible for. + +- [ ] **Step 2: Find MQTT topics** + +Use the Grep tool (NOT bash grep) to find what this subsystem publishes +and subscribes to: + +``` +Grep pattern: publish\(|subscribe\( path: vigilar/camera output_mode: content +``` + +Write down every topic string literal you see, and whether it is being +published or subscribed to. If a topic comes from `vigilar/constants.py`, +resolve it. + +- [ ] **Step 3: Find DB tables touched** + +``` +Grep pattern: from vigilar.storage path: vigilar/camera output_mode: content +Grep pattern: \.insert\(|\.select\(|\.update\(|\.delete\( path: vigilar/camera output_mode: content +``` + +Note any table names referenced. If none, the Database section will say +"none". + +- [ ] **Step 4: Write the file** + +Create `docs/architecture/subsystems/camera.md` using this exact template. +Fill every section from the evidence gathered in Steps 1–3. + +```markdown +# camera + +## Purpose + + + +## Key files + +- `vigilar/camera/worker.py` — +- `vigilar/camera/manager.py` — +- `vigilar/camera/motion.py` — +- `vigilar/camera/recorder.py` — +- `vigilar/camera/ring_buffer.py` — +- `vigilar/camera/hls.py` — + +## MQTT topics + +**Subscribes:** +**Publishes:** + +## Database tables + +
+ +## Depends on + +- +- ... or "none" + +## Consumed by + +- +- ... or "none" + +## Notes + + +``` + +If you genuinely cannot find any MQTT publishers in this subsystem, write +the literal text `"No MQTT publishers found at time of writing."` under +**Publishes:**. Same for subscribes. **Do not invent topics.** + +- [ ] **Step 5: Verify** + +```bash +ls vigilar/camera/worker.py vigilar/camera/manager.py vigilar/camera/motion.py \ + vigilar/camera/recorder.py vigilar/camera/ring_buffer.py vigilar/camera/hls.py +``` + +All must exist. If any do not, remove them from the Key Files list. + +- [ ] **Step 6: Commit** + +```bash +git add docs/architecture/subsystems/camera.md +git commit -m "docs: add camera subsystem reference" +``` + +--- + +## Task 4: `detection` subsystem + +Follow the exact same 6-step procedure as Task 3, but for the `detection` +subsystem. + +**Files:** +- Create: `docs/architecture/subsystems/detection.md` + +- [ ] **Step 1:** `ls vigilar/detection/` and read every `.py` file. +- [ ] **Step 2:** Grep for `publish(` and `subscribe(` in `vigilar/detection`. +- [ ] **Step 3:** Grep for DB access in `vigilar/detection`. +- [ ] **Step 4:** Write `docs/architecture/subsystems/detection.md` using + the template from Task 3. Fill from evidence. No guessing. +- [ ] **Step 5:** Verify every file path in the Key Files list exists. +- [ ] **Step 6:** Commit. + +```bash +git add docs/architecture/subsystems/detection.md +git commit -m "docs: add detection subsystem reference" +``` + +--- + +## Task 5: `events` subsystem + +Follow the Task 3 procedure for `events`. + +**Files:** +- Create: `docs/architecture/subsystems/events.md` + +- [ ] **Step 1:** `ls vigilar/events/` and read every `.py` file. +- [ ] **Step 2:** Grep for `publish(` and `subscribe(` in `vigilar/events`. +- [ ] **Step 3:** Grep for DB access in `vigilar/events`. This subsystem + almost certainly writes to an events table — name it. +- [ ] **Step 4:** Write `docs/architecture/subsystems/events.md` using + the template from Task 3. Fill from evidence. +- [ ] **Step 5:** Verify Key Files paths. +- [ ] **Step 6:** Commit. + +```bash +git add docs/architecture/subsystems/events.md +git commit -m "docs: add events subsystem reference" +``` + +--- + +## Task 6: `alerts` subsystem + +Follow the Task 3 procedure for `alerts`. + +**Files:** +- Create: `docs/architecture/subsystems/alerts.md` + +- [ ] **Step 1:** `ls vigilar/alerts/` and read every `.py` file. Expect + sub-channels for local (syslog/desktop), web push (VAPID), and + email. +- [ ] **Step 2:** Grep for `publish(` and `subscribe(` in `vigilar/alerts`. + Alerts almost certainly subscribe to event topics. +- [ ] **Step 3:** Grep for DB access in `vigilar/alerts`. +- [ ] **Step 4:** Write the doc. In **Notes**, mention the VAPID / PWA + push path if and only if you find the code for it (do not guess). +- [ ] **Step 5:** Verify paths. +- [ ] **Step 6:** Commit. + +```bash +git add docs/architecture/subsystems/alerts.md +git commit -m "docs: add alerts subsystem reference" +``` + +--- + +## Task 7: `sensors` subsystem + +Follow the Task 3 procedure for `sensors`. + +**Files:** +- Create: `docs/architecture/subsystems/sensors.md` + +- [ ] **Step 1:** `ls vigilar/sensors/` and read every `.py` file. +- [ ] **Step 2:** Grep for `publish(` / `subscribe(` in `vigilar/sensors`. + Sensors likely integrate with Zigbee2MQTT — look for + `zigbee2mqtt` topic prefixes too. +- [ ] **Step 3:** Grep for DB access in `vigilar/sensors`. +- [ ] **Step 4:** Write the doc. Mention Zigbee2MQTT in **Depends on** if + the code actually subscribes to its topics. +- [ ] **Step 5:** Verify paths. +- [ ] **Step 6:** Commit. + +```bash +git add docs/architecture/subsystems/sensors.md +git commit -m "docs: add sensors subsystem reference" +``` + +--- + +## Task 8: `ups` subsystem + +Follow the Task 3 procedure for `ups`. + +**Files:** +- Create: `docs/architecture/subsystems/ups.md` + +- [ ] **Step 1:** `ls vigilar/ups/` and read every `.py` file. +- [ ] **Step 2:** Grep for `publish(` / `subscribe(` in `vigilar/ups`. +- [ ] **Step 3:** Grep for DB access in `vigilar/ups`. +- [ ] **Step 4:** Write the doc. In **Notes**, mention that this subsystem + polls NUT (`127.0.0.1:3493` by default, from `[ups]` in the TOML) + and triggers graceful shutdown on critical battery — but only if + you can point to the code that does each of those things. +- [ ] **Step 5:** Verify paths. +- [ ] **Step 6:** Commit. + +```bash +git add docs/architecture/subsystems/ups.md +git commit -m "docs: add ups subsystem reference" +``` + +--- + +## Task 9: `storage` subsystem + +Follow the Task 3 procedure for `storage`. + +**Files:** +- Create: `docs/architecture/subsystems/storage.md` + +- [ ] **Step 1:** `ls vigilar/storage/` and read `db.py`, `schema.py`, + `encryption.py`, `queries.py`, `__init__.py`. +- [ ] **Step 2:** Grep for `publish(` / `subscribe(` in `vigilar/storage` + — this subsystem may have none, which is fine. +- [ ] **Step 3:** This IS the DB layer, so the **Database tables** + section should list every table defined in `schema.py`, with a one + phrase description of each. +- [ ] **Step 4:** Write the doc. In **Notes**, explain the `.vge` + encryption format (AES-256-GCM) and where the key lives + (`/etc/vigilar/secrets/storage.key`) — but only if `encryption.py` + actually implements that. Quote the function name. +- [ ] **Step 5:** Verify paths. +- [ ] **Step 6:** Commit. + +```bash +git add docs/architecture/subsystems/storage.md +git commit -m "docs: add storage subsystem reference" +``` + +--- + +## Task 10: `highlights` subsystem + +Follow the Task 3 procedure for `highlights`. + +**Files:** +- Create: `docs/architecture/subsystems/highlights.md` + +- [ ] **Step 1:** `ls vigilar/highlights/` and read every `.py` file. + Expect scoring, reel assembly, and possibly timelapse code. +- [ ] **Step 2:** Grep for `publish(` / `subscribe(`. +- [ ] **Step 3:** Grep for DB access. +- [ ] **Step 4:** Write the doc. Mention in **Notes** how scoring works + if the code makes it obvious (e.g. FFmpeg clip assembly, event + score thresholds). Do not invent scoring weights. +- [ ] **Step 5:** Verify paths. +- [ ] **Step 6:** Commit. + +```bash +git add docs/architecture/subsystems/highlights.md +git commit -m "docs: add highlights subsystem reference" +``` + +--- + +## Task 11: `presence` subsystem + +Follow the Task 3 procedure for `presence`. + +**Files:** +- Create: `docs/architecture/subsystems/presence.md` + +- [ ] **Step 1:** `ls vigilar/presence/` and read every `.py` file. +- [ ] **Step 2:** Grep for `publish(` / `subscribe(`. +- [ ] **Step 3:** Grep for DB access. +- [ ] **Step 4:** Write the doc. If you cannot figure out what this + subsystem does from its code, say so honestly in **Purpose** — + e.g. "Tracks resident presence state. Mechanism not documented at + time of writing; see source for details." +- [ ] **Step 5:** Verify paths. +- [ ] **Step 6:** Commit. + +```bash +git add docs/architecture/subsystems/presence.md +git commit -m "docs: add presence subsystem reference" +``` + +--- + +## Task 12: `pets` subsystem + +Follow the Task 3 procedure for `pets`. + +**Files:** +- Create: `docs/architecture/subsystems/pets.md` + +- [ ] **Step 1:** `ls vigilar/pets/` and read every `.py` file. +- [ ] **Step 2:** Grep for `publish(` / `subscribe(`. +- [ ] **Step 3:** Grep for DB access. +- [ ] **Step 4:** Write the doc. +- [ ] **Step 5:** Verify paths. +- [ ] **Step 6:** Commit. + +```bash +git add docs/architecture/subsystems/pets.md +git commit -m "docs: add pets subsystem reference" +``` + +--- + +## Task 13: `health` subsystem + +Follow the Task 3 procedure for `health`. + +**Files:** +- Create: `docs/architecture/subsystems/health.md` + +- [ ] **Step 1:** `ls vigilar/health/` — expected files: `digest.py`, + `monitor.py`, `pruner.py`, `__init__.py`. Read all four. +- [ ] **Step 2:** Grep for `publish(` / `subscribe(`. +- [ ] **Step 3:** Grep for DB access. The pruner likely deletes old rows + and old recordings — note what it prunes. +- [ ] **Step 4:** Write the doc. In **Notes**, describe what the pruner + enforces (e.g. `max_disk_usage_gb`, `free_space_floor_gb` from the + `[storage]` TOML section) — but only if the code actually reads + those settings. Quote the variable name. +- [ ] **Step 5:** Verify paths. +- [ ] **Step 6:** Commit. + +```bash +git add docs/architecture/subsystems/health.md +git commit -m "docs: add health subsystem reference" +``` + +--- + +## Task 14: `web` subsystem + +Follow the Task 3 procedure for `web`. This one is slightly different: +the **Key files** list should enumerate every blueprint, and the +**Purpose** should name the HTTP-facing surface. + +**Files:** +- Create: `docs/architecture/subsystems/web.md` + +- [ ] **Step 1:** `ls vigilar/web/blueprints/` and read each blueprint's + top-of-file routes. Also read `vigilar/web/__init__.py` to find the + Flask app factory. +- [ ] **Step 2:** Grep for `publish(` / `subscribe(` in `vigilar/web`. + The web tier likely subscribes to event topics for live updates — + verify. +- [ ] **Step 3:** Grep for DB access. Blueprints likely use + `vigilar.storage.queries`. +- [ ] **Step 4:** Write `docs/architecture/subsystems/web.md`. Under + **Key files**, list each blueprint from + `vigilar/web/blueprints/` with a one-sentence description of what + it serves: + +``` +- `vigilar/web/blueprints/cameras.py` — live view and camera management UI +- `vigilar/web/blueprints/events.py` — event timeline +- `vigilar/web/blueprints/recordings.py` — recording playback +- `vigilar/web/blueprints/sensors.py` — sensor status +- `vigilar/web/blueprints/system.py` — system health, settings, PIN +- `vigilar/web/blueprints/kiosk.py` — wall display mode +- `vigilar/web/blueprints/pets.py` — pet tracking UI +- `vigilar/web/blueprints/visitors.py` — visitor tracking UI +- `vigilar/web/blueprints/wildlife.py` — wildlife tracking UI +``` + +Adjust each description based on the actual routes you read. + +In **Notes**, mention: Jinja2 + Bootstrap 5 dark, `hls.js` for the grid, +MJPEG for single-camera low-latency, and PWA + VAPID push — but only +the items you can locate in the codebase. + +- [ ] **Step 5:** Verify paths. + +```bash +ls vigilar/web/blueprints/cameras.py vigilar/web/blueprints/events.py \ + vigilar/web/blueprints/recordings.py vigilar/web/blueprints/sensors.py \ + vigilar/web/blueprints/system.py vigilar/web/blueprints/kiosk.py \ + vigilar/web/blueprints/pets.py vigilar/web/blueprints/visitors.py \ + vigilar/web/blueprints/wildlife.py +``` + +- [ ] **Step 6:** Commit. + +```bash +git add docs/architecture/subsystems/web.md +git commit -m "docs: add web subsystem reference" +``` + +--- + +## Task 15: Home user guide + +**Files:** +- Create: `docs/home-user-guide.md` + +- [ ] **Step 1: Re-read the grounding sources** + +```bash +ls scripts/install.sh scripts/gen_vapid_keys.sh scripts/gen_cert.sh \ + scripts/backup.sh systemd/vigilar.service +``` + +Read `scripts/install.sh` in full so you can accurately describe in 3 +bullets what it does (install system deps, create vigilar user, set up +venv, install the package, generate VAPID keys, install the systemd unit +— confirm each against the actual script). + +Read `config/vigilar.toml` and confirm the web port default is `49735` +under `[web]`. If it has changed, use the actual value. + +- [ ] **Step 2: Create `docs/home-user-guide.md`** + +Write the file with exactly these sections, in this order. Keep the tone +direct and practical. Target length 1500–2500 words. + +```markdown +# Vigilar Home User Guide + +This guide takes you from a bare mini PC to working cameras on your phone. +It assumes you are comfortable with a Linux command line and can SSH into +a machine on your network. + +If you are running Vigilar on a server you already administer, see the +[Operator Guide](operator-guide.md) instead. + +## What you will end up with + +- A small always-on box in your house that records from your IP cameras. +- A web UI at `http://:49735` on your LAN. +- Push notifications on your phone when motion is detected. +- Optional nightly config + database backup to a NAS share. + +``` + [IP cameras] ──RTSP──▶ [mini PC running Vigilar] ──LAN──▶ [your phone/browser] + │ + └── optional nightly ──▶ [NAS] +``` + +## What you need + +- A mini PC. x86_64, 4 GB RAM minimum, 128 GB SSD minimum. Any modern + Intel NUC, Beelink, MinisForum, or similar will do. +- A USB stick (8 GB or larger) for installing Linux. +- One or more RTSP IP cameras on your LAN. For picks, see the + [Camera Hardware Guide](camera-hardware-guide.md). +- A phone for push notifications. +- Optional: a NAS on your LAN for backups. + +## Step 1 — Install Linux on the mini PC + +Install Debian 12 or Ubuntu Server 24.04 (or later) on the mini PC. The +official installers walk you through it. During install: + +- Choose **OpenSSH server** when asked for tasks/components. +- Set a hostname you will remember (e.g. `vigilar`). +- Create a user for yourself. You will need `sudo`. + +After install, note the machine's LAN IP address (`ip addr` on the box) +and make sure you can SSH in from your laptop. + +## Step 2 — Install Vigilar + +SSH into the mini PC and run: + +```bash +git clone vigilar +cd vigilar +sudo ./scripts/install.sh +``` + +`install.sh` will: + +- Install system dependencies (ffmpeg, mosquitto, Python 3, NUT client). +- Create a dedicated `vigilar` system user. +- Set up a Python virtualenv at `/opt/vigilar/venv`. +- Generate VAPID keys for web push notifications. +- Install the systemd unit and Mosquitto broker config. + +Review the `[INFO]` output as it runs. Anything marked `[FAIL]` needs +attention before you continue. + +## Step 3 — Start it up + +```bash +sudo systemctl enable --now vigilar +sudo systemctl status vigilar +``` + +You should see `active (running)`. If it is not, run +`journalctl -u vigilar -e` and look for the first `ERROR` line. + +Open a browser on any device on the same LAN: + +``` +http://:49735 +``` + +The port is configurable under `[web] port` in +`/etc/vigilar/vigilar.toml` if 49735 conflicts with something else you +run. + +## Step 4 — Set your PIN + +On the first visit you will be asked to set an arm/disarm PIN. Choose +something you will remember. The PIN is stored as a hash — there is no +recovery. If you lose it you will need to reset it from the command line +with `vigilar config set-pin` on the mini PC. + +> **Screenshot placeholder:** `docs/images/first-run-pin.png` + +## Step 5 — Add your first camera + +From the web UI, go to **Cameras → Add camera**. You will need: + +- A name for the camera (e.g. "Front door"). +- The camera's RTSP URL. Format varies by vendor — see the + [Camera Hardware Guide](camera-hardware-guide.md) for the common ones. +- Username and password for the camera's RTSP stream (not the camera's + web UI login, if they are different). + +Click **Test** to confirm Vigilar can pull a frame, then **Save**. + +The camera will appear in the live grid. It idles at 2 FPS to save CPU +and jumps to 30 FPS when motion is detected. + +> **Screenshot placeholder:** `docs/images/add-camera.png` + +## Step 6 — Phone push notifications + +Vigilar runs as a Progressive Web App. To get notifications on your +phone: + +1. Open `http://:49735` in your phone's browser on the same + Wi-Fi. +2. Use your browser's "Add to Home Screen" option. +3. Open the installed app from your home screen. +4. When prompted, allow notifications. + +Under the hood, `install.sh` already generated the VAPID keys at +`/etc/vigilar/secrets/vapid_private.pem`. You do not need to do anything +else. + +## Step 7 — Optional: NAS backup + +This step backs up your SQLite database and `/etc/vigilar` (config and +secrets) to a NAS share once a day. It does **not** back up recordings — +recordings stay on the mini PC's local disk. Recording backup is planned +as a later improvement. + +### Mount the NAS share + +If your NAS offers NFS: + +```bash +sudo apt-get install -y nfs-common +sudo mkdir -p /mnt/nas/vigilar-backups +echo "nas.lan:/volume1/vigilar-backups /mnt/nas/vigilar-backups nfs defaults,_netdev 0 0" | sudo tee -a /etc/fstab +sudo mount -a +``` + +Replace `nas.lan:/volume1/vigilar-backups` with your NAS's actual export +path. + +### Point `backup.sh` at the mount and schedule it + +Create `/etc/systemd/system/vigilar-backup.service`: + +```ini +[Unit] +Description=Vigilar nightly backup +After=vigilar.service + +[Service] +Type=oneshot +Environment=VIGILAR_BACKUP_DIR=/mnt/nas/vigilar-backups +Environment=VIGILAR_BACKUP_RETENTION_DAYS=30 +ExecStart=/opt/vigilar/scripts/backup.sh +``` + +Create `/etc/systemd/system/vigilar-backup.timer`: + +```ini +[Unit] +Description=Run vigilar backup nightly + +[Timer] +OnCalendar=daily +Persistent=true + +[Install] +WantedBy=timers.target +``` + +Enable it: + +```bash +sudo systemctl daemon-reload +sudo systemctl enable --now vigilar-backup.timer +sudo systemctl list-timers | grep vigilar +``` + +The first run happens on the next daily rollover. To test it now: + +```bash +sudo systemctl start vigilar-backup.service +ls -lh /mnt/nas/vigilar-backups/ +``` + +You should see a file like `vigilar-backup-20260405-030000.tar.gz`. + +## Troubleshooting + +### The web UI will not load + +- `sudo systemctl status vigilar` — is it running? +- `journalctl -u vigilar -e` — what does the last error say? +- Is port 49735 blocked by a firewall? `sudo ss -tlnp | grep 49735` + should show the Vigilar process listening. + +### A camera will not connect + +- Can you open the RTSP URL in VLC from your laptop? If not, it is a + camera/network problem, not a Vigilar problem. +- Double-check username/password — many cameras have a separate RTSP + credential. +- Some cameras need a specific stream path (e.g. `/stream1`). See the + [Camera Hardware Guide](camera-hardware-guide.md). + +### No push notifications on my phone + +- Did you open the PWA from your home screen, not from the browser tab? +- Check the browser's notification permission for the site. +- Safari on iOS only supports web push from installed PWAs, not from a + tab. + +### Service keeps restarting + +- `journalctl -u vigilar -n 100` will show the last 100 log lines. +- A common cause is an invalid `vigilar.toml`. Run + `vigilar config validate` to check. + +### I forgot my PIN + +```bash +sudo -u vigilar /opt/vigilar/venv/bin/vigilar config set-pin +``` + +### Motion detection is too sensitive + +See the `[camera]` and motion-related sections in the +[Operator Guide](operator-guide.md) for tuning. + +## Where to go next + +- [Operator Guide](operator-guide.md) — config reference, backups, + upgrades, security hardening. +- [Architecture Overview](architecture/overview.md) — how Vigilar is put + together, if you are curious. +``` + +**Important:** every CLI command (`vigilar config set-pin`, +`vigilar config validate`) must be verified against `vigilar/cli/` before +you write it. If a command does not exist, remove that part of the doc or +reword it around what does exist. + +- [ ] **Step 3: Verify CLI commands exist** + +Use Grep to check: + +``` +Grep pattern: set-pin path: vigilar/cli/ output_mode: content +Grep pattern: set-password path: vigilar/cli/ output_mode: content +Grep pattern: validate path: vigilar/cli/ output_mode: content +``` + +If `set-pin` is not found, the "I forgot my PIN" and Step 4 sections must +be rewritten to match what actually exists. + +- [ ] **Step 4: Verify file paths referenced** + +```bash +ls scripts/install.sh scripts/backup.sh config/vigilar.toml +``` + +- [ ] **Step 5: Commit** + +```bash +git add docs/home-user-guide.md +git commit -m "docs: add home user setup guide" +``` + +--- + +## Task 16: Operator guide + +**Files:** +- Create: `docs/operator-guide.md` + +- [ ] **Step 1: Gather grounding facts** + +Read, in full: +- `config/vigilar.toml` — note every `[section]` and every key. +- `vigilar/cli/main.py` — note every top-level Click command. +- `vigilar/cli/cmd_config.py` — note every `config` subcommand. +- `vigilar/cli/cmd_start.py` — note the `start` command's behavior. +- `scripts/install.sh` +- `scripts/backup.sh` +- `scripts/gen_cert.sh` +- `scripts/gen_vapid_keys.sh` +- `scripts/setup_nut.sh` +- `scripts/uninstall.sh` +- `systemd/vigilar.service` +- `systemd/vigilar-mosquitto.conf` + +For each TOML section, write a one-line purpose. For each CLI command, +write a one-line purpose. + +- [ ] **Step 2: Find health endpoints** + +``` +Grep pattern: @.*route path: vigilar/web/blueprints/system.py output_mode: content +``` + +Also check `vigilar/health/` for any HTTP-facing code. Note any `/health`, +`/status`, `/metrics`, etc. endpoints that actually exist. + +- [ ] **Step 3: Create `docs/operator-guide.md`** + +Write the file with these sections, in this order. Target length 2500–4000 +words. Be concrete — every key gets a real description, every command +gets a real usage line. + +```markdown +# Vigilar Operator Guide + +This guide is a reference for running Vigilar on a server you administer. +It assumes comfort with Linux, systemd, and the CLI. For first-time home +setup instead, see the [Home User Guide](home-user-guide.md). + +## Layout on disk + +| Path | Purpose | +|----------------------------------------|----------------------------------| +| `/opt/vigilar/` | Source tree and Python venv. | +| `/opt/vigilar/venv/bin/vigilar` | CLI entry point. | +| `/etc/vigilar/vigilar.toml` | Main configuration file. | +| `/etc/vigilar/certs/` | TLS cert and key (if enabled). | +| `/etc/vigilar/secrets/storage.key` | AES-256 key for recordings. | +| `/etc/vigilar/secrets/vapid_*.pem` | VAPID keys for web push. | +| `/var/vigilar/data/vigilar.db` | SQLite database (WAL mode). | +| `/var/vigilar/recordings/` | Encrypted .vge recording files. | +| `/var/vigilar/hls/` | Live HLS segments (rolling). | +| `/var/vigilar/backups/` | Default backup destination. | + +## Installation + +`scripts/install.sh` is the blessed install path. It detects the package +manager (apt or pacman), installs system dependencies, creates a +`vigilar` system user, builds a virtualenv at `/opt/vigilar/venv`, +installs the Python package, generates VAPID keys if missing, and drops +the systemd unit and Mosquitto broker config into place. + +### System dependencies + +On Debian/Ubuntu: `ffmpeg mosquitto python3 python3-venv python3-pip nut-client`. +On Arch: `ffmpeg mosquitto python python-virtualenv nut`. + +### Systemd unit + +`systemd/vigilar.service` runs `vigilar start` as the `vigilar` user. +Mosquitto runs as a separate unit, configured by +`systemd/vigilar-mosquitto.conf` to bind to loopback only. + +## Configuration reference (`/etc/vigilar/vigilar.toml`) + + + +### `[system]` +- `name` (default: `"Vigilar Home Security"`) — display name shown in + the web UI. Cosmetic. +- `timezone` (default: `"America/New_York"`) — used for timestamps in + logs and the UI. Set this to your local timezone. +- `data_dir` (default: `"/var/vigilar/data"`) — SQLite database + location. Change only if you are redirecting state to a different + disk. +- `recordings_dir` (default: `"/var/vigilar/recordings"`) — where + encrypted recordings live. May be pointed at a larger disk. +- `hls_dir` (default: `"/var/vigilar/hls"`) — live HLS segments. + Consider tmpfs for reduced SSD wear. +- `log_level` (default: `"INFO"`) — one of `DEBUG`, `INFO`, `WARNING`, + `ERROR`. + +### `[mqtt]` +- `host` (default: `"127.0.0.1"`) +- `port` (default: `1883`) + +Leave as-is unless you are running an external broker. + +### `[web]` +- `host` (default: `"0.0.0.0"`) — bind address. Leave as-is for LAN + access; set to `127.0.0.1` to expose only via a reverse proxy. +- `port` (default: `49735`) — HTTP port. +- `tls_cert`, `tls_key` — PEM paths for HTTPS. Generate with + `scripts/gen_cert.sh`. +- `username` (default: `"admin"`) — admin username. +- `password_hash` — set via `vigilar config set-password`. + + + +## CLI reference + +The CLI is `vigilar`, installed at `/opt/vigilar/venv/bin/vigilar`. Run +commands as the `vigilar` user: + +```bash +sudo -u vigilar /opt/vigilar/venv/bin/vigilar +``` + + + +### `vigilar start` + + +Usage: +```bash +sudo -u vigilar /opt/vigilar/venv/bin/vigilar start +``` + +Normally you run this via systemd (`systemctl start vigilar`) rather +than directly. + +### `vigilar config ` + + +## Secrets and security + +### `/etc/vigilar/secrets/` layout + +- `storage.key` — AES-256 key used to encrypt `.vge` recording files. + **If you lose this file, every existing recording becomes + unrecoverable.** Include it in your backups (`scripts/backup.sh` does + this by default because it tars `/etc/vigilar`). +- `vapid_private.pem`, `vapid_public.pem` — VAPID keys for web push + notifications. Generated by `scripts/gen_vapid_keys.sh`. +- Any other files in `secrets/` discovered at runtime. + +File permissions should be `0600 root:root` or `0600 vigilar:vigilar` +for anything containing a key. `install.sh` sets these. + +### Arm/disarm PIN + +```bash +sudo -u vigilar /opt/vigilar/venv/bin/vigilar config set-pin +``` + +PINs are stored as hashes. Comparisons are constant-time. + +### Admin password + +```bash +sudo -u vigilar /opt/vigilar/venv/bin/vigilar config set-password +``` + +### TLS + +```bash +sudo ./scripts/gen_cert.sh +``` + +Then set `[web] tls_cert` and `[web] tls_key` in the TOML and restart +Vigilar. + +### Firewall stance + +Vigilar is LAN-only by default — the Mosquitto broker binds to +`127.0.0.1` and the web server binds to `0.0.0.0` on port 49735. If you +need remote access, use the `[remote]` tunnel feature rather than +exposing port 49735 to the internet. + +## UPS / NUT integration + +`scripts/setup_nut.sh` configures a local NUT server for a USB-connected +UPS. After running it, set `[ups] enabled = true` and adjust: + +- `nut_host`, `nut_port`, `ups_name` — NUT connection. +- `poll_interval_s` — how often Vigilar polls the UPS. +- `low_battery_threshold_pct` — triggers a warning alert. +- `critical_runtime_threshold_s` — triggers a graceful shutdown. +- `shutdown_delay_s` — seconds to wait before issuing `shutdown -h`. + +## Backups + +`scripts/backup.sh` captures: + +- The SQLite database (via `sqlite3 .backup` for a consistent snapshot + if `sqlite3` is installed, otherwise a raw file copy). +- Any WAL/SHM sidecars next to the DB. +- The entire `/etc/vigilar/` directory (config + certs + secrets). + +It does **not** back up recordings under `/var/vigilar/recordings/`. + +### Environment variables + +- `VIGILAR_BACKUP_DIR` (default: `/var/vigilar/backups`) — destination. + Set this to a NAS mount if you want off-box backups. +- `VIGILAR_BACKUP_RETENTION_DAYS` (default: `30`) — older archives are + pruned. + +### Suggested systemd timer + +Create `/etc/systemd/system/vigilar-backup.service`: + +```ini +[Unit] +Description=Vigilar nightly backup +After=vigilar.service + +[Service] +Type=oneshot +Environment=VIGILAR_BACKUP_DIR=/var/vigilar/backups +Environment=VIGILAR_BACKUP_RETENTION_DAYS=30 +ExecStart=/opt/vigilar/scripts/backup.sh +``` + +Create `/etc/systemd/system/vigilar-backup.timer`: + +```ini +[Unit] +Description=Run vigilar backup nightly + +[Timer] +OnCalendar=daily +Persistent=true + +[Install] +WantedBy=timers.target +``` + +Enable: + +```bash +sudo systemctl daemon-reload +sudo systemctl enable --now vigilar-backup.timer +``` + +### Restore procedure + +```bash +sudo systemctl stop vigilar +sudo tar -C / -xzpf /path/to/vigilar-backup-YYYYMMDD-HHMMSS.tar.gz +sudo chown -R vigilar:vigilar /var/vigilar/data /etc/vigilar +sudo systemctl start vigilar +``` + +## Upgrades + +```bash +cd /opt/vigilar +sudo -u vigilar git pull +sudo -u vigilar /opt/vigilar/venv/bin/pip install -e . +sudo systemctl restart vigilar +``` + + + +### Rolling back + +```bash +sudo systemctl stop vigilar +# Check out the previous tag or commit +sudo -u vigilar git -C /opt/vigilar checkout +sudo -u vigilar /opt/vigilar/venv/bin/pip install -e /opt/vigilar +# Restore the pre-upgrade backup if schema changed +sudo systemctl start vigilar +``` + +## Logs and health + +### Logs + +```bash +sudo journalctl -u vigilar -f # follow +sudo journalctl -u vigilar -n 200 # last 200 lines +sudo journalctl -u vigilar --since "1 hour ago" +``` + +Raise verbosity by setting `[system] log_level = "DEBUG"` in the TOML +and restarting. + +### Health endpoints + + + +- `GET /system/health` — JSON summary of subsystem status. +- `GET /system/logs` — recent log entries from the web UI. + +If none exist, write: "There are no dedicated health HTTP endpoints at +time of writing. Use `journalctl -u vigilar` and the system status page +in the web UI." + +## Remote access + +`[remote] enabled = true` turns on the tunnel-based remote viewing +feature. It serves a downscaled HLS stream shaped to +`upload_bandwidth_mbps` over a tunnel on `tunnel_ip`. This is **not** a +cloud service — you are still the one running the tunnel endpoint. + +Key knobs: + +- `upload_bandwidth_mbps` — your upload ceiling. +- `remote_hls_resolution`, `remote_hls_fps`, `remote_hls_bitrate_kbps` — + shape the remote stream. +- `max_remote_viewers` — concurrency cap. + +## Troubleshooting + +### Service is in a crash loop + +`journalctl -u vigilar -n 200` and look for the first `ERROR`. Common +causes: + +- Invalid TOML — run `vigilar config validate` (if that command exists). +- Missing secrets — `storage.key` or `vapid_private.pem` not readable by + the `vigilar` user. +- Port 49735 already in use by something else. + +### Mosquitto broker will not start + +- `sudo systemctl status mosquitto` +- Check `/etc/mosquitto/conf.d/` for a conflicting config file on the + same port. +- Run `mosquitto -v -c /etc/mosquitto/conf.d/vigilar.conf` by hand to + see the error directly. + +### A camera worker is thrashing + +- Check `journalctl -u vigilar | grep `. +- Verify the RTSP URL with `ffprobe `. +- Reduce expected FPS in the camera's entry if the stream is unstable. + +### Disk is full / `free_space_floor_gb` triggered + +The health pruner is supposed to delete the oldest recordings when free +space drops below `[storage] free_space_floor_gb`. If it is not keeping +up: + +- Lower `[storage] max_disk_usage_gb`. +- Move `recordings_dir` to a larger disk. +- Check `journalctl -u vigilar | grep -i prun` for pruner errors. + +### HLS grid is stalling + +- The mini PC's CPU may be saturated. Check `top`. +- Drop the grid to fewer cameras or lower resolution. +- Single-camera view uses MJPEG, which is cheaper to serve than HLS. +``` + +Every TOML key, every CLI command, every endpoint, every file path in +this document must be backed by something you actually read in Step 1 or +Step 2. Delete any bullet you cannot verify. + +- [ ] **Step 4: Verify CLI commands exist** + +``` +Grep pattern: @.*\.command path: vigilar/cli/ output_mode: content +``` + +Every `vigilar ` mentioned in the doc must be present here. +Remove any that are not. + +- [ ] **Step 5: Verify TOML coverage** + +Every `[section]` in `config/vigilar.toml` must have a subsection in +"Configuration reference". Every subsection in the doc must correspond to +a real `[section]`. + +- [ ] **Step 6: Commit** + +```bash +git add docs/operator-guide.md +git commit -m "docs: add operator guide" +``` + +--- + +## Task 17: Top-level README + +**Files:** +- Create: `README.md` + +- [ ] **Step 1: Confirm no existing README** + +```bash +ls README.md 2>/dev/null || echo "no existing README — good" +``` + +- [ ] **Step 2: Confirm all linked targets exist** + +Every relative link in this README will point to a file created in +Tasks 1–16. Verify: + +```bash +ls docs/home-user-guide.md docs/operator-guide.md \ + docs/architecture/overview.md docs/architecture/conventions.md \ + docs/camera-hardware-guide.md +ls docs/architecture/subsystems/*.md | wc -l # should be 12 +``` + +- [ ] **Step 3: Create `README.md`** + +```markdown +# Vigilar + +> DIY offline-first home security. Your cameras, your house, your data. + +Vigilar runs on a small mini PC in your home, records from your IP +cameras, detects motion, and pushes notifications to your phone — with +no cloud, no account, and no external service in the critical path. + + +![Vigilar web UI grid view](docs/images/grid.png) + +## Why Vigilar + +- **Offline-first.** No cloud dependency. No external calls in the hot + path. +- **Multi-camera live view.** HLS grid for bandwidth efficiency, MJPEG + single view for low-latency focus. +- **On-device motion detection.** OpenCV MOG2 with a 5-second pre-motion + ring buffer so you never miss the moment leading up to a trigger. +- **Event timeline, highlight reels, timelapses.** Visitor, pet, and + wildlife tracking as separate views. +- **Phone push notifications.** PWA + VAPID web push. No Firebase, no + Google Cloud Messaging. +- **Encrypted recordings.** AES-256-GCM `.vge` files. Key stays on your + box. +- **UPS aware.** NUT integration, graceful shutdown on critical + battery. +- **Runs on a cheap mini PC.** 4 GB RAM, 128 GB SSD, any modern x86_64 + box. + +## Quick paths + +| I want to… | Start here | +|----------------------------------------------|--------------------------------------------------------------| +| Set it up at home on a mini PC | [Home User Guide](docs/home-user-guide.md) | +| Run it as a self-hoster / sysadmin | [Operator Guide](docs/operator-guide.md) | +| Understand how it works internally | [Architecture Overview](docs/architecture/overview.md) | +| Pick cameras that work well with it | [Camera Hardware Guide](docs/camera-hardware-guide.md) | + +## 60-second overview + +``` + [IP cameras] ──RTSP──▶ [mini PC running Vigilar] ──LAN──▶ [phone / browser] + │ + └── optional nightly ──▶ [NAS] +``` + +- **Language:** Python 3.11+ +- **Web:** Flask + Bootstrap 5 dark theme + `hls.js` +- **Storage:** SQLite (WAL) via SQLAlchemy Core +- **Bus:** Local Mosquitto on `127.0.0.1:1883` +- **Video:** OpenCV (capture + motion), FFmpeg subprocess (recording, + HLS) + +## Status + +**Alpha.** Works on the author's hardware. Expect rough edges and +breaking changes. Not recommended for anyone who cannot read a stack +trace. + +## Installation (TL;DR) + +```bash +git clone vigilar +cd vigilar +sudo ./scripts/install.sh +sudo systemctl enable --now vigilar +``` + +Then open `http://:49735` on your LAN. + +For the full walkthrough (OS install, camera setup, PIN, push +notifications, NAS backup), see the +[Home User Guide](docs/home-user-guide.md). + +For configuration, CLI, secrets, backups, and upgrades, see the +[Operator Guide](docs/operator-guide.md). + +## Documentation + +- **User guides** + - [Home User Guide](docs/home-user-guide.md) — mini PC to working + cameras, step by step. + - [Operator Guide](docs/operator-guide.md) — configuration, CLI, + backups, security, upgrades. + - [Camera Hardware Guide](docs/camera-hardware-guide.md) — picking and + wiring up cameras. +- **Architecture** + - [Overview](docs/architecture/overview.md) — process model, MQTT bus, + data flow, storage. + - [Conventions](docs/architecture/conventions.md) — coding rules for + contributors. + - [Subsystems](docs/architecture/subsystems/) — one short reference + per subsystem: `camera`, `detection`, `events`, `alerts`, `sensors`, + `ups`, `storage`, `highlights`, `presence`, `pets`, `health`, + `web`. + +## License + +Vigilar is distributed under the **GNU General Public License v3.0**. +See `LICENSE` for the full text. (If the `LICENSE` file is not yet in +the tree, it will be added in a follow-up.) + +## Contributing + +Issues and pull requests are welcome. Before sending a PR: + +- Read `CLAUDE.md` for the project's code conventions. +- Run `ruff check vigilar/` and `pytest` — both must pass. +- Keep commits small and focused. +``` + +- [ ] **Step 4: Verify every link in the README resolves** + +```bash +ls README.md docs/home-user-guide.md docs/operator-guide.md \ + docs/architecture/overview.md docs/architecture/conventions.md \ + docs/architecture/subsystems docs/camera-hardware-guide.md CLAUDE.md +``` + +All must exist. If any do not, that means an earlier task was skipped — +go back and do it. + +- [ ] **Step 5: Commit** + +```bash +git add README.md +git commit -m "docs: add top-level README" +``` + +--- + +## Task 18: Final verification pass + +**Files:** +- Modify: any of the above, if issues are found + +- [ ] **Step 1: Link check** + +``` +Grep pattern: \]\( path: README.md output_mode: content +Grep pattern: \]\( path: docs/ output_mode: content +``` + +For each relative link discovered, confirm the target exists with +`ls `. Fix any broken links inline. + +- [ ] **Step 2: Command check** + +For every shell command in `docs/home-user-guide.md` and +`docs/operator-guide.md`, confirm either: +- the script exists (`ls scripts/.sh`), or +- the `vigilar` subcommand exists (Grep in `vigilar/cli/`), or +- it is a standard Linux command (`systemctl`, `journalctl`, `git`, + `pip`, `ls`, etc.). + +Remove or rewrite anything that fails. + +- [ ] **Step 3: TOML coverage check** + +Open `config/vigilar.toml` and `docs/operator-guide.md` side by side. +Every `[section]` in the TOML must appear in the operator guide's +Configuration Reference. Every section in the guide must appear in the +TOML. Fix any asymmetry. + +- [ ] **Step 4: Subsystem coverage check** + +```bash +ls vigilar/ +ls docs/architecture/subsystems/ +``` + +Every subsystem directory under `vigilar/` that is listed in the spec +(`camera`, `detection`, `events`, `alerts`, `sensors`, `ups`, `storage`, +`highlights`, `presence`, `pets`, `health`, `web`) must have a matching +`.md` file in `docs/architecture/subsystems/`. If a subsystem directory +exists in `vigilar/` that is NOT in the list (because something was added +to the codebase since the spec was written), note it but do not +necessarily document it — flag it at the end. + +- [ ] **Step 5: Read-through** + +Read every new `.md` file top to bottom in this order: README, +home-user-guide, operator-guide, architecture/overview, +architecture/conventions, then each subsystem doc alphabetically. + +Check for: +- Tone drift (the README should not shift register halfway through). +- Terminology drift (if one doc says "motion event" and another says + "movement trigger" for the same thing, unify). +- Stale references (e.g. a doc saying "see the X section" where X was + removed). + +Fix inline. + +- [ ] **Step 6: Final commit** + +If Steps 1–5 surfaced any fixes: + +```bash +git add -A +git commit -m "docs: final verification pass fixes" +``` + +If no fixes were needed, skip the commit and report "no issues found in +final verification pass" in the task output. + +--- + +## Self-review notes (for the plan author) + +- **Spec coverage:** Each deliverable in §§3-9 of the spec maps to a + task: README → Task 17, home-user → Task 15, operator → Task 16, + overview → Task 1, conventions → Task 2, 12 subsystems → Tasks 3–14. + Verification checklist from spec §10 → Task 18. Out-of-scope items + from spec §11 are explicitly excluded in the "Universal rules". +- **Placeholder scan:** The plan contains `` placeholders + **inside task output templates**. These are **instructions to the + executor** ("replace this with the value you found"), not deferred + work in the plan itself. Each template placeholder is accompanied by + a specific grep or file to read that tells the executor how to fill + it. This is intentional — the alternative would be to pre-populate + architecture facts in the plan without ever reading the code, which + would make the plan itself guess. +- **Type/signature consistency:** N/A — docs, not code. +- **Order:** Task 1 (overview) is first so the MQTT topic convention is + established once and can be referenced consistently by subsystem docs. + README is last so every link it makes already exists.