Dashboard Changelog
Release history for the Nimbus Web Dashboard — versioned independently of the controller.
The Nimbus Web Dashboard is versioned independently of the controller, so its UI-only releases (polish, new screens, UX fixes) don't need to wait for a full controller release. The format follows Keep a Changelog.
Dashboard versions use the pattern <controller-major>.<controller-minor>.<controller-patch>-<channel>.<n> (e.g. 0.9.1-beta.1). The leading 0.9.1 tracks which controller release the dashboard was validated against; -beta.1 is the dashboard's own iteration counter on that base.
[1.0.0-stable.1] — 2026-04-25
Controller compatibility: 1.0.0
Visual polish for the stable release: the login page background is upgraded from the hand-rolled SVG pixel sky to real layered pixel-art assets, with separate day and night themes.
Changed
- Layered pixel-art login background. Replaces the
<PixelSky />SVG component with<LoginBackground />, which composites multiple transparent PNG layers from/public/backgrounds/sky-light/(3 layers, daytime) and/public/backgrounds/sky-dark/(6 layers, night) depending on the active colour scheme. Per-layer opacity tuning in dark mode — upper cloud layer (layer 3) at 0.35 opacity so the moon remains visible beneath.image-rendering: pixelatedkeeps the 576×324 source assets crisp at any viewport size. An SSR-safe fallback gradient prevents a flash of unstyled content before the client mounts and the theme resolves.
[0.11.0-beta.1] — 2026-04-19
Controller compatibility: 0.11.0
Dashboard surface for the new Auth module: multi-user login, a /profile section with TOTP + passkey + session management, a permission-aware navigation that hides what the user can't reach, and a ground-up rewrite of the login screen as a multi-screen wizard.
Added
- Login wizard at
/login. Replaces the old Tabs + ToggleGroup layout with a 7-screen flow (connect → method → code | magic-link | TOTP | API token) and direction-aware slide transitions. One concern per screen, back-arrow navigation,autoFocusper step, stock shadcn primitives end-to-end. - Passkey login + enrollment. Thin
lib/passkeys.tswrapper around the browser's nativePublicKeyCredential.parseCreationOptionsFromJSON/parseRequestOptionsFromJSONAPI — no external WebAuthn dependency. Login MethodCard is gated onisPasskeySupported()so unsupported browsers don't see a dead option. NewPasskeyCardon/profile/securityfor labeled enrollment and a list-with-delete of registered credentials. Cancellations andNotAllowedErrorare silent — the user just sees the login screen again, no stack-traced toast. - TOTP setup UI. QR render via
api.qrserver.comfallback (no new deps), confirm step, disable. Ten recovery codes presented in a copy-all grid with a one-shot "save these somewhere safe" warning; the grid never re-renders them. /profilesection. Layout owns a singlePageShelltitled Profile with pill-tabs as sub-navigation; sub-pages render section cards directly instead of stacking their own page headers./profile— avatar, UUID, derived role badge, permission count,totpEnabledflag/profile/security— TOTP card, Passkey card, Sessions card (list with revoke + "sign out everywhere else")/profile/permissions— permission nodes grouped by the second dotted segment, admin callout when the user holdsnimbus.dashboard.admin
- Permission-aware UI.
lib/permissions.ts—nimbus.dashboard.*node registry + longest-match route-prefix lookup<RequirePermission>/<IfPermission>/<PermissionButton>components for inline gatingAppSidebarfilters nav items by required permission (API-token auth still sees everything)<RoutePermissionGuard>wraps(dashboard)children and 404-fallbacks routes the user can't access — doesn't leak page existence through the status code
NavUserthree-mode render.api-token— command-block head (real custom head from minecraft-heads.com, rendered via mc-heads.net with the textures.minecraft.net hash), "API Admin" label, connected-controller URL, Disconnect actionuser— realmc-heads.net/avatar/<uuid>head, derived role label (Admin / Developer / Moderator / Supporter / User from permission grants), links to Profile / Security / Permissions, theme toggle, Logoutanonymous— renders nothing
- Magic-link deep-link handler in
app/login/login-client.tsxwithhistory.replaceStatecleanup and aSuspenseboundary arounduseSearchParams. Click a magic link → land on the dashboard authenticated, URL token is stripped so it never shows up in the address bar. - Pixel-sky login background. New
<PixelSky />component: Minecraft-style animated sky, day-time vertical gradient, 20 seeded twinkling star pixels, three drifting pixel clouds at different altitudes and speeds. Pure SVG + CSS, no external media.prefers-reduced-motionhonoured — the animations freeze to their initial frame and the twinkle holds at full opacity. - OTP tile input. New
<OtpInput />: six single-digit cells with auto-advance, Backspace-jumps-back, arrow-key nav, full paste-spread, one-time-code autofill. Shared between the login code screen and the TOTP screen. Submit button disabled until the code is complete; auto-submit fires on last-digit entry so the user rarely touches the button. - Minecraft-head icons on method cards. Each login method now shows a themed pixel-head:
- Passkey — keypad head (minecraft-heads.com #43871, texture hash
7e1959dd…) - Minecraft Account —
MHF_Alex, the default modern skin - API Token — command-block head (minecraft-heads.com #120843), same asset reused in the
NavUserAPI-token mode
- Passkey — keypad head (minecraft-heads.com #43871, texture hash
Changed
- Login visuals back to stock shadcn primitives. Earlier iterations drifted into custom
rounded-2xl, primary-tinted shadows, bespoke hover-lifts, and a floating-circle back button that read as a different product. Reverted:Carduses its stockrounded-4xl+shadow-md+ring-1 ring-foreground/5; back arrow is<Button variant="ghost" size="icon">withrounded-md; headings are<CardTitle>; submit buttons are<Button size="lg">; MethodCards are<Button variant="secondary">with a custom inner layout. Pixel-sky background and screen transitions kept. - Login collapses to a single choice screen. The intermediate
mc-methodscreen and the magic-link waiting UI are gone from the dashboard — the in-game/nimbus dashboard logincode path is friction-free enough on its own, and the extra options made the grid feel cluttered. MethodCards now all use the samesecondaryvariant so nothing implies a default. (The backend magic-link endpoints are still live — the in-game/nimbus dashboard login linkcommand and the Velocity plugin delivery path still depend on them.) apiFetchhonours session auth. The client auth state is a union (loading/anonymous/api-token/user), and every request attaches either the bearer token or the session cookie based on the current kind. A silent 5-minute refresh keeps theuserstate's permission snapshot fresh without round-tripping on every page load.useAuth().hasPermission(node)mirrors backend wildcard semantics (*,a.b.*,nimbus.dashboard.adminshortcut). Single source of truth for permission checks across the UI.- Breadcrumbs now render
profile/security/permissionswith proper casing viarouteLabelsinsite-header.tsx— no more raw URL segments.
Removed
- Magic-link screen state from
login-form.tsx—mcName,linkSent,linkTtl,linkTimer,startLinkCountdown,handleSendMagicLink. Thenimbus-sparkleCSS keyframes the waiting state relied on are gone too. - Inline
NimbusLogo,SkinHeadIcon,CoinIconcomponents from earlier login iterations (~140 lines). The real banner + MC-head CDN cover both needs.
Security hardening
Landed alongside the controller 0.11.0 review pass:
- WebSocket + SSE auth via short-lived tickets.
apiWebSocket()andapiProxyWebSocket()nowawaitfetchWsToken()before opening a connection. For user sessions that call hits the newGET /api/auth/ws-ticketcontroller endpoint and places a single-usewt_…ticket in the?token=query param; the long-lived session bearer never lands in a URL. API tokens pass through unchanged (operator-chosen long-lived credential). - Session tokens moved to
sessionStorage.setUserSessionCredentials()persists tosessionStorageso the login survives a Cmd-R without landing inlocalStorage(no cross-tab reads, cleared on tab close).getToken()rehydrates the in-memory cache on the first call after a refresh. API-token credentials remain in-memory only.
Notes
- No dashboard-only changes needed on existing pages. Permission gating is additive: the sidebar filters for user sessions, every other screen works identically whether the caller is an API token or a session-authed user.
- Beta quality. The new auth flow has been exercised end-to-end against a local controller with all three login methods (code, magic link, passkey) plus TOTP enrolled. Live-testing on your own controller before relying on the browser UI for account-critical operations is still recommended.
[0.10.0-beta.1] — 2026-04-17
Controller compatibility: 0.10.0
Dashboard surface for the new Docker module, landing alongside the controller 0.10.0 release.
Added
- New page:
/modules/docker. Four status cards (module enabled, daemon reachable, daemon version + API version, running/total container counters) plus a table of every Nimbus-managed container with state badges, image, ports, and short ID. Prune button wires toPOST /api/docker/prune. Polls atPOLL.normal, pauses on tab hide, and silences transient failures so a brief daemon hiccup doesn't spam toasts. - Daemon-offline banner on the Docker page that surfaces the configured socket path when
reachable: false, so operators see why opted-in services are running as plain processes. - Per-group Docker section on
GroupEditDialog. Enable switch (switch off = no overrides written, TOML stays clean) plus memory limit, CPU limit, and Java image fields. Empty values mean "inherit module default". - Docker badge on the services list. Sky-blue
Dockertag next to each containerised service name, backed by a newbackedByfield on/api/services. Paired with the existing Dedicated + Persistent badges — no crowding, same row. - Sidebar icon.
Containerlucide icon exposed through@/lib/iconsand wired into the moduleiconMap, so the Docker entry renders with a dedicated icon instead of the genericBoxfallback.
Changed
GroupResponsenow carries adockersub-object. The types ingroup-edit-dialog.tsxreflect this; existing screens that don't read the field ignore it.ServiceResponsegainedbackedBy. Values:"process"(default),"docker","remote". Future runtime backends can add new values without breaking older dashboards — unknown values fall through without a badge.
Notes
- No dashboard changes required on the existing /services page beyond the badge. Container-specific deep-links (logs, shell exec) stay out of Phase 1 — the /modules/docker page handles inspection; service-level log streaming continues to flow through the existing Console sheet, which works identically for process- and Docker-backed services.
[0.9.1-beta.1] — 2026-04-15
Controller compatibility: 0.9.1
The dashboard moves from alpha to beta. It is now suitable for day-to-day operation of a single-server or small-cluster Nimbus deployment — most workflows that previously required the console are available in the browser.
Highlights
- Full group management (create, edit, delete) with typed-name delete confirmation.
- Per-service console sheet with live log stream and command execution over WebSocket.
- File and template browser at
/files— breadcrumb navigation, upload with progress, mkdir, rename, delete, inline text editor for config-shaped files. - Global broadcast dialog in the header (network or per-group).
- Player row actions: kick and transfer.
- Maintenance whitelist card on Settings.
- Cluster bootstrap card on Nodes (fingerprint, WebSocket URL, collapsible PEM reveal).
- Load balancer card on Nodes (backend health, active connections).
- Modpack import dialog now shows real upload progress.
Added
- Beta channel. Dashboard now ships its own version (
0.9.1-beta.1), independent of controller patch cadence. A Beta/Alpha badge in the sidebar reflects the channel. - Shared UX primitives. New
PageShellcomponent,useApiResourcehook, and semantic severity color tokens (--severity-ok,--severity-warn,--severity-err,--severity-info). - Group CRUD UI with form validation that mirrors the controller's DTO (name regex, memory bounds, scaling ranges, JVM args).
- Service Console Sheet — opens from the row action on both dynamic services and dedicated services. Raw WebSocket frames, 1000-line ring buffer, ANSI rendering, reconnect-aware.
- File / Template Browser. Scopes: templates (rw), services (rw), groups (read-only). Multipart upload with XHR progress, mkdir, binary download path.
- File editor with line numbers, tab-handling (tab inserts two spaces, shift-tab dedents), Ctrl/Cmd+S to save, Ctrl/Cmd+/ to toggle the language-appropriate line comment, dirty indicator, and unsaved-changes guard on page unload.
- Image preview lightbox. Known image extensions (
png,jpg,jpeg,gif,webp,svg,bmp,ico,avif) open in a Dialog with a blob-loaded preview instead of the text editor. Per-row "Preview" action. - Broadcast dialog in the site header. Target: network or a specific group.
- Player row actions — kick (with reason) and transfer (with service/group target).
- Maintenance whitelist card on the Settings page (toggle + add/remove).
- Cluster bootstrap card and load balancer card on the Nodes page.
- Modpack upload progress. Groups import dialog now shows a real percentage bar plus an indeterminate phase while the server resolves the pack.
Changed
- Every page migrated to
PageShell+useApiResource. Loading skeletons, empty states, and error banners are now consistent across the UI. apiFetchauto-surfaces 4xx/5xx errors as toasts. Callers opt out with{ silent: true }when rendering their own error UI.- Doctor page status colors extracted to semantic tokens. No more hardcoded
emerald-*/amber-*/destructiveclasses in status UI. - Polling intervals standardized:
POLL.fast(3s),POLL.normal(5s),POLL.slow(30s). Polling pauses while the tab is hidden.
Known backend gaps
These UIs were intentionally not shipped in beta because the controller endpoints aren't there yet:
- Token management UI — the controller currently only exposes
POST /api/tokens(generate) andGET /api/tokens/scopes. List and revoke are missing, so there's nothing sensible to render. - Scaling rule editor —
/api/scaling/schedulesis read-only. Create, edit, and delete require new controller routes first.
Both are tracked as controller-side follow-ups; the dashboard will pick them up automatically once the endpoints exist.
Beta quality notes
- Safe for day-to-day operation of a single-server or small-cluster deployment.
- Long-running cluster operations (cross-node migration, state-sync troubleshooting) still benefit from the console.
- Live-testing against a running controller is recommended before relying on the browser UI for critical operations.