github MacRimi/ProxMenux v1.2.6.1-beta

pre-release3 hours ago

ProxMenux logo ProxMenux v1.2.6.1-beta

The headline of this beta is Audit & Report, a new page under Admin that documents a Proxmox node, assesses it against forty-three checks and prints the result as a report you can hand to somebody else. Beside it lives a change journal: what ProxMenux itself has done to this host — every configuration file, package and service it touched, with the state each one was in before.

Around those two: a read-only API token can no longer reach a stored credential, application update notifications arrive as one grouped message instead of one per application, applications running inside Docker are registered with their own identity while their updates stay tied to the image, the Monitor is complete in Slovak, and a long list of fixes across Docker web links, LXC updates, notification delivery, post-install and host restore.

🔎 Audit & Report — a new page under Admin

The page holds three views, and every one of them only reads: an assessment never modifies the host.

Assessment evaluates the node and lists what it finds. Changes shows what ProxMenux has done to it. Policy is where you declare what the host is supposed to be, which is what turns a description into a judgement.

The assessment

  • Forty-three Proxmox-specific checks across backup, system, guests, security, storage, network and hardware. Among them: which guests no enabled backup job covers and how old the copies the destinations actually hold are; whether Proxmox Backup Server verified the newest copy of each guest; how the recorded backup runs ended; space and inodes on the filesystems the node itself needs; ZFS pool integrity and device error counters; software RAID and multipath redundancy; Ceph's own verdict; cluster quorum and the corosync links carrying it; high availability where quorum cannot answer; the EFI partitions that would start the kernel; essential services and units systemd gave up on; certificate validity; how SSH admits root; the update chain; and whether the channels that would notify you are themselves working.
  • Every finding carries the evidence it rests on — the command output or configuration fragment — together with what the check examines and where the reading came from. Evidence is presented as tables and labelled values, with field names humanised and figures formatted by what they are: bytes as a size, hours as a duration, an instant as a date in your own locale.
  • Six results, each with a definition it has to earn. Critical is an interruption or an urgent threat to availability, integrity or recoverability, with evidence. Warning is a verified degradation, an absent protection that was expected, or a declared policy that is not met. Observation is configuration, a limit or planning information. Conformant means the criterion was verified and met. Unverified means there was not enough information to conclude. Not applicable means there was nothing to evaluate.
  • A check that could not be evaluated says what it could not read — the destination that refused the connection, the command that failed — beside the result rather than two collapsed panels below it. Missing evidence is never counted as a favourable result.
  • Gravity belongs to the situation, not to the check. A storage at 90% capacity and an unreachable storage come from the same check and no longer share a severity; a finding takes the gravest of the objects it reports on.
  • Accepted risks are recorded against a finding with a mandatory reason and an optional review period. The technical classification stands and the decision sits beside it, so a report still shows that a critical finding was accepted and by whom. When the period ends, the finding returns to the active set.
  • Lynis is read, not reimplemented. Its warnings keep their test identifiers and are collapsed to one row per distinct warning with the number of times it was raised. Where Lynis is installed but has no usable report, the assessment offers to run one before it starts, so the choice of paying that minute is yours.
  • Findings store the identity of their message and its values rather than a rendered sentence, so a report reads in the language of whoever opens it.

Changes — what ProxMenux did to this host

The complaint that lands hardest against a tool like this one is not that it changes things: it is that afterwards nobody can say what it changed. Reading the script does not answer it either — a function of four hundred lines may alter two values, and the reader has no way to know which two.

  • Three blocks answer the three questions an administrator actually asks: what the post-install left configured, which ProxMenux scripts wrote to the host and what each one changed, and which packages and utilities are now installed. Every entry names the script it came from, so a change traces back to the menu that made it.
  • Only the change is shown. optimize_logrotate reads as rotate 7rotate 14, not as forty lines of shell. What was replaced is kept in a content store keyed by digest, so it survives somebody tidying /etc.
  • What was changed is separated from what was run. A function that rewrites a file authored that change; an upgrade launched from a menu did not — apt decided what changed. Both are recorded, in different classes, and the interface says so.
  • How well the previous state is known travels with each entry: captured as it happened, captured on re-application to an already-modified host, or never captured because it predates the journal. Each entry also declares how exactly it could be undone — exact, partial, or not at all.
  • Installations record the difference they made, not the name that was asked for: the packages that were absent and are now present, dependencies included.
  • The journal shows the host as it stands now, not a log of every time a script ran. Re-running the post-install, moving between the stable and beta channels or reinstalling a utility updates the existing entry in place, origin state on one side and current state on the other.
  • Recording never costs an operation. One small JSON file written whole into a spool the Monitor consolidates: no database, no Python, no network on the writing side. If the journal cannot be written at all, the change still happens.
  • The journal has its own section in the printable report, which is where it defends a host.

