• v0.13.0 598ecdecd4

    v0.13.0
    All checks were successful
    test / test (push) Successful in 23s
    release / release (push) Successful in 22s
    Stable

    Ghost released this 2026-07-03 12:55:22 +00:00 | 0 commits to main since this release

    Added

    • Machine-readable package index. Every sweep now writes /routeros/index.json — a human-readable
      (2-space-indented), atomically-written inventory of every RouterOS .npk on the mirror (version → arch →
      packages with sizes, plus channel heads) for automated consumers, starting with rosbackup-ng package
      discovery. It lists only what's actually on disk (retention-pruned versions drop out); additive fields
      won't bump the top-level schema version. The JSON Schema is served alongside at
      /routeros/index.schema.json (canonical: packagemini-schema.json, drift-tested). Both are served
      behind the mirror's optional basic-auth and revalidated (not immutably cached). See
      doc/PACKAGE-INDEX.md.
    Downloads
  • v0.12.0 41a4d55b68

    v0.12.0
    All checks were successful
    test / test (push) Successful in 19s
    release / release (push) Successful in 21s
    Stable

    Ghost released this 2026-07-03 12:29:48 +00:00 | 1 commits to main since this release

    Security hardening sweep — a 4-agent audit (deploy.sh, Python worker, infra/web) plus a docs↔code
    security-claims cross-check. Fixes below, ranked by severity.

    Security

    • CRITICAL — root RCE via set-fqdn. An unvalidated FQDN (set-fqdn <arg> / PM_FQDN) was spliced
      into a sed -i script; a | or newline could trigger GNU sed's e command → arbitrary command
      execution as root (and YAML injection into packagemini.yml). FQDNs are now validated against a
      hostname charset (_check_fqdn) before they reach sed / the config writer.
    • HIGH — GoAccess board exposure. The board's :7443 listener — and its unauthenticated /ws
      WebSocket — inherited none of the mirror's IP allow/deny gate, rate-limit, or headers, so an
      allowlist didn't cover the board and basic-auth brute-force was unthrottled. The board block now applies
      the same allow/deny (covering /ws), a rate-limit, and headers; the raw log archive under
      /srv/report/archive is hidden from browsing.
    • HIGH — path traversal. version/arch/pin tokens reached mkdir before validation. A new
      safe_token() guard gates every token before it builds a filesystem path or URL — at discovery
      (sources.py), at config load (architectures/pin rejected), and defensively before mkdir.
    • MEDIUM — Caddyfile injection. public_base_url, tls_*, usernames, *_hash_env, and the server
      timeouts are validated at config load (reject braces/tabs/newlines; env-name + username charsets),
      matching the existing rate / CIDR validation.
    • MEDIUM — secret & permission exposure. Passwords are hashed via stdin (no longer visible in
      ps / argv); .env and the imported TLS key are written under a private umask (no world-readable
      window); the public-facing web container gets no-new-privileges (matching sync/goaccess).
    • MEDIUM — resource DoS. Response reads are capped (8 MiB text / 4 GiB artifact) so a misbehaving
      upstream can't exhaust worker memory; the main npk is size-checked after download (verify_npk wired
      into the sweep) and re-fetched if it fails.
    • LOW / defense-in-depth. git refs rejected if they start with - or contain metacharacters; strict
      CSP on the (fully self-contained) mirror site; the feed uses quoteattr for XML attributes; the
      SSH key-only guard now requires a non-empty authorized_keys (avoids lockout); marker dotfiles
      (.complete/.size/.absent) hidden from the file server.

    Docs

    • Corrected security claims that over-promised: sources.archive_backfill is now marked not yet
      implemented
      (it was a silent no-op stub), and the false "checks NPK magic bytes" claim is replaced
      with the actual behavior (TLS-verified fetch + a size sanity check; no cryptographic verification).
      SYSTEM-HARDENING documents the 7443/tcp firewall opening and the board's public-listener caveat.
    • New doc/REAL-TIME-MONITORING.md — the GoAccess board + live-update
      WebSocket architecture, with a dashboard screenshot.
    Downloads
  • v0.11.3 3a7a36bcc6

    v0.11.3
    All checks were successful
    release / release (push) Successful in 21s
    test / test (push) Successful in 23s
    Stable

    Ghost released this 2026-07-03 10:12:23 +00:00 | 2 commits to main since this release

    Fixed

    • Per-channel version count could drift above keep_last. seen.json[channel] accumulated targeted
      versions as an ever-growing union and was never capped, so churny pre-release channels (testing = rc,
      development = beta) reported e.g. 11 versions under status → Content → by channel even with
      keep_last: 10 — and seen.json grew unbounded. It's now capped to the newest keep_last per channel
      after the union (which still guards against a sweep that momentarily under-discovers). On-disk retention
      was already correct; this was a reporting/growth drift.
    Downloads
  • v0.11.2 4220749e4e

    v0.11.2
    All checks were successful
    test / test (push) Successful in 20s
    release / release (push) Successful in 22s
    Stable

    Ghost released this 2026-07-03 09:44:41 +00:00 | 3 commits to main since this release

    Fixed

    • status swappiness hint no longer repeats the value (read "0 0 = …"); now "lower = keep hot firmware
      in RAM cache, avoid swapping out".
    Downloads
  • v0.11.1 445f10e85a

    v0.11.1
    All checks were successful
    test / test (push) Successful in 21s
    release / release (push) Successful in 21s
    Stable

    Ghost released this 2026-07-03 09:42:38 +00:00 | 4 commits to main since this release

    Changed

    • status Performance section: each value now carries a short plain-language explanation (what the
      connection backlog / conntrack / congestion-control / swappiness number actually means) so the section
      is self-explanatory instead of opaque numbers.
    Downloads
  • v0.11.0 c1f2e2d305

    v0.11.0
    All checks were successful
    release / release (push) Successful in 22s
    test / test (push) Successful in 26s
    Stable

    Ghost released this 2026-07-03 09:35:29 +00:00 | 5 commits to main since this release

    Codebase audit sweep (5 parallel agents: Python source, tests, deploy.sh, docs, infra) — fixes below.

    Added

    • Continuous CI — a test workflow runs the suite on every push to main, not only on tag releases
      (regressions between versions were previously unseen until the next tag).
    • web waits on a sync healthcheck (Caddyfile present) via depends_on: condition: service_healthy,
      so it no longer crash-loops against a missing config on first boot.
    • More tests — config validation (rate / sync_interval / packages), MikroTik URL-builder token guards,
      HTML-escaping of operator/changelog fields, and asset cache-busting; plus an autouse fixture that resets
      site.py globals between tests (removes order-dependent flakiness).

    Fixed

    • config: web.rate_limit.rate is now format-validated at load — a typo like 600/m used to pass
      silently and render an invalid Caddyfile (written before downloads, so it could wedge first boot).
    • sweep: a version pinned into several channels is downloaded once — it was submitted to the pool
      per-channel, causing redundant CDN fetches and a race on the shared .part file.
    • deploy.sh: operator name/email and GoAccess/basic-auth usernames are escaped/validated before
      being written into packagemini.yml (a " / / / | could corrupt it); cmd_status now exports
      LC_ALL (was an inert local) and restores set -e; the three divergent "is GoAccess enabled" greps
      are unified into one _goaccess_enabled helper; the dnf-automatic sed/mkdir writes are guarded;
      set-cert warns when openssl is missing; dead hf_perms_state removed; the then keyword-variable
      renamed.
    • docs: corrected the reboot_command behaviour in UPDATES.md (packagemini always overwrites it,
      not "kept if present"); DESIGN.md GoAccess row (own port + port key); "five → six" hardening measures
      in SYSTEM-HARDENING.md; marked shipped TODO items (footer toggle, reports-over-web) done; fixed stale
      /goaccess/… URLs in the compose file, env.example, and docs; documented PM_GOACCESS_PORT.

    Changed

    • Docstrings added to run_once, render_caddyfile, classify, same_bytes; Config dataclass type
      hints aligned (tuple[str, ...] / dict[str, ChannelCfg]); .coverage git-ignored.
    Downloads
  • v0.10.4 66a4ce0fb9

    v0.10.4
    All checks were successful
    release / release (push) Successful in 19s
    Stable

    Ghost released this 2026-07-03 06:56:43 +00:00 | 6 commits to main since this release

    Changed

    • --help: moved --force from MANAGE to INSTALL so each section is stylistically uniform (INSTALL =
      --flags, MANAGE = bare-word commands). --force is a reinstall/rebuild of the current version, so it
      belongs with the install flags; behaviour is unchanged (deploy.sh --force still works).
    Downloads
  • v0.10.3 9dfdb4f2f8

    v0.10.3
    All checks were successful
    release / release (push) Successful in 15s
    Stable

    Ghost released this 2026-07-03 06:45:05 +00:00 | 7 commits to main since this release

    Fixed

    • Swept the last harden↔performance crossed wires after the 0.10.1 split (two-agent audit of the
      script + all docs): the status default-profile hint, the comment baked into the generated
      98-packagemini-performance.conf, the tune code comments, and config/packagemini.example.yml now
      all point at tune / tune --remove (never harden). Marked the now-shipped performance-tuning item
      done in TODO, and added PERFORMANCE-TUNING.md / MANAGE.md to the README + DESIGN doc indexes.
    Downloads
  • v0.10.2 0d273649e7

    v0.10.2
    All checks were successful
    release / release (push) Successful in 20s
    Stable

    Ghost released this 2026-07-03 06:38:46 +00:00 | 8 commits to main since this release

    Fixed

    • status Performance line pointed at harden to toggle tuning — now points at tune --remove (harden
      no longer touches performance).
    Downloads
  • v0.10.1 b5d5204998

    v0.10.1
    All checks were successful
    release / release (push) Successful in 21s
    Stable

    Ghost released this 2026-07-03 06:36:21 +00:00 | 9 commits to main since this release

    Added

    • tune --remove — remove the performance profile and revert to kernel defaults (confirms first).

    Changed

    • harden is now security-only. The performance toggle is gone from the harden menu entirely —
      performance lives solely under tune / tune --remove. Hardening and tuning are now fully separate
      commands. Docs (README, MANAGE, SYSTEM-HARDENING, PERFORMANCE-TUNING) updated to match.
    Downloads