## Implementation Plan: molouptime Uptime Monitoring Service ### Summary This plan builds a standalone uptime monitoring application (`molouptime`) integrated into the DevPlace ecosystem. It follows the proven architectural pattern of `TelegramService` (subprocess supervision with JSON-over-stdin/stdout IPC), reuses existing infrastructure (auth, notifications, issue creation, net_guard, CLI, DevII catalog), and provides a clear path to replace the Python prototype worker with a native Swift binary later. The initial implementation uses a Python worker for immediate testability and CI compatibility. ### Phase 1 – Project Scaffold & Worker Subprocess **1. Create directory structure** ``` devplacepy/services/molouptime/ __init__.py service.py → MolouptimeService (subclasses BaseService) worker.py → Python worker process (standalone executable) config.py → MolouptimeConfig (pydantic settings) db.py → Schema & CRUD for monitoring tables escalation.py → Escalation engine (time‑based) metrics.py → Metrics ring buffer & flush logic graphs.py → SVG generation utilities test_worker.py → Worker‑specific unit tests tests/test_molouptime_service.py → Integration tests ``` **2. Implement `MolouptimeService` (service.py)** - Inherit from `BaseService` (defined in `devplacepy/services/base.py`). - Declare `config_fields` for check registry, base interval, Telegram bot token (if different from global), API URL. - In `start()`, launch `asyncio.create_subprocess_exec(sys.executable, "-m", "devplacepy.services.molouptime.worker", ...)`. - Use JSON‑line protocol (same as Telegram: `{ "command": "check", "data": {...} }` → `{ "status": "ok", "result": {...} }`). - Auto‑restart on crash, graceful stop via stdin `{"command": "shutdown"}`. - `collect_metrics()` returns live admin UI status (checks active, metrics queue depth). **3. Implement `worker.py` (temporary Python replacement for eventual Swift)** - Accept `--config` JSON on startup (host, port, api_url, api_key). - Load checks from DevPlace API on startup and periodically refresh. - Main loop: read commands from stdin, dispatch: - `check_url`: use `aiohttp` to fetch, measure latency, validate status, SSRF‑guard via `net_guard.guard_public_url()`. - `check_dns`: use `dns.resolver` (add `dnspython` to `pyproject.toml` `[molouptime]` optional deps), support A/AAAA/MX/TXT/SOA. - `check_port`: use `asyncio.open_connection` with optional TLS upgrade, protocol banners. - `shutdown`: flush metrics, exit. - Store last N results in a bounded deque (configurable via `METRICS_RING`), flush to DevPlace API every `flush_interval` (default 60s) via POST `/monitoring/metrics/batch`. - Log latency, status code, DNS response time, TCP handshake time per check. ### Phase 2 – Database & API Routes **4. Add database tables** (in `devplacepy/database/schema.py` or a new migration) ```sql CREATE TABLE monitoring_checks ( uid TEXT PRIMARY KEY, user_uid TEXT NOT NULL REFERENCES users(uid), name TEXT NOT NULL, group_uid TEXT REFERENCES monitoring_groups(uid), enabled INTEGER DEFAULT 1, interval_seconds INTEGER DEFAULT 300, created_at TEXT NOT NULL, updated_at TEXT NOT NULL ); CREATE TABLE monitoring_check_items ( uid TEXT PRIMARY KEY, check_uid TEXT NOT NULL REFERENCES monitoring_checks(uid), item_type TEXT NOT NULL CHECK (item_type IN ('url', 'dns', 'port')), target TEXT NOT NULL, expected_status INTEGER, -- for HTTP expected_protocol TEXT, -- for port (e.g., 'SMTP', 'IMAPS') expected_dns_type TEXT, -- 'A', 'AAAA', etc. timeout_seconds INTEGER DEFAULT 10, notification_types TEXT DEFAULT '["devplace"]', -- JSON array escalation_policy_uid TEXT REFERENCES monitoring_escalation_policies(uid) ); CREATE TABLE monitoring_escalation_policies ( uid TEXT PRIMARY KEY, name TEXT NOT NULL, steps TEXT NOT NULL -- JSON array of {time_seconds, action: "notification"/"devii"/"telegram"/"issue"} ); ``` **5. Add API routes** under `/monitoring/` in `devplacepy/routers/`: - `GET/POST /monitoring/checks` – list/create checks (filter by `user_uid` for members, all for admins). - `GET/PUT/DELETE /monitoring/checks/{uid}` – read, update, delete (owner or admin). - `POST /monitoring/checks/{uid}/items` – add a check item. - `GET /monitoring/checks/{uid}/metrics?range=24h` – aggregated metrics for graphing. - `GET /monitoring/stats` – admin‑only cross‑user statistics. - `POST /monitoring/metrics/batch` – internal endpoint for worker flush. ### Phase 3 – Integrations **6. CLI** – add `devplacepy/cli/molouptime.py`: - `register_molouptime(sub)` with subcommands: `list`, `add`, `remove`, `pause`, `resume`, `logs`, `stats`. - Follow exactly the `set_defaults(func=...)` pattern from existing CLI modules. **7. DevII Action Catalog** – add `devplacepy/services/devii/actions/catalog/molouptime.py`: - Actions: `molouptime_list_checks`, `molouptime_add_check`, `molouptime_get_metrics`, etc. - Register in `catalog/__init__.py` tuple. **8. Notification Funnel** – in `MolouptimeService`: - On DOWN state change, call `create_notification(user_uid, "molouptime_down", ...)`. - Escalation engine (`escalation.py`) runs as asyncio task: for each DOWN item, track time since transition; when threshold reached, take next action (e.g., `create_issue(...)`). **9. SVG Graphing** – in `routers/monitoring.py`: - Accept `range` parameter, fetch metrics from DB (metrics stored in `monitoring_metrics` table with `timestamp`, `check_uid`, `item_type`, `latency_ms`, `status`). - Render SVG directly using string templates (or `cairosvg` if more complex). Serve at `/monitoring/checks/{uid}/graph.svg`. ### Phase 4 – Tests & CI **10. Unit Tests** (`tests/test_molouptime_service.py`): - Mock subprocess with a simple script that echoes JSON responses. - Test `check_url`, `check_dns`, `check_port` IPC round‑trips. - Test escalation policy evaluation (mock `asyncio.sleep`). - Test metrics flush endpoint integration. - Test CLI commands via `devplace molouptime ...` invocation. - Test DevII action registration. **11. Add optional dependency** in `pyproject.toml`: ```toml [project.optional-dependencies] molouptime = ["dnspython", "aiohttp"] ``` **12. Update `docker-compose.yml` and `Dockerfile`** if needed for deployment, but initially the subprocess model avoids a separate container. ### Definition of Done - All new and existing unit tests pass when run with: ``` pip install -e '.[dev,molouptime]' -q && make test-unit ``` - The lint check passes with zero errors: ``` pip install -q ruff && ruff check . ``` - The following functional assertions hold: - `devplace molouptime --help` prints usage text. - Starting `MolouptimeService` spawns a worker subprocess; sending a `check` command via stdin returns a result on stdout. - A URL check against `https://example.com` returns `200` and a latency value; against `http://127.0.0.1` returns an SSRF error. - A DNS check for `example.com` returns resolved addresses; for `nonexistent.example.com` returns an error. - A port check against `smtp.gmail.com:587` returns `OPEN` with STARTTLS banner (if network available; else mocked). - After a check failure, the escalation engine sends a notification via `create_notification` after the configured threshold. - The `/monitoring/checks` API returns the created check, respects user ownership, and admins see all checks. - Metrics are aggregated for `?range=24h` and returned as a JSON array of `{timestamp, value}`. - The SVG graph endpoint returns a valid SVG string that renders in a browser. - DevII can parse `molouptime_list_checks` action and execute it.