• v0.8.1 8b1fe37cbe

    v0.8.1
    All checks were successful
    release / image (push) Successful in 1m5s
    ci / battery (push) Successful in 1m41s
    Stable

    Ghost released this 2026-06-23 06:54:47 +00:00 | -263 commits to main since this release

    Added

    • Device metadata & grouping (config): per-target description (free-text) and tags
      (labels), and a global groups list. A group's membership is its explicit members
      unioned with targets whose tags match match_tags (match: any|all). Pure resolution
      helpers (select_by_group/select_by_tag) for use by the CLI and the upcoming web UI.

    • CLI selectors: -g/--group and --tag run scoped backups (mutually exclusive with
      -t/--target); --list prints the inventory (targets, groups, tags with device
      counts) and exits.

    • Pluggable storage destinations (storage: in global.yaml): mirror produced backups
      to multiple destinations with per-destination required semantics and group/tag
      routing. Built-in dep-free backends: local and sftp; third-party backends via the
      rosbackup.storage entry-point group. Retention applies per destination; remote-only
      setups delete the local working copy after a successful mirror. Absent storage: is
      byte-for-byte the classic local-only behaviour. Remote types: s3 (S3/MinIO/B2/...,
      via the rosbackup[s3] extra) and nextcloud (stdlib WebDAV, no extra deps).

    • testlab/: a self-contained libvirt/KVM RouterOS test lab — 20 dual-stack CHR
      routers across fictional customers/sites with tags and groups, built in ~4 minutes
      (serial-console provisioning gated on IP reachability, rosbackup-bootstrap
      dogfooding, persistence reboot, lifecycle-aware status).

    • Web platform (optional rosbackup-ng[web], new rosbackup-web command): self-hosted
      FastAPI + Jinja2/htmx UI and /api/v1 JSON API embedding the console-free backup
      engine. First iteration: single-admin auth (scrypt, signed sessions, CSRF, login
      lockout), dashboard, targets inventory with group/tag/search filters, run-now (all /
      group / tag / target, dry-run) with a live SSE progress view, run history, and a
      local-destination backup browser + download. Localhost bind by default; TLS belongs
      to a reverse proxy (Caddy + deSEC DNS-01 and systemd examples under doc/examples/).
      htmx is vendored (no Node/CDN). Single global run lock (one backup run at a time).

    • Optional ICMP latency monitoring (rosbackup-web --latency): background pinger that
      shows per-target reachability + round-trip time in the Targets view.

    • Web Targets page: Table and Cards (grid) views, tag/group truncation with a +N
      overflow chip (full list on hover), and a per-target backup-set count that links to
      the backup browser.

    • Web style guide: doc/WEB_STYLE_GUIDE.md documenting tokens, components, and how to
      add a screen (a repo document — not served by the web app).

    • Web backup schedules: preset cadences (every N hours / daily at HH:MM / weekly on a
      weekday at HH:MM, in the configured timezone — no cron dependency). An in-process
      scheduler fires due schedules through the run manager (so a scheduled run is identical
      to a manual one and obeys the single global run lock; a run already in progress is
      skipped and audited), with catch_up to optionally run a missed schedule on startup.
      Schedules UI (list + create/edit form, enable/delete) and /api/v1/schedules CRUD.

    • Web API tokens + audit log: named bearer tokens (rosbackup-web --api-token-create),
      stored SHA-256-hashed, accepted as Authorization: Bearer on /api/v1 and CSRF-exempt
      (no cookie); --api-token-list/--api-token-revoke manage them. An audit log records
      logins, runs, schedule changes, and token use (viewable in the UI). Run history is
      capped at the newest 1000 runs.

    • Web push-restore: restore a binary .backup onto a router from the backup browser
      (.rsc is never restorable; whole-config only). Guarded — a confirmation page requires
      typing the target name, an automatic pre-restore backup is taken first, the run-lock is
      held (no overlap with a backup), the uploaded file is verified by SHA-256 before the
      destructive load (size fallback; aborts on mismatch), and the action is audited. Core
      restore.perform_restore (validated against RouterOS 7.21.4: /system backup load
      with a mandatory password, non-interactive, reboots on success), a reusable
      orchestrator.open_connection shared with the backup path, and SSHConnection.upload
      (SCP).

    • Web backup browser now downloads from any configured storage destination, not just
      local: StorageBackend.retrieve() (local/sftp/s3/nextcloud) fetches a file back, the
      target page shows which destinations hold its backups, and remote downloads stream via
      a temp file. Restores remain sourced from the local working copy.

    • Web UI can now add, edit, and remove targets (targets_editor): the form edits
      only the common fields and preserves each target's ssh/tmpfs/backup blocks, validates
      through the real loader, and writes targets.yaml atomically after a .bak. Refuses to
      remove the last target; all changes are audited.

    • Web capability toggles: a web: block (allow_download, allow_restore, both
      default true) disables backup download and/or push-restore in the UI/API, with optional
      per-group overrides (allow_download/allow_restore on a group). Resolution is
      deny-wins — a group can only further restrict, never re-grant. Enforced server-side at
      the download choke point (gated even on a direct call) and the restore routes (403), and
      the per-target backup page hides the disabled actions.

    • Web: in-browser preview of .rsc / .info backups — a styled, read-only text view
      (/backups/preview/file) gated by the same download capability; binary .backup files
      remain download/restore only.

    • Web: edit/delete on the targets and schedules lists are now compact icon buttons
      (inline SVG, CSP-safe) in place of text links.

    • Web templates and static assets now ship in the wheel (rosbackup.web package-data).
      Previously only an editable install had them, so pip install rosbackup-ng[web] (and any
      packaged build) would 500 on every page for lack of templates/static. Fixed.

    • Added a single-file shiv zipapp build (scripts/build-pyz.shdist/rosbackup-web.pyz)
      bundling the app + [web] deps + vendored assets; runs anywhere with Python ≥ 3.9, no
      virtualenv/Node. The pip extra stays the primary channel.

    • Web firmware upgrades / downgrades ("Versions" page): tracked, backgrounded runs on
      the live compose view. Channel or specific-version targets (with an "installed on your
      fleet" group for versions off the public index), per-router installed → target plan
      with commands shown once per distinct command-set, version-mode fetch of the main
      package plus installed extras, ≤4 live phases (Pre-flight with a bounded reachability
      probe → Safety backup → Installing → Verifying), post-reboot version verification,
      optional parallel execution, and persisted prev → new version transitions shown on
      History, the run detail, and refreshed into inventory.

    • Web: after an upgrade, the RouterBOARD boot-code firmware is upgraded
      (/system routerboard upgrade + reboot) and tracked in inventory; no-op on CHR/when
      current, never on downgrades.

    • Web: skip targets monitored offline (Settings → Latency, on by default) — a
      known-down host is excluded from backup/upgrade/downgrade runs (incl. scheduled), shown
      as a "skipped offline" banner; not-yet-probed hosts are never skipped.

    • Web: busy indicator on the Targets list for targets currently in a backup / restore
      / upgrade / downgrade; version filter on Targets and Inventory; cache-busting
      ?v= on static assets.

    Fixed

    • Console logging now stamps every line; previously messages within the same second
      showed a blank time column (rich's omit_repeated_times).
    • Web: the background run worker now uses its own SQLite connection, fixing an
      intermittent cannot commit - no transaction is active (and runs spuriously flipping to
      error) caused by a worker commit() landing inside a request thread's transaction on
      the shared connection.
    • Web: non-interactive firmware reboot (get_pty=False) — /system reboot no longer
      hangs waiting on its confirmation prompt during an upgrade/downgrade.
    Downloads