Policy — declaring what is expected

  • An assessment can see what a host does, not what it is for. Whether a guest needs a backup, whether a service must come back by itself, whether a storage is essential — none of that is discoverable, and guessing at it turns ordinary configurations into alarms. A host with nothing declared produces a complete report that describes rather than judges.
  • Declaring is picking from what is there. Guests and storages are listed from this node's own inventory rather than typed in by identifier. Per guest: whether a backup is required, whether it is expected to start with the host, and a recovery objective in hours where the workload has one. Per storage: essential, optional, or nothing stated. Plus every threshold the checks measure against, each showing the shipped value as its placeholder — leaving a field empty keeps that value.
  • Inheritance is explicit. Inherit shows the site default and removes the per-resource override; Not stated is stored as such, so it cannot silently reactivate an inherited requirement.
  • An exempt guest stays visible, carrying the decision that removed it from the count: excluded by policy, which is a different thing from an accepted risk. The report also states which policy it applied, so two reports of the same host — one measured against stated expectations, one against none — no longer read identically.

What changed since last time

  • An assessment now has a before. Any run can be marked as the reference, and every later one is read against it.
  • Fixed is not the same as accepted. A finding that stopped being reported because the host changed and one that stopped because somebody accepted the risk both leave the list, and only the first is progress. They are counted separately.
  • A check that disappears is reported as no longer assessed, not as resolved: nothing verified that it stopped failing, only that it stopped being asked.

Reports

  • A report answers one question, so the profile selects both the checks that run and the sections the document carries: Full audit, Inventory, Security review, Backup assurance, Capacity and wear and Quick diagnosis. Each focused report opens with its own posture header. Sections a profile did not ask for are absent rather than empty, so an omission is never read as a result.
  • The document describes the node before judging it. Identity, cluster, architecture, disks, network, storage, guests, passthrough and managed software come first; the findings follow, because a finding about a bridge means little to a reader who has not been shown the bridge.
  • Diagrams carry the relations, not the lists: processor and memory in their slots and what hangs off each controller; the path from the wire to each guest through adapter, bond, bridge; where guest disks live and which destination backs them up; cluster membership over the corosync links that join it. Every diagram is inline SVG, so it prints as vector and the document stays self-contained.
  • Quick diagnosis carries only what is asking to be decided — the critical and warning findings with the evidence needed to act on them, and the readings that could not be taken. Every check still runs: a short report that skipped checks would be quick and untrustworthy.
  • The inventory profile runs no checks at all, so a node that has never been assessed can still produce its structural document: memory module by module with slot, type, speed and part number; physical disks with model, serial, bus and SMART state; adapters with link speed, driver and PCI address; PCI passthrough with its IOMMU group; and network latency per target.
  • The disks section carries the observation log — what happened over time, including events a disk recovered from, with type, severity, count and the first and last time each was seen.
  • SMART, Lynis, latency and audit reports now share one shell: the same header, numbered sections, cards and callouts, and an icon-only action bar that disappears when the page is printed.
  • The whole page works on a phone. Dense tables stack into blocks where the heading travels with the value instead of scrolling sideways, controls take a row each below the small breakpoint, and the report opens as a real navigation so an installed web app keeps its own back and close controls.

