41 KiB
CDAP Implementation Plan
Version: 1.0.0
Status: Draft
Created: 2026-03-19
Depends on: CUSTOM_DEVICE_API.md v0.2.0
Table of Contents
- Overview
- Architecture Decisions
- Authentication & 2FA Integration
- RustDesk Client Synchronization
- Implementation Phases
- File-Level Change Map
- Testing Strategy
- Migration & Backward Compatibility
- Risk Assessment
Overview
This document translates the CDAP specification (CUSTOM_DEVICE_API.md) into a concrete implementation plan covering:
- CDAP Gateway — WebSocket server on port 21122 in Go server
- Auth integration — CDAP clients authenticate via the same user/2FA system as the Node.js panel and RustDesk clients
- RustDesk synchronization — CDAP native desktop clients coexist with RustDesk clients in the same peer table, address books, connection history, and panel views
- Media channel — Binary frame relay for remote desktop, video, audio, input
- Native BetterDesk client — Desktop agent that combines remote desktop + OS management
Core Principle
One identity, one authority. A user account created in the Node.js panel works for panel login, RustDesk client login, AND CDAP native client login — same credentials, same 2FA, same RBAC.
Architecture Decisions
AD-1: Auth Source of Truth
Decision: The Go server is the auth source of truth for all protocol-level authentication (RustDesk clients, CDAP clients, API keys). The Node.js panel is the auth source of truth for panel UI sessions (cookie-based).
Rationale: RustDesk clients already authenticate via Go server's /api/login (port 21114). CDAP clients will use the same endpoint or a CDAP-specific auth message that routes to the same Go auth backend. This avoids auth duplication and ensures 2FA state is consistent.
┌──────────────────────────────────────────────────────┐
│ Authentication Flow │
│ │
│ Panel User → Node.js session (cookie) │
│ └─ 2FA check → Node.js auth.db (TOTP) │
│ │
│ RustDesk Client → Go server /api/login (JWT) │
│ └─ 2FA check → Go server TOTP (5-min partial) │
│ │
│ CDAP Client → Go server /ws/cdap auth msg (JWT) │
│ └─ 2FA check → Go server TOTP (same path) │
│ │
│ Panel ↔ Go → X-API-Key header (.api_key file) │
│ │
│ ──── Shared ───────────────────────────────────── │
│ Users table: Go server db (users) │
│ TOTP secrets: Go server db (users.totp_secret) │
│ Roles/RBAC: Go server db (users.role) │
│ API keys: Go server db (api_keys) │
│ Panel sessions: Node.js auth.db (separate, UI only) │
└──────────────────────────────────────────────────────┘
AD-2: Dual Auth Database Convergence
Current state: Node.js panel has auth.db (users, sessions) and Go server has db_v2.sqlite3 (users, api_keys). Both have user tables with potentially different passwords and TOTP states.
Decision: Phase 1 keeps dual DBs but syncs user credentials. Phase 3+ converges to Go server as sole user store, with Node.js panel delegating auth to Go server via REST API.
| Phase | Auth Model | User Store |
|---|---|---|
| Current | Dual (Node.js auth.db + Go users table) | Both, may diverge |
| Phase 1 CDAP | CDAP uses Go auth only | Go server primary for clients |
| Phase 3 | Node.js delegates to Go for user CRUD | Go server sole source |
| Long term | Single user store in Go, Node.js is pure UI | Go server |
AD-3: CDAP Auth Message vs HTTP Login
Decision: CDAP clients authenticate within the WebSocket connection using a JSON auth message (not a separate HTTP /api/login call). The Go server validates credentials using the same auth.VerifyPassword() and auth.ValidateTOTP() functions.
Rationale:
- Single connection setup (no HTTP pre-auth + WS upgrade dance)
- Works behind restrictive firewalls that only allow WS on one port
- Consistent with the "one port, one protocol" CDAP philosophy
- Server can immediately associate the WS connection with the authenticated user
// Step 1: Client sends auth message
{
"type": "auth",
"payload": {
"method": "user_password",
"username": "operator1",
"password": "...",
"device_id": "CDAP-A7F3B210",
"client_version": "1.0.0"
}
}
// Step 2a: Server responds (no 2FA)
{
"type": "auth_result",
"payload": {
"success": true,
"token": "jwt_24h",
"role": "operator",
"device_id": "CDAP-A7F3B210"
}
}
// Step 2b: Server responds (2FA required)
{
"type": "auth_result",
"payload": {
"success": false,
"requires_2fa": true,
"tfa_type": "totp",
"partial_token": "jwt_5min"
}
}
// Step 3: Client sends 2FA code
{
"type": "auth_2fa",
"payload": {
"partial_token": "jwt_5min",
"code": "123456"
}
}
// Step 4: Server responds with full auth
{
"type": "auth_result",
"payload": {
"success": true,
"token": "jwt_24h",
"role": "operator",
"device_id": "CDAP-A7F3B210"
}
}
// Alternative: API key auth (for unattended bridges/agents)
{
"type": "auth",
"payload": {
"method": "api_key",
"key": "...",
"device_id": "CDAP-A7F3B210"
}
}
AD-4: RustDesk Client Synchronization Strategy
Problem: When a BetterDesk native client (CDAP) runs on the same machine as a RustDesk client, or replaces it, the following must be synchronized:
| Resource | RustDesk Source | CDAP Source | Sync Strategy |
|---|---|---|---|
| Device ID | Numeric (e.g., 1340238749) |
CDAP- prefix |
Map: Go server maintains cdap_peer_id ↔ rustdesk_peer_id |
| Address book | /api/ab (Go server) |
Same /api/ab |
Shared: Same user, same AB |
| Connection history | Audit log (conn events) | Audit log (conn events) | Shared: Same audit table |
| User account | Go users table |
Same | Shared: Same credentials |
| Peers list | peers table |
Same peers table |
Shared: device_type column |
| Online status | Signal server in-memory map | CDAP gateway in-memory | Merged: Panel queries both |
| Tags/Groups | peers.tags |
Same | Shared: Same field |
AD-5: Native Client ↔ RustDesk Interoperability
Scenario: User A has BetterDesk native client. User B has RustDesk client. Can User A remote-control User B's machine?
Decision: Not directly — CDAP media channel is not compatible with RustDesk's protobuf signal/relay protocol. But the server can bridge the connection:
| From → To | Method | Latency | Status |
|---|---|---|---|
| RustDesk → RustDesk | Native (signal/relay) | Low | ✅ Works now |
| CDAP → CDAP | Native (CDAP media) | Low | Phase 5 |
| CDAP → RustDesk | Server-side protocol bridge | Medium (+50ms) | Phase 6 |
| RustDesk → CDAP | Not needed (CDAP is superset) | — | Not planned |
The server-side protocol bridge (Phase 6) is complex and optional. The primary migration path is: install BetterDesk native client → device appears as both rustdesk AND desktop types → gradually deprecate RustDesk client on that machine.
AD-6: Unified Device Identity
When a machine has both RustDesk and BetterDesk native clients:
┌─────────────────────────────────────────┐
│ Machine: PC-Design-03 │
│ │
│ ┌──────────────┐ ┌─────────────────┐ │
│ │ RustDesk │ │ BetterDesk │ │
│ │ Client │ │ Native Client │ │
│ │ │ │ │ │
│ │ ID: 892734561 │ │ ID: CDAP-D2E9F4│ │
│ │ Port: 21116 │ │ Port: 21122 │ │
│ └──────┬───────┘ └───────┬────────┘ │
│ │ │ │
└─────────┼───────────────────┼───────────┘
│ │
▼ ▼
┌───────────────────────────────────────┐
│ BetterDesk Server │
│ │
│ peers table: │
│ ┌─────────────┬──────────┬─────────┐ │
│ │ 892734561 │ rustdesk │ linked │ │
│ │ CDAP-D2E9F4 │ desktop │ linked │ │
│ └─────────────┴──────────┴─────────┘ │
│ │
│ peer_links table (NEW): │
│ ┌──────────────┬───────────────────┐ │
│ │ 892734561 │ CDAP-D2E9F4 │ │
│ │ (rustdesk) │ (desktop) │ │
│ └──────────────┴───────────────────┘ │
│ │
│ Panel shows: 1 machine, 2 protocols │
│ Admin can merge or keep separate │
└───────────────────────────────────────┘
Authentication & 2FA Integration
CDAP Auth Flow (Detailed)
┌──────────────┐
│ CDAP Client │
│ (bridge/ │
│ agent) │
└──────┬───────┘
│
WS connect :21122
│
▼
┌───────────────┐
│ CDAP Gateway │
│ (Go server) │
└───────┬───────┘
│
┌───────────────┼───────────────┐
│ │ │
method=api_key method=user_password method=device_token
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────────┐
│ Validate │ │ Validate │ │ Validate │
│ API key │ │ user/pass│ │ device token │
│ (SHA256) │ │ (PBKDF2) │ │ (enrollment) │
└──────┬───┘ └──────┬───┘ └──────┬───────┘
│ │ │
│ ┌──────┴──────┐ │
│ │ TOTP check │ │
│ │ if enabled │ │
│ └──────┬──────┘ │
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────┐
│ Session established │
│ - JWT token issued (24h) │
│ - Role assigned (admin/operator/viewer) │
│ - Device ID confirmed/assigned │
│ - WS connection upgraded to authed │
│ - Audit log: auth event │
└─────────────────────────────────────────┘
Auth Methods
| Method | Use Case | 2FA | Description |
|---|---|---|---|
user_password |
Interactive (operator login on desktop) | Yes (if enabled) | Same credentials as panel login |
api_key |
Unattended (IoT bridges, SCADA, headless) | No | API key from Go server api_keys table |
device_token |
First-time enrollment | No | One-time enrollment token (becomes API key) |
2FA for Native Desktop Client
The BetterDesk native desktop client needs a full 2FA UI:
┌──────────────────────────────────────┐
│ BetterDesk - Login │
│ │
│ Server: betterdesk.example.com │
│ │
│ Username: [operator1 ] │
│ Password: [•••••••• ] │
│ │
│ [Login] │
└──────────────────────────────────────┘
│
(server responds: requires_2fa)
│
▼
┌──────────────────────────────────────┐
│ BetterDesk - 2FA │
│ │
│ Enter authenticator code: │
│ │
│ [1] [2] [3] [4] [5] [6] │
│ │
│ ○ Use recovery code instead │
│ │
│ [Verify] [Cancel] │
└──────────────────────────────────────┘
│
(verified → full session)
│
▼
┌──────────────────────────────────────┐
│ ✓ Connected as operator1 │
│ Server: betterdesk.example.com │
│ Device ID: CDAP-D2E9F4 │
│ │
│ This device is ready for remote │
│ connections and management. │
│ │
│ [Settings] [Minimize to tray] │
└──────────────────────────────────────┘
2FA Setup for CDAP Clients
CDAP clients do NOT set up 2FA themselves — 2FA is configured through the Node.js panel:
Panel (Node.js) Go Server CDAP Client
│ │ │
│── POST /api/auth/totp/setup ──►│ │
│◄── {qrCode, secret} ──────────│ │
│ │ │
│ (user scans QR in Authy) │ │
│ │ │
│── POST /api/auth/totp/enable ─►│ │
│ {code: "123456"} │── update users table ──│
│◄── {recoveryCodes} ───────────│ │
│ │ │
│ │ (next login) │
│ │◄── auth {user/pass} ───│
│ │──► auth_result │
│ │ {requires_2fa} ─────►│
│ │ │
│ │◄── auth_2fa {code} ────│
│ │──► auth_result │
│ │ {success, jwt} ─────►│
Token Refresh for Long-Running Agents
Desktop agents and IoT bridges run 24/7. JWT tokens expire after 24h. CDAP supports token refresh:
// Client sends before token expires
{
"type": "token_refresh",
"payload": {
"token": "current_jwt_about_to_expire"
}
}
// Server responds with new token (no re-auth needed)
{
"type": "token_refreshed",
"payload": {
"token": "new_jwt_24h",
"expires_at": "2026-03-21T14:30:00Z"
}
}
Rules:
- Refresh only works if current token is still valid (not expired)
- Max refresh chain: 30 days (then full re-auth required)
- Admin can revoke refresh chain from panel (force re-login)
- API key auth does not need refresh (keys don't expire by default)
RustDesk Client Synchronization
Problem Statement
A BetterDesk deployment typically has a mix of:
- Existing RustDesk clients — already deployed on workstations, using RustDesk protocol on port 21116
- New BetterDesk native clients (CDAP) — being rolled out, using CDAP on port 21122
- IoT/SCADA bridges (CDAP) — new devices via CDAP
These must coexist in the same panel, same device list, same address books, same permissions.
Sync Points
1. Unified Peer Table
Both RustDesk and CDAP devices live in the same peers table:
-- Existing columns (unchanged)
id TEXT PRIMARY KEY, -- "1340238749" (RustDesk) or "CDAP-D2E9F4" (CDAP)
uuid TEXT,
pk BLOB,
hostname TEXT,
os TEXT,
...
-- Existing CDAP columns (from Phase 1)
device_type TEXT DEFAULT 'rustdesk', -- rustdesk, scada, iot, os_agent, desktop, ...
manifest_json TEXT,
bridge_id TEXT,
cdap_session_id TEXT,
-- NEW: Linking columns
linked_peer_id TEXT, -- Cross-reference for dual-client machines
link_type TEXT, -- "auto" (hostname match) or "manual" (admin linked)
auth_user TEXT, -- Username who logged in on this device
last_auth_at TIMESTAMPTZ -- Last CDAP/RustDesk login time
2. Address Book Convergence
RustDesk clients and CDAP clients share the same address book per user:
User "operator1" logs in from:
├── Node.js panel → sees all devices in panel
├── RustDesk client → GET /api/ab → sees address book
└── CDAP native client → GET /api/ab (same endpoint) → sees address book
Address book contains:
├── RustDesk device IDs (numeric)
└── CDAP device IDs (CDAP- prefix)
Both client types can:
├── Add devices to address book
├── Create tags/groups
└── Sync across devices
The Go server's /api/ab endpoint already works for RustDesk clients. CDAP clients use the same endpoint with the same JWT token, so address books are automatically synchronized.
3. Connection History
When a CDAP client connects to another device (remote desktop):
// Audit log entry (same format as RustDesk connections)
{
"action": "connection",
"details": {
"from_id": "CDAP-D2E9F4",
"from_type": "desktop",
"to_id": "892734561",
"to_type": "rustdesk",
"protocol": "cdap_media",
"initiated_by": "operator1",
"duration_sec": 1847,
"timestamp": "2026-03-19T14:30:00Z"
}
}
The Node.js panel's connection history view shows both RustDesk and CDAP connections in the same timeline.
4. Online Status Merging
The panel needs to query both connection pools:
Node.js Panel
│
├── GET /api/peers (Go server)
│ └── Go enriches with:
│ ├── RustDesk live status (signal server in-memory map)
│ └── CDAP live status (CDAP gateway in-memory map)
│
└── Shows unified device list with live status from both protocols
Implementation: handleListPeers in Go server already overlays live status from signal peer map. Extended to also check CDAP gateway's active connections map.
5. Linked Device View
When a machine runs both RustDesk and BetterDesk native clients:
Panel Device List:
┌──────────────────────────────────────────────────────────────┐
│ PC-Design-03 [Linked] │
│ ├── 🖥️ 892734561 | RustDesk | Online | Remote Desktop │
│ └── 📱 CDAP-D2E9F4 | Desktop | Online | Desktop + Mgmt │
│ │
│ srv-prod-01 │
│ └── 💻 CDAP-F9A2B1 | OS Agent | Online | System Management│
│ │
│ Boiler Room PLC │
│ └── 🏭 CDAP-A7F3B2 | SCADA | Online | Widgets │
└──────────────────────────────────────────────────────────────┘
Auto-linking algorithm:
- Same hostname + same user → auto-link
- Same IP (within 5 min window) + same hostname → suggest link
- Admin can manually link/unlink from panel
Implementation Phases
Phase 0: Preparation (1-2 days)
| # | Task | Effort | Files |
|---|---|---|---|
| 0.1 | Add device_type, manifest_json, linked_peer_id, auth_user columns to peers |
0.5d | db/sqlite.go, db/postgres.go |
| 0.2 | Add peer_links table for device linking |
0.5d | db/sqlite.go, db/postgres.go |
| 0.3 | Update handleListPeers to return device_type field |
0.5d | api/server.go |
| 0.4 | Add device type filter to panel device list | 0.5d | web-nodejs/views/, web-nodejs/routes/ |
Gate: Device list shows device_type column. All existing RustDesk devices show as rustdesk.
Phase 1: CDAP Gateway Core (5-7 days)
| # | Task | Effort | Files |
|---|---|---|---|
| 1.1 | Create cdap/ package with Gateway struct |
1d | cdap/gateway.go (NEW) |
| 1.2 | WebSocket server on port 21122 (gorilla/websocket) |
1d | cdap/gateway.go |
| 1.3 | Auth handler — user_password method (reuse auth.VerifyPassword) |
0.5d | cdap/auth.go (NEW) |
| 1.4 | Auth handler — api_key method (reuse auth.ValidateAPIKey) |
0.5d | cdap/auth.go |
| 1.5 | 2FA handler — TOTP flow within WebSocket (reuse auth.ValidateTOTP) |
0.5d | cdap/auth.go |
| 1.6 | Rate limiting on auth messages (reuse ratelimit.IPLimiter) |
0.5d | cdap/auth.go |
| 1.7 | Manifest parser + validation (JSON Schema) | 1d | cdap/manifest.go (NEW) |
| 1.8 | Device registration — write to peers table with device_type |
0.5d | cdap/handler.go (NEW) |
| 1.9 | Heartbeat handler — widget value storage + online status | 0.5d | cdap/handler.go |
| 1.10 | Connection lifecycle — reconnect ID persistence by serial | 0.5d | cdap/handler.go |
| 1.11 | Wire into main.go — start CDAP gateway alongside signal/relay |
0.5d | main.go |
Gate: Python bridge connects, authenticates (including 2FA), registers device, sends heartbeat. Device appears in peers table as device_type=scada.
Phase 2: Panel Widget Rendering (5-7 days)
| # | Task | Effort | Files |
|---|---|---|---|
| 2.1 | REST endpoint: GET /api/cdap/devices/{id}/manifest |
0.5d | api/server.go or cdap/api.go |
| 2.2 | REST endpoint: GET /api/cdap/devices/{id}/state (current widget values) |
0.5d | cdap/api.go |
| 2.3 | REST endpoint: POST /api/cdap/devices/{id}/command (send command) |
0.5d | cdap/api.go |
| 2.4 | WebSocket push: widget state changes → panel (via existing event bus) | 1d | cdap/handler.go, events/ |
| 2.5 | Panel: CDAP device detail page (EJS template) | 1d | web-nodejs/views/cdap-device.ejs (NEW) |
| 2.6 | Panel: Widget renderer — toggle, gauge, button, led |
2d | web-nodejs/public/js/cdap-widgets.js (NEW) |
| 2.7 | Panel: Command sending UI (confirm dialogs, cooldowns) | 1d | web-nodejs/public/js/cdap-commands.js (NEW) |
| 2.8 | Panel: Device list shows device type icons + filter | 0.5d | web-nodejs/views/devices.ejs, devices.js |
| 2.9 | i18n keys for CDAP widgets and device types (EN + PL) | 0.5d | web-nodejs/lang/en.json, pl.json |
Gate: Panel shows CDAP device detail with live-updating gauges, working toggle switches, functioning buttons with confirmations.
Phase 3: Security Hardening + Auth Convergence + Device Revocation (6-8 days)
| # | Task | Effort | Files |
|---|---|---|---|
| 3.1 | TLS auto-detection on CDAP port (reuse config.DualModeListener) |
0.5d | cdap/gateway.go |
| 3.2 | RBAC per-widget permissions enforcement | 1d | cdap/auth.go, cdap/handler.go |
| 3.3 | Command audit logging (every command → audit ring buffer) | 0.5d | cdap/handler.go, audit/ |
| 3.4 | Device token enrollment (one-time tokens for new devices) | 1d | cdap/auth.go, db/ |
| 3.5 | Token refresh for long-running agents (30-day chain) | 0.5d | cdap/auth.go |
| 3.6 | Auth delegation: Node.js panel uses Go /api/auth/ for user CRUD |
1d | web-nodejs/services/authService.js (NEW) |
| 3.7 | Panel: user management through Go API (password change syncs to Go) | 0.5d | web-nodejs/routes/auth.routes.js |
| 3.8 | Device Revocation: CDAP revoke + suspend message handlers in gateway |
1d | cdap/revocation.go (NEW), cdap/gateway.go |
| 3.9 | Connection close on delete: Close TCPConn/WSConn when peer removed from map |
0.5d | peer/map.go, api/server.go |
| 3.10 | Blocklist on revoke: Auto-add ID to security.Blocklist on ?revoke=true |
0.5d | api/server.go, security/blocklist.go |
| 3.11 | Cascade delete: Revoke linked_peer_id when cascade option selected |
0.5d | cdap/revocation.go, api/server.go |
| 3.12 | Panel revocation UI: Delete dialog with wipe/blocklist/cascade checkboxes | 1d | web-nodejs/views/, public/js/devices.js |
| 3.13 | EventPeerRevoked: New event type on event bus for revocation audit trail | 0.25d | events/bus.go |
Gate: Widget RBAC enforced (operator can read gauge but not trigger emergency stop without permission). 2FA works end-to-end for CDAP clients. Password changed in panel reflects in CDAP login. Deleting a CDAP device sends revoke message → client wipes config and disconnects. Cascade delete revokes linked devices.
Phase 4: Advanced Widgets + RustDesk Sync (5-7 days)
| # | Task | Effort | Files |
|---|---|---|---|
| 4.1 | Widget types: chart, select, slider, text |
2d | web-nodejs/public/js/cdap-widgets.js |
| 4.2 | Widget types: table (dynamic rows, sortable) |
1d | cdap-widgets.js |
| 4.3 | Widget type: terminal (WebSocket shell relay) |
2d | cdap/terminal.go (NEW), cdap-widgets.js |
| 4.4 | Device linking — auto-detect + manual link in panel | 1d | cdap/linking.go (NEW), panel views |
| 4.5 | Unified status overlay — CDAP status merged into handleListPeers |
0.5d | api/server.go |
| 4.6 | Address book sync — CDAP clients use same /api/ab endpoint |
0.5d | cdap/api.go (proxy or direct) |
| 4.7 | Connection history — CDAP events in same audit format | 0.5d | cdap/handler.go, web-nodejs/routes/ |
Gate: Full 10+ widget types working. Linked devices show as one machine in panel. Address book shared between RustDesk and CDAP clients.
Phase 5: Media Channel — Remote Desktop (10-15 days)
| # | Task | Effort | Files |
|---|---|---|---|
| 5.1 | Binary frame mux/demux in CDAP gateway | 2d | cdap/media.go (NEW) |
| 5.2 | Media session establishment (REST /api/cdap/devices/{id}/connect) |
1d | cdap/api.go |
| 5.3 | Binary frame relay (viewer ↔ device, E2E opaque) | 1d | cdap/media.go |
| 5.4 | E2E key exchange (X25519 → XSalsa20-Poly1305 via control messages) | 1d | cdap/crypto.go (NEW) |
| 5.5 | Video channel: codec negotiation + keyframe request handling | 1d | cdap/media.go |
| 5.6 | Panel: WebCodecs/Canvas desktop viewer widget | 3d | web-nodejs/public/js/cdap-desktop.js (NEW) |
| 5.7 | Panel: input forwarding (keyboard/mouse → binary frames) | 1d | cdap-desktop.js |
| 5.8 | Panel: clipboard sync widget | 1d | cdap-desktop.js |
| 5.9 | Panel: file transfer two-pane browser | 2d | web-nodejs/public/js/cdap-files.js (NEW) |
| 5.10 | Audio channel: Opus decode/play in browser | 1d | cdap-desktop.js |
| 5.11 | Cursor channel: custom cursor rendering | 0.5d | cdap-desktop.js |
| 5.12 | Adaptive quality (bitrate/fps adjustments) | 1d | cdap/media.go |
| 5.13 | Multi-monitor support (display index routing) | 0.5d | cdap/media.go, cdap-desktop.js |
Gate: Panel can open remote desktop session to a CDAP desktop device. Video, audio, input, clipboard, and file transfer all work through the binary media channel with E2E encryption.
Phase 6: Native BetterDesk Desktop Agent (10-15 days)
| # | Task | Effort | Files |
|---|---|---|---|
| 6.1 | Agent binary scaffold (Rust or Go) — systemd/service installer | 2d | betterdesk-agent/ (NEW repo or dir) |
| 6.2 | CDAP client library — auth + heartbeat + manifest | 2d | betterdesk-agent/cdap/ |
| 6.3 | Screen capture — DXGI (Windows), X11/PipeWire (Linux) | 2d | betterdesk-agent/capture/ |
| 6.4 | Video encoder — H.264/VP9 hardware-accelerated | 2d | betterdesk-agent/encoder/ |
| 6.5 | Audio capture — WASAPI (Windows), PulseAudio (Linux) | 1d | betterdesk-agent/audio/ |
| 6.6 | Input injection — keyboard/mouse (platform-specific) | 1d | betterdesk-agent/input/ |
| 6.7 | Clipboard monitor — text/image sync | 1d | betterdesk-agent/clipboard/ |
| 6.8 | File transfer — chunk read/write with resume | 1d | betterdesk-agent/files/ |
| 6.9 | System widgets — CPU/RAM/disk/services/processes | 1d | betterdesk-agent/sysinfo/ |
| 6.10 | Login UI — username/password + 2FA dialog | 1d | betterdesk-agent/ui/ |
| 6.11 | Tray icon + auto-start | 0.5d | betterdesk-agent/ui/ |
| 6.12 | Update from ALL-IN-ONE scripts (install/update support) | 1d | betterdesk.sh, betterdesk.ps1 |
Gate: BetterDesk agent installs on Windows/Linux, logs in with 2FA, appears as desktop type in panel, supports remote desktop + system management widgets.
Phase 7: Bridge Ecosystem + Polish (5-7 days)
| # | Task | Effort | Files |
|---|---|---|---|
| 7.1 | Python bridge SDK (pip-installable) | 1d | sdks/python/ (NEW) |
| 7.2 | Reference bridge: Modbus TCP | 1d | bridges/modbus/ (NEW) |
| 7.3 | Reference bridge: SNMP | 1d | bridges/snmp/ (NEW) |
| 7.4 | Reference bridge: REST/HTTP (generic webhook → CDAP) | 1d | bridges/rest/ (NEW) |
| 7.5 | Dashboard: CDAP device type counters + overview cards | 1d | Panel views |
| 7.6 | Alert system: threshold-based from manifest definitions | 1d | cdap/alerts.go (NEW) |
| 7.7 | Documentation site / updated README | 1d | docs/ |
Gate: Three working reference bridges. Alert system notifies on threshold breach. Dashboard shows CDAP devices alongside RustDesk.
File-Level Change Map
Go Server (betterdesk-server/)
| File | Action | Phase | Description |
|---|---|---|---|
main.go |
Modify | 1 | Start CDAP gateway, pass config |
config/config.go |
Modify | 1 | Add CDAPPort, CDAPTLSEnabled() |
cdap/gateway.go |
NEW | 1 | WebSocket server, connection lifecycle |
cdap/auth.go |
NEW | 1 | Auth handler (user/pass, API key, 2FA, device token) |
cdap/handler.go |
NEW | 1 | Register, heartbeat, state_update, command routing |
cdap/manifest.go |
NEW | 1 | Manifest parsing + JSON validation |
cdap/api.go |
NEW | 2 | REST endpoints for panel (manifest, state, command) |
cdap/terminal.go |
NEW | 4 | Terminal widget WebSocket relay |
cdap/linking.go |
NEW | 4 | Device auto-linking logic |
cdap/media.go |
NEW | 5 | Binary frame mux/demux, media session, relay |
cdap/crypto.go |
NEW | 5 | X25519 key exchange for E2E media |
cdap/revocation.go |
NEW | 3 | Revoke/suspend message sending, cascade logic, conn close |
cdap/alerts.go |
NEW | 7 | Threshold-based alert evaluation |
db/database.go |
Modify | 0-1 | Add CDAP-related methods to interface |
db/sqlite.go |
Modify | 0-1 | Implement CDAP methods, add columns/tables |
db/postgres.go |
Modify | 0-1 | Same for PostgreSQL |
peer/map.go |
Modify | 3 | Close TCP/WS connections on Remove() |
api/server.go |
Modify | 2-4 | CDAP REST routes, status overlay, ?revoke=true in delete |
events/bus.go |
Modify | 3 | Add EventPeerRevoked event type |
security/blocklist.go |
Modify | 3 | Auto-add ID on revocation |
auth/ (existing) |
No change | — | Reused by CDAP auth (no modification needed) |
Node.js Console (web-nodejs/)
| File | Action | Phase | Description |
|---|---|---|---|
views/cdap-device.ejs |
NEW | 2 | CDAP device detail page with widget panel |
views/devices.ejs |
Modify | 0 | Add device type column, filter, icons |
public/js/cdap-widgets.js |
NEW | 2 | Widget renderer (all 13 types) |
public/js/cdap-commands.js |
NEW | 2 | Command sending, confirmation, cooldown |
public/js/cdap-desktop.js |
NEW | 5 | Remote desktop viewer (WebCodecs + Canvas) |
public/js/cdap-files.js |
NEW | 5 | Two-pane file browser |
public/css/cdap.css |
NEW | 2 | Widget styles, device type icons |
routes/cdap.routes.js |
NEW | 2 | CDAP panel routes (proxy to Go server) |
services/authService.js |
NEW | 3 | Auth delegation to Go server |
lang/en.json |
Modify | 2+ | CDAP i18n keys |
lang/pl.json |
Modify | 2+ | CDAP i18n keys |
services/betterdeskApi.js |
Modify | 2 | Add CDAP API methods |
ALL-IN-ONE Scripts
| File | Action | Phase | Description |
|---|---|---|---|
betterdesk.sh |
Modify | 6 | CDAP port in firewall, agent install option |
betterdesk.ps1 |
Modify | 6 | Same for Windows |
betterdesk-docker.sh |
Modify | 1 | Expose port 21122 in docker-compose |
docker-compose.yml |
Modify | 1 | Add port 21122 mapping |
Dockerfile |
Modify | 1 | Expose 21122 |
Testing Strategy
Unit Tests (Go)
| Test | Phase | Validates |
|---|---|---|
cdap/auth_test.go |
1 | Auth message parsing, password verify, API key, 2FA flow |
cdap/manifest_test.go |
1 | Manifest validation (valid, invalid, edge cases) |
cdap/handler_test.go |
1 | Register, heartbeat, state_update, command routing |
cdap/media_test.go |
5 | Binary frame mux/demux, session pairing |
cdap/linking_test.go |
4 | Auto-link algorithm, unlink |
cdap/revocation_test.go |
3 | Revoke message sent, conn closed, config wipe, cascade |
Integration Tests
| Test | Phase | Setup | Validates |
|---|---|---|---|
| Bridge auth flow | 1 | Go server + Python bridge | WS connect → auth → register → heartbeat |
| 2FA end-to-end | 1 | Go server + test TOTP | Auth → 2FA required → code verify → session |
| Panel widget rendering | 2 | Full stack | Bridge connects → panel shows widgets → values update |
| Command round-trip | 2 | Full stack | Panel sends command → bridge receives → executes → response |
| Linked devices | 4 | Go server + RustDesk + CDAP | Both clients online → panel shows linked view |
| Device revocation | 3 | Go server + CDAP bridge | Delete → revoke msg → bridge disconnects + wipes config |
| Cascade revocation | 3 | Go server + RustDesk + CDAP | Delete CDAP → linked RustDesk also revoked + blocked |
| Media relay | 5 | Go server + 2 CDAP clients | Video frames relayed with E2E encryption |
Load Tests
| Test | Phase | Target |
|---|---|---|
| 100 concurrent CDAP devices | 2 | Gateway handles 100 WS connections, 1000 msg/s |
| 10 concurrent media sessions | 5 | Binary relay at 10 Mbps aggregate without frame loss |
| 1000 widget updates/sec | 2 | Panel receives updates via WS push without lag |
Migration & Backward Compatibility
Zero Breaking Changes
- All existing RustDesk clients continue working unchanged
- All existing Node.js panel features unchanged
- All existing API endpoints unchanged
- CDAP is strictly additive — new port, new protocol, new device types
Migration Path for Organizations
Step 1: Update BetterDesk server (Phase 1-4)
└── CDAP gateway starts on port 21122
└── All existing devices still work
Step 2: Deploy IoT/SCADA bridges (Phase 2+)
└── New device types appear in panel
└── RustDesk devices unaffected
Step 3: Deploy BetterDesk native agent on select machines (Phase 6)
└── Machine shows two entries (RustDesk + CDAP)
└── Admin can link them in panel
Step 4: Gradually replace RustDesk client with native agent
└── Uninstall RustDesk client
└── CDAP agent provides remote desktop + management
└── One device entry per machine
Step 5 (optional): Disable RustDesk protocol
└── Only for environments fully migrated to CDAP
└── Signal/relay servers still available for backward compat
Rollback
Every phase is independent. If Phase 5 (media) has issues, Phases 1-4 (widgets, auth, sync) continue working. CDAP gateway can be disabled entirely by removing the --cdap-port flag — zero impact on RustDesk protocol.
Risk Assessment
| Risk | Impact | Likelihood | Mitigation |
|---|---|---|---|
| WebSocket scalability at 1000+ devices | High | Medium | Connection pooling, per-device message throttle |
| Auth DB divergence (Node.js vs Go) | High | High | Phase 3 convergence, sync checks |
| Binary frame relay performance for desktop | High | Low | E2E opaque (zero decode), tested architecture |
| Native agent cross-platform complexity | High | High | Start with one platform (Linux or Windows), add second |
| Bridge SDK maintenance burden | Medium | Medium | Minimal SDKs (~200 LOC), auto-generate from spec |
| RustDesk protocol changes break existing | Medium | Low | BetterDesk server frozen at RustDesk 1.3.x compat |
| 2FA lockout (lost phone, no recovery codes) | Medium | Medium | Admin can disable 2FA from panel, recovery codes backup reminder |
| CDAP spec changes during development | Medium | Medium | Versioned manifests, backward compat |
| Revocation message not delivered (device offline) | Medium | High | Blocklist + soft-delete ensure re-registration fails regardless; revoke is best-effort optimization |
| Cascade delete accidental scope | High | Low | Cascade is opt-in checkbox, admin must explicitly confirm; audited |
| RustDesk client cannot be config-wiped | Medium | N/A | Protocol limitation; blocklist prevents re-registration; BetterDesk native client supports full wipe |
Timeline Summary
| Phase | Duration | Cumulative | Key Deliverable |
|---|---|---|---|
| 0: Preparation | 1-2 days | 1-2 days | DB schema, device type column |
| 1: Gateway Core | 5-7 days | 6-9 days | CDAP auth + registration working |
| 2: Panel Widgets | 5-7 days | 11-16 days | Widgets visible and interactive in panel |
| 3: Security + Auth + Revocation | 6-8 days | 17-25 days | Full RBAC + 2FA + auth convergence + device revocation |
| 4: Advanced + Sync | 5-7 days | 22-32 days | Full widget set + RustDesk sync |
| 5: Media Channel | 10-15 days | 32-47 days | Remote desktop via CDAP |
| 6: Native Agent | 10-15 days | 42-62 days | BetterDesk desktop agent binary |
| 7: Ecosystem | 5-7 days | 47-69 days | Bridge SDKs + reference bridges |
MVP (Phases 0-2): ~16 days → CDAP devices with widgets in panel
Production (Phases 0-4): ~32 days → Secure, synced, full widget set + revocation
Full Stack (Phases 0-7): ~69 days → Native client + bridge ecosystem