ProxMenux v1.2.1.3 (Beta)
Third beta of the v1.2.1.x cycle. This release closes three
distinct issues uncovered after v1.2.1.2 shipped: terminal sessions
silently failed to open on hosts with HTTPS enabled, the LXC update
checker reported zero pending updates on long-running CTs even when
the upstream had hundreds queued, and the AI Enhancement section in
Notifications was invisible enough that several testers reported
never noticing it. The LXC update logic is also re-architected
around a dedicated operator toggle so the per-CT pct exec scan is
properly opt-in and the notification channel behaves consistently
with it.
Main changes in v1.2.1.3
LXC Update Detection — dedicated section, opt-in scan, cache refresh
A new LXC Update Detection card lands in Settings, between
Health Monitor Thresholds and Notifications. It exposes a single
toggle that gates the periodic per-CT apt list --upgradable /
apk list -u scan end-to-end:
- Toggle ON (default). ProxMenux walks every running CT on the
host once per cycle, queries the in-container package manager, and
surfaces the pending update count on the Hardware tab + the
lxc_updates_availablenotification stream. - Toggle OFF. The scan stops entirely (zero
pct execcalls),
everytype=lxcentry is purged from
/usr/local/share/proxmenux/managed_installs.jsonimmediately
instead of waiting for the next 24h cycle, and the matching
per-channel notification toggle in Notifications → Services
disappears from the UI. The notification preference is preserved
in the DB and re-appears at its previous value the moment
detection is re-enabled.
The state is persisted in the user_settings SQLite table as
lxc_updates.detection_enabled and exposed via
GET/POST /api/lxc-updates/detection (auth-gated). The Notifications
component subscribes to a proxmenux:lxc-detection-changed
CustomEvent so the conditional toggle hides/reappears in real time
without a page reload.
LXC update checker — auto-refresh stale apt/apk caches
The checker used to run apt list --upgradable / apk list -u
against whatever the CT's local package cache happened to contain at
that moment. On long-running appliance CTs (a Docker host last
touched in 2024, a Plex CT where only the Plex binary auto-updates,
etc.) the cache could be months out of date and the checker would
honestly report zero pending updates from a frozen snapshot — leaving
real security backlogs invisible.
The checker now reads the mtime of the CT's package-manager metadata
cache (/var/cache/apt/pkgcache.bin on Debian/Ubuntu, the latest
/var/cache/apk/*.tar.gz on Alpine). If the cache is older than 24h
it runs apt-get update -qq / apk update from outside via
pct exec, with a 60s timeout. Any failure (no network, broken repo,
timeout) is swallowed silently — the listing below still runs against
whatever cache exists, so a transient repo issue can never make the
situation worse than the pre-existing CT state. CTs already on a
healthy cadence (unattended-upgrades, cron) keep their fresh
caches and are not touched. Two reproductions on lab hardware: a
Debian 12 CT whose cache was 524 days old went from "0 updates" to
"117 updates (12 security)", a Plex CT went from "0" to "128 (91
security)" of Ubuntu base packages that plexupdate never manages.
Terminal in HTTPS hosts — fix WebSocket connection error on every modal
On hosts with HTTPS enabled in the Monitor, every terminal modal
(the dashboard's full terminal, the LXC terminal modal, the script
terminal modal) failed to connect with [ERROR] WebSocket connection error followed by [INFO] Connection closed. The dashboard worked,
the HTTPS handshake worked, the auth ticket flow worked — but the
moment the browser tried to upgrade to a WebSocket, the connection
dropped.
Root cause: the gevent + SSL path in flask_server.py instantiated
pywsgi.WSGIServer(..., handler_class=WebSocketHandler) on top of a
WebSocket route stack already implemented by flask-sock. Both
layers responded to the client's HTTP upgrade request, so the server
emitted two consecutive HTTP/1.1 101 Switching Protocols
headers — the browser saw the second one as a corrupt WebSocket
frame and closed the connection immediately. Dropping the
handler_class=WebSocketHandler parameter — flask-sock already
provides its own protocol implementation on top of any standard
WSGI server, including gevent.pywsgi.WSGIServer — restores a
single 101 response and lets the handshake complete normally.
Verified on all four lab hosts: HTTPS host now serves one 101, the
three HTTP hosts continue to serve one 101 (they were using the
Flask dev server path which never had this issue).
AI Enhancement section in Notifications — actually visible now
The collapsible Advanced: AI Enhancement row inside the
Notifications card lived in text-xs muted-foreground uppercase
with a chevron icon and no leading badge — testers consistently
reported scrolling past it without noticing the feature existed.
The row is rewritten to use a normal-case text-sm foreground
label, a leading Sparkles icon in purple to match the AI body
underneath, and a persistent badge to the right of the title:
green Active when AI is enabled, neutral Optional when it
isn't. The chevron and the hover affordance survive; the row is
still collapsible and still nested inside Notifications.
Thanks again to the testers — the WebSocket-on-HTTPS report and the
"my old LXC has nothing pending but the binary is 18 months out of
date" report drove most of this beta. Feedback is welcome on the
same channels.