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.
[0.11.0-beta.3] — 2026-05-25
Controller compatibility: 1.1.1
Bugfix release covering the modpack-upload pipeline (browser ↔ mixed-content proxy ↔ controller) and the API-token login screen. Pairs with controller 1.1.1, which carries the matching server-side fixes.
Fixed
- Chunked upload for dedicated services no longer mis-creates a group.
apiChunkedUploadpreviously hardcoded the finalize step to/api/modpacks/upload/finalizeregardless of the original path, so dedicated uploads — initiated against/api/dedicated/modpack/upload— landed in the group-creation handler and failed withInvalid group namebecause they sentname/port/memory/proxyEnabledwhile the handler expectedgroupName/type/minInstances/maxInstances. The chunked path now detects the target from the original path (/api/dedicated/...→target=dedicated, otherwisegroup), passestargettoPOST /api/modpacks/upload/init, and calls the matching finalize endpoint (/api/dedicated/modpack/upload/finalizefor dedicated). - Large modpack uploads no longer surface a spurious error after a connection drop. For 1 GB+ server packs the controller takes 30–60 s to extract and run the modloader installer after the byte stream completes, and intermediate proxies often drop the idle HTTPS connection in that window. Both the Dedicated and Groups import dialogs now detect the network-drop shape of the error and poll
/api/dedicatedor/api/groupsfor up to 2 minutes for the new service/group to appear, then show success — instead of showing "Failed to fetch" while the import is still finishing server-side. - API-token login works against an HTTP-only controller from an HTTPS dashboard. The login form's
controllerFetchputs the bearer in the standardAuthorizationheader, but the dashboard's/api/proxy/[...path]/route.tswas only readingX-Nimbus-Tokenand silently discarded the inboundAuthorization. The proxy now passes the inboundAuthorizationthrough as a fallback when noX-Nimbus-Tokenis set, so the API-token login screen reaches the controller correctly.
Notes
- The chunked-upload routing fix is split between the dashboard and the controller (new
targetparameter + new dedicated finalize endpoint). Running an older controller (1.1.0 or earlier) with this dashboard means single-shot dedicated uploads still work, but the proxy-mode chunked path will fail on the finalize step until the controller is upgraded to 1.1.1.
[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.