Conversation
Final ingredient of the v1.2.2 stable release: flip version.txt from 1.2.1 to 1.2.2 so the stable channel's update notifier picks it up on every running install, ship the consolidated v1.2.2 entry on both CHANGELOG.md (English) and lang/es/CHANGELOG.md (Spanish), and add the GitHub link to Jonatan Castro on the contributors page. CHANGELOG.md entry (and its ES mirror) consolidates the four v1.2.1.x betas into a single stable note grouped by theme — Health Monitor configurability, Apprise full feature parity, LXC update detection, Coral TPU latest upstream drivers, performance optimizations (smartctl scheduler, fail2ban cache, lxc-info /proc), HTTPS terminal handshake, PVE 9.x kernel update detection, NVIDIA installer improvements, i18n documentation site — plus an Acknowledgments section crediting @jcastro (5 direct commits), @pespinel (1 commit) and @ghosthvj (field reports that shaped the GPU + Coral work). contributors/page.tsx: Contributor interface now carries an optional `githubUrl`; when set, the displayed name is wrapped in an ExternalLink to that URL (target=_blank). Jonatan Castro's entry gets `githubUrl: https://github.com/jcastro` so users can reach his repos from the testers grid. After this PR merges: - Users running `menu` will be offered the 1.2.2 upgrade - proxmenux.com/en/changelog and /es/changelog ship the new entry (deploy.yml triggers because CHANGELOG.md, lang/** and web/** are all touched) - Jonatan Castro's name on the contributors page becomes clickable Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The drafting date (today) was used as a placeholder. The actual release date is Tuesday 2026-06-02, when PR #218 merges and the update notifier picks up 1.2.2. Aligns the changelog header in both EN and ES with the publication date users will see. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…menu Three changes that fold into the v1.2.2 release PR: 1. AppImage: bump Next.js 15.1.6 -> 15.1.9 (CVE-2025-55182) GHSA-9qr9-h5gf-34mp / React2Shell is a pre-auth RCE in React Server Components when Server Functions deserialize attacker payloads. The ProxMenux Monitor ships Next.js in `output: "export"` mode behind Flask on :8008, so there is no runtime Next.js server and no "use server" directive in the source tree — the exploitable path is not reachable. Bumping to 15.1.9 anyway because OpenVAS and similar scanners flag the version string from the JS bundle regardless of architecture; raising the floor removes false-positive noise across every install. Reported by @rost43 in #219. 2. web/components/ui/doc-navigation.tsx: handle sidebar entries that point to in-page anchors. The Storage Share Manager sidebar has entries for `/docs/storage-share#host` and `/docs/storage-share#lxc-net` as section headers, but usePathname() does not include the hash so every visit collapsed to the parent page. As a result Next/Previous on /docs/storage-share stayed stuck at #host, and Next from .../lxc-mount-points/ pointed back at #host instead of #lxc-net. Read window.location.hash on mount (and on hashchange) and try the pathname+hash match before falling back to the pathname-only lookup. SSR hydrates with an empty hash and refreshes once mounted — brief render before hydration is the same as the previous behaviour, so no regression. 3. scripts/help_info_menu.sh: user-side improvement (mirrored from develop). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…odules The previous bump commit (2f24de2) shipped a binary that still carried Next.js 15.1.6 in the bundled chunks even though AppImage/package.json was at 15.1.9. Root cause: build_appimage.sh only ran `npm install` when `node_modules` did not exist; on the .50 build host node_modules had been cached since the 1.2.1 build cycle, so the bump was silently ignored and the build re-used the stale tree. Fix the script: always run `npm install --legacy-peer-deps` on every build. npm reconciles against the lockfile in under a second when everything is already in sync, so the change is free on a warm tree and correct on a stale one. Rebuild from a clean node_modules on .50, redeploy to all four hosts (SHA 4602b8d4aa130c6f...), runtime grep confirms the bundle now contains 15.1.9 with no traces of 15.1.6 left. Same architecture and threat model as before — Flask serves the static export on :8008, no Next.js runtime — but the version banner now matches the lockfile. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
When a host gets transient I/O events on a disk while smartctl is
momentarily unavailable (the canonical case: late in a noisy
shutdown), the disk-scan code records a `disk_<name>` WARNING tagged
"SMART: unavailable" exactly once and trusts the next scan to clear
it. That trust is misplaced: the clear path only fires when the
device shows up in the current dmesg window with zero events. After
a reboot, dmesg is empty for that device — so the device never gets
iterated, resolve_error is never called, and the dashboard stays
orange for a disk whose SMART now reports PASSED.
Caught on a lab host where `disk_nvme2n1` had been stuck as WARNING
for hours after a reboot. SMART was 100% healthy at the moment of
inspection (Critical Warning 0x00, 0 media errors, 100% spare). The
error's first_seen and last_seen were identical and pre-dated the
current boot, confirming a one-shot record that nothing had cleared.
Fix: add a `_reconcile_stale_disk_warnings()` pass at the top of
`_check_disks_optimized()`. For every active `disk_*` error
(skipping `disk_fs_*`, which is already reconciled separately):
- device gone from /dev/ → resolve "Device no longer present"
- device present + SMART PASSED → resolve "Transient I/O cleared,
SMART now reports healthy"
- device present + SMART UNKNOWN/FAILED → leave active so the
main loop can re-classify on the next dmesg window
Acknowledged errors are left alone so the user's explicit dismiss
intent isn't overridden.
Verified end-to-end: re-injected the original `disk_nvme2n1`
warning into the persistence DB on the lab host, waited one scan
cycle, error was resolved automatically with `resolved_at` set and
`resolution_reason = 'Transient I/O cleared, SMART now reports
healthy'`.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The UPDATE in `_resolve_error_impl` only touched `resolved_at` — the `reason` argument every caller passes was silently dropped, and the `resolution_reason` / `resolution_type` columns stayed NULL for every auto-resolved error. The columns were added back in a previous sprint for exactly this audit-log purpose, but the writer was never updated to populate them. Fix the SQL to write `resolution_reason = ?` and tag `resolution_type = COALESCE(existing, 'auto')` so admin-cleared errors (whose type is set elsewhere) keep their value while the default auto path correctly labels itself. Verified end-to-end on the lab host: re-injected the `disk_nvme2n1` warning, waited one scan cycle, the row now reads `resolution_type='auto'` and `resolution_reason='Transient I/O cleared, SMART now reports healthy'` — previously these columns stayed NULL even though the resolve_error call passed a descriptive reason. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`getDiskHealthBreakdown` carried its own hardcoded ladder (HDD ≤45 normal, ≤55 warning) that was much stricter than the configurable defaults consumed by `getTempColor` via `useDiskTempThresholds` (HDD warn 60, hot 65). HDDs at 48 °C therefore rendered a green "Healthy 48°C" badge on the card but were tallied as "warning" in the top-of-page "X normal, Y warning, Z critical" summary, leaving the user with the misleading "6 normal, 5 warning" line. Use the same threshold map as the per-disk badge so the colour and the count are always consistent, and so Settings → Health Monitor Thresholds → Disk temperature actually applies to the breakdown. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The tracked binary still pointed at the build made before the last two fixes landed (resolution_reason persistence in health_persistence and disk-temp breakdown alignment in storage-overview). Re-build the AppImage so the GitHub-published binary matches what is actually running on the deploy targets. New SHA-256: d043e2f27f21315931ab53d87f02390b1a66b0c1730e8b7699aafb565809efbb Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The v1.2.0 binary lingered in the repo after later releases. Remove it so AppImage/ holds only the current shipping artefact. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`get_disks_observation_counts` maps each serial's count to that serial's "most recent" device_name (so renames like ata8 -> sdh keep the badge attached). When several physical disks have passed through the same kernel name across reboots — common with NVMe, the kernel probes in a different order depending on which slots are populated — disk_registry keeps a row per (device_name, serial) seen and the "most recent" device_name for a serial can now be in use by an entirely different disk. Concrete case from the wild: serial 211716800490 was nvme0n1 during the previous boot and earned a real I/O observation. After removing four of five NVMes, the surviving disk (serial 243332800236) booted into nvme0n1. The badge layer mirrored 211716800490's count onto nvme0n1 — which is now a different physical disk — and showed "1 obs." on the wrong drive, while the modal (which scopes by the current (device_name, serial) registry row) found nothing and rendered an empty history. Only mirror a serial's count onto its device_name when that device_name is currently owned by the same serial, determined from the freshest disk_registry row. The serial-keyed entry stays unconditional so observations remain reachable when the disk is re-plugged under another device name. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
New build picks up the get_disks_observation_counts NVMe-rename fix. SHA-256: 3b44eb1172b4b1b7e6a36d1c9f1cd5a237ec04d52543bb791358525b0653a402 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Updated contributors image link to include version parameter.
A mass-backup webhook that exceeded ~2 KB used to be silently truncated by `desc = message[:MAX_EMBED_DESC]` with MAX_EMBED_DESC set to 2048 — half of Discord's real description limit and far below what a multi-VM backup digest produces. The trailing jobs just vanished from the channel. Bring the channel up to Discord's actual webhook contract: * description limit raised to the real 4096-char cap * if the body still doesn't fit, split it on line boundaries into one embed per chunk so every backup entry is preserved * keep title + fields on the first embed only; attach the footer and timestamp to the last embed so the rendered card has the normal head/tail framing even when split across many embeds * enforce Discord's 6000-char-per-embed cap (title + description + every field name+value) — only kicks in when many large fields combine with a chunk already near the description ceiling * batch up to 10 embeds per webhook POST (Discord's per-message limit) and POST additional messages sequentially with a 0.4 s gap so a >10-embed digest doesn't trip the 5/2 s webhook rate limit Verified with synthetic mass-backup payloads: * 14 KB / 200 jobs → 4 embeds, 1 POST * 60 KB / 60 lines → 15 embeds, 2 POSTs (10 + 5) New AppImage SHA-256: 16ad59ea63a64e5be460cd73f87315e8b39b756bf1c61f3cb2019e9fa3e76361 Closes #220. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…_host
Two release-day fixes in the host-side share tooling, both reported
during testing of the v1.2.2 candidate.
lxc-mount-manager_minimal.sh
After adding a mount point on a stopped LXC the script offered to
`pct reboot $ct` unconditionally — which fails on a stopped CT
because `pct reboot` only accepts running ones, so the user saw a
bogus "Failed to restart" right after a successful mount. Gate the
prompt on `pct status` and, when the CT is stopped, tell the user
the mount will activate on next start instead of trying to reboot
it. The matching restart prompt in the remove flow (around line
540) was already doing the check correctly; this just brings the
add flow in line.
disk_host.sh
The script always registered the disk as a Proxmox storage via
`pvesm add dir|zfspool`. nfs_host.sh and samba_host.sh already
offered a dual-flow chooser ("Proxmox storage" / "host fstab only"
/ both) so a user could mount the share on the host for LXC
bind-mounts without surfacing it as a Proxmox storage. Replicate
that chooser for local disks:
* new `select_mount_method` checklist with `pvesm` and `fstab`,
inserted after filesystem selection. ZFS is forced into the
pvesm path because a ZFS pool can't be expressed as an fstab
mount.
* `configure_disk_storage` skips the Content Types prompt when
only fstab is selected and renames "Storage ID" → "Mount Name"
in the same case so the wording matches what the user will
actually see (or not see) in Proxmox.
* `format_and_mount_disk` title and summary lines adapt to the
chosen mode.
* the trailing `add_proxmox_dir_storage` call in `add_local_disk_storage`
runs only when `MODE_PVESM=1`; in fstab-only mode the final
message points users at the LXC Mount Manager for bind-mounts.
Verified end-to-end on a 32 GB USB disk against LXC 112
(unprivileged) on .55: fstab-only path → bind-mount → root inside
CT writes mapped to host uid 100000, regular user writes mapped to
host uid 101000, both reads/writes successful from inside the
container.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two related gaps in the disk-host script that surfaced while testing the new dual-flow (pvesm / fstab-only / both) added earlier today. view_disk_storages used to read only from `pvesm status`, so a disk added via the new fstab-only path — exactly the case where the user wants a local disk available for LXC bind-mounts without registering it as a Proxmox storage — never showed up. Replicate the same fstab scan remove_disk_storage already performs and list those mounts in a second section underneath the pvesm ones. Empty state and wording updated so the panel no longer claims "No local storage configured in Proxmox" when fstab-only mounts are present. Both view_disk_storages (new code) and remove_disk_storage (existing fstab branch) were happily picking up `/mnt/Archivos` and any other CIFS/NFS share mounted under /mnt. samba_host.sh and nfs_host.sh own those — surfacing them in the local-disk menus would let a user remove a network share from the wrong screen. Filter by fstype (skip cifs/smbfs/nfs/nfs4/nfsv4/sshfs/fuse) and additionally require that the resolved source be a real block device, which drops bind mounts and anything else whose backing source isn't a disk. Verified on .55 with the test disk-sda fstab-only mount alongside the existing //192.168.0.15/Archivos CIFS mount: only disk-sda is listed by the local-disk view/remove flows. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The fstab-only mount method explicitly says "for LXC bind-mounts" in its dialog wording, but the mount point left behind by mkfs + mkdir is owned root:root with mode 0755. An unprivileged LXC sees the directory through its uid offset (root inside → host uid 100000) which lands under the directory's "others" bits — so the container can read but never write, and the user has to track down the chmod / setfacl step manually. lxc-mount-manager_minimal.sh already offers exactly this fix as `lmm_offer_host_permissions` when the user adds the bind-mount through that script, but the disk-side script never closed its half of the loop. Add a small `_apply_lxc_bind_mount_perms` helper that runs `chmod o+rwx` plus `setfacl o::rwx + default ACL` whenever MODE_FSTAB=1, and call it from both `mount_disk_permanently` (format path) and `mount_existing_disk` (use_existing path). Pure pvesm-only mounts keep the original behaviour — chmod o+rwx on a VM/backup storage isn't desirable. Verified on .55 against the existing /mnt/disk-sda + LXC 112 (unprivileged): unprivileged container root could not write before (Permission denied), writes succeed after the perms are applied and land on the host as uid 100000 as expected. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The earlier fix to handle stopped containers in the add flow used `msg_info` for the "Container is stopped — mount will activate automatically on next start" line. `msg_info` is the spinner-start half of the msg_info/msg_ok pair — it never gets closed here because there's nothing to wait on, so the spinner glyph stays visible (⠋) right before the "Press Enter to continue" prompt. Switch to `msg_ok` so the line renders as a clean static success mark, matching the visual style of the other terminal messages. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two related improvements to the post-add verification step that the user hit while testing the stopped-CT case. A stopped container couldn't be probed at all — the previous patch just told the user "mount will activate on next start" and left them to discover any issues later (the typical issue being permission denied on the host directory, since the dialog confirms the bind-mount was added but never proves it works). Offer to start the container right now so the user gets feedback in the same session; if they decline, fall back to the informational line. The post-restart / post-start probe used `test -d $ct_mount_point` which only checks that the directory is visible inside the container. That always succeeds whenever the bind-mount took effect, even if the host directory permissions don't let the unprivileged-LXC mapped uid write — exactly the case the user just ran into with /mnt/disk-sda (700 → others gets r-x). Replace with a touch+rm probe in a new `_lmm_verify_writable` helper used by both branches so the user is told straight away when writes will fail and, when they will, is given the exact `chmod o+rwx` / `setfacl` command and a pointer to the host-perms prompt. Verified on .55 / LXC 112 (unprivileged) against /mnt/disk-sda: container stopped → start prompt → start → directory visible → touch probe → success. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The HOST block already groups everything that runs on the host side, so the secondary "Host-only resources" divider between item 4 and item 5 was visual noise — the user reading the menu sees two HOST sub-titles back to back and has to wonder how items 1-4 differ from item 5 in scope. They don't; items 1-4 register an external or local resource as a Proxmox storage, item 5 creates a local shared directory. Both are host-side actions. While here, retitle item 5 from "Add Shared Directory on Host" to "Create Shared Directory on Host" — items 1-4 add an existing external/local resource, item 5 creates a new one. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Final ingredient of the v1.2.2 stable release. Do not merge before Tuesday June 2 — by design, this is the moment the stable channel's update notifier picks up 1.2.2, so the merge timing matters.
What this PR does
Acknowledgments included in the CHANGELOG
Workflows that will run after merge
After merge — manual follow-ups
Test plan