-
v0.13.0
Stablereleased this
2026-07-03 12:55:22 +00:00 | 0 commits to main since this releaseAdded
- Machine-readable package index. Every sweep now writes
/routeros/index.json— a human-readable
(2-space-indented), atomically-written inventory of every RouterOS.npkon 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-levelschemaversion. 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
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- Machine-readable package index. Every sweep now writes
-
v0.12.0
Stablereleased this
2026-07-03 12:29:48 +00:00 | 1 commits to main since this releaseSecurity 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 ased -iscript; a|or newline could trigger GNU sed'secommand → arbitrary command
execution as root (and YAML injection intopackagemini.yml). FQDNs are now validated against a
hostname charset (_check_fqdn) before they reachsed/ the config writer. - HIGH — GoAccess board exposure. The board's
:7443listener — and its unauthenticated/ws
WebSocket — inherited none of the mirror's IPallow/denygate, 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/archiveis hidden from browsing. - HIGH — path traversal.
version/arch/pintokens reachedmkdirbefore 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/pinrejected), and defensively beforemkdir. - 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 existingrate/ CIDR validation. - MEDIUM — secret & permission exposure. Passwords are hashed via stdin (no longer visible in
ps/ argv);.envand the imported TLS key are written under a private umask (no world-readable
window); the public-facingwebcontainer getsno-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_npkwired
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 usesquoteattrfor XML attributes; the
SSH key-only guard now requires a non-emptyauthorized_keys(avoids lockout); marker dotfiles
(.complete/.size/.absent) hidden from the file server.
Docs
- Corrected security claims that over-promised:
sources.archive_backfillis 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 the7443/tcpfirewall 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
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- CRITICAL — root RCE via
-
v0.11.3
Stablereleased this
2026-07-03 10:12:23 +00:00 | 2 commits to main since this releaseFixed
- 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 understatus → Content → by channeleven with
keep_last: 10— andseen.jsongrew unbounded. It's now capped to the newestkeep_lastper 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
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- Per-channel version count could drift above
-
v0.11.2
Stablereleased this
2026-07-03 09:44:41 +00:00 | 3 commits to main since this releaseFixed
statusswappiness hint no longer repeats the value (read "0 0 = …"); now "lower = keep hot firmware
in RAM cache, avoid swapping out".
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
v0.11.1
Stablereleased this
2026-07-03 09:42:38 +00:00 | 4 commits to main since this releaseChanged
statusPerformance 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
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
v0.11.0
Stablereleased this
2026-07-03 09:35:29 +00:00 | 5 commits to main since this releaseCodebase audit sweep (5 parallel agents: Python source, tests,
deploy.sh, docs, infra) — fixes below.Added
- Continuous CI — a
testworkflow runs the suite on every push tomain, not only on tag releases
(regressions between versions were previously unseen until the next tag). webwaits on asynchealthcheck (Caddyfile present) viadepends_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.pyglobals between tests (removes order-dependent flakiness).
Fixed
- config:
web.rate_limit.rateis now format-validated at load — a typo like600/mused 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.partfile. - deploy.sh: operator name/email and GoAccess/basic-auth usernames are escaped/validated before
being written intopackagemini.yml(a"///|could corrupt it);cmd_statusnowexports
LC_ALL(was an inertlocal) and restoresset -e; the three divergent "is GoAccess enabled" greps
are unified into one_goaccess_enabledhelper; the dnf-automaticsed/mkdirwrites are guarded;
set-certwarns whenopensslis missing; deadhf_perms_stateremoved; thethenkeyword-variable
renamed. - docs: corrected the
reboot_commandbehaviour in UPDATES.md (packagemini always overwrites it,
not "kept if present"); DESIGN.md GoAccess row (own port +portkey); "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; documentedPM_GOACCESS_PORT.
Changed
- Docstrings added to
run_once,render_caddyfile,classify,same_bytes;Configdataclass type
hints aligned (tuple[str, ...]/dict[str, ChannelCfg]);.coveragegit-ignored.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- Continuous CI — a
-
released this
2026-07-03 06:56:43 +00:00 | 6 commits to main since this releaseChanged
--help: moved--forcefrom MANAGE to INSTALL so each section is stylistically uniform (INSTALL =
--flags, MANAGE = bare-word commands).--forceis a reinstall/rebuild of the current version, so it
belongs with the install flags; behaviour is unchanged (deploy.sh --forcestill works).
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
released this
2026-07-03 06:45:05 +00:00 | 7 commits to main since this releaseFixed
- Swept the last
harden↔performance crossed wires after the 0.10.1 split (two-agent audit of the
script + all docs): thestatusdefault-profile hint, the comment baked into the generated
98-packagemini-performance.conf, thetunecode comments, andconfig/packagemini.example.ymlnow
all point attune/tune --remove(neverharden). Marked the now-shipped performance-tuning item
done in TODO, and addedPERFORMANCE-TUNING.md/MANAGE.mdto the README + DESIGN doc indexes.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- Swept the last
-
released this
2026-07-03 06:38:46 +00:00 | 8 commits to main since this releaseFixed
statusPerformance line pointed athardento toggle tuning — now points attune --remove(harden
no longer touches performance).
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
released this
2026-07-03 06:36:21 +00:00 | 9 commits to main since this releaseAdded
tune --remove— remove the performance profile and revert to kernel defaults (confirms first).
Changed
hardenis now security-only. The performance toggle is gone from thehardenmenu entirely —
performance lives solely undertune/tune --remove. Hardening and tuning are now fully separate
commands. Docs (README, MANAGE, SYSTEM-HARDENING, PERFORMANCE-TUNING) updated to match.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads