WebSocket API
PMDaemon's WebSocket API provides real-time communication for monitoring process status, receiving live updates, and managing processes interactively. Built for high performance and low latency, it's perfect for dashboards, monitoring tools, and real-time applications.
Overview
The WebSocket API offers:
- ⚡ Real-time updates - Instant process status changes
- 📊 Live metrics - CPU, memory, and system metrics streaming
- 🔄 Bidirectional communication - Send commands and receive responses
- 📱 Multiple clients - Support for concurrent connections
- 🎯 Event filtering - Subscribe to specific event types
Connection
Basic Connection
const ws = new WebSocket('ws://localhost:9615/ws');
ws.onopen = function() {
console.log('Connected to PMDaemon WebSocket');
};
ws.onmessage = function(event) {
const data = JSON.parse(event.data);
console.log('Received:', data);
};
ws.onclose = function() {
console.log('WebSocket connection closed');
};
ws.onerror = function(error) {
console.error('WebSocket error:', error);
};
Connection with Authentication (Future)
const ws = new WebSocket('ws://localhost:9615/ws', {
headers: {
'Authorization': 'Bearer your-jwt-token' // Node only
}
});
Message Format
All WebSocket messages use JSON format:
{
"type": "message_type",
"data": {
// Message-specific data
},
"timestamp": "2024-01-15T14:30:25.123Z",
"id": "unique-message-id"
}
Incoming Message Types
Process Status Updates
Sent when process status changes:
{
"type": "process_status",
"data": {
"id": 0,
"name": "web-api",
"status": "online",
"pid": 1234,
"port": 3000,
"previous_status": "starting"
},
"timestamp": "2024-01-15T14:30:25.123Z"
}
Status values:
starting
- Process is starting uponline
- Process running normallystopping
- Process shutting downstopped
- Process not runningerrored
- Process crashed or failedrestarting
- Process restarting
Process Metrics Updates
Real-time metrics for all processes:
{
"type": "process_metrics",
"data": {
"processes": [
{
"id": 0,
"name": "web-api",
"cpu": 2.5,
"memory": 47185920,
"uptime": 7890000,
"restarts": 0
},
{
"id": 1,
"name": "worker",
"cpu": 1.2,
"memory": 32145920,
"uptime": 5430000,
"restarts": 1
}
]
},
"timestamp": "2024-01-15T14:30:25.123Z"
}
System Metrics Updates
System-wide performance metrics:
{
"type": "system_metrics",
"data": {
"cpu": {
"usage": 15.2,
"cores": 8
},
"memory": {
"total": 8589934592,
"used": 2147483648,
"available": 6442450944,
"usage_percent": 25.0
},
"load": {
"one": 0.85,
"five": 1.2,
"fifteen": 0.9
}
},
"timestamp": "2024-01-15T14:30:25.123Z"
}
Health Check Updates
Health status changes:
{
"type": "health_update",
"data": {
"process": "web-api",
"status": "healthy",
"previous_status": "unhealthy",
"check_type": "http",
"details": {
"url": "http://localhost:3000/health",
"response_time": 45,
"status_code": 200
}
},
"timestamp": "2024-01-15T14:30:25.123Z"
}
Process Events
Lifecycle events for processes:
{
"type": "process_event",
"data": {
"event": "started",
"process": "new-api",
"details": {
"pid": 5678,
"port": 3001,
"startup_time": 1250
}
},
"timestamp": "2024-01-15T14:30:25.123Z"
}
Event types:
started
- Process successfully startedstopped
- Process stoppedcrashed
- Process crashed unexpectedlyrestarted
- Process restartedhealth_check_failed
- Health check failedmemory_limit_exceeded
- Memory limit reached
Log Messages
Real-time log streaming:
{
"type": "log_message",
"data": {
"process": "web-api",
"instance": 0,
"level": "info",
"message": "Server started on port 3000",
"stream": "stdout"
},
"timestamp": "2024-01-15T14:30:25.123Z"
}
Outgoing Message Types
Subscribe to Events
Subscribe to specific event types:
{
"type": "subscribe",
"data": {
"events": ["process_status", "process_metrics"],
"processes": ["web-api", "worker"],
"interval": 1000
}
}
Subscription options:
events
- Array of event types to receiveprocesses
- Array of process names (optional, defaults to all)interval
- Update interval in milliseconds (for metrics)
Unsubscribe from Events
{
"type": "unsubscribe",
"data": {
"events": ["system_metrics"]
}
}
Process Commands
Send process management commands:
{
"type": "command",
"data": {
"action": "start",
"process": "new-service",
"config": {
"script": "node",
"args": ["server.js"],
"port": "3002"
}
}
}
Available actions:
start
- Start a new processstop
- Stop a processrestart
- Restart a processdelete
- Delete a processreload
- Reload process configuration
Request Process Information
{
"type": "get_info",
"data": {
"process": "web-api"
}
}
Request System Information
{
"type": "get_system",
"data": {}
}
Client Examples
JavaScript/Browser
class PMDaemonWebSocket {
constructor(url = 'ws://localhost:9615/ws') {
this.url = url;
this.ws = null;
this.listeners = new Map();
}
connect() {
this.ws = new WebSocket(this.url);
this.ws.onopen = () => {
console.log('Connected to PMDaemon');
this.emit('connected');
};
this.ws.onmessage = (event) => {
const message = JSON.parse(event.data);
this.emit(message.type, message.data);
};
this.ws.onclose = () => {
console.log('Disconnected from PMDaemon');
this.emit('disconnected');
};
this.ws.onerror = (error) => {
console.error('WebSocket error:', error);
this.emit('error', error);
};
}
subscribe(events, processes = null, interval = 1000) {
this.send('subscribe', {
events,
processes,
interval
});
}
startProcess(name, config) {
this.send('command', {
action: 'start',
process: name,
config
});
}
// Note: WebSocket API is read-only for security
// Use REST API for process management commands
on(event, callback) {
if (!this.listeners.has(event)) {
this.listeners.set(event, []);
}
this.listeners.get(event).push(callback);
}
emit(event, data) {
if (this.listeners.has(event)) {
this.listeners.get(event).forEach(callback => callback(data));
}
}
}
// Usage
const pmdaemon = new PMDaemonWebSocket();
pmdaemon.on('connected', () => {
// Subscribe to process status and metrics
pmdaemon.subscribe(['process_status', 'process_metrics']);
});
pmdaemon.on('process_status', (data) => {
console.log(`Process ${data.name} is now ${data.status}`);
});
pmdaemon.on('process_metrics', (data) => {
data.processes.forEach(process => {
console.log(`${process.name}: CPU ${process.cpu}%, Memory ${process.memory}`);
});
});
pmdaemon.connect();
Node.js
const WebSocket = require('ws');
class PMDaemonClient {
constructor(url = 'ws://localhost:9615/ws') {
this.url = url;
this.ws = null;
}
async connect() {
return new Promise((resolve, reject) => {
this.ws = new WebSocket(this.url);
this.ws.on('open', () => {
console.log('Connected to PMDaemon');
resolve();
});
this.ws.on('message', (data) => {
const message = JSON.parse(data.toString());
this.handleMessage(message);
});
this.ws.on('close', () => {
console.log('Disconnected from PMDaemon');
});
this.ws.on('error', (error) => {
console.error('WebSocket error:', error);
reject(error);
});
});
}
handleMessage(message) {
switch (message.type) {
case 'process_status':
console.log(`Process ${message.data.name} status: ${message.data.status}`);
break;
case 'process_metrics':
this.updateMetrics(message.data.processes);
break;
case 'system_metrics':
this.updateSystemMetrics(message.data);
break;
}
}
subscribe(events, processes = null) {
this.send('subscribe', { events, processes });
}
send(type, data) {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify({ type, data }));
}
}
}
// Usage
async function main() {
const client = new PMDaemonClient();
await client.connect();
// Subscribe to all events
client.subscribe(['process_status', 'process_metrics', 'system_metrics']);
}
main().catch(console.error);
Python
import asyncio
import websockets
import json
class PMDaemonClient:
def __init__(self, url="ws://localhost:9615/ws"):
self.url = url
self.websocket = None
async def connect(self):
self.websocket = await websockets.connect(self.url)
print("Connected to PMDaemon")
async def listen(self):
async for message in self.websocket:
data = json.loads(message)
await self.handle_message(data)
async def handle_message(self, message):
msg_type = message.get('type')
data = message.get('data')
if msg_type == 'process_status':
print(f"Process {data['name']} status: {data['status']}")
elif msg_type == 'process_metrics':
for process in data['processes']:
print(f"{process['name']}: CPU {process['cpu']}%, Memory {process['memory']}")
elif msg_type == 'system_metrics':
cpu = data['cpu']['usage']
memory = data['memory']['usage_percent']
print(f"System: CPU {cpu}%, Memory {memory}%")
async def subscribe(self, events, processes=None):
message = {
'type': 'subscribe',
'data': {
'events': events,
'processes': processes
}
}
await self.websocket.send(json.dumps(message))
async def start_process(self, name, config):
message = {
'type': 'command',
'data': {
'action': 'start',
'process': name,
'config': config
}
}
await self.websocket.send(json.dumps(message))
# Usage
async def main():
client = PMDaemonClient()
await client.connect()
# Subscribe to events
await client.subscribe(['process_status', 'process_metrics'])
# Listen for messages
await client.listen()
if __name__ == "__main__":
asyncio.run(main())
Real-time Dashboard Example
<!DOCTYPE html>
<html>
<head>
<title>PMDaemon Dashboard</title>
<style>
.process { margin: 10px; padding: 10px; border: 1px solid #ccc; }
.online { background-color: #d4edda; }
.stopped { background-color: #f8d7da; }
.starting { background-color: #fff3cd; }
</style>
</head>
<body>
<h1>PMDaemon Real-time Dashboard</h1>
<div id="processes"></div>
<div id="system-metrics"></div>
<script>
const ws = new WebSocket('ws://localhost:9615/ws');
const processesDiv = document.getElementById('processes');
const systemDiv = document.getElementById('system-metrics');
ws.onopen = function() {
// Subscribe to all updates
ws.send(JSON.stringify({
type: 'subscribe',
data: {
events: ['process_status', 'process_metrics', 'system_metrics'],
interval: 1000
}
}));
};
ws.onmessage = function(event) {
const message = JSON.parse(event.data);
switch (message.type) {
case 'process_metrics':
updateProcesses(message.data.processes);
break;
case 'system_metrics':
updateSystemMetrics(message.data);
break;
}
};
function updateProcesses(processes) {
processesDiv.innerHTML = processes.map(process => `
<div class="process ${process.status}">
<h3>${process.name}</h3>
<p>Status: ${process.status}</p>
<p>CPU: ${process.cpu}%</p>
<p>Memory: ${(process.memory / 1024 / 1024).toFixed(1)}MB</p>
<p>Uptime: ${Math.floor(process.uptime / 1000)}s</p>
</div>
`).join('');
}
function updateSystemMetrics(metrics) {
systemDiv.innerHTML = `
`;
}
</script>
</body>
</html>
Best Practices
1. Handle Connection Failures
class RobustPMDaemonClient {
constructor(url) {
this.url = url;
this.reconnectInterval = 5000;
this.maxReconnectAttempts = 10;
this.reconnectAttempts = 0;
}
connect() {
this.ws = new WebSocket(this.url);
this.ws.onopen = () => {
this.reconnectAttempts = 0;
this.onConnected();
};
this.ws.onclose = () => {
this.reconnect();
};
}
reconnect() {
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectAttempts++;
setTimeout(() => this.connect(), this.reconnectInterval);
}
}
}
2. Efficient Event Filtering
// Subscribe only to needed events
ws.send(JSON.stringify({
type: 'subscribe',
data: {
events: ['process_status'], // Only status changes
processes: ['critical-service'], // Only critical processes
interval: 5000 // Less frequent updates
}
}));
3. Message Queuing
class QueuedWebSocket {
constructor(url) {
this.url = url;
this.messageQueue = [];
this.connected = false;
}
send(message) {
if (this.connected) {
this.ws.send(JSON.stringify(message));
} else {
this.messageQueue.push(message);
}
}
onConnected() {
this.connected = true;
// Send queued messages
while (this.messageQueue.length > 0) {
const message = this.messageQueue.shift();
this.ws.send(JSON.stringify(message));
}
}
}
Next Steps
- REST API - HTTP API reference
- API Examples - More client examples
- Library Usage - Using PMDaemon as a library
- Integration Examples - Framework integration