This TypeScript device agent is designed for efficient outbound mTLS WebSocket communication with a central server, providing control over multiple USB-to-serial devices on Debian. It supports hot-plug detection, multiple serial sessions, nonce-based replay protection, and CLI access for operators with pubkey-based authentication.
The agent is optimized for low memory usage and high throughput on typical serial rates (e.g., 11500 baud). It can be used for managing serial-connected devices.
- Outbound mTLS WebSocket: Secure connection to a central server, keeps alive and automatically reconnects with exponential backoff.
- Auto Serial Discovery: Detects
/dev/ttyUSB*,/dev/ttyACM*,/dev/ttyAMA*, etc., dynamically, and reports changes to central. - Serial Session Management: Open multiple serial sessions with unique session IDs; streams raw binary to server.
- Hotplug Handling: Automatically detects USB serial devices being plugged/unplugged, prevents crashes.
- Replay Protection: Nonce DB with persistence to disk prevents replay attacks.
- CLI Access: Unix domain socket CLI with pubkey-based operator authentication, supports commands
list,open,close, andstatus. - Minimal Memory Footprint: Streams data, no unnecessary buffering.
- Graceful Shutdown: Closes all sessions on WebSocket drop or process termination.
ws- WebSocket client library@serialport/streamand@serialport/bindings-cpp- USB-to-serial supportfs-extra- File persistence for nonce DBuuid- Session ID generation- Node.js >= 18
agent/
├── src/
│ ├── agent.ts # Main agent logic
│ ├── serialManager.ts # Serial session handling and hotplug
│ ├── wsClient.ts # WebSocket connection and messaging
│ ├── cliServer.ts # Unix socket CLI
│ ├── utils.ts # Logging, nonce DB, helpers
│ └── types.ts # Type definitions
├── package.json
├── tsconfig.json
├── certs/ # Device and CA certificates
└── data/ # Persistent storage (nonce DB)
-
Interfaces & Types
interface SerialSession { sessionId: string; portPath: string; baudRate: number; port: SerialPortStream; writer: WritableStreamDefaultWriter<Buffer>; } interface CommandMessage { cmd: string; session_id?: string; port?: string; baud?: number; nonce: string; ts: string; operator_id?: string; signature?: string; }
-
Async Iteration for Serial Reads
async function readSerialData(session: SerialSession) { const reader = session.port.readable.getReader(); try { while (true) { const { value, done } = await reader.read(); if (done) break; if (value) { // send to server } } } finally { reader.releaseLock(); } }
-
WebSocket with mTLS
const ws = new WebSocket(url, { cert: fs.readFileSync(certPath), key: fs.readFileSync(keyPath), ca: fs.readFileSync(caPath), perMessageDeflate: false });
-
Hotplug Detection
setInterval(() => { const current = discoverDevices(); const added = current.filter(d => !knownDevices.has(d)); const removed = Array.from(knownDevices).filter(d => !current.includes(d)); // update knownDevices and notify central }, 2000);
-
CLI Communication
const client = net.createConnection(SOCKET_PATH); client.write(JSON.stringify(signedCommand) + '\n');
- mTLS for WebSocket communication.
- Pubkey-based CLI authentication: operators sign requests, agent verifies signatures.
- Nonce-based replay protection.
- Permissions: agent runs as
serialsvcuser,/dev/ttyUSB*assigned via udev rules.
- Use
tscto compile TypeScript:
tsc- Ensure
tsconfig.jsonhas strict types and ES2022 modules. - Deploy compiled JS with Node >= 20.