Files
inspector/CHANGELOG.md
T

42 KiB
Executable File
Raw Permalink Blame History

Changelog

All notable changes to Inspector are documented here.

Format follows Keep a Changelog. Version numbers follow Semantic Versioning.


[0.9.5] — 2026-10-28

Fixed

  • A stack redeploy no longer freezes Inspector. After a successful Portainer redeploy, Inspector used to stop everything for post_redeploy_wait_seconds (default 90 s): no other container was checked, dashboard times stood still, and stopping Inspector during the wait wasn't clean. Now the redeployed app is left to settle while every other container keeps being checked, and its one-time HTTP re-check (with the "recheck failed" alert) happens on its first check after the wait.
  • The redeployed app's cooldown now starts after the settle time. Before, the wait ate into it (with the defaults, 300 s cooldown became 210 s).

[0.9.4] — 2026-10-28

Fixed

  • Containers without a Docker health check now "recover" properly. Many images don't define a health check, so Docker reports them as NO HEALTH CHECK even when they work. Inspector only treated HEALTHY as back to normal, so for these containers:

    • the error count never reset, and one-off failures days apart added up to a restart;
    • no "recovered" alert was sent;
    • with Restart, then redeploy, the next unrelated problem skipped the restart and redeployed the whole stack.

    Now NO HEALTH CHECK counts as working (unless you made it a restart trigger): the count resets, the "recovered" alert is sent ("back to running (no health check)"), and the next problem starts with a plain restart. Containers with a health check behave exactly as before.


[0.9.3] — 2026-10-28

Changed

  • Every time follows the TZ of the Inspector stack. Before, the dashboard, Events page and alerts were always in US Pacific time while the maintenance window and logs used TZ, so anyone outside Pacific saw times hours off (e.g. a 02:00 Berlin window shown as 5:00 PM). Now the dashboard, the Events page (including where each day starts), alert times, cooldown times and logs all use TZ.
  • Alert times include the zone abbreviation (10-28 09:27 AM CET), and "Cooldown until" in alerts, event messages and logs is a local time instead of a UTC timestamp.
  • The active time zone is shown at the bottom of every Settings page, on the Events page and on the Maintenance page.
  • If you never set TZ, times are now shown in UTC instead of Pacific. Set TZ in the stack (e.g. America/New_York). An unrecognized name logs a warning and falls back to UTC.

Docs

  • INSTALL: TZ is now marked as needed, with a "Why the time zone matters" section (maintenance window, alerts, dashboard, logs), how to find your zone name, and a troubleshooting row. Guide: a Time zone section and a first-login check. Compose and README updated.

[0.9.2] — 2026-10-28

Fixes from a code and security review. None of them change how containers are monitored or recovered.

Fixed

  • A bad value saved from Settings can no longer stop Inspector from starting. The forms now reject invalid times (maintenance start/end), unknown log levels and out-of-range numbers (for example interval_seconds: 0, which made the loop spin). If config.yml already has such a value, Inspector starts with the default and logs a warning instead of crashing in a restart loop.
  • The dashboard password can no longer be cleared. Saving an empty password (or username) is refused, and an empty password never signs anyone in. Before, clearing it left the running dashboard open to anyone.
  • Passwords with non-ASCII characters (é, ü, £ …) now work. Before, every login attempt with one crashed.
  • The Events page no longer errors for a moment after a container is removed.
  • The HTTP check hint no longer suggests 302 as an expected status; redirects are followed, so the final page's code is what counts.

Security

  • Sign-in throttling: after 5 failed sign-ins (form or Basic Auth) from one address within 5 minutes, that address is refused for the rest of the window.
  • Changing the dashboard username or password signs out existing sessions. Expired sessions are cleaned up.
  • Request bodies are limited to 1 MB; an invalid Content-Length gets a 400.
  • The Gotify token is sent in the X-Gotify-Key header instead of the URL, so it no longer appears in Gotify or reverse-proxy access logs.
  • Portainer token environment variables must be named PORTAINER_… (a name already in your config.yml keeps working), so Test connection can't be used to send other container secrets to an arbitrary URL.
  • Single quotes are escaped in HTML, and the delete confirmations build their text safely.
  • Two settings saves at the same moment can no longer overwrite each other.
  • requests updated to 2.32.5 (CVE-2024-47081).

[0.9.1] — 2026-10-28

