- OIDC: RP-Initiated Logout is now opt-in (default off) via a new
'single_logout' setting, with the exact post-logout redirect URL shown
in Settings for the admin to register at their provider. Fixes the
regression from last time: enabling it unconditionally broke sign-out
for anyone whose IdP hadn't been told to trust the redirect yet
(Keycloak's invalid_redirect_uri, browser stuck on a stale page).
- formatBytes shows up to 2 decimals (was an adaptive 0-or-1 rule); every
bytes/rate chart now computes a rounded 'nice' axis scale (0/5/10/15/20
GB, the standard Heckbert algorithm) and locks every tick + the tooltip
to one consistent unit derived from the axis's own max.
- Storage page: the capacity donut is now sized by used bytes + a free
remainder instead of by total capacity share, which was always 100%
the moment there was only one pool — completely disconnected from the
"224 GB used" text next to it.
- Topology: Export SVG (fits the full diagram regardless of current pan/
zoom) and a Snap-to-grid toggle.
- New Disks tab on the node detail page: every physical disk with model/
serial/size/type, PASSED/FAILED health, SSD/NVMe wearout %, and a
per-drive temperature read from SMART, plus a full SMART attribute
table per disk. Backed by new /nodes/{node}/disks and
/nodes/{node}/disks/smart endpoints.
- CPU/GPU temperature is not exposed by Proxmox's own API (no built-in
lm-sensors/nvidia-smi integration) and isn't something this can add
without a node-side agent Proxmox doesn't ship — disk temperature via
SMART is the thermal data actually available.
- Fleet Overview's Storage KPI and per-connection totals (internal/api/
overview.go) were summing shared storage once per node that mounts it,
since PVE's own Shared flag is unreliable for storage added per-node
without ticking it. New dedupeSharedStorage collapses those by an exact
(name, total, used) match within a connection; local-only plugin types
are never touched. Mirrored on the Storage page, which also now
disambiguates same-named local pools (e.g. every node's "local-lvm") by
node so a bar chart's category axis never gets duplicate labels.
- Sign-out: React Query keeps a query's last-successful data through a
failed refetch, so the cached signed-in user survived the post-logout
401 and the app never noticed. signOut() now pins ["auth","me"] to null
directly instead of invalidating-and-hoping a refetch lands in time.
- SMTP: net/smtp.SendMail opportunistically attempts STARTTLS whenever the
server advertises it regardless of the admin's "Use STARTTLS" toggle, so
an internal relay with a bad cert failed sends the admin explicitly asked
to be plaintext. A dedicated sendPlain path never attempts TLS.
- OIDC: added an "Auto-create new accounts" toggle — when off, a
first-time SSO login for an unrecognized identity is refused instead of
silently provisioning one. Also added RP-Initiated Logout: signing out of
an SSO session now also ends the session at the identity provider when it
advertises support for it (end_session_endpoint), via a stored id_token
on OIDC sessions (new sessions.oidc_id_token column).
- ResourceAreaChart tooltips now fall back to the same yTickFormatter given
to the axis when a series has no formatter of its own — fixes raw
unformatted numbers in Fleet Trend and every RRD chart built on it
(Node/Guest detail) that only ever set the axis formatter.
- Default all queries to a 20s poll + refetch-on-focus (main.tsx) instead of
a per-page opt-in, so every page/widget stays live without manual tuning.
- New internal/notify package: Gotify and SMTP (stdlib net/smtp, STARTTLS
and implicit-TLS-on-465) notifications, each independently optional. Fires
from the alert evaluator on new alert triggers; admin-configurable from
Settings with a send-test-notification action per channel.
- OIDC/SSO moved from config.yaml-only to a DB-backed, admin-editable
Settings card — swaps the live client with no restart. config.yaml is
used to seed the database once on first boot after upgrading.
- New Security settings: session TTL, login lockout policy, and a real
"require 2FA for admins" enforcement (requireTOTPEnrolled middleware)
that blocks non-enrolled admins from everything but /profile and logout.
- New org-wide default preferences (theme/accent/look/landing page) for
brand-new accounts, plus a personal landing-page picker and an
email-me-alerts opt-in on Profile.
- Storage page: separate Local vs Shared/External storage tables and
capacity donuts, fixing shared-storage totals that were being summed once
per node that mounts them (e.g. a 2TB NFS share on 4 nodes read as 8TB).
- RankedBarChart: stop the longest bar's value label wrapping onto two
lines (recharts auto-wraps LabelList when space is tight).
- Add glassmorphism, neumorphism, and brutalist looks (backend validation,
theme.tsx types, AppearanceCard picker) alongside the existing six.
- Give looks real structural knobs, not just color: sidebar width/border,
header height/blur are now CSS vars AppShell reads, and card shadow/hover
states are fully token-driven instead of a hardcoded dark-mode override.
- Sidebar nav active state drops its hard box border on glass/neumorphic
looks (glow pill / inset shadow) instead of always drawing a box.
- Fix RankedBarChart's value label wrapping onto two lines on the longest
bar (recharts auto-wraps LabelList when space gets tight) by rendering
it as a plain unconstrained <text> with a wider right margin.
The GitHub merge of feature/glass-flight-deck and feature/theme-selector
left :root/.dark (the "no [data-look] override" baseline) set to Glass
Flight Deck's tokens instead of the plain Enterprise baseline, so every
look inherited its Oswald type, tight radii, persistent caution bar, and
corner brackets regardless of which one was selected — the themes were
indistinguishable because they were all secretly the same rejected design
underneath. Restores :root/.dark to a clean Enterprise baseline and moves
every Glass-Flight-Deck-specific token into its own explicit
[data-look="glassFlightDeck"] block, same as the others.
Also makes .panel-label's tracked-caps transform and .corner-frame's
visible border look-gated (glassFlightDeck only) instead of global, and
MasterCautionBar look-aware (persistent nameplate only in Glass Flight
Deck; a conventional reactive banner — hidden when nominal — everywhere
else), so no look can leak another look's structural signature.
Expands the six-value Look enum/backend whitelist with two new designs:
- Midnight: modern dev-tool dark (Nord/GitHub-dark-adjacent), cool
blue-slate ground with a visible glow instead of a hard shadow.
- Paper: ultra-minimal and warm, off-white ground, zero shadow, hairline
borders only, with a genuinely warm (not blue-black) dark variant.
All six verified live end-to-end (build/typecheck clean, each rendered and
screenshotted in both themes where applicable).
Adds a whole-app visual-register switch, orthogonal to the light/dark
toggle and accent color, via a new [data-look] token layer in index.css:
- Enterprise (default): unchanged clean SaaS baseline.
- Proxmox-native: utilitarian and dense — plain system font, smaller root
scale, zero radius, no shadows, flat bordered panels.
- Terminal: quiet and precise — the whole UI (not just data) sets in
monospace, no display face, flat near-zero-radius panels.
Persisted server-side alongside theme/accent (new `look` column on
user_preferences, migration 00008) with a live-scoped preview per option
in Settings > Appearance.