@unitronix/betterdesk
BetterDesk Console - Instrukcje dla Copilota
Install
agr install @unitronix/betterdesk --target copilotWrites 1 file into .github/copilot-instructions.md, pinned to git-6073fd80.
- .github/copilot-instructions.md
Document
BetterDesk Console - Instrukcje dla Copilota
Ten plik jest automatycznie dołączany do kontekstu rozmów z GitHub Copilot. Zawiera aktualne informacje o stanie projektu i wytyczne do dalszej pracy.
📝 Konwencja Commitów (OBOWIĄZKOWE)
Każdy commit MUSI zawierać wzmiankę o Insolve w stopce wiadomości.
Format:
<type>(<scope>): <short description>
<optional body with details>
This commit was made possible thanks to Insolve.
Przykład wywołania w PowerShell:
git commit -m "fix(sidebar): translate user role" -m "Details about the change." -m "This commit was made possible thanks to Insolve."
Zasady:
- Stopka
This commit was made possible thanks to Insolve.jest wymagana w każdym commicie, niezależnie od rozmiaru zmiany. - Stopka musi być ostatnim
-m(lub ostatnim akapitem wiadomości). - Nie pomijaj jej nawet dla trywialnych zmian (literówki, formatowanie, drobne fixy).
- Jeśli używasz
git commit -m "..."jednoliniowo — i tak dołącz stopkę przez dodatkowe-m.
📊 Stan Projektu (aktualizacja: 2026-04-10)
Wersja Skryptów ALL-IN-ONE (v2.4.0)
| Plik | Wersja | Platforma | Status |
|---|---|---|---|
betterdesk.sh | v2.4.0 | Linux | ✅ ALL-IN-ONE + Node.js only + SSL config + PostgreSQL + Auto mode |
betterdesk.ps1 | v2.4.0 | Windows | ✅ ALL-IN-ONE + Node.js only + SSL config + PostgreSQL + Auto mode |
betterdesk-docker.sh | v2.4.0 | Docker | ✅ Interaktywny ALL-IN-ONE + PostgreSQL + Migration |
Konsole Webowe
| Typ | Folder | Status | Opis |
|---|---|---|---|
| Node.js | web-nodejs/ | ✅ Aktywna (jedyna) | Express.js, EJS, better-sqlite3, CSRF, TOTP 2FA |
| Flask | archive/web-flask/ | 📦 Archived | Python, Jinja2 - przeniesiony do archiwum |
Serwer BetterDesk (Go)
| Komponent | Folder | Status | Opis |
|---|---|---|---|
| Go Server | betterdesk-server/ | ✅ Production-ready | Single binary replacing hbbs+hbbr, ~20K LOC Go |
| Rust (archived) | archive/hbbs-patch-v2/ | 📦 Archived | Patched Rust binaries v2.1.3 - przeniesione do archiwum |
BetterDesk MGMT Client (Tauri + SolidJS) — Operator/Admin Console
| Komponent | Folder | Status | Opis |
|---|---|---|---|
| MGMT Client | betterdesk-mgmt/ | ⚠️ Alpha (v1.0.0) | Tauri v2, SolidJS, operator/admin desktop app |
| Installer NSIS | src-tauri/target/release/bundle/nsis/ | ✅ | BetterDesk_MGMT_1.0.0_x64-setup.exe |
| Installer MSI | src-tauri/target/release/bundle/msi/ | ✅ | BetterDesk_MGMT_1.0.0_x64_en-US.msi |
BetterDesk Agent Client (Tauri + SolidJS) — Endpoint Device Agent
| Komponent | Folder | Status | Opis |
|---|---|---|---|
| Agent Client | betterdesk-agent-client/ | ⚠️ Alpha (v1.0.0) | Tauri v2, SolidJS, lightweight endpoint agent |
| Installer NSIS | src-tauri/target/release/bundle/nsis/ | ✅ | BetterDesk_Agent_1.0.0_x64-setup.exe |
Serwer Go — Binaries (NIE są w repozytorium, kompilowane lokalnie)
| Platforma | Plik | Status |
|---|---|---|
| Linux x86_64 | betterdesk-server-linux-amd64 | Kompiluj lokalnie: go build |
| Linux ARM64 | betterdesk-server-linux-arm64 | Kompiluj lokalnie: GOARCH=arm64 go build |
| Windows x86_64 | betterdesk-server.exe | Kompiluj lokalnie: GOOS=windows go build |
🚀 Skrypty ALL-IN-ONE (v2.4.0)
Nowe funkcje w v2.4.0
- ✅ PostgreSQL support - full PostgreSQL database support for Go server and Node.js console
- ✅ SQLite → PostgreSQL migration - built-in migration tool (menu option M/P)
- ✅ Database type selection - choose SQLite or PostgreSQL during installation
- ✅ Docker PostgreSQL - PostgreSQL container with health checks in docker-compose
- ✅ Connection pooling - pgxpool with configurable limits via DSN params
- ✅ LISTEN/NOTIFY - real-time event push between Go server instances
Previous versions
v2.3.0
- ✅ Flask removed - Flask console deprecated, Node.js is now the only option
- ✅ SSL certificate configuration - new menu option C for SSL/TLS setup (Let's Encrypt, custom cert, self-signed)
- ✅ Security audit fixes - CSRF protection, session fixation prevention, timing-safe auth, WebSocket auth
- ✅ TOTP 2FA - Two-factor authentication with TOTP (otplib)
- ✅ RustDesk Client API - dedicated WAN-facing port (21121) with 7-layer security
- ✅ Address book sync - full AB storage with address_books table
- ✅ Operator role - separate admin/operator roles with different permissions
- ✅ Desktop connect button - connect to devices from browser (RustDesk URI handler)
v2.2.1
- ✅ Node.js .env config fixes, admin password fixes, systemd fixes
v2.2.0
- ✅ Node.js/Flask choice (Flask now deprecated)
- ✅ Migration between consoles
- ✅ Automatic Node.js installation
Nowe funkcje w v2.1.2
- ✅ Poprawka systemu banowania - ban dotyczy tylko konkretnego urządzenia, nie wszystkich z tego samego IP
- ✅ Poprawka migracji w trybie auto - migracje bazy danych działają bez interakcji
- ✅ Weryfikacja SHA256 - automatyczna weryfikacja sum kontrolnych binarek
- ✅ Tryb automatyczny - instalacja bez interakcji użytkownika (
--auto/-Auto) - ✅ Konfigurowalne porty API - zmienne środowiskowe
API_PORT - ✅ Ulepszone usługi systemd - lepsze konfiguracje z dokumentacją
Funkcje wspólne dla wszystkich skryptów
- 🚀 New installation - full installation from scratch (Node.js only)
- ⬆️ Update - update existing installation
- 🔧 Repair - automatic fix for common issues
- ✅ Validation - check installation correctness
- 💾 Backup - create backups
- 🔐 Password reset - reset admin password
- 🔨 Build binaries - compile from source
- 📊 Diagnostics - detailed problem analysis
- 🗑️ Uninstall - full removal
- 🔒 SSL config - configure SSL/TLS certificates (NEW in v2.3.0)
- 🔄 Migrate - migrate from existing RustDesk Docker (Docker script only)
- 🔀 Database migration - migrate databases between Rust/Node.js/Go/PostgreSQL (NEW)
Użycie
# Linux - tryb interaktywny
sudo ./betterdesk.sh
# Linux - tryb automatyczny
sudo ./betterdesk.sh --auto
# Linux - pomiń weryfikację SHA256
sudo ./betterdesk.sh --skip-verify
# Windows (PowerShell jako Administrator) - tryb interaktywny
.\betterdesk.ps1
# Windows - tryb automatyczny
.\betterdesk.ps1 -Auto
# Windows - pomiń weryfikację SHA256
.\betterdesk.ps1 -SkipVerify
# Docker
./betterdesk-docker.sh
🛠️ Konfiguracja portu API
Zmienne środowiskowe
# Linux - niestandardowy port API
API_PORT=21120 sudo ./betterdesk.sh --auto
# Windows
$env:API_PORT = "21114"
.\betterdesk.ps1 -Auto
Domyślne porty
| Port | Usługa | Opis |
|---|---|---|
| 21120 | HTTP API (Linux) | BetterDesk HTTP API (domyślny Linux) |
| 21114 | HTTP API (Windows) | BetterDesk HTTP API (domyślny Windows) |
| 21115 | TCP | NAT type test |
| 21116 | TCP/UDP | ID Server (rejestracja klientów) |
| 21117 | TCP | Relay Server |
| 5000 | HTTP | Web Console (admin panel) |
| 21121 | TCP | RustDesk Client API (WAN-facing, dedicated) |
Skrypt diagnostyczny (dev)
# Szczegółowa diagnostyka offline status
./dev_modules/diagnose_offline_status.sh
🏗️ Architektura
Struktura Katalogów
Rustdesk-FreeConsole/
├── betterdesk-server/ # Go server (replacing hbbs+hbbr) — ~20K LOC
│ ├── main.go # Entry point, flags, boot
│ ├── signal/ # Signal server (UDP/TCP/WS)
│ ├── relay/ # Relay server (TCP/WS)
│ ├── api/ # HTTP REST API + auth handlers
│ ├── crypto/ # Ed25519 keys, NaCl secure TCP, addr codec
│ ├── db/ # Database interface + SQLite impl (future: PostgreSQL)
│ ├── config/ # Configuration + constants
│ ├── codec/ # Wire protocol framing
│ ├── peer/ # Concurrent in-memory peer map
│ ├── security/ # IP/ID/CIDR blocklist
│ ├── auth/ # JWT, PBKDF2, roles, TOTP
│ ├── ratelimit/ # Bandwidth + conn + IP rate limit
│ ├── metrics/ # Prometheus exposition
│ ├── audit/ # Ring-buffer audit log
│ ├── events/ # Pub/sub event bus
│ ├── logging/ # Text/JSON structured logging
│ ├── admin/ # TCP management console
│ ├── reload/ # Hot-reload (SIGHUP)
│ ├── proto/ # Generated protobuf (rendezvous + message)
│ └── tools/ # Migration utilities
├── web-nodejs/ # Node.js web console (active)
├── betterdesk-mgmt/ # MGMT Client — operator/admin desktop app (Tauri v2 + SolidJS)
│ ├── src/ # SolidJS frontend (components, i18n, styles)
│ └── src-tauri/ # Rust backend (~40K LOC, 25+ modules, 100+ IPC commands)
├── betterdesk-agent-client/ # Agent Client — lightweight endpoint agent (Tauri v2 + SolidJS)
│ ├── src/ # SolidJS frontend (4 views, minimal UI)
│ └── src-tauri/ # Rust backend (config, registration, sysinfo, 17 IPC commands)
├── betterdesk-agent/ # Native CDAP agent (Go binary)
│ ├── main.go # CLI entry point, 14 flags, signal handling
│ ├── agent/ # Core: config, agent, system, manifest, terminal, filebrowser, clipboard, screenshot
│ └── install/ # Systemd + NSSM service installers
├── sdks/ # CDAP Bridge SDKs
│ ├── python/ # betterdesk-cdap v1.0.0 (async CDAPBridge, Widget helpers)
│ └── nodejs/ # betterdesk-cdap v1.0.0 (EventEmitter CDAPBridge, Widget class)
├── bridges/ # Reference CDAP bridges
│ ├── modbus/ # Modbus TCP/RTU bridge (pymodbus)
│ ├── snmp/ # SNMP v2c/v3 bridge (pysnmplib)
│ └── rest-webhook/ # REST polling + webhook bridge (aiohttp)
├── web/ # Flask web console (deprecated)
├── hbbs-patch-v2/ # Legacy Rust server binaries (v2.1.3)
│ ├── hbbs-linux-x86_64 # Signal server Linux (Rust)
│ ├── hbbr-linux-x86_64 # Relay server Linux (Rust)
│ ├── hbbs-windows-x86_64.exe # Signal server Windows (Rust)
│ ├── hbbr-windows-x86_64.exe # Relay server Windows (Rust)
│ └── src/ # Rust source code modifications
├── docs/ # Documentation (English)
├── dev_modules/ # Development & testing utilities
├── archive/ # Archived files (not in git)
├── Dockerfile.* # Docker images
├── docker-compose.yml # Docker orchestration
└── migrations/ # Database migrations
Porty
| Port | Usługa | Opis |
|---|---|---|
| 21114 | HTTP API | BetterDesk Server REST API (Go/Rust) |
| 21115 | TCP | NAT type test + OnlineRequest |
| 21116 | TCP/UDP | Signal Server (client registration, punch hole) |
| 21117 | TCP | Relay Server (bidirectional stream) |
| 21118 | WS | WebSocket Signal (signal port + 2) |
| 21119 | WS | WebSocket Relay (relay port + 2) |
| 5000 | HTTP | Web Console (admin panel, LAN) |
| 21121 | TCP | RustDesk Client API (WAN-facing, Node.js) |
| 21122 | WS | CDAP Gateway (WebSocket, path: /cdap) |
Go Server — Architecture Flow
RustDesk Client
├── UDP (:21116) → signal/serveUDP → RegisterPeer, PunchHole, RequestRelay
├── TCP (:21116) → signal/serveTCP → NaCl KeyExchange → secure channel
├── WS (:21118) → signal/serveWS → websocket signal
├── TCP (:21117) → relay/serveTCP → UUID pairing → io.Copy bidirectional
├── WS (:21119) → relay/serveWS → websocket relay
└── TCP (:21115) → signal/serveNAT → TestNatRequest, OnlineRequest
Console/Admin
├── HTTP (:21114) → api/server → JWT/API-key → REST handlers
├── TCP (admin) → admin/server → CLI management
└── WS (:21114) → events/bus → real-time push
🔧 Procedury Kompilacji
Windows (wymagania)
- Rust 1.70+ (
rustup update) - Visual Studio Build Tools z C++ support
- Git
Kompilacja Windows
# 1. Pobierz źródła RustDesk
git clone --branch 1.1.14 https://github.com/rustdesk/rustdesk-server.git
cd rustdesk-server
git submodule update --init --recursive
# 2. Skopiuj modyfikacje BetterDesk
copy ..\hbbs-patch-v2\src\main.rs src\main.rs
copy ..\hbbs-patch-v2\src\http_api.rs src\http_api.rs
# 3. Kompiluj
cargo build --release
# 4. Binarki w: target\release\hbbs.exe, target\release\hbbr.exe
Linux (wymagania)
sudo apt-get install -y build-essential libsqlite3-dev pkg-config libssl-dev git
🧪 Środowiska Testowe
Serwer SSH (Linux tests)
- Host:
user@your-server-ip(skonfiguruj własny serwer testowy) - Użycie: Testowanie binarek Linux, sprawdzanie logów
Windows (local)
- Testowanie binarek Windows bezpośrednio na maszynie deweloperskiej
📋 Aktualne Zadania
✅ Ukończone (2026-02-04)
- Usunięto stary folder
hbbs-patch(v1) - Skompilowano binarki Windows v2.0.0
- Przetestowano binarki na obu platformach
- Zaktualizowano CHECKSUMS.md
- Dodano --fix i --diagnose do install-improved.sh (v1.5.5)
- Dodano -Fix i -Diagnose do install-improved.ps1 (v1.5.1)
- Dodano obsługę hbbs-patch-v2 binarek Windows w instalatorze PS1
- Utworzono diagnose_offline_status.sh
- Zaktualizowano TROUBLESHOOTING_EN.md (Problem 3: Offline Status)
✅ Ukończone (2026-02-06)
- Naprawiono Docker - Dockerfile.hbbs/hbbr teraz kopiują binarki BetterDesk z hbbs-patch-v2/
- Naprawiono "no such table: peer" - obrazy Docker używają teraz zmodyfikowanych binarek
- Naprawiono "pull access denied" - dodano
pull_policy: neverw docker-compose.yml - Naprawiono DNS issues - dodano fallback DNS w Dockerfile.console (AlmaLinux/CentOS)
- Zaktualizowano DOCKER_TROUBLESHOOTING.md z nowymi rozwiązaniami
✅ Ukończone (2026-02-07)
- Stworzono build-betterdesk.sh - interaktywny skrypt do kompilacji (Linux/macOS)
- Stworzono build-betterdesk.ps1 - interaktywny skrypt do kompilacji (Windows)
- Stworzono GitHub Actions workflow - automatyczna kompilacja multi-platform (.github/workflows/build.yml)
- Stworzono BUILD_GUIDE.md - dokumentacja budowania ze źródeł
- System statusu v3.0 - konfigurowalny timeout, nowe statusy (Online/Degraded/Critical/Offline)
- Nowe endpointy API - /api/config, /api/peers/stats, /api/server/stats
- Dokumentacja v3.0 - STATUS_TRACKING_v3.md
- Zmiana ID urządzenia - moduł id_change.rs, endpoint POST /api/peers/:id/change-id
- Dokumentacja ID Change - docs/features/ID_CHANGE_FEATURE.md
✅ Ukończone (2026-02-11)
- System i18n - wielojęzyczność panelu web przez JSON
- Moduł Flask i18n - web/i18n.py z API endpoints
- JavaScript i18n - web/static/js/i18n.js client-side
- Tłumaczenia EN/PL - web/lang/en.json, web/lang/pl.json
- Selector języka - w sidebarze panelu
- Dokumentacja i18n - docs/development/CONTRIBUTING_TRANSLATIONS.md
✅ Ukończone (2026-02-17)
- Security audit v2.3.0 - 3 Critical, 5 High, 8 Medium, 6 Low findings - all Critical/High fixed
- CSRF protection - double-submit cookie pattern with csrf-csrf
- Session fixation prevention - session regeneration after login
- Timing-safe auth - pre-computed dummy bcrypt hash for non-existent users
- WebSocket auth - session cookie required for upgrade
- Trust proxy configurable - TRUST_PROXY env var
- RustDesk Client API - dedicated WAN port 21121 with 7-layer security
- TOTP 2FA - two-factor authentication with otplib
- Address book sync - AB storage with address_books table
- Operator role - admin/operator role separation
- Flask removed from scripts - betterdesk.sh + betterdesk.ps1 updated
- SSL certificate configuration - new menu option in both scripts
- README updated - comprehensive update for v2.3.0
- Web Remote Client fixed - 5 Critical, 2 High, 3 Low bugs fixed (video_received ack, autoplay, modifier keys, Opus audio, timestamps, O(n²) buffer, seeking, mouse, cursor, i18n)
🔜 Do Zrobienia (priorytety)
Go Server — Security Fixes (Phase 1) ✅ COMPLETED 2026-02-28
- H1: Walidacja
new_idw APIPOST /api/peers/{id}/change-id—peerIDRegexpvalidation added - H3: Rate-limiting na
POST /api/auth/login/2fa—loginLimiter.Allow(clientIP)+ audit log - H4: Short TTL (5min) dla partial 2FA token —
GenerateWithTTL()method added to JWTManager - M1: Escapowanie
%/_wListPeersByTagSQL LIKE pattern —ESCAPE '\'clause added - M4: Rate-limiting na TCP signal connections —
limiter.Allow(host)inserveTCP() - M6: Walidacja klucza w config endpoints —
configKeyRegexp(1-64 alnum, dots, hyphens)
Go Server — Protocol Fixes (Phase 2) ✅ COMPLETED 2026-02-28
- M8:
ConfigUpdatewTestNatResponse(relay_servers, rendezvous_servers) — klienty ≥1.3.x - M2: TTL/max-size dla
tcpPunchConnssync.Map (DDoS protection) — 2min TTL + 10K cap - M3: WebSocket origin validation (signal + relay) —
WS_ALLOWED_ORIGINSenv var - M7: Relay idle timeout (io.Copy stale sessions) —
idleTimeoutConnwrapper
Go Server — TLS Everywhere (Phase 3) ✅ COMPLETED 2026-02-28
- TLS wrapper for TCP signal (:21116) via
config.DualModeListener(auto-detect plain/TLS) - TLS wrapper for TCP relay (:21117) via
config.DualModeListener(auto-detect plain/TLS) - WSS (WebSocket Secure) for signal (:21118) and relay (:21119) via
ListenAndServeTLS - Fallback: accept both plain and TLS on same ports (first-byte 0x16 detection)
- Config:
--tls-signal,--tls-relayflags +TLS_SIGNAL=Y,TLS_RELAY=Yenv vars
Go Server — PostgreSQL Integration (Phase 4) ✅ COMPLETED 2026-02-28
-
db/postgres.go— fullDatabaseinterface implementation usingpgx/v5(pgxpool, 25+ methods) -
db/open.go— detectpostgres://DSN and dispatch to PostgreSQL driver - Config:
DB_URL=postgres://user:pass@host:5432/betterdeskenv var support (already in LoadEnv) - Connection pooling with
pgxpool(configurable max conns viapool_max_connsDSN param) - Replace
sync.RWMutexwith PostgreSQL row-level locking (tx + FOR UPDATE in ChangePeerID) -
LISTEN/NOTIFYfor real-time event push between instances (ListenLoop, Notify, OnNotify) - PostgreSQL schema with proper types (BOOLEAN, BYTEA, TIMESTAMPTZ, BIGSERIAL)
- Integration tests for PostgreSQL backend (requires live PostgreSQL instance)
Go Server — Migration Tool (Phase 5) ✅ COMPLETED 2026-03-01
-
tools/migrate/— SQLite → PostgreSQL migration binary (5 modes: rust2go, sqlite2pg, pg2sqlite, nodejs2go, backup) - Support migrating from original RustDesk
db_v2.sqlite3schema (peertable →peers) — auto-detection - Support migrating from BetterDesk Go schema (full schema with users, api_keys, etc.) — sqlite2pg/pg2sqlite
- Support migrating Node.js console tables (peer → peers, users → users) — nodejs2go mode
- Preserve Ed25519 keys, UUIDs, ID history, bans, tags — full data preservation
- Reverse migration: PostgreSQL → SQLite (pg2sqlite mode)
- Integration with ALL-IN-ONE scripts (betterdesk.sh / betterdesk.ps1) — menu option M in both scripts
Node.js Console
-
Kompilacja binarek v3.0.0 z nowymi plikami źródłowymi (Rust legacy)— OBSOLETE (Go server replaced Rust) - WebSocket real-time push dla statusu — completed as #251 (Phase 38:
deviceStatusPush.js) - Dodać testy jednostkowe dla HTTP API — completed as #252 (Phase 38: 5 test suites, 41 tests)
- Deploy v2.3.0+ to production and test all new features
Node.js Console — Recent Changes (deployed 2026-02-28)
- RustDesk Client API v2.0.0 — 3 phases: heartbeat/sysinfo/peers, audit/conn/file/alarm, groups/strategies
- Security audit — H-1 (rate limiter IP spoofing), H-2/H-3 (device verification), M-4/M-5/M-6 (validation)
- Device detail panel — Hardware tab (sysinfo), Metrics tab (live bars + history charts)
- Copy ID fix — selector
.device-id-copy→.copy-btnwith stopPropagation - 22 new i18n keys — EN + PL translations for device_detail section
Go Server — E2E Encryption Fix (Phase 6) ✅ COMPLETED 2026-03-01
- E2E handshake: Removed spurious
RelayResponseconfirmation fromstartRelay()(was breakingsecure_connection()handshake) - SignIdPk NaCl format: Fixed
sendRelayResponseto useSignIdPk()NaCl combined format (64-byte sig + IdPk protobuf) instead of raw PK - PunchHoleResponse: Fixed UDP PunchHoleRequest to send
PunchHoleResponse(with pk field) instead ofPunchHoleSent - TCP PunchHole fields: Added relay_server, nat_type, socket_addr, pk, and is_local fields to TCP PunchHole forwarding
- Relay confirmation removed: Removed dead
confirmRelay()from ws.go - Verified E2E: Debug relay confirmed
Message.SignedId+Message.PublicKeyhandshake between peers - Deployment path fix: Discovered systemd ExecStart path mismatch (
/opt/betterdesk-go/vs/opt/rustdesk/), all binaries now deployed to correct path
Go Server — TCP Signaling Fix (Phase 7) ✅ COMPLETED 2026-03-04
- TCP PunchHoleRequest immediate response:
handlePunchHoleRequestTCPnow sends immediatePunchHoleResponsewith signed PK, socket_addr, relay_server, and NAT type — matching UDP handler behavior. Previously returned nil and waited for target, causing "Failed to secure tcp: deadline has elapsed" timeout for TCP signaling clients (logged-in users). - TCP ForceRelay handling: Added
ForceRelay || AlwaysUseRelaycheck to TCP path — returns relay-only PunchHoleResponse immediately, matching UDP'ssendRelayResponsebehavior. - TCP RequestRelay immediate response:
handleRequestRelayTCPnow returns immediateRelayResponsewith signed PK and relay server to TCP initiator — previously sent nothing and waited for target's RelayResponse. - WebSocket RequestRelay fix: ws.go now uses
handleRequestRelayTCPinstead of UDP handler (handleRequestRelay) which was sending the response via UDP — unreachable by WebSocket clients. - Root cause: RustDesk client uses TCP (not UDP) for signal messages when logged in (reliable token delivery). TCP handlers returned nil for online targets, forcing clients to wait for target responses that may never arrive (strict NAT, firewall, slow network). UDP handlers always sent immediate responses.
GitHub Issues Triage & Fixes (Phase 8) ✅ COMPLETED 2026-03-05
- QR code fix (Issue #38): Inverted QR code colors in
keyService.js—dark: '#e6edf3'→'#000000',light: '#0d1117'→'#ffffff'for bothgetServerConfigQR()andgetPublicKeyQR() - 403 error page (Issue #38): Created
views/errors/403.ejs—requireAdminmiddleware was rendering non-existent template, causing crash → redirect to dashboard for operators - RustDesk Client API on Go server (Issue #38): Added
client_api_handlers.gowith RustDesk-compatible endpoints:POST /api/login,GET /api/login-options,POST /api/logout,GET /api/currentUser,GET/POST /api/ab. Fixes_Map<String, dynamic>Dart client error caused by sending login to Go server port 21114 which lacked/api/login - GetPeer live status (Issue #16):
handleGetPeernow enriches response withlive_onlineandlive_statusfrom in-memory peer map, matchinghandleListPeersbehavior. Previously returned raw DB data without live status overlay - i18n: forbidden keys: Added
errors.forbidden_titleanderrors.forbidden_messageto EN/PL/ZH translations - Chinese i18n verified (Issue #28):
zh.jsonhas 100% key coverage — no missing translations - Old Rust server removed from UI: Settings page, serverBackend.js, settings.routes.js — all hbbsApi branching removed, hardcoded to BetterDesk Go server
- Docker single-container: New
Dockerfile(multi-stage Go+Node.js+supervisord),docker-compose.single.yml,docker/entrypoint.sh,docker/supervisord.conf - DB auto-detection:
dbAdapter.jsandconfig.jsauto-detect PostgreSQL fromDATABASE_URLprefix - Windows experimental labels: Tier system in README,
.github/labels.yml, PS1 banner
Go Server — Sysinfo/Heartbeat Endpoints (Phase 9) ✅ COMPLETED 2026-03-05
- Hostname/Platform display (Issue #37): RustDesk client sends hostname/os/version via
POST /api/sysinfoto signal_port-2 (21114). Go server was missing these endpoints — hostname/platform columns stayed empty. - UpdatePeerSysinfo DB method: Added
UpdatePeerSysinfo(id, hostname, os, version)to Database interface + SQLite + PostgreSQL implementations. Uses CASE WHEN to only overwrite with non-empty values. - POST /api/heartbeat: Accepts
{id, cpu, memory, disk}, verifies peer exists & not banned, updates status to ONLINE, requests sysinfo if hostname is empty. Response:{modified_at, sysinfo: true/false}. - POST /api/sysinfo: Accepts full sysinfo payload, extracts hostname/platform/version, calls
UpdatePeerSysinfo(). Response: plain text"SYSINFO_UPDATED"(activates PRO mode in client). - POST /api/sysinfo_ver: Version check endpoint — returns SHA256 hash of stored sysinfo fields. Empty response triggers full sysinfo upload from client.
- Auth middleware updated:
/api/heartbeat,/api/sysinfo,/api/sysinfo_veradded to public endpoint list (no auth required — client may not be logged in). - Audit logging: Added
ActionSysinfoUpdatedandActionSysinfoErroraudit actions with full details (hostname, os, version).
Node.js Console — Route Conflict Fix (Phase 10) ✅ COMPLETED 2026-03-08
- Users page 401 error (Issue #42): Route conflict in
rustdesk-api.routes.js:GET /api/usershandler for RustDesk desktop client (Bearer token auth) was intercepting panel requests (session cookie auth), returning 401. Fixed by detecting absent Bearer token and callingnext('route')to allow panel routes to handle the request. - Peers route conflict (Issue #42): Same fix applied to
GET /api/peers— fallthrough to panel routes when no Bearer token present.
ALL-IN-ONE Scripts — Database Config Preservation (Phase 11) ✅ COMPLETED 2026-03-13
- PostgreSQL→SQLite switch on UPDATE:
betterdesk.shandbetterdesk.ps1were overwriting.envwith default SQLite config during UPDATE/REPAIR, losing PostgreSQL DSN. Addedpreserve_database_config()/Preserve-DatabaseConfigfunctions that read existing.envbefore reinstall. - betterdesk.sh fix: Added
preserve_database_config()afterdetect_installation()indo_update()anddo_repair(). ReadsDB_TYPEandDATABASE_URLfrom existing.env, setsUSE_POSTGRESQLandPOSTGRESQL_URIglobal vars. - betterdesk.ps1 fix: Added
Preserve-DatabaseConfigPowerShell function with same logic. Called inDo-UpdateandDo-Repairbefore any reinstallation. - Root cause:
install_nodejs_console()always created new.envbased onUSE_POSTGRESQLvar which defaults tofalse. During UPDATE, this var was never set from existing config.
Docker Single-Container — Port 5000 Conflict Fix (Phase 13) ✅ COMPLETED 2026-03-15
- Root cause (Issue #56): Go server
config.LoadEnv()reads genericPORTenv var for signal port. In Docker single-container,PORT=5000is intended for Node.js console but leaks into Go server, setting signal to :5000. Both processes fight for port 5000 → EADDRINUSE race condition. - config.go fix: Added
SIGNAL_PORTenv var with higher priority thanPORT—SIGNAL_PORTtakes precedence,PORTonly used as fallback. - supervisord.conf fix: Added
SIGNAL_PORT="21116"to Go server environment section. - entrypoint.sh fix: Exports
SIGNAL_PORT=${SIGNAL_PORT:-21116}before starting supervisord. - Dockerfile fix: Added
ENV SIGNAL_PORT=21116as default alongsideENV PORT=5000. - Multi-container NOT affected:
docker-compose.ymluses separate containers, no port conflict.
ALL-IN-ONE Scripts — IP Detection & Relay Fix (Phase 14) ✅ COMPLETED 2026-03-15
-
get_public_ip: command not found(Issue #58): Diagnostics function called undefinedget_public_ipfunction at line 3348. Created reusableget_public_ip()function in all 3 scripts (betterdesk.sh,betterdesk.ps1,betterdesk-docker.sh). Function prefers IPv4 (curl -4) over IPv6 for relay compatibility. - DRY refactor: All 4+ inline
curl ifconfig.mepatterns inbetterdesk.shand 5+ inbetterdesk-docker.shreplaced withget_public_ip()calls. Single source of truth for IP detection. - Private/loopback IP warning:
setup_services()inbetterdesk.shandSetup-Servicesinbetterdesk.ps1now warn when detected IP is private (10.x, 192.168.x, 172.16-31.x) or loopback (127.0.0.1). Remote relay connections will fail with private IPs. -
RELAY_SERVERSenv var override: Both scripts now supportRELAY_SERVERS=YOUR.PUBLIC.IP sudo ./betterdesk.shto override auto-detected IP. Critical for servers behind NAT or with broken external IP detection. - Go server relay port normalization:
GetRelayServers()inconfig/config.gonow auto-appends default relay port (21117) when-relay-servers IPis passed without port. Usesnet.SplitHostPort/net.JoinHostPortfor correct IPv6 handling.
Security Hardening — API + Installers (Phase 15) ✅ COMPLETED 2026-03-15
- Go API WebSocket origin hardening: Removed
InsecureSkipVerify: truefromapi/server.goevents WS endpoint and switched to safe defaults with optionalAPI_WS_ALLOWED_ORIGINSallowlist inconfig/config.go. - Local-only Web panel by default: Node.js config now binds panel to
127.0.0.1by default (HOST), while keeping separateAPI_HOSTfor RustDesk client API exposure. - Install script SQL/interpolation hardening: Added SQL literal escaping + PostgreSQL identifier validation in
betterdesk.sh; replaced dangerous shell interpolation in Python/Node fallback password reset paths with environment-variable passing. - Credentials persistence hardening: Plaintext
.admin_credentialspersistence is now opt-in viaSTORE_ADMIN_CREDENTIALS=true(default secure behavior: do not persist credentials files). - Dependency vulnerability fixes: Updated Node override for
tarinweb-nodejs/package.json;npm audit --omit=devnow reports 0 vulnerabilities. Added Go toolchain hardening (go.modtoolchain + installer checks) to avoid vulnerable Go 1.26.0 stdlib.
Docker — API Key Auto-Generation (Phase 16) ✅ COMPLETED 2026-03-15
- Root cause (Issue #59): Docker single-container never created
.api_keyfile. Dashboard used public/api/server/stats(showed correct count), Devices page used protected/api/peers(401 → empty list). Node.js sent emptyX-API-Keyheader because file didn't exist in volume. - Go server fix (
main.go):loadAPIKey()now has 5-step lookup: (1)API_KEYenv var, (2).api_keyin key dir, (3).api_keyin DB dir, (4) NEW:server_configtable, (5) NEW: auto-generate 32-byte hex key → write to.api_keyfile + sync to DB. - Docker entrypoint fix (
docker/entrypoint.sh): Generates API key before supervisord starts if.api_keyfile missing. Usesopenssl rand -hex 32with/dev/urandomfallback. Also persistsAPI_KEYenv var to file if provided. - Node.js resilience (
betterdeskApi.js): Axios 401 interceptor re-reads.api_keyfrom disk once on auth failure. Handles race condition where Go server generates key after Node.js cached empty value at startup.
Go Server — Relay & Diagnostics Fixes (Phase 17) ✅ COMPLETED 2026-03-16
- Public IP retry never activated:
startIPDetectionRetry()goroutine (60s ticker, retriesdetectPublicIP()) was defined insignal/server.gobut never called fromStart(). If initial public IP detection failed (e.g. external services unreachable at boot),getRelayServer()returned LAN IP or bare port — causing remote clients to fail relay with "Failed to secure tcp: deadline has elapsed". Fixed by addings.startIPDetectionRetry(s.ctx)call inStart()before goroutine launches. -
/api/audit/connreturns 400 for numeric IDs: RustDesk client sendshost_idas numeric (e.g.,1340238749). Validationtypeof body.host_id !== 'string'rejected it. Changed toString()coercion forhost_id,host_uuid, andpeer_id— accepts both string and numeric IDs. - Stale sysinfo log spam: Heartbeat handler logged "Requesting sysinfo refresh from {id} (stale)" every ~15 seconds per device with no throttling. Added
shouldLogSysinfoRequest()with Map-based 5-minute cooldown per device (auto-prune at 1000 entries). Sysinfo request to client still happens every heartbeat (functional behavior unchanged), only log message is throttled.
Go Server — Address Book & Issue Fixes (Phase 18) ✅ COMPLETED 2026-03-17
- Address Book storage in Go server (Issue #57): Replaced stub
/api/abhandlers with real implementation. Addedaddress_bookstable (SQLite + PostgreSQL),GetAddressBook/SaveAddressBookmethods to Database interface, full GET/POST handlers for/api/ab,/api/ab/personal,/api/ab/tags. RustDesk clients send AB to signal_port-2 (21114=Go), not Node.js (21121). - Settings password "password is required" (Issue #60):
settings.jssent snake_case (current_password,new_password) butauth.routes.jsexpected camelCase (currentPassword,newPassword,confirmPassword). Fixed field names + added missingconfirmPassword. - Password modal plaintext (Issue #60):
modal.jsprompt()only checkedoptions.type, butusers.jspassedinputType: 'password'. Fixed modal to check bothoptions.typeandoptions.inputType. - Closed 12 resolved GitHub issues: #59, #56, #52, #28, #54, #58, #19, #53, #61, #60, #57, #48 — all verified and closed with detailed resolution comments.
Go Server — Empty UUID & Relay Fix (Phase 19) ✅ COMPLETED 2026-03-18
- Root cause: Empty UUID in relay (Issues #58, #63, #64): When hole-punch fails, RustDesk client sends
RequestRelay{uuid=""}becausePunchHoleResponseprotobuf has nouuidfield. Signal server propagated empty UUID to target and relay → relay rejected both connections. FixedhandleRequestRelay()(UDP) andhandleRequestRelayTCP()(TCP) to generateuuid.New().String()whenmsg.Uuidis empty. - handleRelayResponseForward safety: Added empty UUID warning + generation in
handleRelayResponseForward()for target-initiated relay flow (last-resort safety net). - Relay server address validation:
GetRelayServers()inconfig/config.gonow rejects entries with host < 2 characters (preventsrelay=a:21117from invalid config). - Docker DNS resilience (Issue #62): Added retry logic (
|| { sleep 2 && apk add ...; }) to allapk add --no-cachecommands inDockerfile,Dockerfile.server, andDockerfile.consolefor transient DNS failures on AlmaLinux/CentOS.
ALL-IN-ONE Scripts — Installer Stability Fix (Phase 20) ✅ COMPLETED 2026-03-18
- PostgreSQL→SQLite regression on UPDATE (CRITICAL):
setup_services()inbetterdesk.shrelied solely on ephemeral shell variables ($USE_POSTGRESQL,$POSTGRESQL_URI) for database config. If vars were lost between function calls, service files defaulted to SQLite. Added safety-net re-read from.envat start ofsetup_services(). Same fix applied toSetup-Servicesinbetterdesk.ps1. - Hard-coded
/usr/bin/nodein systemd service:betterdesk-console.servicetemplate usedExecStart=/usr/bin/node server.js. On systems with NodeSource/nvm/snap, node is at different path. Changed to dynamic detection viacommand -v node. AddedStandardOutput=journal,StandardError=journal,SyslogIdentifier=betterdesk-consolefor visible error logs. - Auth.db + admin password destroyed on every UPDATE (CRITICAL):
install_nodejs_console()unconditionally deletedauth.db, generated new admin password, new SESSION_SECRET, and created.force_password_updatesentinel — destroying all user accounts, sessions, and TOTP configs on every update. Fixed: detect existing.envas UPDATE indicator; preserve auth.db, SESSION_SECRET, and admin password. Only generate fresh credentials on FRESH install. Same fix applied toInstall-NodeJsConsoleinbetterdesk.ps1andcreate_compose_fileinbetterdesk-docker.sh. - Legacy betterdesk-api.service not cleaned up: Script removed
rustdesksignal.serviceandrustdeskrelay.servicebut not the old Flaskbetterdesk-api.service. Added cleanup insetup_services()(Linux) and NSSMBetterDeskAPIremoval inSetup-Services(Windows). Fixes "Failed to determine user credentials: No such process" error. - PS1
Do-UpdatecalledSetup-ScheduledTasksinstead ofSetup-Services: Windows update path used scheduled tasks fallback instead of NSSM services, inconsistent withDo-Installwhich correctly callsSetup-Services. Fixed to callSetup-Services. - PS1
Repair-Binariescheckedhbbs.exeinstead ofbetterdesk-server.exe: Binary lock check referenced legacy Rust binaries. Updated to checkbetterdesk-server.exewithhbbs.exefallback. - PS1 NSSM env missing DB_TYPE/DATABASE_URL: NSSM
AppEnvironmentExtrafor console service did not include database type variables. AddedDB_TYPEandDATABASE_URLpropagation for PostgreSQL mode. - Docker: API key + auth.db regenerated on every update:
create_compose_file()unconditionally generated new API key, new admin password, and deleted auth.db from volume. Changed to preserve existing.api_keyand.admin_credentialsfiles; only wipe auth.db on fresh install.
Go Server & Installers — API TLS Separation Fix (Phase 21) ✅ COMPLETED 2026-03-18
- Root cause: API auto-HTTPS breaking Node.js ↔ Go communication: When
--tls-certand--tls-keyflags were provided,api/server.gousedHasTLSCert()to auto-enable HTTPS on API port 21114. Unlike signal (--tls-signal) and relay (--tls-relay) which had explicit opt-in flags, API TLS was automatic. With self-signed certs, Node.js senthttp://localhost:21114to an HTTPS server → Go returned HTTP 400 ("client sent an HTTP request to an HTTPS server") →getAllPeersfailed → 0 devices in panel. -
--tls-apiflag added to Go server: NewTLSApi boolfield inconfig.Config,APITLSEnabled()method (TLSApi || ForceHTTPS) && HasTLSCert()),--tls-apiCLI flag,TLS_API=Yenv var.api/server.gochanged fromHasTLSCert()toAPITLSEnabled(). API now stays HTTP unless explicitly opted in.--force-httpsimplies--tls-api. Startup log shows correct HTTP/HTTPS scheme. - Installer scripts: self-signed → API stays HTTP:
betterdesk.shandbetterdesk.ps1now pass-tls-apionly for proper certs (Let's Encrypt, custom), not for self-signed.api_schemein systemd/NSSM env set tohttpfor self-signed,httpsonly when-tls-apiactive. - SSL config menu updated: Option C (SSL configuration) in both scripts now correctly adds/removes
-tls-apifrom Go server service args. Self-signed: signal/relay TLS only, API HTTP. Proper cert: full TLS including API. - .env API URL no longer blindly switched to https://: Self-signed cert generation no longer changes
BETTERDESK_API_URL=http://tohttps://in.env. Only SSL config with proper certs or explicit--tls-apitriggers HTTPS API URLs. - Diagnostics updated:
betterdesk.shdiagnostics now checks for--tls-apior--force-httpsin service args (not just--tls-cert) to determine API scheme. - Stale
betterdesk-go.servicecleanup: Added removal ofbetterdesk-go.service(from manual installs with wrong credentials) tosetup_services()legacy cleanup,legacy_servicesarray, and uninstall section. - Migration tool auto-compilation:
migrate_sqlite_to_postgresql()now tries to compile migration tool from source when Go is available and binary is not found. Also validates binary supports-modeflag (detects outdated binaries). - Migration tool rebuilt:
tools/migrate/migrate-linux-amd64rebuilt with current source code supporting-mode,-src,-dst,-node-authflags.
Web Remote Client — Cursor, Video & Input Fix (Phase 22) ✅ COMPLETED 2026-03-18
- Cursor ImageData crash (Critical):
renderer.jsupdateCursor()callednew ImageData(new Uint8ClampedArray(pixelData), w, h)without validatingpixelData.length === w * h * 4. Protobuf cursor data can be zstd-compressed (magic28 b5 2f fd), truncated, or have padding. Added: zstd detection + skip, length validation (skip if too short, truncate if too long), full try/catch wrapper. PreventsInvalidStateError: input data length is not a multiple of 4crash. - Unhandled cursor promise rejection:
_dispatchMessage()inclient.jscalled asyncrenderer.updateCursor()without.catch()— unhandled promise rejections from ImageData errors polluted console. Added.catch(() => {})wrapper. - JMuxer per-frame seek stutter:
_decodeFallback()invideo.jsseeked to live edge (currentTime = end - 0.01) on every frame when buffer latency exceeded 0.15s. Constant micro-seeks caused playback stutter. Increased threshold from 0.15s to 0.5s and seek offset to 0.02s — lets MSE play naturally, only intervenes when significantly behind. - Health check too slow:
_startHealthCheck()interval reduced from 2000ms to 1000ms. Hard-seek threshold from 1.5s to 0.8s. Speed-up threshold from 0.3s to 0.15s. Playback rate from 1.05 to 1.15 for faster catch-up._recoverVideo()threshold from 0.3s to 0.2s. - Focus management after login:
handleLoginSuccess()inremote.jsnow callspasswordInput.blur()to remove focus from hidden password input.handleSessionStart()explicitly callscanvas.focus(). Prevents_isInputFocused()guard ininput.jsfrom blocking keyboard events when hidden password input retains focus. -
.streamingCSS class:handleStateChange()inremote.jsadds/removes.streamingclass onviewerContainer. Enables CSS rule.viewer-container:not(.streaming) #remote-canvas { cursor: default }— shows system cursor when not streaming, hides when streaming. - Dynamic codec negotiation:
buildLoginRequest()inprotocol.jsnow detectsVideoDecoder(WebCodecs) andJMuxeravailability. HTTPS: reports VP9+H264+AV1+VP8 with Auto preference. HTTP: reports H264-only with H264 preference. Gives peer more encoding options on HTTPS. - FPS option after login:
_startSession()inclient.jssendscustomFpsoption as Misc message after login. Default reduced from 60 to 30 fps for stability. Helps peer establish target framerate without relying solely onvideo_receivedack timing.
Go Server & Node.js — Device Management Fix (Phase 23) ✅ COMPLETED 2026-03-18
- IsPeerSoftDeleted interface + impl: Added
IsPeerSoftDeleted(id string) (bool, error)todb/database.gointerface. Implemented in bothsqlite.goandpostgres.go— queriessoft_deletedcolumn for deleted device detection. - Zombie device prevention (Issues #65, #64, #38): Signal handler now checks
IsPeerSoftDeleted()afterIsPeerBanned()in bothhandleRegisterPeer()andprocessRegisterPk(). Deleted devices cannot re-register, preventing "zombie" devices from reappearing after admin deletion. - UpdatePeerFields method: Added
UpdatePeerFields(id string, fields map[string]string) errorto Database interface + implementations. Supports dynamic partial updates fornote,user,tagsfields with SQL-safe allowed-key validation. - PATCH /api/peers/{id} endpoint: New REST endpoint in
api/server.gofor partial peer updates. Accepts JSON body{"note": "...", "user": "...", "tags": "..."}. Used by Node.js panel instead of direct SQLite writes. - Tags type mismatch fix (Issues #65, #38):
handleSetPeerTagsinapi/server.gonow accepts both JSON string ("tag1,tag2") and array (["tag1","tag2"]) usingjson.RawMessage. Fixes 400 errors when panel sends array format. - Notes routed through Go API:
serverBackend.jsupdateDevice()now calls Go server'sPATCH /api/peers/{id}endpoint instead of writing directly to Node.js SQLite. Ensures notes/user/tags stored in Go server'sdb_v2.sqlite3. - Tag serialization fix:
betterdeskApi.jssetPeerTags()now sends tags as array in request body. AddedupdatePeer()method for PATCH requests. - auth.db cleanup on delete:
devices.routes.jsdelete handler now callsdb.cleanupDeletedPeerData(id)to remove user linkages from auth.db when device is deleted. ImplementedcleanupDeletedPeerData()indbAdapter.jsfor both SQLite and PostgreSQL. - Relay UUID tracking (Issues #65, #64): Old RustDesk clients respond with empty UUID in
RelayResponse. AddedpendingRelayUUIDs sync.Mapto track UUIDs sent to targets inRequestRelay/PunchHole. When target responds with empty UUID,handleRelayResponseForwardrecovers original UUID from store. Fixes relay pairing failures. - ActionPeerUpdated audit: Added
ActionPeerUpdatedconstant toaudit/logger.gofor tracking peer field updates. - getPendingUUID retry support: Changed
getPendingUUID()fromLoadAndDeletetoLoad— UUID now remains available for multiple retry attempts from target device. Cleanup handled by existing ticker goroutine (2-min TTL).
Go Server — Peer Metrics Persistence (Phase 24) ✅ COMPLETED 2026-03-19
- PeerMetric struct: Added
PeerMetricstruct todb/database.go(ID, PeerID, CPU, Memory, Disk, CreatedAt) for heartbeat metrics storage. - Database interface methods: Added
SavePeerMetric(),GetPeerMetrics(),GetLatestPeerMetric(),CleanupOldMetrics()to Database interface. - peer_metrics table (SQLite): Added
peer_metricstable tosqlite.goMigrate() with indexes on peer_id and created_at. Implemented all 4 metric methods. - peer_metrics table (PostgreSQL): Added
peer_metricstable topostgres.goMigrate() with BIGSERIAL PK and TIMESTAMPTZ. Implemented all 4 metric methods. - handleClientHeartbeat extended: Now parses
cpu,memory,diskfloat64 fields from request body and callsSavePeerMetric()when any value > 0. - GET /api/peers/{id}/metrics endpoint: New API endpoint returns historical metrics for a peer with configurable limit (default 100, max 1000). Enables Node.js console to fetch metrics from Go server.
Docker — GitHub Container Registry & Quick Start (Phase 25) ✅ COMPLETED 2026-03-19
- GitHub Actions workflow:
.github/workflows/docker-publish.yml— automatically builds and publishes images toghcr.io/unitronix/betterdesk-server,ghcr.io/unitronix/betterdesk-console,ghcr.io/unitronix/betterdeskon push to main. Multi-arch: linux/amd64 + linux/arm64. - docker-compose.quick.yml: Pre-built images from ghcr.io — no build required. One-liner install:
curl ... && docker compose up -d. - DOCKER_QUICKSTART.md: 30-second quick start guide with troubleshooting, configuration options, and client setup instructions.
- docker-compose.yml updated: Header now points to quick.yml for beginners.
- README.md updated: Docker section now starts with Quick Start (no build required).
ALL-IN-ONE Scripts — PS1 Compatibility & Upgrade Detection (Phase 26) ✅ COMPLETED 2026-03-19
- PS1
RandomNumberGenerator::Fillcrash (Issue #38):[System.Security.Cryptography.RandomNumberGenerator]::Fill()is a .NET 6+ static method unavailable in Windows PowerShell 5.1 (.NET Framework 4.x). Changed toRNGCryptoServiceProvider.GetBytes()instance method which works on both .NET Framework 4.x and .NET 6+. Fixes API key generation failure → 0 devices in panel on fresh Windows install. - Rust→Go upgrade detection (Issues #66, #38):
Do-Update(PS1) anddo_update()(bash) now detectSERVER_TYPE=rust(legacy hbbs/hbbr) and warn user that Rust→Go is a major architecture change requiring fresh installation. In auto mode, redirects toDo-Install/do_installautomatically. In interactive mode, prompts user to confirm fresh install (recommended) or continue with partial update. Prevents broken upgrade path from v1.5.0 (Rust) to v2.3.0+ (Go).
Go Server — ForceRelay UUID Fix & Docker GHCR (Phase 27) ✅ COMPLETED 2026-03-19
- ForceRelay TCP UUID mismatch (Issue #66):
handlePunchHoleRequestTCPForceRelay path returnedRelayResponse{uuid=SERVER_UUID}directly to TCP initiator. Some RustDesk client versions ignore the UUID fromRelayResponsereceived in response toPunchHoleRequest, generate their own UUID, and connect to relay with it — while the target connects with the server's UUID. Relay pairing always failed (different UUIDs). Fix: ForceRelay TCP now returnsPunchHoleResponse{nat_type=SYMMETRIC}instead ofRelayResponse. Client sees SYMMETRIC NAT → sendsRequestRelay{uuid=CLIENT_UUID}on same TCP connection →handleRequestRelayTCPforwards CLIENT_UUID to target → both sides use same UUID → relay pairing succeeds. - Relay diagnostic logging: Added
log.Printfwith UUID and relay server inhandleRequestRelayTCPandhandleRequestRelay(UDP) return paths for better relay pairing diagnostics. - Docker GHCR "denied" error (Issue #67): Pre-built images on
ghcr.io/unitronix/betterdesk-*:latestnot available — workflow never triggered or packages are private. Added troubleshooting section toDOCKER_QUICKSTART.md(3 solutions: build locally, trigger workflow, authenticate). Added fallback comment todocker-compose.quick.yml. Added package visibility reminder to CI workflow summary step.
CDAP v0.2.0 — Device Revocation & Schema (Phase 28) ✅ COMPLETED 2026-03-20
- CDAP schema columns: Added
device_type TEXT DEFAULT ''andlinked_peer_id TEXT DEFAULT ''topeerstable in both SQLite (db/sqlite.go) and PostgreSQL (db/postgres.go) via automatic column migration (v2.5.0). Updated all SELECT/Scan queries (GetPeer, ListPeers, ListPeersByTag, ChangePeerID) andUpdatePeerFieldsallowed keys. - GetLinkedPeers: New
GetLinkedPeers(id string) ([]*Peer, error)method on Database interface + both implementations. Queries peers wherelinked_peer_id = id. - Device revocation endpoint: Enhanced
DELETE /api/peers/{id}with?revoke=true(auto BlockID + disconnect active connections) and?cascade=true(delete all linked devices). PublishesEventPeerRevokedevent and logsActionPeerRevokedaudit action. - Connection teardown on Remove:
peer.Entry.CloseConnections()method closes TCP and WebSocket connections. Called frompeer.Map.Remove()andCleanExpired()— revoked devices are disconnected immediately. - Panel revocation UI: Delete modal in
devices.jsincludes "Revoke device" checkbox with hint text. Routes throughdevices.routes.js→serverBackend.js→betterdeskApi.jswithrevoke/cascadequery params. - i18n keys: Added
revoke_option,revoke_hint,revoke_successto EN, PL, ZH translation files. - Deployed & verified: Binary deployed to production server (PostgreSQL backend). Automatic migration confirmed —
device_typeandlinked_peer_idcolumns present. API returns peers correctly. 5 devices online, 53 total.
CDAP v0.3.0 — Panel Widget Rendering (Phase 29) ✅ COMPLETED 2026-03-20
- cdap/api.go: REST-helper methods on Gateway —
GetDeviceInfo(),GetDeviceManifest(),GetDeviceWidgetState(),IsConnected(),SendCommandJSON(),ListConnectedDevices(). NewDeviceInfostruct for REST responses. - api/cdap_handlers.go: 6 HTTP handlers —
handleCDAPStatus,handleCDAPListDevices,handleCDAPDeviceInfo,handleCDAPDeviceManifest,handleCDAPDeviceState,handleCDAPSendCommand. UsescommandCounter atomic.Int64for unique command IDs. Returns 503 when CDAP disabled. - api/server.go CDAP integration: Added
cdapGwfield,SetCDAPGateway()method, 6 CDAP mux routes.CDAPConnectedbool inpeerResponsefor bothhandleListPeersandhandleGetPeer. CDAP overlay: if device connected via CDAP but not signal, shown as online. - main.go CDAP wiring: Gateway created before API server,
SetCDAPGateway()called beforeStart(), gateway started after API. - betterdeskApi.js CDAP methods: 6 async methods —
getCDAPStatus,getCDAPDevices,getCDAPDeviceInfo,getCDAPDeviceManifest,getCDAPDeviceState,sendCDAPCommand. - cdap.routes.js: Page route
GET /cdap/devices/:id+ 6 API proxy routes. UsesrequireAuth+requireRole('operator')for command sending. - routes/index.js: Registered
cdapRoutesasrouter.use('/', cdapRoutes). - cdap-device.ejs: Device detail page with header (name, type, version, uptime, status), offline banner, widget grid, empty state, command log panel.
- cdap-widgets.js: Widget renderer supporting 8 types (toggle, gauge, button, led, text, slider, select, chart). State polling every 3s. Info polling every 10s. User interaction guard (
_userInteractingflag) prevents state overwrite during input. Grouped by category. - cdap-commands.js: Command sender with per-widget cooldown (1s), confirmation dialog integration, command log (max 50 entries), toast notifications.
- cdap.css: Full widget styling — grid layout, toggle switch, gauge bar with danger/warning thresholds, LED indicator, slider with range labels, select dropdown, chart bars, command log panel. Responsive breakpoints. Dark theme CSS variables.
- i18n keys: 22 CDAP keys added to EN, PL, ZH translation files (device_detail, loading, connected, disconnected, widgets, commands, etc.).
- Deployed & verified: Go binary + Node.js files deployed. Server running, console active. CDAP routes return 302 (auth redirect) for unauthenticated, 401 for API without key — both correct.
Devices Page UI Redesign (Phase 30) ✅ COMPLETED 2026-03-20
- devices.ejs rewrite: Removed 280px sidebar layout. New single-column layout with horizontal scrollable folder chips (
.folder-chipbuttons), unified toolbar (search + segmented filter pills + column visibility toggle), slim table with 7 columns (id, hostname, device_type, platform, last_online, status, actions), kebab menu (more_verticon) replacing 5 inline action buttons, mobile bottom sheet overlay for phone kebab menu. - devices.css rewrite: ~780 lines. 4 responsive breakpoints: ≤1024px (hide device_type), ≤768px (hide platform+last_online, full-width search, icon-only buttons), ≤600px (card-style rows via CSS grid 2-col, hidden thead, fixed bottom sheet kebab with overlay), ≤400px (chip labels hidden, compact filters). Folder chip styles with hover-reveal edit/delete actions. Kebab dropdown with color-coded menu items.
- devices.js updates:
renderDevices()outputs new HTML template with.device-status-dot,.kebab-wrapper/.kebab-btn/.kebab-menu.renderFolders()changed from.folder-itemdivs to.folder-chipbuttons with.chip-actionedit/delete.attachRowEventListeners()handles kebab toggle + menu item actions. AddedinitKebabGlobalClose()+closeAllKebabMenus(). Updated all selectors:.folder-item→.folder-chipinselectFolder(),updateFolderCounts(),initFolders(),attachFolderDropEvents(). Double-click guard updated from.action-btn/.drag-handleto.kebab-wrapper. - Deployed & verified: All 3 files deployed to production server. Console returns 302 (service running). Responsive layout active.
Security & Installer Fixes (Phase 31) ✅ COMPLETED 2026-03-20
- API TLS breaking clients (Issues #70, #71): Fresh install with proper SSL certs added
-tls-api -force-https→ API port 21114 HTTPS-only → RustDesk clients (HTTP only) get 400 → 0 devices. Fix: removed-tls-api -force-httpsfrom betterdesk.sh + betterdesk.ps1 for ALL cert types.config.go:ForceHTTPSno longer impliesAPITLSEnabled(). SSL config menu: always removes-tls-api/-force-https. API URLs always HTTP. - Password
$escaping in systemd (Issue #68): systemd interprets$as variable substitution in ExecStart and Environment directives. Admin password and PostgreSQL URL now escaped$→$$before writing to.servicefiles. Auto-generated passwords (alphanumeric) unaffected. - Port CONFLICT false positive:
ss -tlnpshowsMainThreadinstead ofnodeon some Linux systems (Ubuntu 24.04+). AddedMainThreadto expected process patterns for ports 5000 and 21121.
Web Remote Client — Mouse, Quality & FPS Fix (Phase 32) ✅ COMPLETED 2026-03-21
- Mouse click fix (Critical): RustDesk parses mouse mask as
button = mask >> 3; type = mask & 7. Web client sent flat values (mask=1 for left click →button = 1>>3 = 0= no button). Hover worked because mask=0 is correct for both formats. Fixedinput.js: replaced flat values withTYPE | (BUTTON << 3)encoding (left click =1|(1<<3)=9, right click =1|(2<<3)=17, etc.). Added static constantsMOUSE_TYPE_DOWN=1,MOUSE_TYPE_UP=2,MOUSE_TYPE_WHEEL=3,MOUSE_BUTTON_LEFT=1,MOUSE_BUTTON_RIGHT=2,MOUSE_BUTTON_MIDDLE=4. - Image quality fix:
buildLoginRequestinprotocol.jshardcodedimageQuality: Balanced. Changed to configurable with defaultBest.remote.jspassesimageQuality: 'Best'in constructor. - FPS fix: Login used
customFps: opts.fps || 30despite wanting 60fps. Changed default to 60.client.js_startSession()now sends bothcustomFpsandimageQualityoptions.authenticate()passesfps: 60andimageQuality: 'Best'. - Beta banner: Replaced large orange "WIP" banner in
remote.ejswith slim blue "Beta" banner with dismiss button.
CDAP Full-Stack — Audio, Clipboard, Cursor, Quality, Codec, Multi-Monitor (Phase 33) ✅ COMPLETED 2026-03-21
- clipboard.go rewrite: Fixed all field mismatches (sync.Map Load, DeviceConn.WriteMessage, session.browser, session.DeviceID, context.Background(), gw.auditAction()). Bidirectional browser↔device clipboard sync with format detection.
- audio.go: Full audio session management (~230 lines). AudioSession struct, AudioStartPayload (codec/sample_rate/channels/direction), AudioFramePayload (codec/data/timestamp/duration/sequence). StartAudioSession checks "audio" capability on device manifest. HandleAudioFrame/RelayAudioInput/EndAudioSession.
- media_control.go: Cursor rendering, adaptive quality, codec negotiation, multi-monitor, key exchange relay, keyframe requests (~320 lines). CursorUpdatePayload (format/width/height/hotspot_x/y/data/cursor_id/hidden), QualityReportPayload (bandwidth_kb/latency_ms/frame_loss/fps), computeQualityAdjustment (adaptive), CodecOffer/Answer relay, MonitorList/MonitorSelect, HandleKeyExchange, RelayKeyframeRequest.
- gateway.go + handler.go integration: Added audioSessions sync.Map. 7 new message cases in messageLoop: audio_frame, audio_end, clipboard_update, key_exchange, cursor_update, codec_answer, monitor_list. handleAudioFrame/handleAudioEnd in handler.go.
- cdap_handlers.go extensions: Desktop handler: 6 new switch cases (clipboard_set, quality_report, codec_offer, key_exchange, keyframe_request, monitor_select). Video handler: 4 new switch cases (quality_report, codec_offer, key_exchange, keyframe_request). New handleCDAPAudio WS handler (~100 lines) with init/ready/audio_input/close protocol.
- server.go audio route:
GET /api/cdap/devices/{id}/audiowith operator role requirement. - cdapMediaProxy.js audio entry: Added audio channel to DRY proxy factory (subprotocol: cdap-audio, minRole: operator).
- cdap-audio.js (~310 lines, NEW): Web Audio API browser client. PCM 16-bit decode + Opus via decodeAudioData. Microphone capture via getUserMedia + ScriptProcessorNode. Volume/mute control, RMS level meter. WS init/ready/audio_frame/error/end protocol. Public API: CDAPAudio.open/close/isActive/setVolume/toggleMute/isMuted.
- cdap-desktop.js rewrite (~500 lines): Cursor rendering (PNG/RGBA format, LRU cache 50, hidden cursor), clipboard sync (navigator.clipboard API, paste events, clipboard indicator), quality reporting (5s interval, bandwidth/latency/frame_loss/fps), codec negotiation (sendCodecOffer on ready), multi-monitor (select UI in toolbar), keyframe requests.
- cdap-video.js rewrite (~280 lines): Quality reporting (5s interval), codec negotiation, keyframe request, frame byte/drop tracking.
- cdap-widgets.js updates: Audio widget renderer (status indicator, level meter, mute/connect buttons), desktop toolbar with clipboard indicator, audio connect/mute event listeners.
- cdap.css (~170 lines added): Audio widget styles (streaming/connecting/disconnected status, level meter with color thresholds), desktop toolbar, clipboard indicator (fade animation), monitor selector, .cdap-widget-md grid span.
- i18n: 7 new keys in EN/PL/ZH: connect_audio, audio_connecting, audio_streaming, clipboard_in, clipboard_out, monitor_select, keyframe_request, quality_auto.
- Deployed & verified: Go binary (28MB) + 10 Node.js files deployed to lab host
203.0.113.10. Both services active. CDAP endpoint returns JSON, console returns 302 (auth redirect) — all correct.
Native BetterDesk Agent — Go Binary (Phase 34) ✅ COMPLETED 2026-03-21
- betterdesk-agent/main.go: CLI entry point with 14 flags, signal handling (SIGINT/SIGTERM), graceful shutdown.
- agent/config.go: Config struct + JSON/env loading + Validate(). Supports
server,auth_method(api_key/device_token/user_password),device_id,device_name,device_type,tags,terminal,file_browser,clipboard,screenshot,file_root,heartbeat_sec,reconnect_sec,log_level. - agent/agent.go (~750 lines): Core agent — WebSocket connect, CDAP auth, manifest registration, heartbeat loop with system metric → widget_values mapping (sys_cpu, sys_memory, sys_disk, sys_hostname, sys_uptime), message dispatch for 20+ CDAP message types (command, terminal_start/input/resize/kill, file_list/read/write/delete, clipboard_get/set, screenshot_capture, state_update, bulk_update, alert_ack, ping).
- agent/system.go: gopsutil metrics (CPU 1s sample, Memory, Disk root), SystemInfo (hostname/os/platform/version/arch/uptime/total_memory/total_disk), live Uptime() method.
- agent/manifest.go: CDAP manifest builder — device descriptor, capabilities (telemetry, commands, remote_desktop, file_transfer, clipboard), 9 system widgets (3 gauges, 2 text, 1 terminal, 1 file_browser, 1 button, 1 clipboard text),
heartbeat_intervalfield. - agent/terminal_{unix,windows}.go: Cross-platform terminal — creack/pty on Unix, cmd.exe StdinPipe/StdoutPipe on Windows.
- agent/filebrowser.go: safePath() path traversal protection, ListDirectory, ReadFileChunk (base64, 1MB max), WriteFileChunk (base64 decode), DeletePath.
- agent/clipboard.go: Cross-platform clipboard via OS commands (xclip/xsel/pbcopy/powershell).
- agent/screenshot_{unix,windows}.go: Platform-specific screenshot capture (screencapture/import/scrot on Unix, System.Drawing on Windows).
- install/install.sh: Linux systemd installer with ProtectSystem=strict, PrivateTmp, NoNewPrivileges security hardening.
- install/install.ps1: Windows NSSM service installer.
- Protocol mismatches fixed: terminal_output (not terminal_data), terminal_end (not terminal_close), file_write_response (not file_write_ack), file_delete_response (not file_delete_ack), flat widget fields (label/group, not nested config), heartbeat_interval (not heartbeat).
- Deployed & verified: Binary on lab host
203.0.113.10, device_id=CDAP-EXAMPLE01, type=os_agent, 9 widgets, heartbeat=15s, telemetry flowing (CPU/Memory/Disk/Hostname/Uptime). CDAP API key created via REST (POST /api/keys),api_keystable entry active.
Bridge Ecosystem SDK — Python + Node.js + Reference Bridges (Phase 35) ✅ COMPLETED 2026-03-21
- sdks/python/: betterdesk-cdap v1.0.0 — CDAPBridge async class (~330 lines), Widget dataclass + 9 factory helpers, Message dataclass, all CDAP constants. Deps: websockets>=12.0.
- sdks/nodejs/: betterdesk-cdap v1.0.0 — CDAPBridge extends EventEmitter (~300 lines), Widget class + factory helpers, protocol constants. Dep: ws ^8.18.0. Smoke test verified.
- bridges/modbus/: Modbus TCP/RTU bridge (~200 lines) — register polling, data type encode/decode, write-back commands. Dep: pymodbus>=3.6.0.
- bridges/snmp/: SNMP v2c/v3 bridge (~200 lines) — OID polling, timetick formatting, counter rate computation. Dep: pysnmplib>=5.0.0.
- bridges/rest-webhook/: REST polling + aiohttp webhook listener (~230 lines) — JMESPath-lite extraction, configurable polling intervals. Dep: aiohttp>=3.9.0.
- sdks/README.md + bridges/README.md: Architecture overview, quick start, bridge creation guide.
- WebSocket path fixed: All SDKs, bridges, agent, and install scripts updated from
/wsto/cdap(27 replacements across 14 files).
Desktop Widget Dashboard — Sidebar & i18n Fix (Phase 36) ✅ COMPLETED 2026-03-25
- Sidebar navigation duplication: Removed 12 duplicated nav items from widget sidebar that were identical to topnav. Sidebar now only has widget-specific tools: home, add widget, wallpaper, edit layout, reset layout, help.
-
desktop.label_uptimei18n key missing: Server Info widget showed raw keydesktop.label_uptime: 26mbecauselabel_uptimekey did not exist (onlylabel_uptime_prefix). Addedlabel_uptimeto EN/PL/ZH. - Missing i18n keys: Added
action_add_widgetandlabel_merged_server_infoto EN/PL/ZH locale files. - Canvas area calculation: Fixed
getCanvasArea()fallback dimensions to account for new sidebar width.
BetterDesk Desktop Client — Single Instance Fix (Phase 37) ✅ COMPLETED 2026-03-25
- Dual process conflict: Old elevated process (PID from autostart) could not be killed by new MSI install. Two tray icons, two WebSocket connections, stale state. User clicks on old tray → old process responds → nothing works.
-
tauri-plugin-single-instanceadded: Second launch detects existing instance via Windows mutex, shows existing window instead of creating duplicate. Verified: only 1 process regardless of launch count. - i18n imports: Added
t()import from../lib/i18nto all 19 TSX components (6 were missing).
BetterDesk Desktop Client & Web Console — Chat, Remote, Operator, i18n, WS Push (Phase 38) ✅ COMPLETED 2026-03-26
🔴 CRITICAL — Chat System Fixed
- Chat shows "Disconnected": Root cause:
chatRelay.jsreceivedhelloframe but never sent acknowledgment (case 'hello': break;was a no-op). Rust client expected confirmation. Fixed: server now sendswelcomeack with capabilities and server_time. Rust client handleswelcomeandstatusframe types. - Chat window opens blank: ChatWindow.tsx was properly wired with event listeners. The blank state was caused by #230 — server never confirmed connection, so client showed disconnected. Fixed by #230.
- Chat contacts/groups always empty:
get_contactshandler returned nothing when Go API was unavailable. Fixed: fallback contacts (operator + connected agents) now returned even without Go persistence. Initial agent connection also sends fallback contacts.
🔴 CRITICAL — Web Remote Client Performance Fixed
- Max 9 FPS from web console: Fixed
video_receivedack timing — now sent BEFORE decoding (was after) for better pipelining. Added stall recovery: auto-requestsrefreshVideokeyframe if no frames arrive for 5 seconds. JMuxer fallback on HTTP remains a limitation (WebCodecs requires HTTPS). - No remote desktop control (mouse/keyboard):
_isInputFocused()ininput.jswas blocking keyboard events when hidden password input retained focus after login. Fixed: now ignores hidden/invisible inputs (el.offsetParent === null). Mouse encoding was correct from Phase 32. - Video blurry/unstable on fullscreen:
renderer.jsresize()now triggersonResizeRefreshcallback which sendsrefreshVideokeyframe request. Peer sends fresh keyframe after fullscreen toggle, eliminating blur from stale P-frames. - Video freezes after 30-60 seconds: Health check in
video.jsnow trims MSESourceBufferwhen buffer exceeds 2 seconds viasb.video.remove(start, end - 1.0). Prevents SourceBuffer overflow that caused freeze.
🟡 HIGH — Desktop Client GUI Functions (Partially Fixed)
- RemoteView basic JPEG viewer: Added
start_remote_viewerTauri command — connects to management WS (/ws/bd-mgmt/{device_id}), receives JPEG binary frames, base64 encodes and emits to frontend.RemoteView.tsxnow listens forremote-viewer-frameevents and renders JPEG frames on canvas. Not full H.264 (Phase 43), but functional JPEG streaming. - Remote desktop agent uses JPEG at 15fps: Replaced JPEG 15fps capture with H.264/VP9 codec pipeline in
remote/video_pipeline.rs. Full session-based remote withsession_manager.rsorchestrating relay message loop — Phase 43. - OperatorPanel login flow: Fixed 8 operator endpoints in
commands.rs:/api/bd/operator/login→/api/auth/login,/api/bd/operator/login/2fa→/api/auth/login/2fa,/api/bd/operator/devices→/api/peers,/api/bd/operator/help-requests→/api/audit/events?action=help_request,/api/bd/operator/device-groups→/api/peers?with_tags=true,/api/bd/operator/devices/{id}/config→/api/peers/{id},/api/bd/operator/devices/{id}/install-module→/api/bd/mgmt/{id}/send. - ManagementPanel device info:
get_device_info_cmdreturns local device info viamanagement::get_device_info()— works correctly for local management panel. - HelpRequestPanel submission:
request_helpcorrectly sends to/api/bd/help-requeston Node.js console (port 5000). Endpoint exists inbd-api.routes.jsand is functional. - DiscoveryPanel mDNS:
discover_mdns_serversusesmdns-sdcrate for LAN discovery. Fully implemented indiscovery/mdns.rs+scanner.rs+ Tauri command. Works on local network but may timeout if firewall blocks mDNS (UDP 5353).
🟢 MEDIUM — Desktop Client Improvements
- Implement real video decoder in RemoteView: Replaced JPEG viewer with H.264/VP9 decode using
openh264crate inprotocol/codec.rs. Video frames received via relay, decoded, rendered to canvas via IPC. RemoteView.tsx updated with full remote desktop UI — Phase 43. - Input injection via enigo: Full implementation —
input/mod.rsnow usesenigocrate for keyboard (40+ keys mapped incl F1-F12, modifiers, arrows, Unicode), mouse (move, click, scroll), and text typing. Addedsimulate_local_key,simulate_local_mouse,simulate_local_textTauri commands. - File transfer UI: Created
FileTransferPanel.tsxwith local file browsing viaFileBrowser::list_dir. Navigate folders, show hidden toggle, file size formatting. Addedbrowse_local_filesandopen_file_nativeTauri commands. Addedopencrate dependency. - DataGuard integration: Created
DataGuardPanel.tsxwith feature cards (file monitoring, USB control, policy engine). Marked "Coming Soon" — backend stubs not yet connected to server policies. Sidebar navigation + i18n EN/PL. - Automation panel: Created
AutomationPanel.tsxwith feature cards (script runner, scheduled tasks, command channel). Marked "Coming Soon" — backend stubs not yet connected to server. Sidebar navigation + i18n EN/PL. - Activity tracking UI: Created
ActivityPanel.tsxwith filterable log, action icons, color coding, auto-refresh 30s. AddedActivityTracker(500-entry ring buffer) toAppState.get_activity_logTauri command. Sidebar nav + i18n EN/PL. - Desktop client i18n completion: Added 60 missing keys to EN/PL locale files: operator (24 keys — login, devices, help_requests, totp, etc.), management (21 keys — device_info, system commands, etc.), chat (15 keys — contacts, groups, search, typing, etc.). All 19 TSX components already import
t()function. - NSIS Polish translation: Created
nsis/languages/pl.nshwith 40 Tauri NSIS message keys translated to Polish. AddedcustomLanguageFilesconfig totauri.conf.json. RemovedRussianfrom NSIS languages list.
🟢 MEDIUM — Web Console Improvements
- WebSocket real-time device status push: Created
deviceStatusPush.jsservice — connects to Go server WS event bus (/api/ws/events?filter=peer_online), pushespeer_online/peer_offlineevents to browser clients via/ws/device-status. AddedinitDeviceStatusWS()indevices.js— updates device status dots in-place without full table reload. Wired inserver.js. - Unit tests for HTTP API: Created 5 test suites (41 tests) with jest+supertest:
auth.routes.test.js(7 tests),devices.routes.test.js(9 tests),middleware.auth.test.js(9 tests),i18n.test.js(6 tests),validation.test.js(10 tests). All passing. Addedtest/test:cinpm scripts. - PostgreSQL integration tests: Requires live PostgreSQL instance for Go server db/postgres.go testing.
i18n — Remove Russian Language (Phase 39) ✅ COMPLETED 2026-03-26
- Remove
ru.jsonfromweb-nodejs/lang/: Deleted Russian translation file. Languages auto-discovered fromlang/directory — removing file is sufficient. - Block Russian in desktop client:
betterdesk-client/src/lib/i18n.tslocale list only hasenandpl— no Russian. RemovedRussianfrom NSIS languages list intauri.conf.json. - Audit all language references: No hardcoded Russian strings found in templates, configs, or scripts.
Desktop Widget Dashboard — Full App Widgets (Phase 40) ✅ COMPLETED 2026-03-26
- Weather widget: Fetches weather from wttr.in API. Shows temperature, humidity, wind, conditions, city name. Configurable location via widget config. Auto-refresh every 10 minutes.
- Calendar/agenda widget: Full calendar with month navigation, event creation via double-click, localStorage persistence, today highlight. Registered as 'calendar' widget type.
- System process monitor widget: Top 15 CPU/memory-consuming processes from
/api/system/info. Linux (ps aux) and Windows (PowerShell Get-Process) support. Color-coded CPU thresholds. - Disk usage breakdown widget: Segmented bar per partition from
/api/system/info. Linux (df) and Windows (wmic logicaldisk) support. Color-coded usage thresholds. - Log viewer widget: Stream recent lines from
/api/logs/recent. Console/Go source selector, auto-scroll toggle, severity color coding (error/warning/info). journalctl + file fallback. - Alert feed widget: Live feed of security alerts from audit log. Color-coded actions (ban=red, login=green, info=gray). Auto-refresh every 30s.
- User sessions widget: Lists logged-in operators/admins with role badges. Auto-refresh every 15s.
- Speed test widget: Measures download speed from
/api/speed-test(1MB payload). Gauge SVG visualization, latency measurement, Mbps display. Button-triggered test. - Database stats widget: Table row counts from
/api/database/stats. SQLite file size or PostgreSQLpg_size_pretty. Last backup detection from data/backups dir. - Docker containers widget: Container list from
/api/docker/containers(docker ps). Status icons (running/stopped), image, ports, uptime display. - Custom shell command widget: Executes whitelisted commands via
/api/system/exec(admin only). Configurable command + refresh interval. Strict whitelist security. - World clock widget: Multiple time zones with configurable zones. Tabular-nums second-precise display. Updates every second.
- Bookmark/link launcher widget: Grid of configurable URL shortcuts with hover effects. Opens in new tab. Configurable via Name|URL pairs.
- Device map widget: Equirectangular world map with IP-hash-positioned device pins. Online/offline color coding, cluster counts. Hash-based deterministic positioning.
Desktop Widget Dashboard — Modern App-Style UI Redesign (Phase 41) ✅ COMPLETED 2026-03-26
- Glassmorphism widget cards: Enhanced frosted glass background with
backdrop-filter: blur(28px) saturate(1.5), 14px border-radius, subtle drop shadows, smooth hover lift transitions (translateY(-1px)). Windows 11 / macOS Sonoma aesthetic. - Animated widget transitions: Widgets fade/slide in with spring cubic-bezier (0.34, 1.56, 0.64, 1). Drag with opacity reduction. Hover micro-animation. Improved leave animation timing.
- Dark/light/auto theme for desktop mode: Full theme system with
DesktopMode.setTheme/cycleTheme/getThemeAPI. Auto mode usesprefers-color-scheme. Light theme: white glassmorphism, dark text, blue accents. Persisted to localStorage. - Widget header redesign: Compact title bar with icon, title, and kebab menu (⋮). Settings, remove actions. Draggable from header. Actions revealed on hover.
- Snap grid system: Widgets snap to 20px grid. Edge snapping (15px threshold) to other widgets and canvas borders. Visual grid overlay (radial-gradient dots) togglable via
DesktopWidgets.toggleGrid().getSnapEdges()builds edge list from all widgets. - Widget presets/templates: Save/load JSON presets via localStorage. 4 built-in presets (Monitoring, Helpdesk, Minimal, Developer). User presets with save/delete. Sidebar access.
- Responsive desktop mode: Auto-adjust widget positions for different screen resolutions. Breakpoints for 1080p, 1440p, 4K. Mobile-aware fallback for tablet access.
- Widget groups/stacking: Group multiple widgets into a tabbed container. Click tabs to switch between widgets in the same space. Save group layout.
Desktop Widget Dashboard — Multi-Window Snap Layout (Phase 42) — Partially Complete
- Snap layout overlay: Windows 11-style layout picker via sidebar button. 6 predefined zone layouts (2col, 2col-60/40, 3col, 2×2, 1+2, 1+3). Visual zone previews. Glassmorphism picker with dark/light theme.
- Zone-based widget placement: Click layout in overlay → widgets distributed across zones round-robin. Zone dimensions computed from canvas area with padding. Min width/height enforced.
- Draggable zone borders: Resize zones by dragging the divider between them. Cursor changes to
col-resize/row-resize. Adjacent zones adjust proportionally. Minimum zone width/height enforced (15% min fraction). Zone dividers auto-detected from shared edges. Windows update in real-time during drag. - Layout persistence: Save current zone layout + widget assignment per zone to localStorage/server. Restore on page load. Support named layouts ("Operator View", "Monitoring", "Custom 1"). Extended with widget groups + auto-reposition.
- Multi-monitor support: Detect browser window position and available screen space via
window.screen. Allow desktop mode to span across two browser windows (each window = one monitor). Sync state via BroadcastChannel API or SharedWorker. - Floating widget windows: Option to "pop out" a widget into an independent browser popup (
window.openwith specific size). The popup communicates with the main desktop mode viapostMessage. Useful for putting a widget on a secondary monitor. - Auto-arrange: Button in snap layout picker auto-tiles widgets in √n grid. Cells computed from canvas area. Left-to-right, top-to-bottom fill. Saves layout after arrangement.
Desktop Client (Tauri) — Full Remote Desktop Rewrite (Phase 43) ✅ COMPLETED 2026-03-27
- H.264/VP9 video decoder in Rust: Implemented in
protocol/codec.rsusingopenh264crate. Decoded frames rendered to Tauri WebView canvas via IPC. Supports 30-60fps depending on network. - RustDesk protobuf video pipeline:
remote/video_pipeline.rsconnects to relay via TCP/WS, receives protobufVideoFramemessages, extracts H.264 NALUs, feeds to decoder. - Input forwarding pipeline:
remote/input_pipeline.rscaptures keyboard/mouse events from SolidJS canvas, serializes as protobufMouseEvent/KeyEvent, sends through relay. Supports modifier keys, special keys (F1-F12, PrintScreen), mouse wheel. - Clipboard sync during session:
remote/clipboard_sync.rs— bidirectional clipboard sync usingarboardcrate. Auto-detects text/image content. Throttled to prevent clipboard storm. - File transfer during session:
remote/file_transfer_session.rs— drag-and-drop local↔remote via protobufFileTransfermessages. Progress bar, cancel, resume on disconnect. - Multi-monitor selection: Queries remote displays, shows monitor picker toolbar in RemoteView.tsx. Switch between monitors during session. "All monitors" stitched view supported.
- Session recording:
remote/session_recorder.rs— records H.264 frames + input events to local file. Playback viewer for audit/review. Configurable auto-recording policy. - Connection quality indicator:
remote/quality_monitor.rs— overlay showing latency, FPS, bandwidth, packet loss. Adaptive quality: auto-reduces resolution/fps when bandwidth drops.
Desktop Client (Tauri) — Operator & Management Features (Phase 44) — Partially Complete
- Operator login → Go server
/api/auth/login: Fixed in Phase 38. 8 operator endpoints corrected. 2FA flow with TotpDialog. JWT token stored. Operator badge in sidebar. - Device list with live status: Fetch from
GET /api/peers. Status dots (online/offline). Search by ID/hostname/platform/tags. Filter by all/online/offline. Group chips. CPU/RAM metrics. - One-click remote connect: Click "Connect" button on device card →
connectionStore.connect(deviceId)→ navigates to remote view. Context menu also has "Remote Connect". - Device actions panel: Right-click context menu: Remote Connect, Send Message, Transfer Files, Configure, Install Module, View Info, Restart, Shutdown, Lock Screen, Log Off, Wake-on-LAN (offline only).
operator_send_device_actionTauri command. - Help request management: Help Requests tab with inbox UI. Accept & Connect button → auto-connect. Badge counter for pending requests. 15s polling interval.
- Session history dashboard: Session History tab in OperatorPanel. Fetches from
/api/audit/events?action=conn_start. Table with device, operator, start time, duration.operator_get_session_historyTauri command. - Unattended access management: Set/change device passwords (bcrypt), enable/disable unattended access, configure access schedules (day/time/timezone), restrict allowed operators. Go server
access_policiestable + 3 API endpoints. Node.js panel modal with full UI. - Wake-on-LAN: Go server
POST /api/peers/{id}/wolendpoint with UDP broadcast magic packet (255.255.255.255:9). MAC address provided in request body.operator_wake_on_lanTauri command.
Performance & Optimization — Project-Wide (Phase 45)
- Go server memory profiling: Profile heap allocations in signal/relay hot paths. Reduce
sync.Mapentries with aggressive TTL. Pool protobuf buffers withsync.Pool. - Go relay zero-copy: Replace
io.Copywithsplice/sendfilesyscalls on Linux for TCP relay. Reduce memory copies for high-throughput relay sessions. - Node.js console startup time: Lazy-load routes and heavy services (chat relay, CDAP proxy). Measure and reduce time-to-first-response.
- Desktop client binary size reduction: Enabled
strip,opt-level = "s",lto = true,codegen-units = 1,panic = "abort"in Cargo release profile. Reduced tokio/sysinfo to needed features only. MSI: 7.17 → 4.12 MB (42.5% reduction). NSIS: 2.99 MB. - Desktop client startup time: Profile Tauri init + WebView2 load. Defer non-critical services (inventory, chat) further. Target < 2s to tray icon visible.
- Widget dashboard rendering performance: Virtualize widget list when > 20 widgets. Use
requestAnimationFramefor drag animations. Debounce resize observers. - Web remote client frame pipeline optimization: Profile decode → render path. Use
OffscreenCanvas+ worker thread for video decode. Reduce GC pressure from Uint8Array allocations. - Database query optimization: Add missing indexes in Go server SQLite/PostgreSQL. Use prepared statements for hot-path queries. Implement connection pooling health checks.
Desktop Widget Dashboard — OS-Style Login Screen (Phase 46) ✅ COMPLETED 2026-03-26
- Full-screen login page for desktop mode:
desktop-login.ejsserved whenbetterdesk_desktop_modecookie is set or?desktop=1. Win11-style with wallpaper, frosted glass card (backdrop-filter: blur(40px)), avatar, spring-animated card entry.desktop-login.css+desktop-login.js. - TOTP 2FA flow on login screen: Smooth transition to 6-digit TOTP input. Individual digit boxes with auto-advance, paste support, auto-submit on 6th digit. Shake animation on error. Back-to-login link.
- Multi-user selector (bottom-left): Avatar chips in bottom-left with initials, username, role. Click → pre-fills username, focuses password, updates avatar. Highlighted selected chip. User list from
getAllUsersForBackup(). - Clock and date overlay: Large clock (clamp 64-120px) and localized date on lock screen. Click/keypress dismisses lock and reveals login form with smooth fade transition. 1s update interval.
- Session persistence: Login redirects to
/(Express session cookie persists). Desktop mode cookiebetterdesk_desktop_mode=trueroutes to desktop login on session expiry. - Wallpaper preload: Reads
bd_widget_wallpaperfrom localStorage. Preloads vianew Image(). Supportssolid:prefix for solid colors. Default gradient fallback. Smooth fade on load. - i18n for login screen: 14 keys in
desktop_loginsection added to EN/PL/ZH: click_to_sign_in, username/password_placeholder, sign_in, verify, totp_title/subtitle, back_to_login, fill_all_fields, invalid_credentials/code, enter_6_digits, network_error, session_expired.
Windows 11 Snap Layouts & Window Management (Phase 47) ✅ COMPLETED 2026-03-27
- Edge snap zones: Dragging window to screen edges triggers snap preview (left half, right half, corners quarter, top maximize).
detectSnapZone()withSNAP_EDGE_THRESHOLD=12pxandSNAP_CORNER_SIZE=80px.showSnapPreview()creates animated blue zone overlay. Snap applied on mouse up. - Snap layout picker on maximize button hover: Hovering over maximize button for 350ms shows Win11-style layout picker with 6 predefined layouts (2col, 2col-60/40, 3col, 2×2, 1+2, 1+3).
showSnapPicker()creates glassmorphism dropdown with zone previews. Click distributes visible windows across zones. - Drag from maximized: Dragging a maximized window's title bar un-maximizes and positions window proportionally to mouse cursor (Win11 behavior). Previous bounds restored from
prevBounds. - Aero Shake: Shaking a window rapidly (3+ direction changes in 500ms) minimizes all other windows. Shaking again restores them.
detectAeroShake()withSHAKE_THRESHOLD=40px. - Snap animations: Smooth CSS transitions (0.2s ease) for snap positioning. Preview overlay with
backdrop-filter: blur(2px)and spring animation. - Dark/light theme support: Full light theme styles for snap preview and snap picker. Glassmorphism adapts to theme.
Desktop Login & Session Expiry Fix (Phase 48) ✅ COMPLETED 2026-03-27
- Cookie-based desktop mode detection:
activate()now sets HTTP cookiebetterdesk_desktop_mode=true(1 year, SameSite=Lax).deactivate()clears it. Previously only stored in localStorage — server-sideauth.routes.jscould not detect desktop mode for login page routing. - Session expiry → login screen: When session expires and user is redirected from a non-login page,
BetterDesk.sessionExpired=trueflag is set.desktop-login.jsdetects this, skips lock screen, goes directly to login form with "Session expired" error message. - BetterDesk.users + csrfToken injection:
desktop-login.ejsnow properly injects user list and CSRF token via script tag for multi-user selector functionality.
Chat E2E Encryption (Phase 2) ✅ COMPLETED 2026-03-27
- chatCrypto.js client module: P-256 ECDH key exchange via WebCrypto API + HKDF-SHA256 key derivation + AES-256-GCM message encryption/decryption. Key pair persisted to localStorage. Key rotation every 24h or 1000 messages.
- File encryption:
encryptFile()/decryptFile()for files up to 50MB with encrypted metadata (filename, size, timestamp). Packed format: metaIV + metaLen + encMeta + dataIV + encData. - chatRelay.js E2E protocol: Added 5 new message types to both agent and operator handlers:
key_exchange(public key relay),read_receipt(message ID arrays),presence_update(online/away/busy),file_share(encrypted metadata relay). - Capabilities updated: Welcome message now includes
e2e_encryption,read_receipts,typing,presence,file_sharecapabilities.
Web Remote Client Enhancement (Phase 3 Remaining) ✅ COMPLETED 2026-03-27
- Session recording in RDClient:
startRecording()/stopRecording()/downloadRecording()— canvas capture stream + audio at 15fps, WebM VP9+Opus encoding. Auto-download on stop. - Monitor switching:
getMonitors()returns peer display list.switchMonitor(idx)sendsswitchDisplaymisc message. UI dropdown in toolbar shows monitor names and resolutions with primary indicator. - Quality presets:
setQualityPreset('speed'|'balanced'|'quality'|'best')— configures imageQuality and customFps via misc messages. Emitsquality_changedevent.
UI/UX Polish & Theming (Phase 13) ✅ COMPLETED 2026-03-27
- Page transitions:
pageEnteranimation (fade+translateY) on.page-content. Staggered list items (30ms delay per row). Card hover lift effect. - Skeleton loading:
.skeleton,.skeleton-text,.skeleton-title,.skeleton-avatar,.skeleton-card,.skeleton-table-rowclasses with shimmer pulse animation (1.5s). - Toast notification system:
toast.js(130 lines) —Toast.success/error/warning/info(title, message, duration). Progress bar auto-dismiss, hover-pause. Max 5 toasts. Slide-in/out animations. - Light theme: Full CSS variable override set for light theme (
[data-theme="light"]). Auto-theme via@media (prefers-color-scheme: light). - Reduced motion:
@media (prefers-reduced-motion: reduce)disables all animations. - Theme selector component:
.theme-selectorCSS component with dark/light/auto buttons.
Desktop Widget Dashboard — Groups & Responsive (Phase 11 Remaining) ✅ COMPLETED 2026-03-27
- Widget groups/stacking:
createGroup(widgetIds, label)— tabbed container combining multiple widgets. Tab bar shows widget type labels. Active tab switches visibility.ungroupWidgets()restores individual positioning. - Responsive auto-reposition:
autoReposition()on window resize — clamps widgets within canvas bounds, shrinks oversized widgets. Debounced 300ms. - Group persistence:
STORAGE_GROUPSin localStorage.loadGroups()/saveGroups()called during init/save cycle. - i18n: Added ~50 new keys to EN/PL/ZH for snap layouts, widget groups, remote features, chat E2E, toast, theme selector.
BetterDesk MGMT Client — Standalone Operator Desktop App (Phase 49) — Partially Complete
- Architecture design: Tauri v2 + SolidJS frontend + Rust backend. Renamed from
betterdesk-client/tobetterdesk-mgmt/. 25+ Rust modules (~40K LOC), 100+ IPC commands. Dark/light theme, operator-optimized density. - Device panel: Unified device list from Go server API (
GET /api/peers). Online/offline status, device type, OS, hostname, network addresses, security status, tags, groups, owner. Search, filter, sort, group, bulk actions. Source indicator (BetterDesk / RustDesk integration). - Premium remote session: Max quality video with adaptive bitrate, dynamic resolution, hardware codec acceleration (H.264/VP9/AV1), software fallback. Audio streaming, multi-monitor selection, clipboard sync, file transfer, remote shell/terminal. Session recording per audit policy. In-session quality reconfiguration.
- Chat & communication: 1:1 chat (operator ↔ end user), operator ↔ agent chat, group chat per incident/ticket. Canned responses, conversation history, push/in-app notifications, ticket escalation, urgent help flagging. E2E encryption via existing chatCrypto module.
- Server management panel: ServerPanel.tsx (6 tabs: overview/clients/operators/audit/keys/config), 8 Rust IPC commands (server_get_health, server_get_clients, server_get_operators, server_get_audit, server_get_api_keys, server_disconnect_client, server_ban_client, server_revoke_api_key). RBAC-gated admin panel.
- CDAP operator mode: Multiple concurrent sessions, ticket queue with priorities, active connection dashboard, session quality metrics (bandwidth, codec, FPS, latency), agent alert monitoring, session takeover/transfer/end per RBAC.
- Security hardening: Mutual auth (client ↔ server), TLS/mTLS, session token rotation, short-lived access tokens, RBAC roles, MFA for operators, certificate pinning, full admin event audit, update signature verification, OS credential store for secrets, replay/downgrade attack resistance.
- RustDesk integration layer: Device source adapter pattern — normalize metadata, separate device sources logically, consistent permission model, secure session/status/ID mapping. Staged integration plan if full integration requires major changes.
- Cross-platform build & installers: Windows (MSI/NSIS), Linux (deb/rpm/AppImage), macOS (dmg). Auto-update mechanism. Per-platform codec/acceleration detection, secret storage, service/tray/notification differences.
- UX/UI: NotificationCenter.tsx (type filtering, real-time push, 30s polling), sidebar nav entries, full i18n EN+PL (~60 keys: server., notif., common.*). Spec:
docs/new_agents/client1.md. - Testing: Unit tests, integration tests, E2E tests, security tests, streaming performance tests, cross-platform compatibility tests.
BetterDesk Agent Client — Endpoint Device Agent (Phase 50) — Partially Complete
- Architecture design: Tauri v2 + SolidJS frontend + Rust backend.
betterdesk-agent-client/— lightweight endpoint agent. 4 modules (commands, config, registration, sysinfo_collect), 17 IPC commands. Single window (480x520), tray icon, autostart. - Installation & server onboarding: SetupWizard.tsx with 5-step flow: address input with format validation → sequential server validation (availability/protocol/registration/certificate) → device registration → config sync → complete.
registration.rswithvalidate_step()andregister(). - Device identity & registration: Machine UID-based device ID (
BD-{hash}), SHA-256 device fingerprint, secure token storage via OS keyring (keyringcrate), config persistence viaAgentConfigstruct. - Remote access support: Screen capture sharing, remote control input injection, file transfer, chat with operator, multi-session per policy, connection quality reporting, hardware capability reporting. Adaptive quality, weak-link adaptation, selective feature activation post-sync.
- System info collection:
sysinfo_collect.rs— hostname, OS, version, arch, CPU name/cores, total RAM/disk, username.SystemSnapshot::collect()used in registration and diagnostics. - Administrative automation: Execute approved scripts, admin commands, policy deployment, diagnostics collection, operator-tasked jobs. Task signing/authorization model, source validation, strict permission model, full action audit, scope restrictions, abuse resistance, server-policy disable/limit capability.
- Staged sync after registration: 5-step validation in
registration.rs(availability → protocol → registration_open → certificate), thenregister()via heartbeat API, thensync_config()via sysinfo API. - Background service mode: Tauri tray icon (show/quit menu), autostart via
tauri-plugin-autostart, single-instance viatauri-plugin-single-instance, minimize to tray on close. - Minimal end-user UI: StatusPanel (connection hero, info grid, copy ID, reconnect, diagnostics), ChatPanel (operator chat), HelpRequest (4-state flow), SettingsPanel (connection, privacy, general, about). Full i18n EN+PL (~120 keys).
- Security hardening: Secure device registration, mutual server auth, encrypted communication, cert pinning/trust model, anti-server-impersonation, anti-unauthorized-control, token/session rotation, process hardening, least-privilege, attended/unattended mode distinction, full admin action audit, secure update mechanism, server config validation, automation sandboxing.
- Cross-platform build & installers: Windows (MSI + NSSM service), Linux (deb/rpm + systemd), macOS (pkg + launchd). Per-platform: screen capture, input model, permissions/UAC, secret storage, firewall, autostart, script execution differences.
- Testing: Unit tests, integration tests, registration tests, security tests, automation tests, cross-platform compatibility tests, update tests, connection loss resilience tests, server/certificate reconfiguration tests. Spec:
docs/new_agents/client2.md.
GitHub Issue Triage & Fixes (Phase 51) ✅ COMPLETED 2026-04-10
- Closed 16 GitHub issues: Full audit of all 22 open issues — 14 already fixed in codebase (verified + closed with detailed comments), 2 fixed with new code
- TCP accept EOF log spam (#100): Added
errors.Is(err, io.EOF)+strings.Contains("connection reset"|"use of closed")filter in bothsignal/server.goandrelay/server.goserveTCP()loops. Silences benign scanner/probe noise. - Startup banner port confusion (#98):
server.jsprintStartupBanner()now shows all active ports, protocol labels (HTTP/HTTPS), redirect info, and Go API URL. - KEYS_PATH auto-detect warning (#89):
config.jswarns when KEYS_PATH was auto-detected but no.api_keyorid_ed25519found at resolved path. - CSS hover layout shift (#75): Added
transform: translateY(0)base state to 7 elements across 5 CSS files. Fixedtransition: all→ specific properties on.widget-action-btn. - Admin password race condition (#88):
ensureDefaultAdmin()inauthService.jsnow retries reading.admin_credentialsafter 3-second delay on fresh install (Go server may not have written file yet). Falls back to writing generated password todata/.admin_credentialsfor discoverability. - ID change ghost entries (#97): Two fixes: (1)
syncGoPeersSqlite()now cross-referencesid_change_historyto DELETE ghost peer entries with renamed IDs. (2)/api/bd/registerchecksgetRenamedPeerId()and returns 409 withnew_idfor stale IDs. - Posted analysis comments: Remaining open issues (#93 EJS template, #94 MGMT token, #78 Docker SQLITE_READONLY, #76 tag sync, #74 access controls) received detailed analysis comments with actionable steps.
- Discussion #99 RBAC response: Comprehensive RBAC analysis comparing current 4-tier vs proposed 6-tier hierarchy, 5 critical gaps identified, 3-phase implementation roadmap. Saved to
docs/_internal/DISCUSSION_99_RBAC_RESPONSE.md.
RBAC — Granular Permissions & Data Scoping (Phase 52) ✅ COMPLETED 2026-04-10
-
auth/permissions.gocreated: 28 granular permission constants (device.view/.connect/.edit/.delete/.ban/.change_id, user.view/.create/.edit/.delete, server.config/.keys, org.create/.edit/.delete/.manage_users/.manage_devices, audit.view, metrics.view, blocklist.edit, cdap.view/.command/.terminal/.files, enrollment.manage/.approve, chat.access, branding.edit).DefaultRolePermissionsmap (admin=all 28, operator=12, viewer=5, pro=1).RoleHasPermission(),ValidPermission()helpers. - JWT org context: Added
OrgID stringtoauth.Claims,GenerateOrgToken()method. Org login now embedsorg_idin JWT.authMiddlewareextracts and injectsorg_idinto request context. -
requirePermission()middleware (Go): Checks DBrole_permissionstable for custom overrides first, falls back toDefaultRolePermissions. Admin always passes. ~30 routes migrated fromrequireRoletorequirePermission. -
requireOrgMembership()middleware: Enforces org membership for org-scoped endpoints. Global admins bypass. JWTorg_idmatching + DB lookup fallback. - Data scoping:
handleListPeersusesListPeersForOrg(orgID)when JWT has org_id.handleListOrgsfiltered by membership for non-admins. -
role_permissionstable: SQLite + PostgreSQL schema migration.ListRolePermissions,SetRolePermission,DeleteRolePermission,HasRolePermission,ListPeersForOrg— implemented in both adapters. -
User.IsServerAdminfield: Added to DB model, SQLite + PostgreSQL schema migration (is_server_admincolumn), all user queries updated to scan/persist it. Exposed in/api/auth/meand/api/usersresponses. - Super admin protection: Self-demotion prevention (admin cannot lower own role). Role boundary enforcement (cannot assign role > own). Server admin protection (only server admins can modify/delete other server admins). Last-admin deletion guard (pre-existing).
- Node.js
requirePermission()middleware: Added toweb-nodejs/middleware/auth.jswith matchingDEFAULT_ROLE_PERMISSIONSmap mirroring Go defaults. ExportedrequirePermission()androleHasPermission()functions. - Documentation:
docs/features/RBAC_PHASE52.mdwith full permission table, default role maps, override examples, data scoping details, DB schema.
RBAC — 6-Role Hierarchy & Org Boundary Enforcement (Phase 52b) ✅ COMPLETED 2026-04-10
- 3 new global roles: Added
super_admin,server_admin,global_admintoauth/roles.go.RoleSuperAdmin,RoleServerAdmin,RoleGlobalAdminconstants. Legacyadmin= alias forsuper_admin.ValidRole()accepts 7 roles. - Branched role hierarchy:
RoleLevel()returns 5 for super_admin/admin, 4 for server_admin/global_admin (parallel), 2 for operator, 1 for viewer, 0 for pro.IsSuperAdminRole(),IsServerLevel()helpers. -
CanAssignRole()function: Enforces branched role assignment boundaries. Super admin → any, global_admin → operator/viewer/pro only, server_admin → none, operator/viewer/pro → none. - 7-role permission maps:
DefaultRolePermissionsexpanded to 7 entries.server_admin: 8 perms (server.config, server.keys, blocklist.edit, user.view, device.view, audit.view, metrics.view, enrollment.manage).global_admin: 22 perms (user/org/device/cdap/audit — NO server.config/server.keys).RoleHasPermission()updated to useIsSuperAdminRole(). - Org role boundary enforcement:
OrgRoleLevel()(owner=40, admin=30, operator=20, user=10),OrgCanAssignRole()(owner → admin/op/user, admin → op/user, others → none),ValidOrgRole()— all indb/database.go. - Org privilege escalation fixed:
handleCreateOrgUser— caller's org-role checked viaGetOrgUserByUsername+OrgCanAssignRole. Super/global admins bypass.handleUpdateOrgUser— self-modification blocked, caller authority check, cannot modify user at or above own org level. - Org user visibility scoping:
handleListOrgUsers— org users with role "user" only see themselves. Org admin/operator/owner see all. - Last-admin demotion guard:
handleUpdateUser— when demoting a super_admin/admin, counts remaining admins. If sole admin, returns 409 Conflict. - Peer org scope check:
peerOrgScopeCheck()helper verifies org-scoped users can only access devices assigned to their org. Applied to 7 critical endpoints: GET/DELETE/PATCH peer, ban/unban, change-id, metrics. - requirePermission super_admin bypass: Updated Go
requirePermission()fromuserRole == RoleAdmintoIsSuperAdminRole(userRole). - requireOrgMembership global_admin bypass: Updated to allow super_admin AND global_admin to access any org.
- Node.js 7-role middleware: Updated
DEFAULT_ROLE_PERMISSIONSwith 7 entries (super_admin, admin, server_admin, global_admin, operator, viewer, pro). AddedSUPER_ADMIN_ROLESset +isSuperAdminRole(). UpdatedrequireRole,requireAdmin,requirePermissionto handle new roles.
Node.js Frontend — CSRF Token Fixes (Phase 53) ✅ COMPLETED 2026-04-10
- Organization policy save failing (Issue #112):
policies.jsretrieved CSRF token viadocument.querySelector('meta[name="csrf-token"]')which does NOT exist — layout useswindow.BetterDesk.csrfTokeninstead. All PUT requests failed CSRF validation silently. Fixedpolicies.jsto usewindow.BetterDesk?.csrfToken || ''. - Attestation verify/revoke failing: Same CSRF issue in
attestation.js— bothverify()andrevoke()functions used non-existent meta tag. Fixed to usewindow.BetterDesk?.csrfToken. - Toolkit API calls failing:
toolkit.jscached CSRF from meta tag at module init. Fixed to callgetCsrfToken()dynamically which reads fromwindow.BetterDesk?.csrfToken.
Agent Client — Security Hardening & Native Agent Completeness (Phase 54) ✅ COMPLETED 2026-04-10
- AGENT-C2 device ID entropy (CRITICAL):
betterdesk-agent-client/src-tauri/src/registration.rs::registerused 4 bytes of SHA-256 (~65k unique IDs, trivial brute-force). Extended to full 16 bytes (32 hex chars → 3.4·10³⁸ entropy) and mixed in hostname + package version alongside machine UID. Closes AUDIT_BETTERDESK_2026-04-17 C2 finding. - AGENT-H3 URL scheme + private-IP guard (SSRF): New
validate_address()inregistration.rs— rejects non-http(s)schemes, literal IPs in RFC1918 / 169.254/16 / ::1 / fc00::/7 / fe80::/10 / multicast / broadcast. Called from all 4 validation steps,register()andsync_config(). Opt-out viaBETTERDESK_ALLOW_PRIVATE_IPS=1env var for LAN deployments. - Keyring wiring after registration:
register()now generates a local registration marker token, assigns it toconfig.auth_token, persists config, AND callsconfig.store_token_secure()(was previously defined but never invoked). Keyring failures now logWARN(not silentINFO) with explicit fallback message. Config JSON file remains as last-resort fallback. - Native Go agent
clipboard_gethandler:betterdesk-agent/agent/agent.goaddedhandleClipboardGet()+clipboard_getdispatch case. Responds withclipboard_dataenvelope{request_id, format, data[, error]}. Returns explicit error whencfg.Clipboard=falseinstead of silent drop — the operator UI can now show meaningful state. Closes part of NATIVE-C1. - Honest codec negotiation:
handleCodecOfferno longer hard-codes"jpeg".video_codecset to"jpeg"only whencfg.Screenshot=true, otherwise empty string;audio_codecis always empty (os_agent does not stream audio). Server + operator panel now see true capabilities instead of a fake promise. - Roadmap doc:
docs/AGENT_CLIENT_ROADMAP_2026-04-10.md— comprehensive audit with honest scope split. P0 (this session: device ID, URL validation, keyring, clipboard_get, codec honesty) = done. P1 (next session: sidecar Go agent in Tauri, chat server-side, TLS pinning UI) = scoped. P2 (separate phases, 4-6 weeks: screen capture, H.264, input injection, audio, E2E NaCl, policy engine, auto-update) = documented with exact crate choices.
Agent Client — Sidecar Architecture (Phase 55) ✅ COMPLETED 2026-04-21
-
sidecar.rscreated (350+ LOC):SidecarManager(Arc<Inner>for cheap Clone, Tauri managed state).find_binary()4-step search ($BETTERDESK_AGENT_BINenv → exe dir → data dir → PATH).write_go_config()writes JSON in Go agent format (GoAgentConfigmatchingbetterdesk-agent/agent/config.go).spawn_process()launches go agent with-config <path>.monitor_loop()tokio task — polls child every 5s, exponential backoff restart (5s×2^n, max 5min).terminate_child()— SIGTERM on Unix (libc::kill) + 5s grace + force kill.Dropimpl kills child on Tauri exit. -
config.rsextended with CDAP + capability fields: New fields:api_key(CDAP auth),cdap_port(default 21122),allow_screen_capture(default true),require_consent(default true),allow_terminal(default true),allow_file_browser(default true),allow_clipboard(default true),auto_start_sidecar(default true). Removed:allow_remote,allow_file_transfer(replaced by granular fields). Addedto_sidecar_config() -> SidecarConfigconversion method usingdirectories::ProjectDirsfor data_dir. -
commands.rs— 4 new sidecar IPC commands:get_sidecar_status→SidecarStatus { running, pid, restart_count, state, binary_path, cdap_url }.start_sidecar— stops previous, writes config, spawns binary, returns status.stop_sidecar— SIGTERM + cleanup.restart_sidecar— alias for start.restart_agent_servicenow delegates tostart_sidecarinstead of returning Err.AgentSettingsstruct updated with new capability fields.save_agent_settings/get_agent_settingsupdated to match. -
lib.rs— sidecar wired into Tauri state:pub mod sidecaradded.SidecarManageradded toAgentState. Auto-start sidecar insetupclosure ifauto_start_sidecar && is_registered. 4 new sidecar commands registered ininvoke_handler![]. Tray menu: new "Restart CDAP agent" item — callssidecar.stop()+sidecar.start()using current config, no admin required. - Agent is now truly hidden:
skipTaskbar: true+visible: falseintauri.conf.jsonalready set. Agent does not appear in taskbar/dock. Main window only shows on tray click or first-time setup. Goal achieved: behaves like RustDesk desktop but invisible. - Roadmap updated:
docs/AGENT_CLIENT_ROADMAP_2026-04-21.md— full architecture diagram, current state table, Phase 56-61 plan (bundling, continuous capture, input injection, H.264, audio, E2E NaCl). Sidecar testing procedures documented.
Update Mechanism Fix & Rewrite (Phase 56) ✅ COMPLETED 2026-05-29
- In-app update SHA tracking fix (Issue #154, CRITICAL):
updateService.jsapplyUpdate()only saved SHA whenresults.failed.length === 0. Server binary download/compile failure (no pre-built release + no Go installed) added tofailed[]→ SHA never saved → same updates shown after every restart (infinite loop). Fix: addedNON_CRITICAL_FILESset (betterdesk-server,betterdesk-server-deploy,server-source) — SHA saved when only non-critical steps fail. Critical failures (file download/write errors) still block SHA save. - CLI updater fix:
update-cli.jsnow distinguishes critical vs non-critical failures matching server-side logic. Non-critical failures log warning but don't set exit code 1, preventing the infinite retry loop when called from ALL-IN-ONE scripts. - betterdesk.sh — GitHub pull update: New
update_from_github()function.git clone --depth 1(or tarball fallback). Downloads latest code → updates Go server source → compiles Go server → deploys binary → copies Node.js console files (preserving .env, data/, node_modules/) → npm install → updates installer scripts → updates SHA tracking.do_update()rewritten with 3-method menu: (1) Online GitHub update (recommended), (2) In-app Node.js updater, (3) Legacy local copy. Auto mode uses GitHub path. - betterdesk.ps1 — GitHub pull update: New
Update-FromGitHubfunction. Same 3-method menu.git cloneor ZIP archive fallback viaSystem.Net.WebClient. Preserves .env, data/, node_modules/. Compiles Go server if Go available, warns if not. - betterdesk-docker.sh — GitHub pull update: New
update_docker_from_github()function. Downloads latest source → updates Go server source, Node.js console, Dockerfiles, compose files → regenerates docker-compose.yml → rebuilds images → restarts containers. 2-method menu: (1) Online GitHub + rebuild, (2) Local rebuild.
Konfiguracja przez Zmienne Środowiskowe
PEER_TIMEOUT_SECS=15 # Timeout dla offline (domyślnie 15s)
HEARTBEAT_INTERVAL_SECS=3 # Interwał sprawdzania (domyślnie 3s)
HEARTBEAT_WARNING_THRESHOLD=2 # Próg dla DEGRADED
HEARTBEAT_CRITICAL_THRESHOLD=4 # Próg dla CRITICAL
Nowe Statusy Urządzeń
ONLINE → Wszystko OK
DEGRADED → 2-3 pominięte heartbeaty
CRITICAL → 4+ pominięte, wkrótce offline
OFFLINE → Przekroczony timeout
Dokumentacja
Pełna dokumentacja: STATUS_TRACKING_v3.md
� Zmiana ID Urządzenia
Endpoint API
POST /api/peers/:old_id/change-id
Content-Type: application/json
X-API-Key: <api-key>
{ "new_id": "NEWID123" }
Pliki Źródłowe
| Plik | Opis |
|---|---|
id_change.rs | Moduł obsługi zmiany ID przez protokół klienta |
database_v3.rs | Funkcje change_peer_id(), get_peer_id_history() |
http_api_v3.rs | Endpoint POST /api/peers/:id/change-id |
Walidacja
- Długość ID: 6-16 znaków
- Dozwolone znaki: A-Z, 0-9,
-,_ - Unikatowość: Nowe ID nie może być zajęte
- Rate limiting (klient): 5 min cooldown
Dokumentacja
Pełna dokumentacja: ID_CHANGE_FEATURE.md
🌍 System i18n (Wielojęzyczność)
Pliki Systemu
| Plik | Opis |
|---|---|
web/i18n.py | Moduł Flask z API endpoints (deprecated) |
web-nodejs/middleware/i18n.js | Node.js i18n middleware |
web-nodejs/lang/*.json | Pliki tłumaczeń (Node.js) |
web/static/js/i18n.js | Klient JavaScript |
web/static/css/i18n.css | Style dla selektora języka |
web/lang/*.json | Pliki tłumaczeń (Flask, deprecated) |
API Endpoints
| Endpoint | Metoda | Opis |
|---|---|---|
/api/i18n/languages | GET | Lista dostępnych języków |
/api/i18n/translations/{code} | GET | Pobierz tłumaczenia |
/api/i18n/set/{code} | POST | Ustaw preferencję języka |
Dodawanie nowego języka
- Skopiuj
web/lang/en.jsondoweb/lang/{kod}.json - Przetłumacz wszystkie wartości
- Zaktualizuj sekcję
metaz informacjami o języku
Dokumentacja
Pełna dokumentacja: CONTRIBUTING_TRANSLATIONS.md
🔨 Skrypty Budowania
Interaktywne skrypty kompilacji
| Skrypt | Platforma | Opis |
|---|---|---|
build-betterdesk.sh | Linux/macOS | Interaktywny build z wyborem wersji/platformy |
build-betterdesk.ps1 | Windows | Interaktywny build PowerShell |
Użycie
# Linux - tryb interaktywny
./build-betterdesk.sh
# Linux - tryb automatyczny
./build-betterdesk.sh --auto
# Windows - tryb interaktywny
.\build-betterdesk.ps1
# Windows - tryb automatyczny
.\build-betterdesk.ps1 -Auto
GitHub Actions CI/CD
Workflow .github/workflows/build.yml automatycznie:
- Buduje binarki dla Linux x64, Linux ARM64, Windows x64
- Uruchamia się przy zmianach w
hbbs-patch-v2/src/** - Pozwala na ręczne uruchomienie z wyborem wersji
- Opcjonalnie tworzy GitHub Release
Dokumentacja
Pełna dokumentacja budowania: BUILD_GUIDE.md
⚠️ Znane Problemy
Aktywne
Desktop Client: Chat always "Disconnected"✅ ROZWIĄZANE — chatRelay.jscase 'hello': breakwas a no-op. Now sendswelcomeack. Rust client handleswelcome+statusframes — Phase 38Web Remote: Max 9 FPS✅ ROZWIĄZANE —video_receivedack sent before decoding, stall recovery requests keyframe after 5s silence — Phase 38Web Remote: No mouse/keyboard control✅ ROZWIĄZANE —_isInputFocused()blocked keyboard on hidden inputs after login. Now ignoresel.offsetParent === null— Phase 38Web Remote: Video blurry on fullscreen✅ ROZWIĄZANE —renderer.resize()triggersrefreshVideokeyframe request viaonResizeRefreshcallback — Phase 38Desktop Client: RemoteView is stub✅ ROZWIĄZANE — Full H.264/VP9 remote desktop withsession_manager.rs,video_pipeline.rs,input_pipeline.rs, multi-monitor, session recording — Phase 38 + Phase 43Desktop Client: Remote agent JPEG 15fps✅ ROZWIĄZANE — Replaced with H.264/VP9 codec pipeline inremote/video_pipeline.rs+protocol/codec.rs. Full session-based remote withsession_manager.rsorchestrating relay message loop — Phase 43Desktop Client: GUI panels mostly stubs✅ ROZWIĄZANE — OperatorPanel 8 endpoints fixed (/api/bd/operator/*→ actual Go server routes). ManagementPanel and HelpRequestPanel confirmed functional — Phase 38
Resolved
Docker pull error✅ ROZWIĄZANE - Obrazy budowane lokalnie zpull_policy: never- Axum 0.5 vs 0.6 - Projekt używa axum 0.5, nie 0.6 (różnica w API State vs Extension)
- Windows API key path - Na Windows
.api_keyjest w katalogu roboczym, nie w/opt/rustdesk/ Urządzenia offline✅ ROZWIĄZANE - Docker obrazy używają teraz binarek BetterDesk"no such table: peer"✅ ROZWIĄZANE - Dockerfile.hbbs kopiuje zmodyfikowane binarkiGo Server: 2FA brute-force✅ ROZWIĄZANE -loginLimiter.Allow(clientIP)+ audit log (H3)Go Server: Partial 2FA token TTL✅ ROZWIĄZANE -GenerateWithTTL()5min (H4)Go Server: No TLS on signal/relay✅ ROZWIĄZANE -DualModeListenerz auto-detekcją TLS, WSS, flagi--tls-signal/--tls-relay(Phase 3)Go Server: ConfigUpdate missing✅ ROZWIĄZANE -TestNatResponse.Cupopulated with relay/rendezvous servers (M8)Go Server: SQLite only✅ ROZWIĄZANE - PostgreSQL backend implemented (db/postgres.go, pgx/v5, pgxpool, LISTEN/NOTIFY) — Phase 4Go Server: E2E encryption "nieszyfrowane"✅ ROZWIĄZANE - 4 bugs fixed in signal/handler.go + relay/server.go (SignIdPk format, PunchHoleResponse, RelayResponse removal). Root cause: deployment path mismatch (/opt/betterdesk-go/vs/opt/rustdesk/) — Phase 6Go Server: "Failed to secure tcp" when logged in✅ ROZWIĄZANE - TCP/WS signal handlers returned nil for online targets, forcing logged-in clients (which use TCP) to wait for target responses that may never arrive. Fixed: immediate PunchHoleResponse/RelayResponse with signed PK matching UDP behavior — Phase 7QR code invalid on Windows✅ ROZWIĄZANE - Inverted QR colors fixed (dark:'#e6edf3'→'#000000',light:'#0d1117'→'#ffffff') — Phase 8Users tab redirect for operators✅ ROZWIĄZANE - Createdviews/errors/403.ejs(missing template caused crash → redirect) — Phase 8Client login✅ ROZWIĄZANE - Added RustDesk-compatible_Map<String, dynamic>error/api/loginendpoint to Go serverclient_api_handlers.go— Phase 8GetPeer missing live status✅ ROZWIĄZANE -handleGetPeernow returnslive_online+live_statusfrom memory map — Phase 8Hostname/Platform columns empty (Issue #37)✅ ROZWIĄZANE - Go server was missing/api/heartbeat,/api/sysinfo,/api/sysinfo_verendpoints. RustDesk client sends hostname/os/version via HTTP API to signal_port-2 (21114), but Go server had no handlers. Added all 3 endpoints +UpdatePeerSysinfoDB method — Phase 9Users page 401 error (Issue #42)✅ ROZWIĄZANE - Route conflict inrustdesk-api.routes.js:/api/usersand/api/peershandlers were blocking panel requests (expecting Bearer token). Fixed by addingnext('route')fallthrough when no Bearer token present, allowing session-based panel requests to reachusers.routes.js— Phase 10PostgreSQL→SQLite switch on UPDATE✅ ROZWIĄZANE -betterdesk.shandbetterdesk.ps1were overwriting.envwith default SQLite config during UPDATE/REPAIR. Addedpreserve_database_config()function to read existing DB config before reinstalling console — Phase 11Folders not working with PostgreSQL (Issue #48)✅ ROZWIĄZANE -folders.routes.jsandusers.routes.jsused SQLite-specificresult.lastInsertRowidinstead ofresult.id. Fixed for PostgreSQL compatibility — Phase 12TOTP column missing on upgrade (Issue #38)✅ ROZWIĄZANE - Added automatic migration oftotp_secret,totp_enabled,totp_recovery_codescolumns to existinguserstable for both SQLite and PostgreSQL — Phase 12SELinux volume mount issues (Issue #31)✅ ROZWIĄZANE - Added SELinux documentation to DOCKER_TROUBLESHOOTING.md with 4 solutions (named volumes,:zflag, chcon, setenforce) — Phase 12Docker single-container port 5000 conflict (Issue #56)✅ ROZWIĄZANE - Go serverconfig.LoadEnv()read genericPORT=5000(meant for Node.js console) and set signal port to 5000 instead of 21116, causing EADDRINUSE race condition. Fixed by addingSIGNAL_PORTenv var with priority overPORTinconfig.go, settingSIGNAL_PORT=21116insupervisord.confandentrypoint.sh, addingENV SIGNAL_PORT=21116toDockerfile— Phase 13✅ ROZWIĄZANE - Diagnostics function called undefinedget_public_ip: command not found(Issue #58)get_public_ipat line 3348. Created reusableget_public_ip()function (IPv4-first) in all 3 scripts, replaced all inline curl patterns. Added private IP warning +RELAY_SERVERSenv var override insetup_services(). Go serverGetRelayServers()now auto-appends relay port when missing — Phase 14Docker: Devices page 0 while Dashboard shows count (Issue #59)✅ ROZWIĄZANE - Docker single-container never created.api_keyfile. Dashboard used public/api/server/stats(correct), Devices used protected/api/peers(401 → empty). Go serverloadAPIKey()now auto-generates key on first run, Docker entrypoint also generates as safety net, Node.jsbetterdeskApi.jshas 401-interceptor to reload key from file — Phase 16Relay fails when initial public IP detection fails✅ ROZWIĄZANE -startIPDetectionRetry()goroutine was defined but never called fromStart()insignal/server.go. If boot-timedetectPublicIP()failed, no retry ever happened, causinggetRelayServer()to return LAN IP. Fixed by callings.startIPDetectionRetry(s.ctx)inStart()— Phase 17✅ ROZWIĄZANE - RustDesk client sends/api/audit/connreturns 400 for numeric device IDshost_idas number. Validation rejected non-string. Changed toString()coercion — Phase 17Stale sysinfo log spam every 15 seconds✅ ROZWIĄZANE - Added 5-minute per-device throttle for sysinfo log messages in heartbeat handler — Phase 17Address Book sync fails (Issue #57)✅ ROZWIĄZANE - Go server/api/abendpoints were stubs returning empty data. Addedaddress_bookstable + full GET/POST handlers for/api/ab,/api/ab/personal,/api/ab/tagswith SQLite + PostgreSQL support — Phase 18Settings password "password is required" (Issue #60)✅ ROZWIĄZANE -settings.jssent snake_case fields,auth.routes.jsexpected camelCase. Fixed field names + added missingconfirmPassword— Phase 18Password modal plaintext (Issue #60)✅ ROZWIĄZANE -modal.jsprompt checkedoptions.typebutusers.jspassedinputType. Fixed to check both — Phase 18Empty UUID in relay causes all WAN connections to fail (Issues #58, #63, #64)✅ ROZWIĄZANE -PunchHoleResponsehas nouuidfield, so when hole-punch fails, client sendsRequestRelay{uuid=""}. Signal server now generatesuuid.New().String()when empty in bothhandleRequestRelay()(UDP) andhandleRequestRelayTCP()(TCP). Relay address validation rejectshost < 2 chars(preventsrelay=a:21117) — Phase 19Docker DNS failures during build (Issue #62)✅ ROZWIĄZANE - Added retry logic to allapk add --no-cachecommands in Dockerfile, Dockerfile.server, Dockerfile.console — Phase 19Target device sends empty UUID in RelayResponse (Issues #64, #65)✅ ROZWIĄZANE - Old RustDesk clients don't echo UUID back inRelayResponse. AddedpendingRelayUUIDs sync.Mapto track UUIDs sent to targets inRequestRelay/PunchHole. When target responds with empty UUID,handleRelayResponseForwardrecovers original UUID from store. Fixes relay pairing failures where initiator and target used mismatched UUIDs — Phase 23Notes/tags written to wrong database✅ ROZWIĄZANE - Node.js panel was writing notes/user/tags directly to local SQLite instead of Go server's database. Now routes throughPATCH /api/peers/{id}endpoint on Go server — Phase 23Deleted devices reappear as zombies✅ ROZWIĄZANE - AddedIsPeerSoftDeleted()check in signal handlers. Soft-deleted devices cannot re-register, preventing "zombie" devices from reappearing after admin deletion — Phase 23Metrics not visible in device detail (Issue #65)✅ ROZWIĄZANE - Addedpeer_metricstable to Go server database (SQLite + PostgreSQL), extendedhandleClientHeartbeatto parse and save CPU/memory/disk metrics, addedGET /api/peers/{id}/metricsendpoint for Node.js console to fetch metrics from Go server — Phase 24
📝 Wytyczne dla Copilota
Przy kompilacji:
- Zawsze używaj
git submodule update --init --recursivepo sklonowaniu rustdesk-server - Sprawdź wersję axum w Cargo.toml przed modyfikacją http_api.rs
- Po kompilacji zaktualizuj CHECKSUMS.md
Przy modyfikacjach kodu:
-
Kod API jest w
hbbs-patch-v2/src/http_api.rs -
Kod main jest w
hbbs-patch-v2/src/main.rs -
Używaj
hbb_common::log::info!()zamiastprintln!() -
Testuj na SSH (Linux) i lokalnie (Windows)
-
W plikach projektu używaj angielskiego, dokumentacja także ma być po angielsku, upewnij się za każdym razem że twoje zmiany są zgodne z aktualnym stylem i konwencjami projektu, nie wprowadzaj nowych konwencji bez uzasadnienia oraz są napisane w sposób spójny z resztą kodu, unikaj mieszania stylów kodowania, jeśli masz wątpliwości co do stylu, sprawdź istniejący kod i dostosuj się do niego, pamiętaj że spójność jest kluczowa dla utrzymania czytelności i jakości kodu. Wykorzystuj tylko język angielski w komunikacji, dokumentacji i komentarzach, nawet jeśli pracujesz nad polskojęzyczną funkcją, zachowaj angielski dla wszystkich aspektów kodu i dokumentacji, to ułatwi współpracę z innymi deweloperami i utrzyma spójność projektu.
-
Tworząc nowe moduły i zakładki pamiętaj o zachowaniu spójności z istniejącym stylem kodowania, strukturą projektu i konwencjami nazewnictwa, sprawdź istniejące moduły i zakładki, aby upewnić się że twoje zmiany są zgodne z aktualnym stylem, unikaj wprowadzania nowych konwencji bez uzasadnienia, jeśli masz wątpliwości co do stylu, dostosuj się do istniejącego kodu, pamiętaj że spójność jest kluczowa dla utrzymania czytelności i jakości kodu.
-
Przy dodawaniu nowych elementów do panelu web czy innych części projektu upewnij się że są one zgodne z systemem i18n, dodaj odpowiednie klucze do plików tłumaczeń i przetestuj działanie w obu językach, pamiętaj że wszystkie teksty powinny być tłumaczalne i nie powinno się używać hardcoded stringów w kodzie, to ułatwi utrzymanie wielojęzyczności projektu i zapewni spójność w komunikacji z użytkownikami (nie stosuj tych praktyk w przypadku elementów które nie będą bezpośrednio dostępne w interfejsie i które są zwyczajnymi funkcjami w kodzie).
-
Przy wprowadzaniu zmian projekcie upewnij się że będą one możliwe do instalacji przez obecne skrypty ALL-IN-ONE, jeśli wprowadzasz nowe funkcje lub zmieniasz istniejące, zaktualizuj skrypty instalacyjne, aby uwzględniały te zmiany, przetestuj instalację na czystym systemie, aby upewnić się że wszystko działa poprawnie, pamiętaj że skrypty ALL-IN-ONE są kluczowym elementem projektu i muszą być aktualizowane wraz z rozwojem funkcji, to zapewni użytkownikom łatwą i bezproblemową instalację najnowszych wersji projektu. Skrypty ALL-IN-ONE powinny być aktualizowane i testowane przy każdej większej zmianie, aby zapewnić kompatybilność i łatwość instalacji dla użytkowników, pamiętaj że skrypty te są często używane przez osoby bez zaawansowaną wiedzą techniczną, więc ważne jest aby były one jak najbardziej niezawodne i łatwe w użyciu, zawsze testuj skrypty po wprowadzeniu zmian, aby upewnić się że działają poprawnie i nie powodują problemów z instalacją.
-
Postaraj się rozwiązywać problemy z warningami porzy kompilacji, stosować najnowsze wersje bibliotek i narzędzi, utrzymywać kod w czystości i zgodności z aktualnymi standardami, to ułatwi utrzymanie projektu i zapewni jego długoterminową stabilność, pamiętaj że regularne aktualizacje i dbanie o jakość kodu są kluczowe dla sukcesu projektu, unikaj pozostawiania warningów bez rozwiązania, jeśli pojawią się warningi podczas kompilacji, postaraj się je rozwiązać jak najszybciej, to pomoże utrzymać kod w dobrej kondycji i zapobiegnie potencjalnym problemom w przyszłości.
-
Przy wprowadzaniu zmian w API, upewnij się że są one kompatybilne wstecz, jeśli wprowadzasz zmiany które mogą wpłynąć na istniejące funkcje lub integracje, postaraj się zachować kompatybilność wsteczną, jeśli to nie jest możliwe, odpowiednio zaktualizuj dokumentację i poinformuj użytkowników o zmianach, pamiętaj że stabilność API jest ważna dla użytkowników i deweloperów korzystających z projektu, staraj się unikać wprowadzania breaking changes bez uzasadnienia i odpowiedniej komunikacji, to pomoże utrzymać zaufanie i satysfakcję użytkowników oraz deweloperów współpracujących nad projektem.
-
Przy wprowadzaniu zmian w systemie statusu, upewnij się że są one dobrze przemyślane i przetestowane, jeśli wprowadzasz nowe statusy lub zmieniasz istniejące, postaraj się zachować spójność z aktualnym systemem i zapewnić jasne kryteria dla każdego statusu, przetestuj działanie nowych statusów w różnych scenariuszach, to pomoże zapewnić że system statusu jest wiarygodny i użyteczny dla użytkowników, pamiętaj że system statusu jest kluczowym elementem projektu i musi być utrzymywany w dobrej kondycji, staraj się unikać wprowadzania zmian które mogą wprowadzić niejasności lub problemy z interpretacją statusów, to pomoże utrzymać zaufanie użytkowników do systemu i zapewni jego skuteczność.
-
Stosuj wszystkie najlepsze praktyki bezpieczeństwa przy wprowadzaniu nowych funkcji, szczególnie tych związanych z autoryzacją, uwierzytelnianiem i komunikacją sieciową, jeśli wprowadzasz nowe funkcje które mogą mieć wpływ na bezpieczeństwo, upewnij się że są one dobrze zabezpieczone i przetestowane pod kątem potencjalnych luk, pamiętaj że bezpieczeństwo jest kluczowe dla projektu i jego użytkowników, staraj się unikać wprowadzania funkcji które mogą wprowadzić ryzyko bezpieczeństwa bez odpowiednich środków zaradczych, to pomoże utrzymać zaufanie użytkowników i zapewni długoterminowy sukces projektu.
-
Przy problemach z Dockerem, zawsze sprawdzaj czy obrazy są budowane lokalnie, unikaj używania
docker compose pulldla obrazów betterdesk-*, jeśli napotkasz problemy z Dockerem, sprawdź DOCKER_TROUBLESHOOTING.md, to pomoże szybko zidentyfikować i rozwiązać problemy związane z Dockerem, pamiętaj że Docker jest ważnym elementem projektu i musi być utrzymywany w dobrej kondycji, staraj się unikać wprowadzania zmian które mogą wpłynąć na działanie Docker, to pomoże zapewnić stabilność i niezawodność projektu dla użytkowników korzystających z tej platformy. -
Jeżeli napotkasz błędy kompilacji związane z innymi komponentami bądź niezgodności z bibliotekami, zawsze sprawdzaj aktualne wersje używanych bibliotek i narzędzi, upewnij się że są one kompatybilne z kodem projektu, jeśli napotkasz błędy kompilacji, postaraj się je rozwiązać jak najszybciej, to pomoże utrzymać kod w dobrej kondycji i zapobiegnie potencjalnym problemom w przyszłości, pamiętaj że regularne aktualizacje i dbanie o jakość kodu są kluczowe dla sukcesu projektu, staraj się unikać pozostawiania błędów kompilacji bez rozwiązania, to pomoże utrzymać stabilność i niezawodność projektu dla wszystkich użytkowników i deweloperów współpracujących nad projektem.
-
Wprowadzając funkcje powiązane z większą liczbą elementów, modułów czy funkcji staraj się je dobrze zorganizować i przemyśleć, jeśli wprowadzasz funkcje które mają wpływ na wiele części projektu, postaraj się je dobrze zorganizować i przemyśleć, to pomoże zapewnić że są one łatwe do zrozumienia i utrzymania, pamiętaj że spójność i organizacja kodu są kluczowe dla jego czytelności i jakości, staraj się unikać wprowadzania funkcji które są niejasne lub trudne do zrozumienia, to pomoże utrzymać projekt w dobrej kondycji i zapewni jego długoterminowy sukces. Przykładowo dodając nowe funkcje do klienta desktop które mają być powiązane z panelem web, upewnij się że po zakończeniu tworzenia nowego kodu wprowadzisz także zmiany w innych elementach aby funkcje były bardziej kompletne.
-
Po utworzeniu nowych funkcji postaraj zanotować sobie procedury powiązane z ich wdrażaniem i testowaniem, to pomoże ci w przyszłości szybko przypomnieć sobie jak działają i jak je utrzymywać, pamiętaj że dokumentacja jest kluczowa dla utrzymania projektu i jego zrozumienia przez innych deweloperów, staraj się unikać pozostawiania nowych funkcji bez odpowiedniej dokumentacji, to pomoże zapewnić że są one łatwe do zrozumienia i utrzymania dla wszystkich współpracujących nad projektem. Wżnym elementem całego projektu jest nie tylko dokumentacja ale także skrypty instalacyjne pozwalające szybko i łatwo zainstalować najnowsze wersje projektu, dlatego po wprowadzeniu nowych funkcji upewnij się że są one uwzględnione w skryptach ALL-IN-ONE, to pomoże zapewnić że użytkownicy mogą łatwo korzystać z nowych funkcji bez konieczności ręcznej konfiguracji czy rozwiązywania problemów z instalacją. Pamietaj że klienci często nie są technicznie obeznani i mogą mieć trudności z ręczną instalacją, dlatego ważne jest aby skrypty instalacyjne były aktualizowane i testowane przy każdej większej zmianie, to zapewni łatwą i bezproblemową instalację najnowszych wersji projektu dla wszystkich użytkowników, niezależnie od ich poziomu zaawansowania technicznego.
-
Stosuj tylko sprawdzone rozwiązania, moduły czy biblioteki do implementacji nowych funkcji, unikaj eksperymentalnych lub nieprzetestowanych rozwiązań, jeśli wprowadzasz nowe funkcje, postaraj się używać sprawdzonych i stabilnych rozwiązań, to pomoże zapewnić że są one niezawodne i bezpieczne dla użytkowników, pamiętaj że stabilność i bezpieczeństwo są kluczowe dla projektu i jego użytkowników, staraj się unikać wprowadzania funkcji które mogą wprowadzić ryzyko lub problemy bez odpowiednich środków zaradczych, to pomoże utrzymać zaufanie użytkowników i zapewni długoterminowy sukces projektu. Na bierząco aktualizuj biblioteki i narzędzia używane w projekcie, to pomoże zapewnić że korzystasz z najnowszych funkcji i poprawek bezpieczeństwa, jeśli napotkasz problemy z kompatybilnością lub błędy związane z bibliotekami, postaraj się je rozwiązać jak najszybciej, to pomoże utrzymać projekt w dobrej kondycji i zapobiegnie potencjalnym problemom w przyszłości, pamiętaj że regularne aktualizacje i dbanie o jakość kodu są kluczowe dla sukcesu projektu, staraj się unikać pozostawiania problemów związanych z bibliotekami bez rozwiązania, to pomoże utrzymać stabilność i niezawodność projektu dla wszystkich użytkowników i deweloperów współpracujących nad projektem.
-
Bewzględnie eliminuj wszystkie błędy bezpieczeństwa, przestrzałe biblioteki oraz inne problemy z bezpieczeństwem, jeśli napotkasz błędy bezpieczeństwa lub przestarzałe biblioteki, postaraj się je rozwiązać jak najszybciej, to pomoże utrzymać projekt bezpieczny dla użytkowników, pamiętaj że bezpieczeństwo jest kluczowe dla projektu i jego użytkowników, staraj się unikać pozostawiania problemów związanych z bezpieczeństwem bez rozwiązania, to pomoże utrzymać zaufanie użytkowników i zapewni długoterminowy sukces projektu. Regularnie przeprowadzaj audyty bezpieczeństwa i aktualizuj zależności, to pomoże zapewnić że projekt jest odporny na nowe zagrożenia i ataki, jeśli napotkasz problemy związane z bezpieczeństwem, postaraj się je rozwiązać jak najszybciej, to pomoże utrzymać projekt w dobrej kondycji i zapobiegnie potencjalnym problemom w przyszłości, pamiętaj że regularne audyty i dbanie o bezpieczeństwo są kluczowe dla sukcesu projektu, staraj się unikać pozostawiania problemów związanych z bezpieczeństwem bez rozwiązania, to pomoże utrzymać stabilność i niezawodność projektu dla wszystkich użytkowników i deweloperów współpracujących nad projektem.
Dotyczy panelu web i jego zakładek, funkcji itp.
- Zawsze zachowuj spójność z aktualnym stylem kodowania i konwencjami projektu.
- Używaj angielskiego dla wszystkich tekstów, komunikacji i dokumentacji ale twórz także inne wersje językowe zgodne z obecnym systemem i18n.
- Upewnij się że wszystkie teksty są tłumaczalne i nie używaj hardcoded stringów w kodzie.
- Testuj działanie nowych funkcji w obu językach (EN/PL) i upewnij się że są one zgodne z systemem i18n.
- Przy dodawaniu nowych elementów do panelu web, upewnij się że są one dobrze zorganizowane i przemyślane, to pomoże zapewnić że są one łatwe do zrozumienia i utrzymania.
- Zachowaj spójność wyglądu i stylu, stosuj optymalizację oraz najlepsze praktyki dla interfejsu użytkownika, to pomoże zapewnić że panel web jest przyjazny dla użytkowników i łatwy w obsłudze.
- Przy wprowadzaniu zmian w panelu web, upewnij się że są one dobrze przemyślane i przetestowane, staraj się unikać wprowadzania zmian które mogą wprowadzić niejasności lub problemy z użytecznością, to pomoże utrzymać zaufanie użytkowników do panelu web i zapewni jego skuteczność jako narzędzia do zarządzania serwerem BetterDesk dla wszystkich użytkowników, niezależnie od ich poziomu zaawansowania technicznego.
- Upewnij się że wszystkie elementy pokazujące statystyki urządzeń oraz ich parametry są zgodne ze sobą, korzystają z tych samych źródeł danych i są aktualizowane w czasie rzeczywistym, to pomoże zapewnić że użytkownicy mają dostęp do dokładnych i spójnych informacji o swoich urządzeniach, co jest kluczowe dla skutecznego zarządzania i monitorowania serwera BetterDesk. Nie doprowadź do sytuacji w której różne części panelu web pokazują różne informacje o statusie urządzeń, to może wprowadzić użytkowników w błąd i obniżyć zaufanie do panelu web jako narzędzia do zarządzania serwerem BetterDesk.
- Stosuj praktyki bezpieczeństwa.
- Pamiętaj aby panel web operatora zawierał odpowiednią zakładkę logowania operaji operatorów przypisanych do ich kont, domyślnie ma być on używany jednocześnie przez większą ilość operatorów i panel web wraz z jego funkcjami ma być dopasowany do tego stylu zarządzania.
Przy problemach Docker:
- Sprawdź czy obrazy są budowane lokalne (
docker compose build) - Nie używaj
docker compose pulldla obrazów betterdesk-* - Sprawdź DOCKER_TROUBLESHOOTING.md
🤖 AI Roles & Security Policy
Copilot Roles in This Project
| Role | Scope | Description |
|---|---|---|
| Security Auditor | All code changes | Every modification undergoes automatic security review. Identifies vulnerabilities, insecure patterns, and outdated dependencies. |
| Go Backend Developer | betterdesk-server/ | Clean-room RustDesk-compatible server implementation. Protocol handling, crypto, database, API. |
| Node.js Backend Developer | web-nodejs/ | Express.js web console — authentication, CRUD, RustDesk Client API, WebSocket. |
| DevOps Engineer | Scripts, Docker, CI/CD | ALL-IN-ONE installers (betterdesk.sh, betterdesk.ps1), Dockerfiles, GitHub Actions. |
| Frontend Developer | web-nodejs/views/, static/ | EJS templates, CSS, client-side JavaScript, i18n. |
| Documentation Maintainer | docs/, .github/ | Keep all documentation current with code changes. |
Security-First Policy (DEFAULT BEHAVIOR)
All code changes MUST include a security review as part of the implementation process. This is not optional.
Mandatory checks for every change:
- Input validation — All user-supplied data (URL params, body, headers, query strings) must be validated with strict patterns (regexps, type checks, length limits).
- Rate limiting — All public-facing endpoints and connection accept loops must have IP-based rate limiting.
- SQL injection prevention — All database queries must use parameterized queries. LIKE patterns must escape
%and_. - Authentication & authorization — Every non-public endpoint must verify credentials and enforce RBAC.
- Token security — Short-lived tokens for transient states (2FA partial tokens: 5min max). No long-lived tokens for intermediate auth states.
- Dependency audit — Flag outdated or vulnerable dependencies. Update proactively.
- Error handling — Never expose internal error details to clients. Log internally, return generic messages.
- Audit logging — Security-relevant operations (login, failed auth, config changes, bans) must be logged.
📞 Kontakt
- Repozytorium: https://github.com/UNITRONIX/Rustdesk-FreeConsole
- Issues: GitHub Issues
Ostatnia aktualizacja: 2026-05-29 (Phase 56: Update Mechanism Fix & Rewrite — SHA tracking fix for Issue #154, GitHub pull update in all 3 ALL-IN-ONE scripts, critical vs non-critical failure distinction in updateService.js + update-cli.js. Previous: Phase 55 Agent Client Sidecar Architecture) przez GitHub Copilot
Trust
Not scanned yet. Artifacts are graded after they are crawled, so a recently discovered one may have no result for a while.
Versions
git-6073fd806f6e2026-08-04