Real-Time Events
NectoProxy provides real-time event streaming via Socket.IO, enabling live monitoring of traffic, breakpoints, rule changes, WebSocket frames, and proxy status. The Socket.IO server runs on the same host and port as the REST API.
Connection
Connect to the Socket.IO server at the same URL as the Web UI:
import { io } from 'socket.io-client';
const socket = io('http://localhost:8889', {
transports: ['websocket', 'polling'],
});
socket.on('connect', () => {
console.log('Connected:', socket.id);
});
socket.on('disconnect', () => {
console.log('Disconnected');
});CORS
The Socket.IO server accepts connections from any origin (*), so you can connect from any client application without CORS issues.
Server to Client Events
These events are emitted by the server and can be subscribed to by any connected client.
Traffic Events
traffic:batch
Emitted when new traffic entries are captured. Entries are batched for efficiency (flushed every 100ms or when the buffer reaches 50 entries).
socket.on('traffic:batch', (entries: TrafficEntry[]) => {
for (const entry of entries) {
console.log(`${entry.method} ${entry.url} -> ${entry.status}`);
}
});Room-Based Subscription
Traffic events are sent to Socket.IO rooms. Subscribe to a specific session's traffic or all traffic using client-to-server events (see below).
traffic:update
Emitted when an existing traffic entry is updated (e.g., when the response is received for a pending request).
socket.on('traffic:update', (update: Partial<TrafficEntry> & { id: string }) => {
console.log(`Entry ${update.id} updated:`, update.status);
});traffic:clear
Emitted when all traffic in a session is cleared.
socket.on('traffic:clear', (sessionId: string) => {
console.log(`Traffic cleared for session: ${sessionId}`);
});Breakpoint Events
breakpoint:hit
Emitted when a breakpoint is triggered by matching traffic. Contains the full request (and response, for response breakpoints) data for inspection.
interface BreakpointHit {
id: string; // Hit ID (used to resume)
breakpointId: string; // Which breakpoint was triggered
trafficId: string; // Associated traffic entry ID
type: 'request' | 'response';
timestamp: number;
// Request data (always present)
method: string;
url: string;
requestHeaders: Record<string, string | string[]>;
requestBody: Buffer | null;
// Response data (only for response breakpoints)
status?: number;
statusText?: string;
responseHeaders?: Record<string, string | string[]>;
responseBody?: Buffer | null;
}
socket.on('breakpoint:hit', (hit: BreakpointHit) => {
console.log(`Breakpoint hit: ${hit.method} ${hit.url}`);
console.log(`Type: ${hit.type}, Hit ID: ${hit.id}`);
});breakpoint:resumed
Emitted when a paused breakpoint is released (either by user action or programmatically).
socket.on('breakpoint:resumed', (hitId: string) => {
console.log(`Breakpoint resumed: ${hitId}`);
});breakpoint:timeout
Emitted when a breakpoint times out without being resumed.
socket.on('breakpoint:timeout', (hitId: string) => {
console.log(`Breakpoint timed out: ${hitId}`);
});WebSocket Events
websocket:open
Emitted when a new WebSocket connection is established through the proxy.
interface WebSocketConnectionEvent {
trafficId: string;
id: string;
url: string;
timestamp: number;
sessionId?: string;
}
socket.on('websocket:open', (event: WebSocketConnectionEvent) => {
console.log(`WebSocket opened: ${event.url}`);
});websocket:frame
Emitted when a WebSocket frame (message) is captured.
interface WebSocketFrameEvent {
id: string;
trafficId: string;
timestamp: number;
direction: 'client-to-server' | 'server-to-client';
opcode: number; // 1=text, 2=binary, 8=close, 9=ping, 10=pong
data: string | null; // base64 encoded
isBinary: boolean;
length: number;
}
socket.on('websocket:frame', (frame: WebSocketFrameEvent) => {
const arrow = frame.direction === 'client-to-server' ? '->' : '<-';
console.log(`WS ${arrow} opcode:${frame.opcode} length:${frame.length}`);
});websocket:close
Emitted when a WebSocket connection is closed.
interface WebSocketCloseEvent {
trafficId: string;
id: string;
code: number;
reason: string;
timestamp: number;
frameCount: number;
}
socket.on('websocket:close', (event: WebSocketCloseEvent) => {
console.log(`WebSocket closed: code=${event.code}, frames=${event.frameCount}`);
});websocket:error
Emitted when a WebSocket connection encounters an error.
interface WebSocketErrorEvent {
trafficId: string;
id: string;
error: string;
timestamp: number;
}
socket.on('websocket:error', (event: WebSocketErrorEvent) => {
console.error(`WebSocket error: ${event.error}`);
});Rule Events
rule:created
Emitted when a new rule is created.
socket.on('rule:created', (rule: Rule) => {
console.log(`Rule created: ${rule.name} (${rule.action})`);
});rule:updated
Emitted when a rule is updated.
socket.on('rule:updated', (rule: Rule) => {
console.log(`Rule updated: ${rule.name}`);
});rule:toggled
Emitted when a rule is toggled between enabled and disabled.
socket.on('rule:toggled', (rule: Rule) => {
console.log(`Rule ${rule.name}: ${rule.enabled ? 'enabled' : 'disabled'}`);
});rule:deleted
Emitted when a rule is deleted.
socket.on('rule:deleted', (ruleId: string) => {
console.log(`Rule deleted: ${ruleId}`);
});rules:reordered
Emitted when rules are reordered.
socket.on('rules:reordered', (rules: Rule[]) => {
console.log(`Rules reordered: ${rules.map(r => r.name).join(', ')}`);
});Session Events
session:created
Emitted when a new session is created.
socket.on('session:created', (session: Session) => {
console.log(`Session created: ${session.name}`);
});session:updated
Emitted when a session is updated.
socket.on('session:updated', (session: Session) => {
console.log(`Session updated: ${session.name}`);
});session:deleted
Emitted when a session is deleted.
socket.on('session:deleted', (sessionId: string) => {
console.log(`Session deleted: ${sessionId}`);
});Proxy Events
proxy:started
Emitted when the proxy server starts.
interface ProxyConfig {
port: number;
sslPort?: number;
uiPort: number;
autoOpenBrowser: boolean;
recording: boolean;
}
socket.on('proxy:started', (config: ProxyConfig) => {
console.log(`Proxy started on port ${config.port}`);
});proxy:stopped
Emitted when the proxy server stops.
socket.on('proxy:stopped', () => {
console.log('Proxy stopped');
});proxy:error
Emitted when the proxy encounters an error.
socket.on('proxy:error', (error: string) => {
console.error(`Proxy error: ${error}`);
});Certificate Events
certificate:installed
Emitted when a new domain certificate is generated and installed.
socket.on('certificate:installed', (domain: string) => {
console.log(`Certificate installed for: ${domain}`);
});Client to Server Events
These events are sent from the client to the server to subscribe to data or perform actions.
session:subscribe
Subscribe to events for a specific session.
socket.emit('session:subscribe', 'session-uuid');session:unsubscribe
Unsubscribe from a session's events.
socket.emit('session:unsubscribe', 'session-uuid');traffic:subscribe
Subscribe to traffic events. Optionally filter by session.
// Subscribe to all traffic
socket.emit('traffic:subscribe', {});
// Subscribe to a specific session's traffic
socket.emit('traffic:subscribe', { sessionId: 'session-uuid' });traffic:unsubscribe
Unsubscribe from all traffic events.
socket.emit('traffic:unsubscribe');breakpoint:resume
Resume a paused breakpoint, optionally with modifications to the request or response.
// Continue without modifications
socket.emit('breakpoint:resume', {
id: 'hit-uuid',
action: 'continue'
});
// Abort the request
socket.emit('breakpoint:resume', {
id: 'hit-uuid',
action: 'abort'
});
// Continue with modified request
socket.emit('breakpoint:resume', {
id: 'hit-uuid',
action: 'continue',
modifications: {
request: {
method: 'POST',
url: 'https://api.example.com/modified-path',
headers: { 'Authorization': 'Bearer new-token' },
body: '{"modified": true}'
}
}
});
// Continue with modified response
socket.emit('breakpoint:resume', {
id: 'hit-uuid',
action: 'continue',
modifications: {
response: {
status: 200,
statusText: 'OK',
headers: { 'content-type': 'application/json' },
body: '{"overridden": true}'
}
}
});
// Return a mock response instead of forwarding
socket.emit('breakpoint:resume', {
id: 'hit-uuid',
action: 'mock',
modifications: {
mock: {
status: 200,
statusText: 'OK',
headers: { 'content-type': 'application/json' },
body: '{"mocked": true}'
}
}
});Resume Actions
| Action | Description |
|---|---|
continue | Forward the request/response, optionally with modifications |
abort | Drop the request entirely |
mock | Return a mock response instead of forwarding to the server |
Modifications Object
interface BreakpointResumeModifications {
request?: {
method?: string;
url?: string;
headers?: Record<string, string>;
body?: string | Buffer;
};
response?: {
status?: number;
statusText?: string;
headers?: Record<string, string>;
body?: string | Buffer;
};
mock?: {
status: number;
statusText?: string;
headers?: Record<string, string>;
body?: string | Buffer;
};
}proxy:start
Request the proxy to start (optional configuration overrides).
socket.emit('proxy:start', { port: 8888 });proxy:stop
Request the proxy to stop.
socket.emit('proxy:stop');Complete Example
Full TypeScript client that monitors traffic and handles breakpoints
import { io, Socket } from 'socket.io-client';
// Connect to NectoProxy
const socket: Socket = io('http://localhost:8889', {
transports: ['websocket', 'polling'],
});
// Connection lifecycle
socket.on('connect', () => {
console.log('Connected to NectoProxy');
// Subscribe to all traffic
socket.emit('traffic:subscribe', {});
});
socket.on('disconnect', () => {
console.log('Disconnected from NectoProxy');
});
// Monitor new traffic
socket.on('traffic:batch', (entries) => {
for (const entry of entries) {
const status = entry.status ?? 'pending';
const duration = entry.duration ? `${entry.duration}ms` : '...';
console.log(`[${status}] ${entry.method} ${entry.url} (${duration})`);
}
});
// Monitor traffic updates (response received)
socket.on('traffic:update', (update) => {
if (update.status) {
console.log(`Updated ${update.id}: status=${update.status}`);
}
});
// Handle breakpoints
socket.on('breakpoint:hit', (hit) => {
console.log(`\nBreakpoint hit: ${hit.method} ${hit.url}`);
console.log(`Type: ${hit.type}, ID: ${hit.id}`);
// Auto-resume after inspection (in production, you would
// present this to the user via a UI)
setTimeout(() => {
socket.emit('breakpoint:resume', {
id: hit.id,
action: 'continue',
});
console.log('Breakpoint resumed');
}, 1000);
});
socket.on('breakpoint:resumed', (id) => {
console.log(`Breakpoint ${id} resumed`);
});
socket.on('breakpoint:timeout', (id) => {
console.log(`Breakpoint ${id} timed out`);
});
// Monitor WebSocket connections
socket.on('websocket:open', (event) => {
console.log(`WebSocket opened: ${event.url}`);
});
socket.on('websocket:frame', (frame) => {
const dir = frame.direction === 'client-to-server' ? 'SEND' : 'RECV';
console.log(`WS [${dir}] opcode=${frame.opcode} len=${frame.length}`);
});
socket.on('websocket:close', (event) => {
console.log(`WebSocket closed: code=${event.code} (${event.frameCount} frames)`);
});
// Monitor rule changes
socket.on('rule:created', (rule) => {
console.log(`Rule created: ${rule.name}`);
});
socket.on('rule:toggled', (rule) => {
console.log(`Rule ${rule.name}: ${rule.enabled ? 'ON' : 'OFF'}`);
});
// Monitor proxy status
socket.on('proxy:started', (config) => {
console.log(`Proxy started on port ${config.port}`);
});
socket.on('proxy:error', (error) => {
console.error(`Proxy error: ${error}`);
});
// Graceful shutdown
process.on('SIGINT', () => {
socket.emit('traffic:unsubscribe');
socket.disconnect();
process.exit(0);
});Batched Traffic Events
Traffic events use a traffic:batch event instead of individual traffic:new events. This batching improves performance when many requests are captured simultaneously. The batch is flushed every 100ms or when 50 entries accumulate, whichever comes first.