UI: group chip buttons changed to icon-only (matching folder chips), Create Group tile styling unified with Create Folder, old .group-chip-action CSS replaced with unified .chip-action class.
Go server: peerResponse and singlePeerResponse now return status as int (1=active, 0=disabled) instead of string, added status_text for admin panel backward compat. Fixes RustDesk client crash 'type String is not a subtype of type int?'.
Go server: new handleUsersWithClientFallback — detects RustDesk client requests to /api/users and returns current user without requiring user.view permission. Fixes disappearing folders/groups caused by _getUsers() 403 short-circuiting _pull().
Node.js: normalisePeer updated to use status_text fallback for status_tier.
Branding: RustDesk Server Management -> BetterDesk Server Management across themes, i18n, settings. Console version bumped to 3.0.0.
This commit was made possible thanks to Insolve.
Removes browsing-topics=() directive which caused console warnings in browsers
that don't support this non-standard feature.
This commit was made possible thanks to Insolve.
- Remove string 'id' from group payload (caused Dart 'String is not subtype of int?' crash)
- Add team.peers array with device IDs, access_perm, sort to group response
- Change device filter from online-only to all non-banned/non-disabled peers
- Fix peer format: nested info object, int status, bool online (matching Go server)
- Enable includeDevices for admin/operator AB sync (60 peers now visible)
- Collect peer_ids from device_group_members and device_folder_assignments
This commit was made possible thanks to Insolve.
- Add menu option T (Toggle HTTP/HTTPS) to betterdesk.sh and betterdesk.ps1
- Add --protocol http|https CLI flag (bash) and -Protocol param (PS1)
- Toggle updates .env, systemd/NSSM services, and Go server TLS flags
- Auto-generate self-signed cert when switching to HTTPS if none exists
- Go API (:21114) always stays HTTP (internal Node.js<->Go communication)
- Fix betterdeskApi.js: conditional httpsAgent/httpAgent based on URL scheme
- Fix deviceStatusPush.js: TLS options only applied for wss:// connections
This commit was made possible thanks to Insolve.
5 root causes identified and fixed:
- H1: /api/peers returned status as string 'ONLINE' — RustDesk PeerPayload expects int (1=active, 0=disabled). New handleClientPeersList() detects client requests (?accessible/?pageSize) and returns {total,data} envelope with PeerPayload format (nested info map, int status).
- H2: mergeAdminTagsIntoAB() used typed struct → silently dropped tag_colors field. Rewritten with map[string]any to preserve all AB fields.
- H3: /api/device-group/accessible missing (404) — RustDesk GroupModel._getDeviceGroups() calls this endpoint. Added route pointing to handleClientGroupList.
- H4: /api/users returned plain array with wrong field names — RustDesk expects {total,data} with UserPayload (name, display_name, status:int, is_admin:bool). New handleClientUsersList() returns correct format.
- L1: .group-chip.chip-add CSS didn't fully override base 3-column grid. Added explicit grid/centering/dashed-border overrides.
Also updated handleClientGroupPeers (/api/peers/list) to use PeerPayload format with nested info map for consistency.
This commit was made possible thanks to Insolve.
Sidebar footer previously rendered the raw DB role string (e.g. 'global_admin'). Now resolves user.role to users.role_<role> translation key with snake_case->Title Case fallback when key is missing.
Node.js (no recompile required):
- H-04: RUSTDESK_API_DISABLE_TOTP now requires explicit _ACKNOWLEDGED flag
- M-03: drop Referer-based skip from apiLimiter; add dedicated widgetLimiter
- M-06: gate /api/system/info, /logs/recent, /database/stats, /docker/containers, /speed-test behind requirePermission('metrics.view')
- L-01: startup banner now warns when TRUST_PROXY is off in production / errors when TOTP bypass is set without acknowledgement
Scripts / Docker:
- M-04: betterdesk.sh migration tool invocation switched from eval(cmd-string) to bash array exec
- M-05: all admin / PostgreSQL password generators switched from openssl rand -base64+tr+head to openssl rand -hex 16 (full entropy)
- L-02: docker-compose.yml / single.yml / quick.yml services gain security_opt: no-new-privileges and cap_drop: ALL
Documentation:
- I-04: add SECURITY.md (supported versions, reporting channels, SLA, scope, hardening defaults)
Go server (requires rebuild on host: cd betterdesk-server && go build ./...):
- H-03: /metrics now gated by METRICS_IP_ALLOWLIST / METRICS_PUBLIC; per-username login + 2FA rate-limit added on top of per-IP
- M-07: enrollment (/api/devices/register*) and branding (GET /api/branding) endpoints rate-limited per IP
- I-02: bd-mgmt WebSocket gets SetReadLimit(16 MiB) to bound memory
- L-04: auth middleware skips noisy public probes and redacts /peers/{id} segments
This commit was made possible thanks to Insolve.
- Updated Polish and Chinese language files to reflect changes in update descriptions and confirmations.
- Modified settings.js to automatically include server updates in the update process.
- Simplified the update installation API to automatically handle server updates without user intervention.
- Improved updateService.js to ensure all supported components are updated together, enhancing reliability.
- Added logic to check and install the Go toolchain as needed during updates, ensuring compatibility.
- Enhanced error handling and logging during the update process for better visibility of issues.
Make device group edit/delete actions visible, add direct user-group management entry points from the device group ACL modal, and allow device group editors to load user groups for ACL assignment.
Refs #140.
- Implemented user groups creation, editing, and deletion features.
- Added API endpoints for managing user groups: create, update, and delete.
- Enhanced user interface with a dedicated user groups manager section.
- Updated translations for user groups related strings in multiple languages.
- Improved CSS styles for user groups display and actions.
- Added tests for user groups API functionality.
- Added user group membership functionality, allowing users to be assigned to groups.
- Introduced validation for group GUIDs and enhanced error handling in user routes.
- Updated device group routes to support allowed user groups, enabling better access control for devices.
- Enhanced database schema to include user group memberships and device group user group access.
- Updated services and database adapters to handle user group data and relationships.
- Modified front-end views to display and manage user groups effectively.
- Added tests to ensure proper functionality of user group assignments and device access control.
Prevent the RustDesk Client API address-book response from auto-creating peers from console inventory. Existing address-book peers still receive console-side tag synchronization, while /api/peers remains the inventory endpoint.
Refs: #137
Reported-by: @boruto79
Also-reported-by: @karabelnikov
Adds explicit RUSTDESK_API_TLS mode so self-signed deployments can keep the web panel HTTPS while serving the RustDesk Client API on HTTP for stock client compatibility.
Refs #138
Address the follow-up feedback on #136 about the user-management UX
feeling disjointed and using non-standard role labels:
- Rename the base organization role from "Viewer"/"User" to
"Member" across all 26 locales. This removes the vocabulary
collision with the server-wide "Viewer" role and makes the
two-axis role model (server role vs per-org role) clearer.
- Add an Organizations column to the System > Users table so
admins can see each user's org memberships at a glance without
opening a modal. Badges are clickable and reopen the existing
Organizations modal for that user.
- Clarify the Add/Edit User form: the role dropdown is now
labelled "Server Role" with a hint that per-organization roles
are configured separately under each user's Organizations.
- The Organizations modal now has an explicit section heading and
a short hint, and uses i18n labels for the role dropdown with
an aria-label so the org role is visually distinct from the
server role.
Backend role values are unchanged ("user"/"operator"/"admin"/
"owner" for orgs, existing 7 server roles), so this is a pure
UX/i18n change with no schema or API impact.
Refs: #136
Suggested-by: Sterlyn Kong <68391309+SterlynKong@users.noreply.github.com>
Complete Czech console translation from PR #133, update Docker quick-start admin credential lookup from PR #134, and fix live device-status updates found while reviewing PR #35.
Refs: #133, #134, #35
Co-authored-by: Karel Lowprize K <lowprize@gmail.com>
Suggested-by: Rafael Monteiro <96262868+rafaelgm@users.noreply.github.com>
- Implemented server management service providing resource snapshots, file browser, service control, and audit logging.
- Added terminal proxy for WebSocket-backed PTY, allowing browser-based shell access with user authentication and role-based access control.
- Created server management view with tabs for overview, terminal, file management, and services, including UI elements for displaying system metrics and managing files/services.
Co-authored-by: Copilot <copilot@github.com>
Render language choices dynamically from loaded locale files instead of hardcoding EN/PL. Add selectors on the navbar, login screen, and desktop login screen so users can choose a language before or after authentication.
Persist the selected language in the browser via cookie/localStorage and on authenticated accounts via users.preferred_language. Merge client translations with the default locale fallback so incomplete language files do not surface raw keys in the UI.
Panel accounts were created only in the Node auth.db while the
Organization 'Add User -> Add Existing' dropdown queries the Go
server's users table via db.ListUsersNotInOrg(). This produced a
mismatch where only the seeded admin appeared as linkable.
Add a userSync service that mirrors create / update / delete /
password-reset operations from the Node panel to the Go server's
/api/users endpoints, plus a one-shot backfill at startup that
creates Go-side records for any pre-existing panel users (random
throwaway password; Node bcrypt remains authoritative for panel
login).
Reported-by: SterlynKong <SterlynKong@users.noreply.github.com>
Backfill missing web console translation keys across all available locale files using the English and Polish catalogs as the completeness baseline.
Also normalize the locale JSON files so they parse consistently during tooling and validation.
Three independent bugs combined to make 'Add Relay' silently do nothing:
1. saveRelay() in public/js/scaling.js used raw fetch() without the panel's
CSRF token, so every POST/PUT/DELETE was rejected by the CSRF middleware.
2. The catch block swallowed every error and the modal closed regardless of
the response code, so the user never saw the rejection.
3. The relay/rules routes proxied to /scaling/relays on the Go server, but
the Go server doesn't (yet) implement those endpoints — every request
ended up as a 404 and the panel's relay table stayed empty forever.
Changes:
- routes/scaling.routes.js: drop the dead Go proxy, persist relay-node
metadata and assignment rules in the existing 'settings' table
(auth.db / DATABASE_URL). Strict input validation (host:port,
Unicode-safe name, ranges for sessions/bandwidth/priority), 409 on
duplicate addresses, capped at 100 relays / 200 rules. Health and
metrics endpoints now return explicit 'live telemetry not yet
available' placeholders instead of 500s.
- public/js/scaling.js: every mutating fetch() now sends x-csrf-token,
validates inputs with a toast, parses JSON error bodies, only closes
the modal and reloads on success. Falls back to alert() if the toast
service is missing so users are never left guessing.
- views/scaling.ejs: info banner above the Relay Nodes table making
clear that the list is operator-side metadata and that actual relay
routing is configured via RELAY_SERVERS on the BetterDesk server.
- public/css/scaling.css: styles for the new info banner.
- lang/en.json, lang/pl.json: i18n keys for the banner, validation
messages and success/failure toasts.
Note: the reporter also tried RELAY=... — only RELAY_SERVERS is read
by the Go server (see config/config.go:160). Worth surfacing in docs
in a follow-up.
Reported-by: @Elektrik1
The 'Add User' modal in an organization showed Owner / Admin / Operator / User
while the server-level 'Add User' modal showed Viewer / Operator / Administrator
/ Global Admin / Server Admin / Super Admin / Pro. Same conceptual roles had
inconsistent labels (Admin vs Administrator) and the lowest tier used different
words (User vs Viewer), making the two modals look unrelated.
Server-only roles (Super Admin, Server Admin, Global Admin, Pro) intentionally
remain absent from the org modal — they are not org-scoped.
Changes:
- web-nodejs/public/js/organizationDetail.js: reorder roleOptions() to
lowest-first ['user','operator','admin','owner'] to match the server modal's
Viewer→Pro ordering pattern.
- web-nodejs/lang/*.json (26 locales): in the 'organizations' section, rename
role_user label to the localized equivalent of 'Viewer' (matching the
server section's role_viewer) and role_admin to 'Administrator' (matching
server section's role_admin). Backend enum values ('user', 'admin') are
unchanged — this is a pure label change, no schema migration required.
Reported-by: @SterlynKong
Three independent bug fixes bundled together; none of the issues are
auto-closed by this commit so the reporters can confirm on a tagged build.
#121 — NAT hairpin: same-public-IP detection forces relay path
The signal handler now distinguishes 'same /24 private subnet' (genuine
LAN, keep direct path) from 'same public IP' (NAT hairpin scenario).
When both peers register from the same public IP, both the UDP and
TCP/WS punch-hole handlers skip the LAN-address exchange and steer the
client straight to the relay — matching upstream RustDesk behavior.
Most consumer routers and cellular gateways drop hairpinned UDP, which
was causing same-Wi-Fi connections to silently time out.
- new helper isSamePublicIP() in signal/handler.go
- new config flag SameNATRelay (default: enabled)
- env: SAME_NAT_RELAY=N to opt out
- cli: --same-nat-relay=false to opt out
Reported-by: @eternalliving (#121)
#122 — Rate limit configurable, 0 disables
The 20 registrations/min/IP cap was unworkable for any deployment with
real NAT/CGNAT in front of it. Made it tunable without recompiling and
hardened the limiter to short-circuit when disabled.
- new config field SignalRateLimitPerIP (default: 20, unchanged)
- env: SIGNAL_RATE_LIMIT_PER_IP=<n> (0 = unlimited)
- cli: --signal-rate-limit-per-ip=<n>
- ratelimit.IPLimiter.Allow() now returns true early when limit<=0
Reported-by: @Henry-739 (#122)
#123 — Updater corrupting live SQLite WAL files
A previous gitleaks-fix commit accidentally tracked live SQLite sidecar
files (auth.db-shm/wal, betterdesk.db-shm/wal, .session_secret,
.update_sha). The in-app updater's diff-against-GitHub logic then
'restored' those tracked-by-mistake files on user installs, which is
exactly how SQLite database corruption gets manufactured.
Layered defense:
- .gitignore now covers *.db-shm, *.db-wal, *.sqlite3-{shm,wal,journal},
web-nodejs/data/, .session_secret, .update_sha, .api_key,
.admin_credentials, .force_password_update, .env*
- removed the offending files from the index (data/.gitkeep retained)
- updateService.js EXCLUDE_PATTERNS extended to match the new gitignore
- new write-time guard isProtectedRuntimePath() wired into all four
write sites in updateService.js so even a stale GitHub diff cannot
overwrite a runtime state file on disk
Reported-by: @SterlynKong (#123)
Adds three small but valuable compatibility patches inspired by progloto's
PR #81 analysis, reworked to fit our codebase and security baseline:
* Stub /api/group, /api/group/get, /api/peers/list with the {total,data,msg}
envelope so RustDesk PRO Flutter clients can finish their device-list
flow and gracefully fall back to address-book mode.
* Log every unmatched route in the Go API catch-all (method, path, client
IP, User-Agent) so missing client compatibility endpoints surface in ops
logs immediately.
* Mirror the same 404 logging in the Node.js RustDesk-compatible API app
and the main panel app (limited to /api/* and /ws/* paths to avoid
static-asset noise).
The original PR #81 also bundled a docker-compose.qnap.yml with hardcoded
production secrets, a private NAS deploy script, and a VERSION bump that
collided with our release cycle, so we are not merging it as-is. The
ideas behind these three patches were sound and are credited here and
in CHANGELOG.md.
Co-authored-by: progloto <progloto@users.noreply.github.com>