Fixed

  • A dependency on a disabled container no longer blocks recovery forever. If depends_on named a container that is disabled in Inspector, the dependent app was never restarted. Inspector now only waits for dependencies it is watching, and the container page warns when the selected dependency is disabled.
  • PORTAINER_API_TOKEN now reaches the container. The public compose.yml didn't pass it in, so setting it as a Portainer stack variable had no effect. Existing stacks need the line - PORTAINER_API_TOKEN=${PORTAINER_API_TOKEN:-} added under environment: (Stacks → inspector → Editor).
  • The "depends_on … is disabled or not found" warning was logged on every check (since 0.8.0). It is now logged once, and again only when the container settings change.

Changed

  • Guide: new sections 6. Apps behind a VPN container (why depends_on matters, how to spot a VPN-routed app, setup, what Inspector does) and 7. Portainer stack redeploy (why it's needed, what a redeploy does to the whole stack, requirements, where to find every value, adding the token, the other settings, Test connection errors).
  • Clearer form hints: vpn_routed only affects logging; depends_on works for any dependency; where to find the endpoint ID.

[0.9.0] — 2026-10-28

Added

  • Every container option is now in the dashboard. The container page has three new groups:
    • Reachability: HTTP check (URL, timeout, expected status) and Uptime Kuma monitor.
    • Dependencies: depends_on (pick from your containers) and vpn_routed.
    • Recovery: recovery_action with plain-language descriptions, plus the Portainer environment and stack name for stack redeploys.
  • Integrations settings. New Uptime Kuma and Portainer pages under Settings. Portainer environments can be added, edited, renamed (containers that use them are updated) and deleted (refused while a container uses one). The API token can be an environment variable name (recommended) or a masked value saved in config.yml.
  • Test connection buttons for Uptime Kuma (lists monitors) and each Portainer environment (lists stacks on the endpoint), using the values in the form even before saving.
  • The All containers grid shows each container's extras (HTTP check, Uptime Kuma, dependency, recovery action).
  • Guide: new sections on reachability checks and apps behind a VPN container.

Changed

  • Uptime Kuma and Portainer changes take effect on the next check; Inspector rebuilds those clients when their settings change instead of needing a restart.
  • Saving a container form unchanged now leaves config.yml byte-identical. Blank optional fields are removed instead of written as empty values (color: '', maintenance_window: false), and an unchanged restart_on list keeps its order and the comments after it. An explicit recovery_action: restart_container is kept.

[0.8.0] — 2026-10-27

Added

  • Install from Portainer with no command line. Mount one folder per purpose (/app/config, /app/logs, /app/data) and deploy. If there's no config yet, Inspector writes a starter config.yml (dashboard on, event history on, no containers). Dashboard login comes from INSPECTOR_USERNAME / INSPECTOR_PASSWORD; without a password, one is generated and printed once in the container logs.
  • Docker socket access is automatic. A new entrypoint starts as root only long enough to read the socket's group and fix ownership of the mounted folders, then runs Inspector as PUID:PGID (default 1000:1000) with that group added. DOCKER_GID / group_add are no longer needed.
  • In-app guide at /guide/ with screenshots, linked from a new Help tab in the header. Also in the repo at src/inspector/assets/guide/guide.html.
  • maintenance_window switch on the container form, so containers can opt in to the daily maintenance window without editing YAML.

Fixed

  • Containers added from the UI are now watched on the next check. Before, the loop only logged that a restart was needed, despite what the UI said. Containers removed or disabled from the UI are dropped the same way.
  • Status page could fail to render while a container was being added or removed (it iterated the live state dict and assumed every entry was still in config).

Changed

  • Config path: --config, then $INSPECTOR_CONFIG, then /app/config.yml if present (existing single-file setups keep working unchanged), then /app/config/config.yml.
  • Public compose.yml, INSTALL.md, and README.md rewritten for Portainer stacks; README gains the logo and screenshots.

Upgrade notes

  • Existing deployments need no changes. To adopt the simpler layout, move config.yml into the new config folder and switch to the new compose file.

[0.7.4] — 2026-10-27

Changed

  • Settings rail container list is compact. Per-container entries are indented under "All containers" and fit on one line: colored dot, name, then enabled / disabled in small muted text. The max-error stat is gone. Long names truncate with an ellipsis, and the status stays visible.

[0.7.3] — 2026-10-27

Changed

  • Containers moved into Settings. The top-nav Containers tab is gone; the settings rail now has an "All containers" entry (the tile grid at /containers) plus one entry per container (/containers/<name>). Both views render inside the same rail layout, and the Settings tab stays highlighted while you browse them.
  • Per-channel priority reference. The shared priority block is replaced by one reference per platform: Gotify (0 silent · 1–3 low · 4–7 normal · 8–10 high) and Pushover (-2 Lowest · -1 Low · 0 Normal · 1 High · 2 Emergency), with the Inspector → Pushover mapping noted under the Pushover one.
  • restart_on redesign. The pill row is replaced by a grid of toggleable cards, each with a color accent and a plain-language description of the status, plus "Recommended" (UNHEALTHY + NOT RUNNING) and "Observe only" quick buttons.

Fixed

  • Unchecking every restart_on status now saves restart_on: [] (observe only). Previously an all-unchecked submit silently kept the old list.

[0.7.2] — 2026-10-27

Added

  • Container CRUD from the UI. /containers/new adds a new container to config.yml; a delete button on /containers/<name> removes one. The config change is picked up on the next polling cycle without a restart.
  • Test-notification buttons on the Gotify and Pushover sub-sections. The button posts the current (unsaved) form values to POST /api/test-notify/<channel> and shows the result inline. Unlike the regular notifier it does not swallow errors — HTTP failures are reported back to the UI.
  • Priority reference block on the Notifications section, including the Inspector → Pushover -2..2 mapping.
  • Config diagnostics strip at the bottom of every settings page: shows the live config.yml path, whether it's writable, and the Inspector version. If the bind mount is read-only (a common deployment misconfiguration — a :ro mount means saves fail silently) a red READ-ONLY badge appears with a hint.
  • Containers listed in the settings left rail under their own group, plus a "+ Add container" link, so you can jump between sections and containers from the same navigation.
  • Color input upgrade on container edit / new forms: ten preset color swatches, a native color picker, and the existing hex text field, all bound together.

Changed

  • Toggle switches replace the stock checkbox + "requires restart" layout for all enabled-style fields (notifications, dashboard, database, health summary, maintenance, notification mute, container enabled, Inspector dry_run / heartbeat). Reads much cleaner.
  • Status page no longer prints "auto-refreshes every 30s" (the <meta refresh> still ticks every 30s).

Internal

  • _apply_container_create() + _apply_container_delete() helpers alongside the existing _apply_settings_update(); container names are validated against ^[a-zA-Z0-9][a-zA-Z0-9_-]{0,62}$ and rejected if duplicate.
  • _render_diag_strip() uses os.access(path, os.W_OK) to detect read-only mounts.
  • Test-notification bypasses GotifyNotifier.notify() / PushoverNotifier.notify() so HTTP failures are surfaced (the real notifier intentionally swallows them to avoid breaking the inspector loop).

[0.7.1] — 2026-10-27

Changed

  • Settings page redesigned to a classic SaaS layout (left rail + content pane):
    • Left rail groups sections under Core / Data / Scheduling with a live subtitle under each item (e.g. "60s · dry-run on", "gotify on · pushover off", "on · :8080") so you can read state without clicking in.
    • Each section is URL-addressable: /settings/inspector, /settings/notifications, /settings/dashboard, /settings/database, /settings/health_summary, /settings/maintenance, /settings/notification_mute.
    • Gotify and Pushover now share a single Notifications section with one Save button per channel, so the fan-out model is visible at a glance.
    • Save flash is a prominent green banner with a success check.
  • Containers moved to a dedicated top-level route (/containers): a grid of tiles showing status, enabled, max errors, cooldown, and restart_on at a glance. Click a tile for the per-container edit form at /containers/<name>. Keeps the Settings page small when buddies have many containers.
  • Top navigation adds a Containers tab alongside Status / Events / Settings.

Internal

  • POST /settings/gotify and POST /settings/pushover now redirect back to /settings/notifications (the grouped page).
  • POST /containers/<name> added as a parallel entry point to the existing settings backend for container edits.

[0.7.0] — 2026-10-26

Added

  • Public packaging under euronvault/inspector on Gitea with a dedicated image at gitea.euronvault.com/euronvault/inspector:0.7.0 (plus :latest and :sha-<short> tags). Dev continues on eugene/inspector.
  • scripts/sync_public.sh with an explicit allowlist (scripts/public_allowlist.txt) and grep-based denylist (scripts/public_denylist_patterns.txt) that refuses to publish any file matching a personal-string pattern (lab IPs, lab container names, lab env var names, agent-workflow scaffolding paths).
  • Sanitized public/ staging tree: generic README, INSTALL guide, MIT LICENSE (Copyright (c) 2026 Europium 63), config.example.yml with two neutral example containers, and a compose.yml that pulls from gitea.euronvault.com/euronvault/inspector.
  • Web settings page at /settings for editing config.yml without SSH: per-section forms for Inspector loop, Gotify, Pushover, Dashboard, Database, Health summary, Maintenance, Notification mute, and per-container tuning (thresholds, cooldowns, restart_on, color).
  • Saves round-trip through ruamel.yaml so comments and key order in config.yml are preserved. Each save keeps a .bak of the prior file. (In the Docker container .bak lives in the overlay fs because config.yml is a single-file bind mount; the live file is still updated cleanly.)
  • In-process hot reload via a new ConfigRef: edits to container-level settings and inspector.dry_run/heartbeat/interval_seconds take effect on the next loop iteration without a container restart. Fields requiring restart are explicitly labeled in the UI.
  • CSRF token on all cookie-authenticated POSTs (Settings forms, maintenance toggle, mute toggle). Basic Auth clients bypass the check because they prove identity on every request.
  • Secret fields (Gotify token, Pushover token/user_key, dashboard password) render masked and treat the __UNCHANGED__ sentinel as "keep existing"; real secret values are never echoed into HTML.

Changed

  • main.run_loop() now takes a ConfigRef, re-reads config each iteration, and rebuilds enabled_configs/check_order so edits apply without a restart.
  • DashboardServer constructor simplified: now takes ConfigRef, states, maintenance, notification_mute (per-section configs are resolved live via ConfigRef.get()).
  • Added ruamel.yaml==0.18.6 to requirements.txt.

[0.6.0] — 2026-10-26

Added

  • Pushover notifications as a second independent channel alongside Gotify. Enable either, both, or neither under notifications.pushover; alerts fan out to every enabled channel. Supports optional device and sound overrides.
  • Priority mapping from Inspector's Gotify-style integer scale to Pushover's -2..2 range: <=2 → -1, 3..4 → 0, 5..6 → 1, >=7 → 2. Emergency tier (2) ships default retry/expire values and triggers the usual Pushover acknowledgement prompt.
  • MultiNotifier internally fans each notify() call out to every enabled channel; a crash in one channel never prevents the others from running.

[0.5.5] — 2026-06-18

Changed

  • Timestamp display format shortened: year and timezone abbreviation (PST/PDT) removed. Timestamps now render as MM-DD HH:MM AM/PM (e.g. 06-18 09:30 AM).

[0.5.4] — 2026-06-17

Added

  • Dashboard cards now surface scheduled maintenance for containers opted into the recurring daily window (maintenance_window: true):
    • while idle, a meta-row 🔧 Next maintenance: in <countdown> (<start> – <end>) shows when the next window begins, with the window times in the dashboard's local format;
    • while the window is active, the phase row shows 🔧 In maintenance — ends in <countdown> so it's clear when normal monitoring resumes.
  • All countdowns reuse the existing 1-second client-side ticker (no page reload needed).
  • /api/status now returns maintenance_scheduled (bool) and maintenance_window_start / maintenance_window_end (ISO UTC or null) per container.

[0.5.3] — 2026-06-15

Added

  • Dashboard status cards now show a phase row below the error bar, indicating what Inspector is currently doing for each container:
    • ⏱ Cooldown — Xs remaining — post-restart cooldown is active (countdown to expiry)
    • ⏱ Grace period — Xs remaining — container is booting post-restart; failures not counted
    • ⚠ Restart limit — resets in Xs — hourly restart cap hit; countdown to when it clears
    • ⚠ Failing X/Y — next check in Zs — error count is accumulating toward the restart threshold
    • 👁 Observe only — restart_on: [] is set; Inspector tracks status but never acts
    • 🔧 In maintenance — container is in a maintenance window
    • Next check in Zs — container is healthy; soft reminder of the monitoring cadence
  • All countdown timers update every second in the browser without a page reload (JS setInterval); the existing 30 s auto-refresh keeps data fresh.
  • /api/status now returns phase, phase_until (ISO UTC or null), last_check_at (ISO UTC or null), and interval_seconds per container entry.

[0.5.2] — 2026-06-15

Added

  • Portainer environments now support api_token: "ptr_..." to embed the API token directly in config.yml, as an alternative to api_token_env (env var lookup). api_token takes precedence when both are set. Useful when injecting env vars into the Inspector container is inconvenient.
  • Improved token-not-found error message: now mentions both api_token and api_token_env options so the fix is self-evident in the logs.
  • _find_stack() now logs a debug line with how many stacks were scanned and which endpoint_id was used, aiding multi-environment Portainer diagnosis.

[0.5.1] — 2026-06-15

Fixed

  • health_summary digest crash: KeyError: 'portainer_redeploy_failed' on the first periodic digest when event history contained Portainer redeploy events. health_summary._LABELS was missing the three Portainer event types (portainer_redeploy, portainer_redeploy_failed, portainer_redeploy_skipped) that were added to SUMMARY_EVENT_TYPES in v0.4.0.

[0.5.0] — 2026-06-16

Added

  • New per-container recovery_action: restart_then_redeploy — tiered recovery for VPN-routed containers. Inspector first tries a plain docker restart (which fixes the common case where the VPN sidecar was merely restarted, keeping the same container id) and only escalates to a Portainer stack redeploy when the restart errors (e.g. the stale-namespace "No such container" case after the sidecar is recreated) or the container is still failing on the next recovery cycle. Escalation is non-blocking — it happens on the following cycle, not via an inline wait. Requires portainer_stack_name like portainer_stack_redeploy, and honors the same depends_on gating.

[0.4.2] — 2026-06-15

Added

  • Result notifications for recovery actions: a successful docker restart / start now sends a ✅ … completed notification, and a successful Portainer stack redeploy sends ✅ … redeployed. A failed post-redeploy HTTP recheck sends ⚠️ … recheck failed.
  • "Testing the Portainer API" guide in the README (uses python3 + requests, since the container image has no curl).

Changed

  • Portainer API errors now include the HTTP status code and response body in the failure message/log (e.g. HTTP 401: {"message":"Invalid JWT token"}), making misconfigured tokens/endpoints far easier to diagnose. Added intermediate debug logging for stack lookup and a warning when a stack name isn't found.

[0.4.1] — 2026-06-15

Fixed

  • Per-container restart_on: [] (empty list) now correctly disables all restart triggers for that container. Previously, an empty list was treated as "not set" and silently fell back to the default [UNHEALTHY, NOT RUNNING], so a container couldn't be configured as observe-only (e.g. for depends_on status tracking without Inspector restarting it itself). Omitting restart_on entirely still defaults to [UNHEALTHY, NOT RUNNING].

[0.4.0] — 2026-06-15

Added

  • New per-container recovery_action: restart_container (default, unchanged behavior), start_container (calls docker start instead of docker restart), and portainer_stack_redeploy.
  • portainer_stack_redeploy recovers containers whose network_mode: container:<vpn> namespace goes stale after the VPN sidecar (e.g. Gluetun) is recreated — the "joining network namespace of container: No such container" failure a plain restart can't fix. It redeploys the owning Portainer stack via the Portainer API. New integrations.portainer config: named environments (token loaded from api_token_env, never logged or stored), default_environment, stack_redeploy_cooldown_seconds, post_redeploy_wait_seconds. New per-container portainer_environment / portainer_stack_name.
  • New per-container depends_on / vpn_routed: containers are checked (and recovered) in dependency order each cycle, so a VPN sidecar is recovered before its dependents. Recovery is deferred — without consuming the container's cooldown — while its depends_on container isn't healthy/starting; vpn_routed: true logs why.
  • Dry-run extended to cover the new actions, including the Portainer redeploy flow (DRY RUN: would resolve/redeploy/wait Portainer ...).
  • New event types: portainer_redeploy, portainer_redeploy_failed, portainer_redeploy_skipped.
  • See Portainer stack redeploy recovery in the README.
  • New per-container notification mute: notification_mute config (enabled, dir, default_hours, default 12h auto-expiring), dashboard "Mute notifications"/"Unmute notifications" button + "Muted" badge, POST /notifications/<name>/mute|unmute. Muting silences outbound Gotify notifications only — monitoring, failure counting, restarts/recovery, cooldowns, event history, and the health-summary digest continue unaffected. New event types notification_mute_enter/exit. See Muting notifications in the README.

All new fields are optional and backward-compatible; existing configs are unaffected. recovery_action: recreate_container is not implemented and is rejected at config load.


[0.3.1] — 2026-06-14

Changed

  • http_check now classifies outcomes into three explicit tiers: the configured expected_status = up, a connection refused / timeout / DNS failure = unreachable (reported as the synthetic status code 0), and any other code = "responding but unexpected". Connection-level failures were already treated as failed checks (→ UNREACHABLE); this makes the distinction explicit in the result (HttpCheckResult.status_code) and in the log / notification messages. Matching remains exact — the README now documents how to find a container's actual status code.

[0.3.0] — 2026-06-14

Added

  • Opt-in "unreachable" detection per container: even when Docker reports a container as healthy/running, Inspector can independently verify it's actually reachable via an HTTP/HTML health check (http_check) and/or a shared Uptime Kuma status page (integrations.uptime_kuma + per-container uptime_kuma.monitor_name). A failing check on an otherwise-healthy container surfaces as a new UNREACHABLE status (red badge on the dashboard); opt it into a container's restart_on to have it count toward failures/restarts like any other status. Both checks default to disabled and never crash Inspector.
  • Events page (/events) redesigned as a daily view: shows one calendar day (12:00 AM-11:59 PM US Pacific) at a time, defaulting to today, with "Prev day" / "Next day" navigation (?date=YYYY-MM-DD, also supported by /api/events). The timeline now always renders all 24 hourly markers (12 AM..11 PM) so you can scroll directly to a time of day, plus a trailing "11 PM (yesterday)" section for the 1-hour lookback overlap from the previous day.

Changed

  • All dashboard and Gotify notification timestamps now render in US Pacific time (PDT/PST, DST-aware) instead of UTC, e.g. 2026-06-14 03:45 PM PDT. JSON API endpoints are unaffected and continue to return raw UTC ISO 8601 timestamps.

[0.2.6] — 2026-06-14

Changed

  • Gotify notifications reformatted as Markdown (extras.client::display.contentType: text/markdown) with richer context per message: error counts and thresholds, previous/current status, Docker error details for failed restarts, restart/cooldown info, and a formatted timestamp. The health-history digest is now a Markdown bulleted list with a header.
  • Dashboard timestamps (container "Last restart" / "Cooldown until", and the events timeline) now render as 12-hour time with AM/PM (e.g. 2026-06-14 03:45 PM UTC) instead of raw ISO 8601.

Fixed

  • "Last restart" no longer reverts to "Never" after Inspector itself restarts: on startup, with database.enabled: true, each container's most recent recorded restart (and any still-active cooldown) is restored from the event history database.

[0.2.5] — 2026-06-14

Added

  • Per-container identity colors on the dashboard: each container card gets a soft background tint and colored name from a 16-color auto-assigned palette, and the same color carries over to that container's chip on the events timeline. Override the auto-assigned color with an optional color: "#rrggbb" field per container in config.yml.

[0.2.4] — 2026-06-14

Fixed

  • Sign out: removed the redirect-if-authenticated on GET /login so sign out always reaches the login form, even with a cached browser Basic Auth session.
  • Mobile header: version/refresh text now hides on narrow screens, and sign-out renders as a small outlined button beside the brand.
  • Status page error bar: shows 100% green when a container has zero errors (previously rendered as an invisible 0-width bar) and a proportional red fill once errors are detected.
  • Container cards: hide the cooldown row when no cooldown is active, and show "Never" instead of "—" when a container has no restart history.
  • Events page: the "event history not enabled" message now includes the database: enabled: true config snippet needed to turn it on.

[0.2.3] — 2026-06-14

Changed

  • Replaced the browser-native Basic Auth prompt with a styled dark-theme login page backed by a session cookie (24h expiry); HTTP Basic Auth still works for API clients. Added a "Sign out" link, and made the dashboard header, stats grid, and card grid responsive on mobile.

[0.2.2] — 2026-06-14

Added

  • Dashboard pages now set a full favicon/app-icon set (favicon.ico, favicon.svg, favicon-96x96.png, apple-touch-icon.png, web app manifest icons, site.webmanifest) generated from the Inspector icon. Served from new GET /static/<file> endpoints alongside the existing icon route (Basic Auth required, same as every other dashboard route).

[0.2.1] — 2026-06-14

Changed

  • Web dashboard redesigned: dark "card grid" theme. Each container gets its own card (status badge, error-count progress bar, cooldown/last-restart details, maintenance badge and toggle button) instead of a plain table; the events page renders a colour-coded vertical timeline instead of a table. The header now shows the Inspector icon plus an "Inspector" / "Monitor. Restart. Report." wordmark set in Google Sans Flex (loaded from Google Fonts; falls back to the system sans-serif if unavailable). New GET /static/icon.png endpoint (Basic Auth required, same as every other dashboard route) serves the icon from a new src/inspector/assets/ package directory.

[0.2.0] — 2026-06-14

Added

  • Optional embedded web dashboard (dashboard.enabled, default false), protected by HTTP Basic Auth (dashboard.username / dashboard.password). If enabled with an empty password, Inspector logs an error and refuses to start the dashboard (fails closed). Runs in the same process as the monitoring loop — no extra container, zero new dependencies (stdlib http.server).
    • Status page (/, JSON at /api/status) — live per-container table: current status, error count vs. threshold, cooldown expiry, last restart time, and whether it's in a maintenance window. Auto-refreshes every 30 seconds.
    • Events page (/events, JSON at /api/events) — last 100 rows from event history, if database.enabled: true.
    • Maintenance toggle — when maintenance.enabled: true, per-container "Enter/Exit maintenance" buttons POST /maintenance/<name>/enter|exit, touching/removing the same marker files described under "Ad-hoc marker files". Returns 409 if maintenance windows are disabled, 404 for an unknown container. New module src/inspector/dashboard.py, new config section dashboard: in config.example.yml.
  • MaintenanceTracker.peek(name) — read-only maintenance check (no transition logging, no eventstore writes) used by the dashboard.
  • tests/test_dashboard.py — unit tests covering the disabled no-op, the empty-password refusal, Basic Auth enforcement, the status page and API, the maintenance toggle (enter/exit, disabled-config 409, unknown-container 404), and the events page/API with and without event history enabled.

[0.1.11] — 2026-06-14

Added

  • Optional maintenance windows (maintenance.enabled, default false). Two complementary mechanisms, both gated by this one flag:

    • Recurring daily window — containers with maintenance_window: true are "in maintenance" every day between maintenance.start and maintenance.end (default 00:00–06:00, local time via TZ, may wrap past midnight).
    • Ad-hoc marker files — touch/rm <maintenance.dir>/<container> (default /app/data/maintenance/<container>) for unplanned maintenance. A marker older than maintenance.max_hours (default 8) auto-expires.

    While either mechanism is active for a container, Inspector is fully silent for it: no failure counting, restarts, notifications, status-change logging, or event recording. Only one-time "entering/exiting maintenance window" log lines and new maintenance_enter/maintenance_exit events (excluded from the health-history digest) mark the transitions. State (error counts, cooldowns, restart history) carries over unchanged across maintenance. New module src/inspector/maintenance.py, new config section maintenance: in config.example.yml, new per-container maintenance_window field.

  • tests/test_maintenance.py — unit tests covering the disabled no-op, marker-file activation and auto-expiry, the recurring window (including midnight wraparound), enter/exit transition logging and event recording, and safe handling of marker-check errors.


[0.1.10] — 2026-06-14

Added

  • Optional periodic health-history digest (health_summary.enabled, default false). When enabled, Inspector logs a per-container summary of notable activity (failures, restarts, restart-limit hits, recoveries, not-found events — status changes are excluded) since the last digest, every health_summary.interval_hours (default 24). The first digest fires immediately on startup, covering the last interval_hours. The digest is always logged; it is pushed to Gotify (at health_summary.priority, default 3) only if any container had activity in the window. Depends on database.enabled: true for real data — if health_summary.enabled is true but event history isn't enabled, Inspector logs a one-time startup warning and digests report "no issues" for every container. New module src/inspector/health_summary.py, new config section health_summary: in config.example.yml.
  • EventStore.summarize(since) — per-container counts of notable event types at/after a timestamp, and EventStore.enabled property.
  • tests/test_health_summary.py — unit tests covering the disabled no-op, the database-disabled warning, immediate first report with a capped window, interval gating, message formatting, and Gotify activity gating.

[0.1.9] — 2026-06-13

Added

  • Optional SQLite event history (database.enabled, default false). When enabled, Inspector persists notable per-container events (status changes, recoveries, failures, restarts, restart-limit hits, container-not-found) to a local SQLite database at database.path (default /app/data/events.db), surviving container restarts. Old events are pruned according to database.retention_days (default 30) on startup and once per 24h thereafter. The database is opened in WAL mode for future concurrent read access (e.g. a read-only dashboard). A database error is logged but never crashes Inspector. New module src/inspector/eventstore.py, new config section database: in config.example.yml, new ./data volume mount in compose.yml / compose.prod.yml, new /app/data directory in Dockerfile.
  • tests/test_eventstore.py — unit tests covering the disabled no-op, row insertion, WAL mode, and retention pruning.

Changed

  • _check_one() signature gains an eventstore: EventStore parameter (always passed; no-ops internally when database.enabled is false), mirroring the existing notifier: GotifyNotifier pattern.
  • README "What Inspector does not do" updated: live ContainerState is still in-memory and resets on restart, but event history can now persist via the new opt-in SQLite database.

[0.1.8] — 2026-06-13

Changed

  • Log format overhauled for readability: container names are now left-padded into a fixed-width column, the redundant logger-name field (inspector:) was dropped from both the file and console formatters, and a blank line now brackets each failure → restart → recovery "incident" so it stands out as a block. Message wording adjusted accordingly (e.g. gotify Restarting due to 3 consecutive UNHEALTHY checks instead of Restarting gotify due to 3 consecutive UNHEALTHY checks).

[0.1.7] — 2026-06-13

Fixed

  • Docker socket permission errors at startup no longer crash with a raw Python traceback. run_loop() now catches the connection failure and logs an actionable message pointing at DOCKER_GID (stat -c '%g' /var/run/docker.sock).

[0.1.6] — 2026-06-13

Added

  • tests/test_config.py — 4 unit tests covering config loading, defaults, and validation (non-mapping containers rejected, unknown restart_on statuses rejected).
  • tests/test_health.py — 8 unit tests covering all Docker status classifications (HEALTHY, UNHEALTHY, STARTING, NO HEALTH CHECK, NOT RUNNING, UNKNOWN, missing state, ERROR constant).
  • tests/test_state.py — 10 unit tests covering failure counting, restart decision logic, and the full per-container state machine (dry-run, grace period, cooldown, hourly restart limit, recovery).

Changed

  • CI workflow now runs the full unit test suite (python3 -m unittest discover -s tests -v) before building the image.

[0.1.5] — 2026-06-13

Changed

  • CI and release workflows reverted to runs-on: ubuntu-latest (the self-hosted runner approach introduced in v0.1.3 was abandoned — the Gitea-hosted runner handles builds reliably).
  • Gitea runner setup guide (docs/gitea_runner_setup.md, ops/gitea-runner/) updated with label-mismatch troubleshooting and clearer registration instructions.

Added

  • README documents the verified release baseline (v0.1.5) and the step-by-step release flow.

[0.1.4] — 2026-06-13

Added

  • Inspector logo and wordmark assets: logo/inspector_icon.{png,svg}, logo/inspector_wordmark.{png,svg}, logo/inspector_icon_chip.{png,svg}.
  • README header now displays the Inspector wordmark image.

Changed

  • Previous logo files moved to logo/archive/.
  • .gitignore — added act_data/ (local act runner data directory).

[0.1.3] — 2026-06-13

Changed

  • CI and release workflows switched to runs-on: self-hosted.
  • CI simplified to use docker build directly (removed docker/setup-buildx-action dependency).

[0.1.2] — 2026-06-13

Changed

  • Release workflow rewritten to use native docker login / docker push shell commands, replacing the docker/build-push-action third-party action.

Added

  • workflow_dispatch trigger on the release workflow — enables manual image builds without a tag push (useful for testing the CI publish path).

[0.1.1] — 2026-06-13

Fixed

  • Release workflow secret names corrected: GITEA_REGISTRY_USER → REGISTRY_USER, GITEA_REGISTRY_TOKEN → REGISTRY_TOKEN. The v0.1.0 release workflow referenced the wrong secret names, causing the publish step to fail.

[0.1.0] — 2026-06-13

Added

  • Initial Inspector v1: self-hosted Python Docker container health watcher.
  • Multi-container support via a single config.yml — replaces the old pattern of one script + one systemd service per watched container.
  • Per-container state machine with independent failure counters.
  • Health status classification: HEALTHY, UNHEALTHY, STARTING, NO HEALTH CHECK, NOT RUNNING, CONTAINER NOT FOUND, ERROR, UNKNOWN.
  • Restart logic: fires after max_error_count consecutive failures.
  • Cooldown: post-restart window that blocks further restarts.
  • Grace period: post-restart window that suppresses failure counting while a container boots.
  • Hourly restart cap (max_restarts_per_hour) to break endless loops; fires a critical Gotify alert when hit.
  • Optional Gotify notifications on state transitions (not per-loop).
  • Dry-run mode: logs decisions without executing restarts.
  • Rotating log file (TimedRotatingFileHandler, midnight, 30-day retention) with colored console output and plain-text file output.
  • Dockerfile — python:3.12-slim base, non-root inspector user (UID/GID 1000).
  • compose.yml — dev compose: builds from source, mounts Docker socket.
  • compose.prod.yml — prod compose: pulls published image from Gitea registry, no local build.
  • requirements.txt — pinned: docker==7.1.0, PyYAML==6.0.2, requests==2.32.3, colorlog==6.9.0.
  • Makefile with build, smoke, compose-config, tag, push-local, release, clean, version, help targets.
  • Gitea Actions CI (ci.yml): byte-compile + Docker build on every push.
  • Gitea Actions release (release.yml): build and publish four image tags on v*.*.* tag push.
  • scripts/bump_version.sh — updates __version__ in src/inspector/__init__.py.
  • scripts/smoke_test.sh — end-to-end dry-run test against the live Docker socket.
  • README with full feature documentation, configuration reference, Portainer deployment guide, and troubleshooting table.