- Added a new API endpoint for enrolled devices to raise help requests via REST when not connected to CDAP.
- Improved the device enrollment process by issuing a device token during status polls, allowing for seamless recovery of CDAP authentication.
- Enhanced error handling for device token validation and ensured proper responses for various failure scenarios.
- Updated the build process to support dual UI binaries for Linux (X11 and Wayland) and included a session-aware launcher.
- Refactored the installation script to accommodate the new Linux UI bundle structure, improving deployment flexibility.
- Updated the system tray icon setup to use a platform-appropriate resource method, improving compatibility across operating systems.
- Introduced a new function to provide a tray icon resource based on the platform, ensuring proper icon display on Windows.
- Enhanced the installation process to include copying the Mesa OpenGL library for better GUI support on Windows, particularly in virtual environments.
- Updated the build worker to stage the OpenGL library alongside the application, improving user experience for GUI operations.
- Refactored command-line options to clarify GUI usage and improve overall application behavior in headless environments.
- Added a new method to check if the agent has an active CDAP session, improving connection status reporting.
- Updated the SendHelpRequest function to utilize the engine for sending help requests, enhancing the request handling process.
- Refactored health check methods to include the Go management API, replacing the console health checks for better integration.
- Improved branding logo handling by introducing a PNG validation function, ensuring only valid images are used.
- Updated UI elements and localization strings to reflect changes in API endpoints and improve user experience.
- Updated the UI to utilize a new status handling method, enhancing the display of connection and enrollment statuses.
- Introduced a status dot indicator for better visual feedback on connection state.
- Refactored the Go toolchain installation script to verify the presence of the JSON package instead of PNG, improving compatibility checks.
- Enhanced error handling in the agent build worker to streamline the build process and ensure proper environment setup.
- Added new UI components and functionality for managing client builds, including a section for displaying build statuses and a button to rebuild all platform installers.
- Enhanced the generator routes to support rebuilding of bundles, including error handling for revoked bundles.
- Updated the agent build worker to facilitate the rebuilding process for platform builds associated with a specific bundle.
- Improved localization by adding relevant strings for build management in English and Polish language files.
- Introduced CSS styles for the new builds section to enhance visual presentation and user experience.
- Simplified the health check for the Go binary in agentBuildWorker by utilizing a new method from updateService to verify the standard library.
- Enhanced the installation process of the Go toolchain to handle concurrent requests and added an option to limit the maximum version during installation.
- Improved error handling and cleanup in the goStdlibHealthy function to ensure temporary files are removed after checks.
- Updated the resolveGoRelease function to support version constraints, ensuring compatibility with specified maximum versions.
- Updated the agentBuildWorker to return null if no healthy Go binary is found, improving error handling.
- Enhanced the updateService with a new function to verify the integrity of the Go standard library, ensuring a complete installation.
- Implemented additional checks during Go toolchain installation to handle incomplete standard library scenarios, improving reliability and user feedback.
- Added a new function to install the Go toolchain if it's missing or broken, ensuring proper setup for support-agent Generator builds.
- Enhanced the agentBuildWorker to check for a healthy Go binary and requeue builds that failed due to a broken Go installation.
- Updated the build environment configuration to prioritize a working Go toolchain, improving the reliability of the build process.
- Added functionality to track the remote SHA of the agent source, improving consistency during updates.
- Implemented a new method to sync the full support-agent source from GitHub, ensuring all necessary files are staged for rebuilds.
- Introduced a mechanism to check for agent source drift, allowing for automatic repairs when discrepancies are detected.
- Updated the update service to trigger agent source synchronization and rebuilds based on specific file changes, streamlining the update workflow.
- Implemented a conditional staging process for the Go support-agent source during updates, creating a rebuild flag for generator bundles.
- Added functions to handle the requeuing of builds and marking rebuilds as pending, improving the update workflow.
- Updated the update service to stage support-agent files and trigger rebuilds after in-app updates.
- Removed the obsolete GitHub Actions workflow for the support agent, streamlining CI/CD processes.
- Added a new function to stage the Go support-agent source for Generator builds, ensuring proper setup without a full git checkout.
- Refactored connection handling to improve TLS configuration, allowing for insecure connections based on environment variables.
- Updated UI elements for better user experience, including resizing and wrapping labels for status messages.
- Enhanced the enrollment process with improved error handling and status updates.
- Introduced new environment variables and command-line options for running the agent without a GUI, catering to environments like VMs or RDP.
- Updated README and build scripts to reflect new features and requirements.
- Added audio stream handling to the agent, including support for audio start and stop messages.
- Introduced lifecycle callbacks for consent and session management in the agent configuration.
- Updated desktop handling to utilize new consent handler and session start/end callbacks.
- Improved UI to reflect enrollment status and consent requests, enhancing user experience.
- Refactored branding structure to include additional color properties for better theming.
Restore POST /api/peers/{id}/restore removed in a9e217c, add signal tests
that managed mode queues pending_device_* while locked does not, and move
RustDesk enrollment controls to Settings with full approval (folder, groups,
tag picker) wired through the registrations flow.
Refactor mouse wheel event handling across the application to normalize delta values to ±1 for both axes. This change ensures consistent behavior when scrolling, particularly when the Shift key is held. Update related functions to utilize the new normalization logic, improving compatibility with the RustDesk Flutter client.
Expose global connection strategy in the web panel with systemd/Docker persistence, extend server health diagnostics, enforce org network policy in the signal handler, and document when relay fallback is expected vs misconfiguration.
Merge .env keys instead of replacing files, patch systemd/NSSM in place,
block panel updates when the server cannot be rebuilt, and stop routine
updates from syncing admin passwords from .env into auth.db/PostgreSQL.
Node requireAuth only accepts 64-char auth.db tokens, so local /api/group
handlers returned 401 after Go JWT login. Proxy group routes to Go again,
always merge peer-tag groups, resolve auth.db path candidates, and map
console user ids for group ACL checks.
Panel groups and folders live in console auth.db, but Go /api/group only
exposed peer tags. Read auth.db (AUTH_DB_PATH), apply allowed_users and
user-group access, and mirror Node getRustDeskDeviceGroups. Keep /api/group
on the Node :21121 proxy for compatibility; document verification steps.
RustDesk clients could not log in after v3 when handlers moved to Go but
nothing listened on :21121 (API_ENABLED off) or Go bound the wrong port.
Go serves /api/login on 21114; the console proxies legacy :21121 URLs to
Go. Installers repair .env/systemd, Docker publishes both ports, and
firewall rules allow 21114 and 21121 on full installs.
Also includes betterdesk-support-agent (Fyne desktop helper) and
docs/important/ operator notes (API ports, update flow, agent roadmap).
Implement new functions to list, acknowledge, and resolve help requests via the API. The listHelpRequests function supports filtering by status and device ID, while acknowledgeHelpRequest and resolveHelpRequest handle acknowledgment and resolution of specific requests. These additions enhance the help request management capabilities in the application.
Utils.api already unwraps API data, so the file list was always empty.
Add fixed paths for session secret, audit log, build.env, and Docker files.
Co-authored-by: Cursor <cursoragent@cursor.com>
Let server.config admins edit .env, blocklist, and systemd units from the
panel with backups on save and service restart via the existing updater.
Co-authored-by: Cursor <cursoragent@cursor.com>
Node now persists auth_provider on SSO provisioning and re-syncs role/provider after Go login success, including SQLite auth.db backfill. Go login returns auth_provider; LDAP group mapping accepts CN keys and newlines. Default agent build cache under dataDir to avoid EACCES.
Co-authored-by: Cursor <cursoragent@cursor.com>
Re-add floating toolbar handlers and returnToDevices() accidentally dropped during the monitor-switching refactor, so the remote.ejs UI works again without duplicate panel tabs.
Co-authored-by: Cursor <cursoragent@cursor.com>
Add auth_provider attribute (local/ldap/oidc) to User model across SQLite and PostgreSQL with automatic migration (existing accounts default to local). Rewrite login to be provider-bound: LDAP-backed accounts never fall through to local password verification, OIDC accounts reject password login, and LDAP/OIDC provisioning stores an unusable random local password instead of the provider password. LDAP/OIDC accounts always re-apply provider role mapping on every login so a matching local account can no longer override AD-mapped permissions. Guard handleUpdateUser against setting a local password on non-local accounts. Propagate auth_provider through the Node.js sync layer and panel API, add a Provider column with badges in the users table, hide local password reset for provider-managed accounts, and add EN/PL i18n keys.
This commit was made possible thanks to Insolve.
Redesign the console backup into a complete .tar.gz disaster-recovery bundle that can be restored on a fresh machine to bring the entire server back online. Adds zero-dependency tar.gz writer/reader (backupArchive.js), logical dumpAllTables/importAllTables for SQLite and PostgreSQL, and packs the console database, auth.db, .env, .session_secret, branding uploads, and Go server identity (Ed25519 keys, .api_key, db_v2.sqlite3) plus a recovery README. Restore auto-detects archive vs legacy JSON snapshot, supports per-component selection (database/uploads/secrets/.env/Go DB) with restart-required signaling, and warns that the archive contains secrets. EN/PL/ZH i18n added.
This commit was made possible thanks to Insolve.
Add console wallpaper, login page branding (title/subtitle/background/footer), global agent download page branding, footer/copyright with 'Powered by BetterDesk' toggle, and custom CSS injection. Includes background image upload route (8MB), sanitization for colors/gradients/CSS, dynamic theme CSS for app and login pages, sidebar attribution, and EN/PL/ZH i18n keys.
This commit was made possible thanks to Insolve.
Issue #148 reporter @Kreol13 confirmed LDAP test connection works but
actual login fails with 'user not found in database'. Root cause: the
Node.js console only delegates unknown-user logins to the Go server when
BETTERDESK_AUTH_AUTOCREATE=true is set explicitly (security audit fix
H-01). When LDAP/OIDC is configured via the UI, that env var is not set,
so LDAP/OIDC users who don't have a local account are rejected before
Go gets a chance to authenticate them.
Go server:
- New public endpoint GET /api/auth/sso/status returning
{ldap_enabled, oidc_enabled, any_enabled}.
- Restored missing POST /api/auth/oidc/exchange route registration and
authMiddleware whitelist (regression from earlier OIDC hardening).
Node.js console:
- authService.getGoSSOStatus() — cached 60s lookup of Go SSO state.
- authenticate() now auto-provisions LDAP/OIDC-authenticated users when
any SSO provider is enabled on Go, in addition to the existing
BETTERDESK_AUTH_AUTOCREATE opt-in. Default role drops from 'admin' to
'viewer' for safer first-login provisioning when Go does not return
an explicit role mapping. Logs name which provider triggered
provisioning for audit clarity.
The H-01 concern (compromised Go server auto-provisioning admins) is
mitigated because enabling LDAP/OIDC requires an admin with the
server.config permission to explicitly opt in via the Authentication
settings tab.
This commit was made possible thanks to Insolve.
The in-app updater computed changed files via the GitHub compare API, whose 'files' array is capped at 300 entries. Large updates were truncated, so changed Go callee files (codec/ws.go, peer/map.go, auth/ldap.go, auth/oidc.go) were never downloaded, leaving inconsistent on-disk source that failed 'go build' with 'undefined' errors. ensureServerSource() also short-circuited whenever go.mod existed, so the full-resync safety net never ran.
Fixes: ensureServerSource() gains a force option that performs a full, consistent source resync (git clone / tree API) regardless of go.mod presence; called with force from applyUpdate's server block and from rebuildServerBinary (the 'Rebuild server binary' button).
Installer parity (same #158 class): betterdesk.sh, betterdesk.ps1 and betterdesk-docker.sh copied the source *directory* into the destination, which nested the new tree inside the stale dir when the pre-update rename/mv failed on a locked file. They now copy *contents* into a guaranteed-existing dir while preserving data/ and node_modules/.
This commit was made possible thanks to Insolve.
Phases 1-4 of the Go-server centralization plan plus optional-TLS transport.
Go server:
- db: HelpRequest model + SQLite/PostgreSQL stores (help_requests_*.go), GetDeviceOrgID.
- cdap: handleHelpRequest/handleChatMessage handlers, SendChatToDevice delivery.
- api: REST help endpoints (help_handlers.go), publish help_request/chat_message events.
Node.js panel:
- bd-api.routes.js: drop local in-memory Maps, proxy all help/chat/notification
endpoints to the Go server (read-proxy) with status/id/timestamp normalization.
Agent (native Go + Tauri sidecar):
- config.go/agent.go: optional EnforceTLS, ServerCertPin (SPKI pin), TLSInsecureSkipVerify
with env overlays and dialOptions() cert pinning via VerifyPeerCertificate.
- HTTP (ws://) stays a fully supported transport: TLS enforcement is an explicit
operator opt-in (never auto-derived from the URL scheme). The agent logs a warning
recommending wss:// for untrusted networks instead of blocking the connection.
- config.rs/sidecar.rs: propagate enforce_tls + server_cert_pin from AgentConfig
through SidecarConfig to the Go agent config; warn on plaintext ws:// to remote hosts.
This commit was made possible thanks to Insolve.
Implement RustDesk-desktop-style display switching and virtual display management in the web remote client.
- client.js: rewrite switchMonitor to send SwitchDisplay + CaptureDisplays messages with keyframe refresh; getMonitors marks primary (origin 0,0) and current display; add getCurrentDisplay, _processPeerInfo, _parseVirtualDisplaySupport, getVirtualDisplaySupport, toggleVirtualDisplay; switchDisplay receive handler tracks current display.
- remote.js: add i18n helper, peer_info/display_switched/virtual_display_toggled listeners, refreshMonitorButton; rewrite updateMonitorMenu with monitor list plus rustdesk_idd (4 checkboxes) and amyuni_idd (+/- counter) virtual display controls and plug-out-all.
- remote.css: styles for divider, active monitor/virtual items, counter controls.
- i18n: add virtual_displays, virtual_display, plug_out_all keys to en/pl/zh.
This commit was made possible thanks to Insolve.
Both the expanded pill and the compact handle use backdrop-filter, which creates separate stacking contexts. With auto z-index, paint order followed DOM order, so the handle (rendered last) covered the dropdown menus that extend down into its region — the dropdown z-index:60 only applied inside the pill's own context. Give the pill z-index:2 and the handle z-index:1 so dropdowns appear in front of the handle and stay clickable.
This commit was made possible thanks to Insolve.
The expanded toolbar pill used overflow-x:auto, which per the CSS spec forces overflow-y to auto as well. This clipped the absolutely-positioned dropdown menus (Actions, Display, Monitors) that open below the pill (top:100%), making them invisible and unclickable. Switch the pill to overflow:visible and let its buttons wrap (flex-wrap) so the bar stays bounded on small screens without a scroll container that traps the dropdowns.
This commit was made possible thanks to Insolve.
Redesign the web remote toolbar into a RustDesk-style floating pill. The compact handle (move / fullscreen / expand) is always visible and the action pill no longer auto-opens on hover - it expands only on an explicit click of the expand button. The left handle drags the toolbar horizontally along one axis. The 'back to devices' control is now a button that closes the script-opened rdclient tab (re-focusing the opener) instead of navigating this tab to /devices, which previously spawned duplicate web-panel tabs. closeSession uses the same returnToDevices() helper. Adds move_toolbar/toggle_menu i18n keys (EN/PL/ZH) and an updated cross-platform feature matrix.
This commit was made possible thanks to Insolve.
Complete the web console locale set so all 26 language files share the EN/PL baseline with no missing keys, extra keys, empty values, or English fallback values.
Keep strict i18n audit behavior and disabled auto-fix flow so incomplete translations are surfaced for manual review instead of being filled with English fallback text.
Validated with the strict web-nodejs i18n audit, JSON parsing for all locale files, placeholder preservation checks, and VS Code diagnostics.
This commit was made possible thanks to Insolve.
Bump golang.org/x/crypto to v0.52.0 and x/sys to v0.45.0 in betterdesk-server, x/sys to v0.45.0 in betterdesk-agent, and aiohttp to >=3.10.11 in the rest-webhook bridge. Validated clean via govulncheck.
Add an explicit Go server rebuild path to the in-app updater so security/library updates actually reach the running binary: updateService now marks the server binary stale when the source changed but a non-critical rebuild/deploy step failed, exposes getServerBinaryStatus() and rebuildServerBinary(), and surfaces a warning banner plus a Rebuild button in the settings update panel. New REST endpoints GET/POST /api/settings/updates/server-binary/{status,rebuild} guarded by server.config permission. i18n keys added across all locales.
This commit was made possible thanks to Insolve.
serializeMessage used Message.create() which does NOT convert string enum names to numeric values. A KeyEvent with controlKey:'Backspace' (or Space/Tab/Return/arrows) and mode:'Legacy' was encoded as control_key=0 (Unknown), so every non-character key was silently dropped by the peer while numeric chr letter keys worked. Switched to Message.fromObject() which converts enum name strings to their numeric values and preserves Uint8Array byte fields (clipboard content, file blocks, password, public keys). Verified round-trip: Backspace->2, Space->30, mode Legacy->0, bytes/login/file-block/mouse intact.
This commit was made possible thanks to Insolve.
Fixes four reported web remote client defects:
- FPS stats stuck at 0: _handleDecodedFrame (WebCodecs/HTTPS path) now records feed timestamps so getStats() reports real FPS (previously only the JMuxer fallback fed the array).
- Top toolbar never auto-hid: mousemove handler no longer re-shows the bar on every move (removed '|| toolbarVisible'), so it hides 3s after the pointer leaves the top edge.
- Keyboard support: rewritten Legacy-mode key handling with full-Unicode codePointAt, AltGr awareness (strips phantom Ctrl+Alt for printable glyphs so accented/national chars type correctly), and proper down/press/up repeat semantics matching RustDesk.
- Clipboard: local clipboard is now auto-pushed to the remote on window focus and viewer mousedown, so a plain Ctrl+V on the remote pastes local content like native RustDesk.
This commit was made possible thanks to Insolve.
The controlled RustDesk peer periodically sends TestDelay{from_client:false} as a QoS probe and measures its round-trip to size both target bitrate and framerate. We were replying with a fresh buildTestDelay() (new timestamp), so the peer computed a bogus delay and throttled the stream down to ~1 fps. Now we echo the probe back verbatim (same time, from_client stays false), restoring the full 24-30 fps stream like the native client.
This commit was made possible thanks to Insolve.
RDVideo.getSupportedCodecs() probed only with hardwareAcceleration:'prefer-hardware', so on machines without hardware decode for VP9/AV1/VP8/H265 every codec except Auto was reported unsupported. That both grayed out the whole codec menu and made the client advertise only H264 in its SupportedDecoding at login, so the peer never offered another codec. Now probe prefer-hardware, then prefer-software, then no-preference and treat a codec as supported if ANY variant works.
This commit was made possible thanks to Insolve.
Remove the user-mode / rootless install and privilege-separation scaffolding that was added in the previous commit — the feature needs more testing before shipping. Reverts: USER_MODE, RUN_AS_ROOT, ensure_service_user, set_service_ownership, systemd User=/Group= directives, all --user/--rootless/--run-as-root CLI flags, and related help text. Restores API_PORT default to 21114.
server.js: fix startup banner — show 'Disabled' (not 'Served by Go server') when apiEnabled is false, and correct the Go API fallback URL from port 21121 to 21114.
This commit was made possible thanks to Insolve.
Add a codec selector (Auto/VP9/AV1/H264/VP8/H265) to the remote viewer display menu so users can request a GPU-friendly codec from the peer. setCodec() re-advertises SupportedDecoding abilities with the chosen prefer codec via OptionMessage and forces a fresh keyframe. Browser decode abilities are probed (VideoDecoder.isConfigSupported) and unsupported codecs are disabled in the menu and advertised honestly at login. Raise default image quality from Balanced to Best at both client-creation sites and pause adaptive auto-quality once the user makes a manual quality/codec choice so it no longer overrides them.
This commit was made possible thanks to Insolve.
WebCodecs VideoDecoder must receive a keyframe as the first chunk after configure(); a leading delta frame throws and the decoder produces no output. Because delta frames kept resetting the last-frame timer, the 3s stall-recovery never fired and the keyframe was never requested, leaving a permanent black screen (and 0x0 remote size, which also broke input mapping).
video.js: drop delta frames until a keyframe arrives (_needKeyframe gate after configure), use a dedicated monotonic input counter for chunk timestamps, drop deltas when the decode queue backs up, and rebuild the decoder with a software-decoding fallback + keyframe request on decoder error. init() now probes codec support without forcing hardware acceleration so software-only environments still pass.
client.js: wire video.onNeedKeyframe to send refresh_video, request an initial keyframe at session start, and extend stall-recovery to also fire when peer frames arrive but the decoder yields no output for 2s.
This commit was made possible thanks to Insolve.
Brings in GPU multi-codec video for the CDAP rdclient (HTTP H.264 via MSE, HTTPS AV1/VP9/H264 via WebCodecs), agent-client runtime branding, and the prebuilt Windows sidecar binary.
This commit was made possible thanks to Insolve.
Enables hardware-accelerated video for the CDAP/OS-agent rdclient path instead of being stuck on 1-2fps MJPEG, including over plain HTTP (no HTTPS/domain/cert required).
Browser viewers (web-nodejs): cdap-adapter.js and cdap-desktop.js now decode video via the shared RDVideo pipeline. WebCodecs (AV1/VP9/H264, hardware-accelerated) is used in secure contexts (HTTPS/localhost); over plain HTTP, H.264 is decoded through JMuxer/MSE using the browser's native (usually GPU) decoder. Viewers advertise decodableCodecs() in the desktop_start init payload so the agent sends a real GPU stream; MJPEG/WebP remain safe fallbacks. cdap-device.ejs preloads jmuxer + RDVideo.
Go agent (betterdesk-agent): codec.go + codec_framing.go add multi-codec engine with GPU encoder probing and selection order AV1 -> VP9 -> H264 -> WebP; desktop.go intersects the operator-advertised codec list with local encoder ability and emits honest desktop_meta (format + codec_string); config.go adds codec config fields.
Go server (betterdesk-server): cdap_handlers.go + cdap/desktop.go thread the codecs[] / video_codec fields through desktop_start so operator codec advertisement reaches the agent.
Tauri agent-client: config.rs/sidecar.rs/commands.rs codec plumbing, SettingsPanel codec dropdown + en/pl/zh i18n, refreshed agent sidecar binary.
Note: AV1/VP9 still require WebCodecs (HTTPS/localhost); H.264-over-MSE covers the plain-HTTP case. Self-signed HTTPS (installer SSL menu option C) unlocks all codecs. Fully backward compatible.
This commit was made possible thanks to Insolve.
Add a device verification/enrollment workflow so new registrations are
held for operator review instead of connecting silently.
Go server (signal + api):
- Signal-mode pending: in 'managed' mode, unknown stock RustDesk clients
are queued (pending_device_<id>) instead of being silently rejected.
- Rich approve: handleApproveDevice accepts display_name, sync_mode and
normalized tags; handleRejectDevice supports an optional ban.
Node.js console:
- registrations.ejs approve modal (name, sync mode, tags, folder) and
reject modal with ban option; betterdeskApi + routes wire display_name,
sync_mode, tags and folder assignment through to the Go server.
- EN/PL/ZH i18n for all new strings.
Installers (managed default for FRESH installs only; existing installs
stay on the Go default 'open' or their DB-persisted mode):
- betterdesk.sh / betterdesk.ps1 write ENROLLMENT_MODE=managed to the
server env only when no existing database is detected (FRESH_INSTALL).
- Docker single + multi container entrypoints detect fresh volumes via a
sentinel plus id_ed25519/db_v2.sqlite3 presence and default to managed;
ENROLLMENT_MODE is now passed through supervisord and all compose files.
This commit was made possible thanks to Insolve.
Includes: alpha bundle generator, cross-platform build pipeline, RustDesk-client API endpoints on Go server (Phase A), privilege separation across all installers (systemd hardening, NSSM virtual accounts, Docker verified).
This commit was made possible thanks to Insolve.
Privilege separation across all installers so the long-running services no longer run with full administrative rights:
betterdesk.sh: installer keeps root but systemd units now run as a dedicated unprivileged 'betterdesk' system account by default (auto-created via ensure_service_user). Added full systemd hardening for the Go server (NoNewPrivileges, ProtectSystem=strict, ProtectHome, PrivateTmp, ReadWritePaths) and light hardening for the Node.js console. chown migrates existing root-owned data to the service account on update. Opt-out via --run-as-root / BETTERDESK_RUN_AS_ROOT=1; custom account via BETTERDESK_SERVICE_USER. Minimal mode covered too.
betterdesk.ps1: NSSM services now run under their per-service low-privilege virtual accounts (NT SERVICE\<service>) instead of LocalSystem, with scoped icacls grants on the install/data dirs (Set-ServiceLeastPrivilege helper). Applied to the Go server, Node.js console and minimal-mode service. Opt-out via -RunAsRoot / BETTERDESK_RUN_AS_ROOT=1.
Docker: verified already privilege-separated (supervisord drops both programs to user=betterdesk; multi-container images drop via su-exec).
Also bundles in-progress changes to the Go server API, Node.js console services and Docker compose/Dockerfiles.
This commit was made possible thanks to Insolve.
The policies/attestation page routes used a local requireAdmin that only accepted the literal role 'admin' and redirected rejected users to /dashboard. Under the new 6-role RBAC (Phase 52b), fresh-install admins have role 'super_admin', so the check failed and users were redirected to /dashboard, which has no route and renders the 404 page.
Replaced the local middleware in policies.routes.js with the shared RBAC-aware requireAuth/requireAdmin from middleware/auth.js (accepts super_admin/admin/global_admin, renders errors/403 for others instead of a 404 redirect). Fixed the same outdated role==='admin' checks in reports.routes.js, tenants.routes.js, and cdap-studio.routes.js using isSuperAdminRole.
This commit was made possible thanks to Insolve.