🔐 Read-only API tokens stay read-only (reported by @f3rs3n)

  • Opening a Monitor terminal and disabling authentication require a full-admin token. Both the terminal-ticket route and the authentication-disable endpoint are guarded by the same admin-scope check the other privileged routes use, and the terminal WebSocket still accepts only a fresh single-use ticket — now issued exclusively to full-admin callers.
  • Reading a stored credential back in cleartext requires a full-admin token too. The settings page masks every stored secret, and the one route that returns a value — the eye toggle beside a field — is guarded the same way. A read-only token issued to a monitoring integration reaches none of them: the GitHub token, the Telegram bot token, the mail password, the AI provider keys, the Discord webhook. A session login carries full-admin implicitly, so the toggle behaves as before for whoever is using the interface.
  • The API token dialog now asks which scope to mint. Read-only is the default and is enough for dashboards and metrics; full-admin is there for integrations that drive the Actions API, and the dialog says plainly what each one can reach.
  • The GitHub API card names the token it needs: that token exists only to raise the anonymous rate limit when checking upstream versions of registered applications, and every call made with it reads public data, so a fine-grained token with read-only access to public repositories and no account permissions is enough.

📦 Grouped application update notifications

  • An automatic check now produces one complete notification instead of one message per application. Pending updates are grouped by LXC and keep the application name plus the installed and available versions, so a quieter inbox does not cost you the detail.
  • A scan with a single pending application keeps the individual notification, and explicit checks you make yourself stay individual. The existing App update available setting controls both forms, and per-application exclusions are still respected.

🐳 Docker applications — one identity, one update state

  • Applications running inside Docker can be registered in the App tab with their own name, logo, web links and installed version, while their updates stay linked to the Docker image. They no longer appear twice in Updates: Portainer, Arcane and their equivalents keep their App entry, and the image row carries the versions.
  • Available versions can be read from image metadata in Docker Hub, GHCR and other compatible registries when the image publishes a usable version label — which describes what the selected tag would install, not necessarily the latest upstream release. Digest-based detection remains available when no label can be read.
  • Installed versions follow the image the container actually uses, including after recreation or when a pulled image has not been applied yet, and image aliases are matched to the workload's configured reference.
  • Image metadata resolves in the background without holding up the inventory, and App and Updates share the result.

Thank you to @byGarcia for the implementation, verified detectors and detailed testing in PR #337 and PR #338, building on the collaboration in discussion #206.

🐳 Docker web links in the App tab

  • The VM and LXC inventory stays available while Docker data warms up. A Docker inventory can exist before its optional application record is loaded, and that state is now an empty app list rather than an empty guest list.
  • Stopped Docker workloads keep their registrable links. Discovery reads a container's persistent published-port configuration when the live mapping is empty, so a stopped Compose service such as HandBrake, MakeMKV or MKVToolNix keeps its detected name and host port ready for registration.
  • Docker LXCs retain every useful candidate: named mappings keep their application, icon and published ports, while listening ports that belong to no mapping — host-network containers, native services in the same LXC — remain available as one-click suggestions, without duplicating a port a named mapping already covers.
  • Adding a detected link stays added. Link insertion and the delayed catalogue-category response update the editor independently, so the category lookup can no longer restore a snapshot from before the insertion.
  • Discovery remains suggestion-only: you choose which published ports actually expose a web interface and save only those.

🛠 Explicit application updater selection (feedback from @SystemIdleProcess)

  • Registering an application no longer enables an updater on your behalf. Choose and save Helper-Scripts or Custom command before running an update; version tracking stays independent of that choice.
  • Each application owns its choice, including in LXCs with several applications, and a custom command replaces that application's helper rather than running both.
  • Editing shows the command that will actually run — the detected launcher or your saved custom command. Modifying a helper launcher switches to Custom command so your edits are used by manual, bulk and scheduled runs; leaving it untouched keeps the official method.
  • A failed or empty updater download stops before execution, so an empty shell command can no longer be reported as a successful update.
  • Information dialogs explain both options, link to each application's documentation and source, and include examples to adapt — which never fill in or execute anything by themselves.

