CDAP - UNITRONIX/BetterDesk GitHub Wiki
CDAP is BetterDesk's protocol for managing IoT devices, servers, and custom hardware through the web console.
CDAP enables:
- Telemetry — Real-time metrics (CPU, memory, disk, custom sensors)
- Widget rendering — 8 widget types displayed in the web console
- Remote commands — Execute actions on devices
- Terminal access — Full PTY terminal emulation
- File management — Browse, read, write, delete files
- Clipboard sync — Bidirectional clipboard
- Screenshots — On-demand screen capture
- Audio streaming — Bidirectional audio via WebSocket
Device / Bridge Go Server Web Console
| | |
|--- WebSocket connect -------->|(:21122/cdap) |
|<-- auth challenge ------------| |
|--- auth response ------------>| |
|<-- auth ok -------------------| |
|--- manifest ----------------->| |
| | |
|--- widget_values ------------>| |
| |--- HTTP /api/cdap ------->|
| |<-- command ---------------|
|<-- command -------------------| |
|--- command_response --------->| |
# CLI flag
betterdesk-server -cdap
# Environment variable
CDAP_ENABLED=YCreate a CDAP API key:
curl -X POST http://your-server:21114/api/keys \
-H "X-API-Key: your-admin-api-key" \
-H "Content-Type: application/json" \
-d '{"name": "cdap-agent", "role": "operator"}'// Server → Device
{"type": "auth_challenge", "nonce": "random-string"}
// Device → Server
{"type": "auth_response", "api_key": "your-cdap-key", "device_id": "DEVICE-001"}
// Server → Device
{"type": "auth_ok", "session_id": "uuid"}Devices send their manifest after authentication:
{
"type": "manifest",
"device": {
"id": "CDAP-6A9A5452",
"name": "Production Server",
"type": "os_agent",
"version": "1.0.0",
"capabilities": ["telemetry", "commands", "remote_desktop", "file_transfer", "clipboard"]
},
"widgets": [
{
"id": "sys_cpu",
"type": "gauge",
"label": "CPU Usage",
"group": "System",
"unit": "%",
"min": 0,
"max": 100,
"danger": 90,
"warning": 70
}
],
"heartbeat_interval": 15
}| Type | Description | Config Fields |
|---|---|---|
gauge |
Percentage bar with thresholds | min, max, unit, danger, warning |
text |
Text display | (none) |
toggle |
On/off switch | (none) |
button |
Action trigger | confirm (boolean) |
led |
Status indicator | (none) |
slider |
Range input | min, max, step, unit |
select |
Dropdown choice | options (array) |
chart |
Bar chart (last N values) | max_points |
{
"type": "widget_values",
"values": {
"sys_cpu": 45.2,
"sys_memory": 72.8,
"sys_disk": 38.5,
"sys_hostname": "prod-server-01",
"sys_uptime": "5d 12h 30m"
}
}// Server → Device
{
"type": "command",
"id": 42,
"widget_id": "reboot_btn",
"action": "press",
"params": {}
}
// Device → Server
{
"type": "command_response",
"id": 42,
"success": true,
"message": "Reboot initiated"
}// Server → Device
{"type": "terminal_start", "cols": 80, "rows": 24}
// Device → Server
{"type": "terminal_output", "data": "user@host:~$ "}
// Server → Device
{"type": "terminal_input", "data": "ls -la\n"}
// Server → Device
{"type": "terminal_resize", "cols": 120, "rows": 40}
// Server → Device
{"type": "terminal_kill"}
// Device → Server
{"type": "terminal_end", "code": 0}// Server → Device
{"type": "file_list", "path": "/var/log"}
// Device → Server
{
"type": "file_list_response",
"path": "/var/log",
"entries": [
{"name": "syslog", "size": 1048576, "is_dir": false, "modified": "2026-03-27T12:00:00Z"},
{"name": "nginx", "size": 0, "is_dir": true, "modified": "2026-03-27T11:00:00Z"}
]
}
// Server → Device
{"type": "file_read", "path": "/var/log/syslog", "offset": 0, "length": 65536}
// Device → Server
{"type": "file_read_response", "path": "/var/log/syslog", "data": "<base64>", "total_size": 1048576}
// Server → Device
{"type": "file_write", "path": "/tmp/config.json", "data": "<base64>"}
// Device → Server
{"type": "file_write_response", "success": true}
// Server → Device
{"type": "file_delete", "path": "/tmp/old-file.txt"}
// Device → Server
{"type": "file_delete_response", "success": true}pip install betterdesk-cdapfrom betterdesk_cdap import CDAPBridge, Widget
bridge = CDAPBridge(
server="ws://your-server:21122/cdap",
api_key="your-key",
device_id="SENSOR-001",
device_name="Temperature Sensor",
device_type="sensor"
)
# Define widgets
bridge.add_widget(Widget.gauge("temperature", "Temperature", unit="°C", min=-20, max=50, danger=40, warning=35))
bridge.add_widget(Widget.toggle("heater", "Heater", group="Controls"))
# Handle commands
@bridge.on_command("heater")
async def toggle_heater(action, params):
# Toggle heater hardware
return {"success": True, "message": "Heater toggled"}
# Update values
bridge.set_value("temperature", 22.5)
# Run
await bridge.connect()npm install betterdesk-cdapconst { CDAPBridge, Widget } = require('betterdesk-cdap');
const bridge = new CDAPBridge({
server: 'ws://your-server:21122/cdap',
apiKey: 'your-key',
deviceId: 'SENSOR-001',
deviceName: 'Temperature Sensor',
deviceType: 'sensor'
});
bridge.addWidget(Widget.gauge('temperature', 'Temperature', { unit: '°C', min: -20, max: 50 }));
bridge.on('command', (cmd) => {
if (cmd.widget_id === 'heater') {
// Toggle heater
bridge.respond(cmd.id, { success: true });
}
});
bridge.setValue('temperature', 22.5);
bridge.connect();Pre-built bridges for common protocols:
| Bridge | Protocol | Description |
|---|---|---|
bridges/modbus/ |
Modbus TCP/RTU | Register polling, data type encode/decode, write-back |
bridges/snmp/ |
SNMP v2c/v3 | OID polling, timetick formatting, counter rates |
bridges/rest-webhook/ |
REST + Webhook | HTTP polling + aiohttp webhook listener |
cd bridges/modbus
pip install -r requirements.txt
python bridge.py \
--server ws://your-server:21122/cdap \
--api-key your-key \
--modbus-host 192.168.1.100 \
--modbus-port 502CDAP devices appear in the device list with a CDAP badge. Click a CDAP device to see:
- Device info — Type, version, uptime, capabilities
- Widget grid — Live widget rendering with interactive controls
- Command log — History of commands sent to the device
The web console renders widgets based on type:
- Gauge — Colored progress bar with danger/warning thresholds
- Toggle — iOS-style switch with on/off state
- Button — Click to send command (optional confirmation dialog)
- LED — Green/red/yellow indicator dot
- Text — Display any text/number value
- Slider — Range input with min/max labels
- Select — Dropdown menu with options
- Chart — Horizontal bar chart for last N values
| Method | Path | Description |
|---|---|---|
GET |
/cdap/devices/{id} |
Device detail page |
GET |
/api/cdap/status |
Gateway status |
GET |
/api/cdap/devices |
Connected devices list |
GET |
/api/cdap/devices/{id}/info |
Device info JSON |
GET |
/api/cdap/devices/{id}/manifest |
Device manifest |
GET |
/api/cdap/devices/{id}/state |
Widget state values |
POST |
/api/cdap/devices/{id}/command |
Send command |
- CDAP gateway uses API key authentication
- WebSocket connections require valid auth response
- Terminal access requires
operatorrole minimum - File operations enforce path traversal protection (
safePath()) - Command sending requires
operatorrole - File deletion is audited