Files
vigilar/docs/superpowers/plans/2026-04-05-project-documentation.md
adlee-was-taken 0e4e2c1ca7 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) <noreply@anthropic.com>
2026-04-05 09:29:57 -04:00

55 KiB
Raw Blame History

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.

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.

# 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

<ASCII diagram — draw this based on what you actually find in cmd_start.py.
It should show: supervisor → N subsystem processes → mosquitto broker →
flask web process. Example shape:>

                     ┌────────────────┐
                     │  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: `<convention you actually found>` — 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:<func>` — RTSP capture at 2 FPS.
2. `vigilar/camera/motion.py:<func>` — OpenCV MOG2 detects movement and
   publishes `<real topic>`.
3. `vigilar/camera/recorder.py:<func>` — flips to 30 FPS, flushes the ring
   buffer, begins writing a `.vge` file.
4. `vigilar/events/processor.py:<func>` — inserts a row into `<table>` and
   publishes `<real topic>`.
5. `vigilar/highlights/...` — scores the event.
6. `vigilar/alerts/...` — sends a VAPID web-push notification.
7. Web UI — updates the timeline (mechanism: <SSE / polling / whatever you
   find>).

## Storage layout

- `vigilar.db` (SQLite, WAL mode). Tables: <list from schema.py>.
- 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 <placeholder> 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:

ls <path>

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
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

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:

# 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: <quote the convention you found in Task 1, e.g.
  `vigilar/<subsystem>/<entity>/<event>`>.
- 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 <quote the convention you found in Task 1> with the actual topic convention observed.

  • Step 3: Verify
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
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 (314) all follow the same template. Task 3 is written in full. Tasks 414 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

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 13.

# camera

## Purpose

<One paragraph — what this subsystem owns. For the camera subsystem: RTSP
capture, adaptive FPS, motion detection, HLS segment production, recording
to encrypted .vge files, ring buffer for pre-motion context.>

## Key files

- `vigilar/camera/worker.py` — <role>
- `vigilar/camera/manager.py` — <role>
- `vigilar/camera/motion.py` — <role>
- `vigilar/camera/recorder.py` — <role>
- `vigilar/camera/ring_buffer.py` — <role>
- `vigilar/camera/hls.py` — <role>

## MQTT topics

**Subscribes:** <list, or "none found">
**Publishes:** <list, or "none found">

## Database tables

<Table name and what it holds, or "none">

## Depends on

- <sister subsystem> — <why / how>
- ... or "none"

## Consumed by

- <sister subsystem> — <why / how>
- ... or "none"

## Notes

<Only include if something is genuinely non-obvious. Examples for camera:
why MOG2 over background subtraction, why the ring buffer is 5 seconds,
why HLS is used for the grid and MJPEG for single view. Otherwise omit
this section entirely.>

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
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
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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.
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.
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

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 15002500 words.

# 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://<mini-pc-ip>: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 <repo URL> 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

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://<mini-pc-ip>: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 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://<mini-pc-ip>: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:

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:

[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:

[Unit]
Description=Run vigilar backup nightly

[Timer]
OnCalendar=daily
Persistent=true

[Install]
WantedBy=timers.target

Enable it:

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:

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.

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

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 for tuning.

Where to go next


**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
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 25004000 words. Be concrete — every key gets a real description, every command gets a real usage line.

# 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`)

<One subsection per [section] you found in Step 1. For each:
 - Bullet list of keys with their default values from the shipped TOML.
 - A short "when to change" sentence for each key.
>

### `[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`.

<Continue in the same shape for [zigbee2mqtt], [ups], [storage],
[remote], [alerts.local], [alerts.web_push], [alerts.email], and any
other section you found. If the shipped TOML has a section not listed
here, add it. If a section listed here is not in the TOML, remove it.>

## 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 <command>

<One subsection per top-level Click command you found in vigilar/cli/main.py. For each: the command name, a one-line purpose, and a usage example. Do not guess command names — only list what exists.>

vigilar start

<purpose — from cmd_start.py>

Usage:

sudo -u vigilar /opt/vigilar/venv/bin/vigilar start

Normally you run this via systemd (systemctl start vigilar) rather than directly.

vigilar config <subcommand>

<List every subcommand you found in cmd_config.py. Examples if they exist: validate, set-pin, set-password, show. Do not invent.>

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

sudo -u vigilar /opt/vigilar/venv/bin/vigilar config set-pin

PINs are stored as hashes. Comparisons are constant-time.

Admin password

sudo -u vigilar /opt/vigilar/venv/bin/vigilar config set-password

TLS

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:

[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:

[Unit]
Description=Run vigilar backup nightly

[Timer]
OnCalendar=daily
Persistent=true

[Install]
WantedBy=timers.target

Enable:

sudo systemctl daemon-reload
sudo systemctl enable --now vigilar-backup.timer

Restore procedure

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

cd /opt/vigilar
sudo -u vigilar git pull
sudo -u vigilar /opt/vigilar/venv/bin/pip install -e .
sudo systemctl restart vigilar

<If the project has a migrations system, document how to run it here. If not, note: "No automated database migrations at time of writing. Do not downgrade — schema changes are forward-only.">

Rolling back

sudo systemctl stop vigilar
# Check out the previous tag or commit
sudo -u vigilar git -C /opt/vigilar checkout <previous-rev>
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

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

<List any routes you found in Step 2. Example shape:>

  • 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 <camera name>.
  • Verify the RTSP URL with ffprobe <rtsp url>.
  • 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 <subcommand>` 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

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 116. Verify:

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
# 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.

<!-- Screenshot placeholder: replace when a real screenshot is available. -->
![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 <repo URL> vigilar
cd vigilar
sudo ./scripts/install.sh
sudo systemctl enable --now vigilar

Then open http://<mini-pc-ip>:49735 on your LAN.

For the full walkthrough (OS install, camera setup, PIN, push notifications, NAS backup), see the Home User Guide.

For configuration, CLI, secrets, backups, and upgrades, see the Operator Guide.

Documentation

  • User guides
  • Architecture
    • Overview — process model, MQTT bus, data flow, storage.
    • Conventions — coding rules for contributors.
    • 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
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 <target>. 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/<name>.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
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 15 surfaced any fixes:

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 314. 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 <angle bracket> 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.