🔍 VM and LXC list — instant search and updates filter (PR #334, contributed by @Vaso73)

  • A search box that matches across name, ID, guest type, tags, PVE note, IP address and registered application names. Multiple terms narrow together and the match ignores accents, so nuria finds Núria and virtualny finds virtuálny.
  • An Updates filter isolates the LXCs with pending work — OS packages, registered applications and Docker images all count towards its badge — and its state is remembered per device.
  • Searching by guest type works in every Monitor language: contenedor, conteneur, contenitore, contêiner, kontajner and Container all find the LXCs on the node.

🗂 LXC mount points — targets written with a trailing slash (PR #336, contributed by @Vaso73)

  • A bind mount configured as mp=/path/ is recognised as mounted. Proxmox accepts the trailing slash while the container's mount table reports the path without it; both spellings now resolve to the same mount point. The path stays on screen exactly as written in the container configuration.
  • The same match applies to ad-hoc detection, so a configured mount is no longer listed as an unmanaged one created inside the container.

🔎 LXC application version detection — runtime-verified detectors (PR #332, contributed by @Vaso73)

  • A bundled override set ships inside the Monitor with detectors proven against real containers, and it takes precedence over the remote catalog. It is deliberately not fetched remotely, so an installed Monitor cannot pull an older entry that would resurrect a stale marker over a detector already verified.
  • Legacy helper markers become a fallback rather than disappearing, so containers installed with older layouts keep a valid version source while newer installs read the application's own binary, package or file.
  • Traefik is the first application on-boarded to the new set, and Changedetection.io joins the catalog this cycle (suggested by @SystemIdleProcess in discussion #306).
  • A successful custom-command update marks the reported version unverified, so the refresh that follows re-reads the live version instead of re-affirming the old one.

📦 Readable LXC package updates (#342, reported by @ash34)

  • Package names are no longer sacrificed to long Debian version strings. Each pending package keeps its complete name on its own line, followed by the installed and available versions in separate, equally sized columns.
  • Long names and version identifiers wrap inside the panel instead of truncating or overflowing, and the security marker stays attached to its package.

🌍 Eight languages, Slovak complete

  • The Monitor is fully Slovak, Audit & Report included, contributed by @Vaso73 in PR #344 and PR #345. Application categories are translated for display while the catalogue's own values stay untouched for filtering, so search matches whichever of the two you type.
  • host and cluster read as themselves in every language, in labels and in prose alike, each language using the term its own catalogue already established.
  • The network interface panel counts packets again: errors and dropped packets are named for reception and transmission instead of being translated as ordinary sentences.
  • Numbers are formatted in the reader's language, and the running state reads as a state rather than as the physical act of running, in German, Spanish, French, Italian, Portuguese and Swedish.
  • Weekday names in scheduled updates resolve correctly, and VM and LXC power actions follow Proxmox's own Spanish wording: Apagar for the graceful action, Parar for the forced one.

🧭 Cluster-owned guest health notifications

  • LXC rootfs and VM filesystem capacity are evaluated only by the node that currently owns the guest, so several Monitor instances in a cluster no longer record and notify the same condition independently. Guest Agent disk refreshes follow the same boundary.
  • A guest that moves between nodes is retired quietly on the previous one. A migration is not reported as a recovery; the new owner is responsible for the current condition.

⚙️ Attributable kernel diagnostics

  • A kernel diagnostic is reported by the line that explains it. Kernel warnings, blocked tasks, lockups, protection faults, oops and memory-safety diagnostics now name the diagnostic type, process and component when present, and carry the journal excerpt around them — instead of a bare Call Trace: marker that identifies nothing and could count one incident twice.
  • Kernel warnings and diagnostic traces have their own per-channel switch, and hidden burst summaries inherit the category of the event they summarise, so silencing one event also silences its +N more.

📮 Notification delivery

  • Events buffered during Quiet Hours are released when the window closes, and the release is recorded like any other delivery, so it appears in the history and the counters rather than arriving on the channel with no trace. The buffer is cleared only once the channel confirms delivery, so an overnight window is never discarded for a summary nobody received.
  • The 24-hour digest is composed and delivered the same way.
  • Pending ProxMenux optimization updates re-fire on Monitor restart alongside every other update report, so a host carrying optimizations it never applied surfaces them again after an update or a reboot. Each version is still announced once per service lifetime.

🔐 Password change with two-factor authentication

  • Changing the Monitor password works with and without 2FA enabled. With two-factor authentication on, the form asks for the current authenticator code or a backup code and sends it with the change. Missing or invalid fields are rejected as validation errors.
  • An already-open browser tab can still complete a password change after the Monitor is updated.

🐍 Portable Python runtime for HTTPS and WebSockets (#331, reported by @f3rs3n)

  • The Monitor AppImage carries isolated gevent runtimes for both CPython 3.11 (Proxmox VE 8) and CPython 3.13 (Proxmox VE 9), and AppRun selects the matching tree from the host interpreter before starting Flask — restoring the intended gevent HTTPS/WSS server on every supported Proxmox VE generation.
  • Native dependencies are pinned and packaged deterministically, and every build imports the bundled stack with real 3.11 and 3.13 interpreters before publishing.

🔐 Node web certificate restore (#333, reported by @tropicaljoe)

  • Host backups already preserved the node-local certificate pair; the post-boot restore now maps it from the source node to the current one instead of leaving Proxmox on its generated certificate.
  • Before installing, ProxMenux validates the certificate, rejects encrypted or invalid private keys and confirms both public keys match; an incomplete pair leaves the current certificate untouched and adds a warning to the restore result. Installation goes through pvenode cert set, followed by fingerprint and service verification, with a fallback to the previous certificate if that verification fails.

🧰 Safe migration of historical Bashrc customizations (#328, reported by @f3rs3n)

  • The Bashrc customization migrates the exact unmarked block written by ProxMenux v1.1.3 and earlier — and the historical variant that wrote its markers literally — into one canonical block, instead of appending a second copy of the prompt, history settings and aliases.
  • Detection requires the complete ProxMenux signature and leaves incomplete marker ranges untouched, so unrelated customizations are never removed. The selected short or full prompt path survives the migration, and atomic replacement keeps the original permissions and ownership.

🛡 Log2RAM updates without interrupting the active journal (#327, reported by @f3rs3n)

  • Existing installations use an in-place ProxMenux migration instead of re-running the upstream installer, whose reinstall path removes both backing directories and temporarily leaves journald without its active /var/log mount.
  • The update preserves the configuration, sync interval, active tmpfs mount and running services, replacing only the files ProxMenux manages. First-time installations defer activation to the reboot the post-install flow already asks for.

📊 Shared historical metrics on slower hosts (reported by Rafael Fernández Díez)

  • CPU, memory and network charts share one in-flight query per time range, with the default 24-hour view warmed in advance and a bounded budget per query.
  • A temporary failure keeps the last valid snapshot with its retrieval time and a translated stale-data notice, and charts recover on their own without replacing a newer range with an older response.

🔄 Consistent application versions across App and Updates (reported by Nicolas Ortuzar)

  • Registered LXC applications use one shared, file-versioned snapshot for the App tab, Updates and the modal cache, so the App tab can no longer show an older available version or check date than the notification you just received.
  • Open App cards receive version-state changes through the existing data feed, without new polling, and a background refresh leaves unsaved editor fields untouched.

🌀 Post-install prompts appear on their own line

  • Each step closes its progress spinner before the next question or menu is drawn, so a prompt such as the Log2RAM install choice is shown on a clean line instead of over a still-turning indicator.

🙏 Acknowledgments

  • @Vaso73 — the complete Slovak Monitor including the whole Audit & Report section, plus the runtime-verified detector set, the VM/LXC search and updates filter, and the LXC mount point fix.
  • @byGarcia — Docker application registration and image version tracking, with verified detectors and lab testing.
  • @f3rs3n — the API token scope report, the portable Python runtime, the Bashrc migration and the Log2RAM journal issue.
  • @SystemIdleProcess — the explicit updater selection design and the Changedetection.io detector.
  • @ash34, @tropicaljoe, Nicolas Ortuzar and Rafael Fernández Díez — reports that shaped the LXC package list, the certificate restore, application version consistency and historical metrics on slower hosts.

Don't miss a new ProxMenux release

NewReleases is sending notifications on new releases.