NodePass provides RESTful API in Master Mode, supporting frontend integration and automation. This document covers all interfaces, data structures, and best practices.
In Master Mode (master://), NodePass supports:
- Creating and managing server/client instances
- Real-time monitoring of status, traffic, and health checks
- Instance control (start, stop, restart, reset traffic)
- Auto-restart policy configuration
- Flexible parameter configuration
master://<api_addr>/<prefix>?<log>&<tls>
<api_addr>: Listen address (e.g.,0.0.0.0:9090)<prefix>: API path prefix (default/api)
nodepass "master://0.0.0.0:9090?log=info"
nodepass "master://0.0.0.0:9090/admin?log=info&tls=1"| Endpoint | Method | Description |
|---|---|---|
/instances |
GET | Get all instances |
/instances |
POST | Create new instance |
/instances/{id} |
GET | Get instance details |
/instances/{id} |
PATCH | Update/control instance |
/instances/{id} |
PUT | Update instance URL |
/instances/{id} |
DELETE | Delete instance |
/events |
GET | SSE real-time event stream |
/info |
GET | Get master service info |
/info |
POST | Update master alias |
/tcping |
GET | TCP connection test |
/openapi.json |
GET | OpenAPI specification |
/docs |
GET | Swagger UI documentation |
API Key authentication is enabled by default, automatically generated and saved in nodepass.gob on first startup.
- Protected endpoints:
/instances,/instances/{id},/events,/info,/tcping - Public endpoints:
/openapi.json,/docs - Authentication method: Add
X-API-Key: <key>to request headers - Reset Key: PATCH
/instances/********, body{ "action": "restart" }
{
"id": "a1b2c3d4",
"alias": "alias",
"type": "client|server",
"status": "running|stopped|error",
"url": "...",
"config": "server://0.0.0.0:8080/localhost:3000?log=info&tls=1&dns=5m&max=1024&mode=0&type=0&dial=auto&read=1h&rate=100&slot=65536&proxy=0¬cp=0&noudp=0",
"restart": true,
"meta": {
"peer": {
"sid": "550e8400-e29b-41d4-a716-446655440000",
"type": "1",
"alias": "remote-service"
},
"tags": {
"environment": "production",
"region": "us-west",
"owner": "team-alpha"
}
},
"mode": 0,
"ping": 0,
"pool": 0,
"tcps": 0,
"udps": 0,
"tcprx": 0,
"tcptx": 0,
"udprx": 0,
"udptx": 0
}mode: Instance modeping/pool: Health check datatcps/udps: Current active connection count statisticstcprx/tcptx/udprx/udptx: Cumulative traffic statisticsconfig: Instance configuration URL with complete startup configurationrestart: Auto-restart policymeta: Metadata information for instance organization and peer identificationpeer: Peer connection information (remote endpoint details)sid: Service ID of the remote service, using UUID v4 format (e.g.,550e8400-e29b-41d4-a716-446655440000)type: Remote service type, using standard enumeration values"0": Single-end Forwarding mode"1": NAT Traversal mode"2": Tunnel Forwarding mode
alias: Service alias of the remote endpoint (no format restriction)
tags: Custom key-value tags for flexible categorization and filtering
- Server:
server://<bind_addr>:<bind_port>/<target_host>:<target_port>?<parameters> - Client:
client://<server_host>:<server_port>/<local_host>:<local_port>?<parameters> - Supported parameters:
log,tls,crt,key,dns,sni,lbs,min,max,mode,type,dial,read,rate,slot,proxy,notcp,noudp
log: Log level (none,debug,info,warn,error,event)tls: TLS encryption mode (0,1,2) - Server/Master mode onlycrt/key: Certificate/key file paths (whentls=2)dns: Custom DNS servers (comma-separated IP addresses, default:1.1.1.1,8.8.8.8) - Server/Client mode onlysni: Server Name Indication, specifies hostname for TLS handshake (default:none) - Client dual-end handshake mode onlylbs: Load balancing strategy (0=round-robin,1=sticky failover, default:0) - Controls target address selection for multi-target configurationsmin/max: Connection pool capacity (minset by client,maxset by server and passed to client during handshake)mode: Runtime mode control (0,1,2) - Controls operation behavior- For server:
0=auto,1=reverse mode,2=forward mode - For client:
0=auto,1=single-end forwarding,2=dual-end handshake
- For server:
type: Connection pool type (0=TCP pool,1=QUIC pool,2=WebSocket pool, default:0) - Server mode only, client receives configuration during handshakedial: Source IP for outbound connections (default:auto) - Server/Client mode onlyread: Data read timeout duration (e.g., 1h, 30m, 15s, default:0for no timeout)rate: Bandwidth rate limit in Mbps (0=unlimited)slot: Maximum concurrent connection limit (default:65536, 0=unlimited)proxy: PROXY protocol support (0,1) - When enabled, sends PROXY protocol v1 header before data transmissionnotcp: TCP support control (0=enabled,1=disabled) - Server/Client mode onlynoudp: UDP support control (0=enabled,1=disabled) - Server/Client mode only
- Event types:
initial,create,update,delete,shutdown,log logevents only push normal logs, traffic/health check logs are filtered- Connect to
/eventsfor real-time instance changes and logs
- All instances, traffic, health checks, aliases, and auto-restart policies are persistently stored and automatically restored after restart
- Detailed API specification at
/openapi.json, Swagger UI at/docs
// Regenerate API Key (requires knowing current API Key)
async function regenerateApiKey() {
const response = await fetch(`${API_URL}/instances/${apiKeyID}`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'current-api-key'
},
body: JSON.stringify({ action: 'restart' })
});
const result = await response.json();
return result.url; // New API Key
}
// Get Master ID
async function getMasterID() {
const response = await fetch(`${API_URL}/instances/${apiKeyID}`, {
method: 'GET',
headers: {
'X-API-Key': 'current-api-key'
}
});
const result = await response.json();
return result.data.config; // Master ID (16-character hex)
}Note:
- API Key ID is fixed as
********(eight asterisks). In the internal implementation, this is a special instance ID used to store and manage the API Key. - The API Key instance's
configfield stores the Master ID, which is a 16-character hexadecimal string (e.g.,1a2b3c4d5e6f7890) used to uniquely identify the master service. - The Master ID is automatically generated on first startup and persisted, remaining constant throughout the master service's lifecycle.
NodePass now supports Server-Sent Events (SSE) functionality for real-time monitoring of instance state changes. This enables frontend applications to receive immediate notifications of instance creation, updates, and deletions without the need for polling.
The SSE endpoint is located at:
GET /events
This endpoint establishes a persistent connection and delivers events in real-time using the SSE protocol format. If API Key authentication is enabled, you need to include a valid API Key in the request headers.
The following event types are supported:
initial- Sent when connection is established, contains current state of all instancescreate- Sent when a new instance is createdupdate- Sent when an instance is updated (state changes, start/stop operations)delete- Sent when an instance is deletedshutdown- Sent when the master service is about to shut down, notifying frontend applications to close connectionslog- Sent when an instance produces new log content, contains log text
In frontend applications, you can handle instance logs by listening to log events. Here's an example function to append logs to a specific instance's UI:
// Handle log events
function appendLogToInstanceUI(instanceId, logText) {
// Find or create log container
let logContainer = document.getElementById(`logs-${instanceId}`);
if (!logContainer) {
logContainer = document.createElement('div');
logContainer.id = `logs-${instanceId}`;
document.getElementById('instance-container').appendChild(logContainer);
}
// Create new log entry
const logEntry = document.createElement('div');
logEntry.className = 'log-entry';
// You can parse ANSI color codes or format logs here
logEntry.textContent = logText;
// Add to container
logContainer.appendChild(logEntry);
// Scroll to latest log
logContainer.scrollTop = logContainer.scrollHeight;
}Best practices for log integration:
- Buffer management: Limit the number of log entries to prevent memory issues
- ANSI color parsing: Parse ANSI color codes in logs for better readability
- Filtering options: Provide options to filter logs by severity or content
- Search functionality: Allow users to search within instance logs
- Log persistence: Optionally save logs to local storage for viewing after page refresh
Here's an example of consuming the SSE endpoint in JavaScript frontend:
function connectToEventSource() {
const eventSource = new EventSource(`${API_URL}/events`, {
// If authentication is needed, native EventSource doesn't support custom headers
// Need to use fetch API to implement custom SSE client
});
// If using API Key, need to use custom implementation instead of native EventSource
// Below is an example using native EventSource
eventSource.addEventListener('instance', (event) => {
const data = JSON.parse(event.data);
switch (data.type) {
case 'initial':
console.log('Initial instance state:', data.instance);
updateInstanceUI(data.instance);
break;
case 'create':
console.log('Instance created:', data.instance);
addInstanceToUI(data.instance);
break;
case 'update':
console.log('Instance updated:', data.instance);
updateInstanceUI(data.instance);
break;
case 'delete':
console.log('Instance deleted:', data.instance);
removeInstanceFromUI(data.instance.id);
break;
case 'log':
console.log(`Instance ${data.instance.id} log:`, data.logs);
appendLogToInstanceUI(data.instance.id, data.logs);
break;
case 'shutdown':
console.log('Master service is shutting down');
// Close event source and show notification
eventSource.close();
showShutdownNotification();
break;
}
});
eventSource.addEventListener('error', (error) => {
console.error('SSE connection error:', error);
// Try to reconnect after delay
setTimeout(() => {
eventSource.close();
connectToEventSource();
}, 5000);
});
return eventSource;
}
// Example of creating SSE connection with API Key
function connectToEventSourceWithApiKey(apiKey) {
// Native EventSource doesn't support custom headers, need to use fetch API
fetch(`${API_URL}/events`, {
method: 'GET',
headers: {
'X-API-Key': apiKey,
'Cache-Control': 'no-cache'
}
}).then(response => {
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
function processStream() {
reader.read().then(({ value, done }) => {
if (done) {
console.log('Connection closed');
// Try to reconnect
setTimeout(() => connectToEventSourceWithApiKey(apiKey), 5000);
return;
}
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n\n');
buffer = lines.pop() || '';
for (const line of lines) {
if (line.trim() === '') continue;
const eventMatch = line.match(/^event: (.+)$/m);
const dataMatch = line.match(/^data: (.+)$/m);
if (eventMatch && dataMatch) {
const data = JSON.parse(dataMatch[1]);
// Handle events - see switch code above
}
}
processStream();
}).catch(error => {
console.error('Read error:', error);
// Try to reconnect
setTimeout(() => connectToEventSourceWithApiKey(apiKey), 5000);
});
}
processStream();
}).catch(error => {
console.error('Connection error:', error);
// Try to reconnect
setTimeout(() => connectToEventSourceWithApiKey(apiKey), 5000);
});
}Using SSE for monitoring instance state provides several advantages over traditional polling:
- Reduced latency: Changes are delivered in real-time
- Reduced server load: Eliminates unnecessary polling requests
- Bandwidth efficiency: Data is only sent when changes occur
- Native browser support: Built-in browser support without additional libraries
- Automatic reconnection: Browser automatically reconnects when connection is lost
When implementing SSE in frontend:
- Handle reconnection: While browsers automatically attempt reconnection, implement custom logic to ensure persistent connection
- Efficient event handling: Keep event handlers fast to avoid UI blocking
- Implement fallback: Implement polling fallback for environments that don't support SSE
- Handle errors: Properly handle connection errors and disconnections
- Log management: Maintain log buffers for each instance to prevent unlimited growth
When integrating NodePass with frontend applications, consider the following important aspects:
NodePass Master Mode now supports instance persistence using gob serialization format. Instances and their states are saved to a nodepass.gob file in the same directory as the executable and automatically restored when the master restarts.
Key persistence features:
- Instance configurations are automatically saved to disk
- Instance states (running/stopped) are preserved
- Auto-restart policies are maintained across master restarts
- Traffic statistics are maintained between restarts
- Instances with auto-restart enabled automatically start when master restarts
- No need to manually re-register after restart
NodePass Master Mode provides automatic backup functionality to periodically backup state files to prevent data loss:
- Backup File: Automatically creates
nodepass.gob.backupbackup file - Backup Interval: Automatically backs up every 1 hour (configurable via
NP_RELOAD_INTERVALenvironment variable) - Backup Strategy: Uses single backup file, new backups overwrite old backups
- Backup Content: Includes all instance configurations, states, auto-restart policies, and statistics
- Disaster Recovery: When the main file is corrupted, backup file can be manually used for recovery
- Auto Start: Backup functionality starts automatically with master service, no additional configuration required
Backup file location: nodepass.gob.backup in the same directory as the main state file nodepass.gob
Note: While instance configurations are now persistent and automatically backed up, frontend applications should still maintain their own instance configuration records as an additional backup strategy.
To properly manage lifecycles:
-
Creation: Store instance configuration and URL
async function createNodePassInstance(config) { const response = await fetch(`${API_URL}/instances`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-API-Key': apiKey // If API Key is enabled }, body: JSON.stringify({ url: `server://0.0.0.0:${config.port}/${config.target}?tls=${config.tls}` }) }); const data = await response.json(); // Configure auto-restart policy based on type if (data.success) { const shouldAutoRestart = config.type === 'server' || config.critical === true; await setAutoStartPolicy(data.data.id, shouldAutoRestart); } // Store in frontend persistence storage saveInstanceConfig({ id: data.data.id, originalConfig: config, url: data.data.url }); return data; }
-
Status Monitoring: Monitor instance state changes
NodePass provides two methods for monitoring instance status:
A. Using SSE (Recommended): Receive real-time events through persistent connection
function connectToEventSource() { const eventSource = new EventSource(`${API_URL}/events`, { // If authentication is needed, use custom implementation }); // Or use custom implementation with API Key // connectToEventSourceWithApiKey(apiKey); eventSource.addEventListener('instance', (event) => { const data = JSON.parse(event.data); // Handle different event types: initial, create, update, delete, log // ...handling logic see "Using SSE for Real-time Event Monitoring" section above }); // Error handling and reconnection logic // ...see previous examples return eventSource; }
B. Traditional Polling (Alternative): Use in environments that don't support SSE
function startInstanceMonitoring(instanceId, interval = 5000) { return setInterval(async () => { try { const response = await fetch(`${API_URL}/instances/${instanceId}`, { headers: { 'X-API-Key': apiKey // If API Key is enabled } }); const data = await response.json(); if (data.success) { updateInstanceStatus(instanceId, data.data.status); updateInstanceMetrics(instanceId, { connections: data.data.connections, pool_size: data.data.pool_size, uptime: data.data.uptime }); } } catch (error) { markInstanceUnreachable(instanceId); } }, interval); }
Recommendation: Prioritize SSE method as it provides more efficient real-time monitoring and reduces server load. Only use polling when client doesn't support SSE or specific environment compatibility is needed.
-
Instance Alias Management: Set readable names for instances
// Batch set instance aliases async function setInstanceAliases(instances) { for (const instance of instances) { // Generate meaningful aliases based on instance type and purpose const alias = `${instance.type}-${instance.region || 'default'}-${instance.port || 'auto'}`; await updateInstanceAlias(instance.id, alias); } } // Find instance by alias async function findInstanceByAlias(targetAlias) { const response = await fetch(`${API_URL}/instances`, { headers: { 'X-API-Key': apiKey } }); const data = await response.json(); if (data.success) { return data.data.find(instance => instance.alias === targetAlias); } return null; }
-
Control Operations: Start, stop, restart instances
async function controlInstance(instanceId, action) { // action can be: start, stop, restart const response = await fetch(`${API_URL}/instances/${instanceId}`, { method: 'PATCH', // Note: API has been updated to use PATCH method instead of PUT headers: { 'Content-Type': 'application/json', 'X-API-Key': apiKey // If API Key is enabled }, body: JSON.stringify({ action }) }); const data = await response.json(); return data.success; } // Update instance alias async function updateInstanceAlias(instanceId, alias) { const response = await fetch(`${API_URL}/instances/${instanceId}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json', 'X-API-Key': apiKey // If API Key is enabled }, body: JSON.stringify({ alias }) }); const data = await response.json(); return data.success; } // Update instance URL configuration async function updateInstanceURL(instanceId, newURL) { const response = await fetch(`${API_URL}/instances/${instanceId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', 'X-API-Key': apiKey // If API Key is enabled }, body: JSON.stringify({ url: newURL }) }); const data = await response.json(); return data.success; } // Update instance metadata async function updateInstanceMetadata(instanceId, metadata) { const response = await fetch(`${API_URL}/instances/${instanceId}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json', 'X-API-Key': apiKey // If API Key is enabled }, body: JSON.stringify({ meta: metadata }) }); const data = await response.json(); return data.success; }
-
Metadata Management: Organize and categorize instances with metadata
// Set peer connection information async function setPeerInfo(instanceId, peerInfo) { const response = await fetch(`${API_URL}/instances/${instanceId}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json', 'X-API-Key': apiKey }, body: JSON.stringify({ meta: { peer: { sid: peerInfo.serviceId, // UUID v4 format type: peerInfo.type, // "0" | "1" | "2" alias: peerInfo.alias }, tags: {} // Preserve existing tags } }) }); const data = await response.json(); return data.success; } // Add or update instance tags async function updateInstanceTags(instanceId, tags) { const response = await fetch(`${API_URL}/instances/${instanceId}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json', 'X-API-Key': apiKey }, body: JSON.stringify({ meta: { peer: {}, // Preserve existing peer info tags: tags } }) }); const data = await response.json(); return data.success; } // Complete metadata update async function updateCompleteMetadata(instanceId, peerInfo, tags) { const response = await fetch(`${API_URL}/instances/${instanceId}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json', 'X-API-Key': apiKey }, body: JSON.stringify({ meta: { peer: peerInfo, tags: tags } }) }); const data = await response.json(); return data.success; }
-
Auto-restart Policy Management: Configure automatic startup behavior
async function setAutoStartPolicy(instanceId, enableAutoStart) { const response = await fetch(`${API_URL}/instances/${instanceId}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json', 'X-API-Key': apiKey }, body: JSON.stringify({ restart: enableAutoStart }) }); const data = await response.json(); return data.success; } async function controlInstanceWithAutoStart(instanceId, action, enableAutoStart) { const response = await fetch(`${API_URL}/instances/${instanceId}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json', 'X-API-Key': apiKey }, body: JSON.stringify({ action: action, restart: enableAutoStart }) }); const data = await response.json(); return data.success; } async function updateInstanceComplete(instanceId, alias, action, enableAutoStart) { const response = await fetch(`${API_URL}/instances/${instanceId}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json', 'X-API-Key': apiKey }, body: JSON.stringify({ alias: alias, action: action, restart: enableAutoStart }) }); const data = await response.json(); return data.success; }
Here are comprehensive examples showing how to use metadata for instance organization and management:
// Example 1: Establish peer-to-peer tunnel with metadata
async function establishPeerTunnel(localConfig, remoteConfig) {
// Create local server instance
const localInstance = await createNodePassInstance({
type: 'server',
port: localConfig.port,
target: localConfig.target
});
// Create remote client instance
const remoteInstance = await createNodePassInstance({
type: 'client',
serverHost: localConfig.serverHost,
port: remoteConfig.port,
target: remoteConfig.target
});
if (localInstance.success && remoteInstance.success) {
// Set peer information on local instance
await updateCompleteMetadata(
localInstance.data.id,
{
sid: remoteConfig.serviceId, // UUID format
type: "2", // Tunnel forwarding
alias: remoteConfig.serviceName
},
{
tunnel_type: 'peer-to-peer',
protocol: 'tcp',
encryption: 'tls'
}
);
// Set peer information on remote instance
await updateCompleteMetadata(
remoteInstance.data.id,
{
sid: localConfig.serviceId, // UUID format
type: "2", // Tunnel forwarding
alias: localConfig.serviceName
},
{
tunnel_type: 'peer-to-peer',
protocol: 'tcp',
encryption: 'tls'
}
);
console.log('Peer tunnel established with metadata');
}
}
// Example 2: Organize instances by environment and region
async function organizeInstancesByEnvironment(instances) {
for (const instance of instances) {
const tags = {
environment: instance.isProduction ? 'production' : 'development',
region: instance.deploymentRegion,
team: instance.owningTeam,
cost_center: instance.costCenter,
criticality: instance.isCritical ? 'high' : 'normal'
};
await updateInstanceTags(instance.id, tags);
console.log(`Tagged instance ${instance.id} with environment metadata`);
}
}
// Example 3: Query instances by metadata tags
async function findInstancesByTags(requiredTags) {
const response = await fetch(`${API_URL}/instances`, {
headers: { 'X-API-Key': apiKey }
});
const data = await response.json();
if (data.success) {
return data.data.filter(instance => {
if (!instance.meta || !instance.meta.tags) return false;
// Check if all required tags match
return Object.entries(requiredTags).every(([key, value]) =>
instance.meta.tags[key] === value
);
});
}
return [];
}
// Example 4: Update metadata based on operational status
async function updateMetadataOnStatusChange(instanceId, newStatus) {
const instance = await fetch(`${API_URL}/instances/${instanceId}`, {
headers: { 'X-API-Key': apiKey }
});
const data = await instance.json();
if (data.success && data.data.meta) {
const updatedTags = {
...data.data.meta.tags,
last_status_change: new Date().toISOString(),
current_status: newStatus,
status_change_count: (parseInt(data.data.meta.tags.status_change_count || '0') + 1).toString()
};
await updateInstanceTags(instanceId, updatedTags);
}
}-
Peer Information: Use the
peerobject to track connections between instancessid: Service unique identifier (required, UUID v4 format, e.g.,550e8400-e29b-41d4-a716-446655440000)- Use standard UUID v4 format to ensure global uniqueness
- Can use JavaScript's
crypto.randomUUID()or third-party libraries to generate
type: Service type identifier (required, string enumeration value)"0": Single-end Forwarding - For simple client forwarding scenarios"1": NAT Traversal - For scenarios requiring NAT traversal"2": Tunnel Forwarding - For establishing encrypted tunnels
alias: Friendly name of the remote service (no format restriction, max 256 chars)
-
Frontend Integration Standards: To ensure consistency, frontends should follow these standards
Service ID (sid) Generation Standards:
// Use browser native API to generate UUID v4 const serviceId = crypto.randomUUID(); // Example output: "550e8400-e29b-41d4-a716-446655440000" // Or use third-party library (e.g., uuid) import { v4 as uuidv4 } from 'uuid'; const serviceId = uuidv4();
Service Type (type) Usage Standards:
// Define service type enumeration const ServiceType = { SINGLE_END: "0", // Single-end forwarding: client unidirectional forwarding, no server callback needed NAT_TRAVERSAL: "1", // NAT traversal: traverse NAT for internal network access TUNNEL: "2" // Tunnel forwarding: establish end-to-end encrypted tunnel }; // Usage example const peerInfo = { sid: crypto.randomUUID(), type: ServiceType.NAT_TRAVERSAL, alias: "Web Server" };
Type Selection Guide:
-
Single-end Forwarding ("0"):
- Scenario: Client only needs to forward traffic to remote server
- Feature: One-way connection, no server callback required
- Example: Local app connecting to cloud database
-
NAT Traversal ("1"):
- Scenario: Need to access internal network services from external network
- Feature: Traverse NAT and firewall restrictions
- Example: Remote access to home NAS, internal web services
-
Tunnel Forwarding ("2"):
- Scenario: Need to establish secure end-to-end connection
- Feature: Encrypted transmission, bidirectional communication
- Example: Secure interconnection between branch offices and headquarters
-
-
Tags Organization: Design a consistent tagging strategy
- Use lowercase keys with underscores (e.g.,
cost_center,deployment_region) - Limit tag values to meaningful, searchable strings
- Common tag categories:
- Environment:
production,staging,development - Location:
us-west,eu-central,ap-southeast - Ownership:
team-alpha,ops-team,platform-team - Function:
database-tunnel,web-proxy,api-gateway - Criticality:
high,medium,low
- Environment:
- Use lowercase keys with underscores (e.g.,
-
Field Length Limits: All metadata fields have length requirements
peer.sid: Fixed 36 characters (UUID v4 format, e.g.,550e8400-e29b-41d4-a716-446655440000)peer.type: Fixed 1 character (enumeration value:"0"|"1"|"2")peer.alias: Max 256 chars (no specific format required)- Tag keys and values: Max 256 chars each
-
Tag Uniqueness: Ensure tag keys are unique within an instance
- Duplicate keys will result in a 400 Bad Request error
-
Filtering and Search: Use metadata for instance filtering
- Client-side filtering by tags for dashboard views
- Query instances by peer information for relationship mapping
- Group instances by tags for batch operations
Here's a comprehensive example showing how to implement auto-restart policy management in real scenarios:
// Scenario: Establish load-balanced server cluster with auto-restart policies
async function setupServerCluster(serverConfigs) {
const clusterInstances = [];
for (const config of serverConfigs) {
try {
// Create server instance
const instance = await createNodePassInstance({
type: 'server',
port: config.port,
target: config.target,
critical: config.isPrimary, // Primary servers are critical instances
tls: config.enableTLS
});
if (instance.success) {
// Set meaningful instance alias
const alias = `${config.role}-server-${config.port}`;
await updateInstanceAlias(instance.data.id, alias);
// Configure auto-restart policy based on server role
const autoStartPolicy = config.isPrimary || config.role === 'essential';
await setAutoStartPolicy(instance.data.id, autoStartPolicy);
// Start instance
await controlInstance(instance.data.id, 'start');
clusterInstances.push({
id: instance.data.id,
alias: alias,
role: config.role,
autoStartEnabled: autoStartPolicy
});
console.log(`Server ${alias} created, auto-restart policy: ${autoStartPolicy}`);
}
} catch (error) {
console.error(`Failed to create server ${config.role}:`, error);
}
}
return clusterInstances;
}
// Monitor cluster health and dynamically adjust auto-restart policies
async function monitorClusterHealth(clusterInstances) {
const healthyInstances = [];
for (const cluster of clusterInstances) {
const instance = await fetch(`${API_URL}/instances/${cluster.id}`, {
headers: { 'X-API-Key': apiKey }
});
const data = await instance.json();
if (data.success && data.data.status === 'running') {
healthyInstances.push(cluster);
} else {
// If critical instance is down, enable auto-restart for backup instances
if (cluster.role === 'primary') {
await enableBackupInstanceAutoStart(clusterInstances);
}
}
}
return healthyInstances;
}
async function enableBackupInstanceAutoStart(clusterInstances) {
const backupInstances = clusterInstances.filter(c => c.role === 'backup');
for (const backup of backupInstances) {
await setAutoStartPolicy(backup.id, true);
console.log(`Enabled auto-restart policy for backup instance: ${backup.id}`);
}
}The Master API provides traffic statistics, but there are important considerations:
-
Basic Traffic Metrics: NodePass periodically provides cumulative values for TCP and UDP traffic in both inbound and outbound directions. Frontend applications need to store and process these values to obtain meaningful statistics.
function processTrafficStats(instanceId, currentStats) { // Store current timestamp const timestamp = Date.now(); // If we have previous stats for this instance, calculate differences if (previousStats[instanceId]) { const timeDiff = timestamp - previousStats[instanceId].timestamp; const tcpInDiff = currentStats.tcp_in - previousStats[instanceId].tcp_in; const tcpOutDiff = currentStats.tcp_out - previousStats[instanceId].tcp_out; const udpInDiff = currentStats.udp_in - previousStats[instanceId].udp_in; const udpOutDiff = currentStats.udp_out - previousStats[instanceId].udp_out; // Store historical data for chart display storeTrafficHistory(instanceId, { timestamp, tcp_in_rate: tcpInDiff / timeDiff * 1000, // bytes per second tcp_out_rate: tcpOutDiff / timeDiff * 1000, udp_in_rate: udpInDiff / timeDiff * 1000, udp_out_rate: udpOutDiff / timeDiff * 1000 }); } // Update previous stats for next calculation previousStats[instanceId] = { timestamp, tcp_in: currentStats.tcp_in, tcp_out: currentStats.tcp_out, udp_in: currentStats.udp_in, udp_out: currentStats.udp_out }; }
-
Data Persistence: Since the API only provides cumulative values, the frontend must implement proper storage and calculation logic
// Frontend traffic history storage structure example const trafficHistory = {}; function storeTrafficHistory(instanceId, metrics) { if (!trafficHistory[instanceId]) { trafficHistory[instanceId] = { timestamps: [], tcp_in_rates: [], tcp_out_rates: [], udp_in_rates: [], udp_out_rates: [] }; } trafficHistory[instanceId].timestamps.push(metrics.timestamp); trafficHistory[instanceId].tcp_in_rates.push(metrics.tcp_in_rate); trafficHistory[instanceId].tcp_out_rates.push(metrics.tcp_out_rate); trafficHistory[instanceId].udp_in_rates.push(metrics.udp_in_rate); trafficHistory[instanceId].udp_out_rates.push(metrics.udp_out_rate); // Keep history data manageable const MAX_HISTORY = 1000; if (trafficHistory[instanceId].timestamps.length > MAX_HISTORY) { trafficHistory[instanceId].timestamps.shift(); trafficHistory[instanceId].tcp_in_rates.shift(); trafficHistory[instanceId].tcp_out_rates.shift(); trafficHistory[instanceId].udp_in_rates.shift(); trafficHistory[instanceId].udp_out_rates.shift(); } }
Since NodePass now uses gob format for persistent storage of instance state, instance IDs no longer change after master restart. This means:
- Frontend applications can safely use instance IDs as unique identifiers
- Instance configurations, states, and statistics are automatically restored after restart
- No longer need to implement logic for handling instance ID changes
This greatly simplifies frontend integration, eliminating the previous complexity of handling instance recreation and ID mapping.
NodePass now supports configuring auto-restart policies for instances, enabling automated instance management and improving reliability. The auto-restart policy feature has the following characteristics:
- Automatic Instance Recovery: Instances with auto-restart enabled automatically start when the master service restarts
- Selective Auto-start: Configure which instances should auto-start based on their importance or role
- Persistent Policy Storage: Auto-restart policies are saved and restored across master restarts
- Fine-grained Control: Each instance can have its own auto-restart policy setting
- Policy Assignment: Each instance has a
restartboolean field that determines its auto-start behavior - Master Startup: When master starts, it automatically starts all instances with
restart: true - Policy Persistence: Auto-restart policies are saved with other instance data in the
nodepass.gobfile - Runtime Management: Auto-restart policies can be modified while instances are running
- Enable for Server Instances: Server instances should typically have auto-restart enabled for high availability
- Selective Client Auto-start: Only enable auto-restart for critical client connections
- Testing Scenarios: Disable auto-restart for temporary or testing instances
- Load Balancing: Use auto-restart policies to maintain minimum instance count for load distribution
// Example: Configure auto-restart policies based on instance role
async function configureAutoStartPolicies(instances) {
for (const instance of instances) {
// Enable auto-start for servers and critical clients
const shouldAutoStart = instance.type === 'server' ||
instance.critical === true;
await setAutoStartPolicy(instance.id, shouldAutoStart);
}
}The instance object in API responses contains the following fields:
{
"id": "a1b2c3d4", // Instance unique identifier
"alias": "web-server-01", // Instance alias (optional, for friendly display name)
"type": "server", // Instance type: server or client
"status": "running", // Instance status: running, stopped, or error
"url": "server://...", // Instance configuration URL
"config": "server://0.0.0.0:8080/localhost:3000?log=info&tls=1&dns=5m&max=1024&mode=0&type=0&dial=auto&read=1h&rate=100&slot=65536&proxy=0¬cp=0&noudp=0", // Complete configuration URL
"restart": true, // Auto-restart policy
"meta": { // Metadata for organization and peer tracking
"peer": {
"sid": "550e8400-e29b-41d4-a716-446655440000", // Remote service ID (UUID format)
"type": "1", // Remote service type (0=Single-end, 1=NAT Traversal, 2=Tunnel)
"alias": "remote-service" // Remote service friendly name
},
"tags": { // Custom key-value tags
"environment": "production",
"region": "us-west",
"team": "platform"
}
},
"mode": 0, // Instance mode
"tcprx": 1024, // TCP received bytes
"tcptx": 2048, // TCP transmitted bytes
"udprx": 512, // UDP received bytes
"udptx": 256 // UDP transmitted bytes
}Note:
aliasfield is optional, empty string if not setconfigfield contains the instance's complete configuration URL, auto-generated by the systemmodefield indicates the current runtime mode of the instancerestartfield controls the auto-restart behavior of the instancemetafield contains structured metadata for instance organizationpeerobject tracks remote endpoint information for peer-to-peer connectionssid: Service unique identifier, must use UUID v4 format (36 chars, e.g.,550e8400-e29b-41d4-a716-446655440000)type: Service type identifier, string enumeration value ("0"|"1"|"2")"0": Single-end Forwarding - Client unidirectional forwarding"1": NAT Traversal - Traverse NAT for internal network access"2": Tunnel Forwarding - Establish end-to-end encrypted tunnel
alias: Custom string, max 256 chars, no format restriction
tagsmap allows flexible categorization with custom key-value pairs- Tag keys and values have a 256-character maximum length
- Tag keys must be unique within an instance
NodePass Master automatically maintains the config field for each instance:
- Auto Generation: Automatically generated when instances are created and updated, no manual maintenance required
- Complete Configuration: Contains the instance's complete URL with all default parameters
- Configuration Inheritance: log and tls configurations are inherited from master settings
- Default Parameters: Other parameters use system defaults
- Read-Only Nature: Auto-generated field that cannot be directly modified through the API
Example config field value:
server://0.0.0.0:8080/localhost:3000?log=info&tls=1&max=1024&mode=0&read=1h&rate=0&slot=65536&proxy=0
This feature is particularly useful for:
- Configuration backup and export
- Instance configuration integrity checks
- Automated deployment scripts
- Configuration documentation generation
The /info endpoint provides system information about the NodePass master service. This endpoint is useful for monitoring, troubleshooting, and system status validation.
GET /info
Requires API Key authentication: Yes
The response contains the following system information fields:
{
"alias": "dev", // Master alias
"os": "linux", // Operating system type
"arch": "amd64", // System architecture
"cpu": 45, // CPU usage percentage (Linux only)
"mem_total": 8589934592, // Total memory in bytes (Linux only)
"mem_used": 2684354560, // Used memory in bytes (Linux only)
"swap_total": 3555328000, // Total swap space in bytes (Linux only)
"swap_used": 3555328000, // Used swap space in bytes (Linux only)
"netrx": 1048576000, // Network received bytes (cumulative, Linux only)
"nettx": 2097152000, // Network transmitted bytes (cumulative, Linux only)
"diskr": 4194304000, // Disk read bytes (cumulative, Linux only)
"diskw": 8388608000, // Disk write bytes (cumulative, Linux only)
"sysup": 86400, // System uptime in seconds (Linux only)
"ver": "1.2.0", // NodePass version
"name": "example.com", // Tunnel hostname
"uptime": 11525, // API uptime in seconds
"log": "info", // Log level
"tls": "1", // TLS enabled status
"crt": "/path/to/cert", // Certificate path
"key": "/path/to/key" // Key path
}// Get system information
async function getSystemInfo() {
const response = await fetch(`${API_URL}/info`, {
method: 'GET',
headers: {
'X-API-Key': apiKey
}
});
return await response.json();
}
// Display service uptime and system resource usage
function displaySystemStatus() {
getSystemInfo().then(info => {
console.log(`Service uptime: ${info.uptime} seconds`);
// Format uptime for friendlier display
const hours = Math.floor(info.uptime / 3600);
const minutes = Math.floor((info.uptime % 3600) / 60);
const seconds = info.uptime % 60;
console.log(`Service uptime: ${hours}h ${minutes}m ${seconds}s`);
// Display system resource usage (Linux only)
if (info.os === 'linux') {
if (info.cpu !== -1) {
console.log(`CPU usage: ${info.cpu}%`);
}
if (info.mem_total > 0) {
const memUsagePercent = (info.mem_used / info.mem_total * 100).toFixed(1);
const memFreeGB = ((info.mem_total - info.mem_used) / 1024 / 1024 / 1024).toFixed(1);
const memTotalGB = (info.mem_total / 1024 / 1024 / 1024).toFixed(1);
console.log(`Memory usage: ${memUsagePercent}% (${memFreeGB}GB free of ${memTotalGB}GB total)`);
}
if (info.swap_total > 0) {
const swapUsagePercent = (info.swap_used / info.swap_total * 100).toFixed(1);
const swapFreeGB = ((info.swap_total - info.swap_used) / 1024 / 1024 / 1024).toFixed(1);
const swapTotalGB = (info.swap_total / 1024 / 1024 / 1024).toFixed(1);
console.log(`Swap usage: ${swapUsagePercent}% (${swapFreeGB}GB free of ${swapTotalGB}GB total)`);
}
} else {
console.log('CPU, memory, swap space, network I/O, disk I/O, and system uptime monitoring is only available on Linux systems');
}
// Display network I/O statistics (cumulative values)
if (info.os === 'linux') {
console.log(`Network received: ${(info.netrx / 1024 / 1024).toFixed(2)} MB (cumulative)`);
console.log(`Network transmitted: ${(info.nettx / 1024 / 1024).toFixed(2)} MB (cumulative)`);
console.log(`Disk read: ${(info.diskr / 1024 / 1024).toFixed(2)} MB (cumulative)`);
console.log(`Disk write: ${(info.diskw / 1024 / 1024).toFixed(2)} MB (cumulative)`);
console.log(`System uptime: ${Math.floor(info.sysup / 3600)} hours`);
}
});
}- Regular checks: Poll this endpoint regularly to ensure service is running properly
- Version verification: Check version number after deploying updates
- Uptime monitoring: Monitor uptime to detect unexpected restarts
- Log level verification: Ensure current log level meets expectations
- Resource monitoring: On Linux systems, monitor CPU, memory, swap space, network I/O, disk I/O usage to ensure optimal performance
- CPU usage is calculated by parsing
/proc/stat(percentage of non-idle time) - Memory information is obtained by parsing
/proc/meminfo(total and used memory in bytes, calculated as total minus available) - Swap space information is obtained by parsing
/proc/meminfo(total and used swap space in bytes, calculated as total minus free) - Network I/O is calculated by parsing
/proc/net/dev(cumulative bytes, excluding virtual interfaces) - Disk I/O is calculated by parsing
/proc/diskstats(cumulative bytes, major devices only) - System uptime is obtained by parsing
/proc/uptime - Values of -1 or 0 indicate system information is unavailable (non-Linux systems)
- Network and disk I/O fields provide cumulative values; frontend applications need to store historical data and calculate differences to get real-time rates (bytes/second)
- CPU usage is calculated by parsing
For detailed API documentation including request and response examples, use the built-in Swagger UI documentation available at the /docs endpoint. This interactive documentation provides comprehensive information about:
- Available endpoints
- Required parameters
- Response formats
- Request and response examples
- Schema definitions
To access the Swagger UI documentation:
http(s)://<api_addr>[<prefix>]/docs
For example:
http://localhost:9090/api/docs
The Swagger UI provides a convenient way to explore and test the API directly in your browser. You can execute API calls against your running NodePass master instance and see actual responses.
- Description: Get list of all instances
- Authentication: Requires API Key
- Response: Array of instances
- Example:
const instances = await fetch(`${API_URL}/instances`, {
headers: { 'X-API-Key': apiKey }
});- Description: Create new instance
- Authentication: Requires API Key
- Request body:
{ "alias": "Instance alias (optional)", "url": "client:// or server:// format URL" } - Response: Newly created instance object
- Example:
const newInstance = await fetch(`${API_URL}/instances`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': apiKey
},
body: JSON.stringify({
alias: 'My Server',
url: 'server://0.0.0.0:8080/localhost:3000'
})
});- Description: Get specific instance details
- Authentication: Requires API Key
- Response: Instance object
- Example:
const instance = await fetch(`${API_URL}/instances/abc123`, {
headers: { 'X-API-Key': apiKey }
});- Description: Update instance state, alias, metadata, or perform control operations
- Authentication: Requires API Key
- Request body:
{ "alias": "new alias", "action": "start|stop|restart|reset", "restart": true|false, "meta": {...} } - Metadata Structure:
peer: Object with fields (all optional):sid: Service ID (UUID v4 format, 36 chars, e.g.,550e8400-e29b-41d4-a716-446655440000)type: Service type (enumeration value:"0"|"1"|"2")"0": Single-end Forwarding"1": NAT Traversal"2": Tunnel Forwarding
alias: Service alias (max 256 chars, no format restriction)
tags: Object with custom key-value pairs (keys and values max 256 chars, keys must be unique)
- Example:
// Update alias and restart policy
await fetch(`${API_URL}/instances/abc123`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
'X-API-Key': apiKey
},
body: JSON.stringify({
alias: "production-server",
restart: true
})
});
// Perform control action
await fetch(`${API_URL}/instances/abc123`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
'X-API-Key': apiKey
},
body: JSON.stringify({
action: "restart"
})
});
// Update metadata with peer information and tags
await fetch(`${API_URL}/instances/abc123`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
'X-API-Key': apiKey
},
body: JSON.stringify({
meta: {
peer: {
sid: "550e8400-e29b-41d4-a716-446655440000", // UUID format
type: "1", // NAT Traversal
alias: "remote-api-server"
},
tags: {
environment: "production",
region: "us-east",
team: "backend",
criticality: "high"
}
}
})
});
// Update only tags (peer info remains unchanged)
await fetch(`${API_URL}/instances/abc123`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
'X-API-Key': apiKey
},
body: JSON.stringify({
meta: {
peer: {}, // Empty object preserves existing peer info
tags: {
environment: "staging",
updated_at: new Date().toISOString()
}
}
})
});- Description: Completely update instance URL configuration
- Authentication: Requires API Key
- Request body:
{ "url": "new client:// or server:// format URL" } - Features: Will restart the instance.
- Restrictions: API Key instance (ID
********) does not support this operation - Example:
// Update instance URL
await fetch(`${API_URL}/instances/abc123`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'X-API-Key': apiKey
},
body: JSON.stringify({
url: 'server://0.0.0.0:9090/localhost:8080?tls=1'
})
});- Description: Delete instance
- Authentication: Requires API Key
- Response: 204 No Content
- Restrictions: API Key instance (ID
********) cannot be deleted - Example:
await fetch(`${API_URL}/instances/abc123`, {
method: 'DELETE',
headers: { 'X-API-Key': apiKey }
});- Description: Establish SSE connection to receive real-time events
- Authentication: Requires API Key
- Response: Server-Sent Events stream
- Event types:
initial,create,update,delete,shutdown,log
- Description: Get master service information
- Authentication: Requires API Key
- Response: Contains system information, version, uptime, CPU and RAM usage, etc.
- Description: Update master alias
- Authentication: Requires API Key
- Request body:
{ "alias": "new alias" } - Response: Complete master information (same as GET /info)
- Note: Master alias is stored in the
aliasfield of the API Key instance (ID********) - Example:
// Update master alias
const response = await fetch(`${API_URL}/info`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': apiKey
},
body: JSON.stringify({ alias: 'My NodePass Server' })
});
const data = await response.json();
console.log('Updated alias:', data.alias);
// Response contains full system info with updated aliasRetrieving Master ID: The Master ID is stored in the config field of the API Key instance and can be retrieved as follows:
// Get Master ID
async function getMasterID() {
const response = await fetch(`${API_URL}/instances/********`, {
headers: { 'X-API-Key': apiKey }
});
const data = await response.json();
return data.data.config; // Returns 16-character hex Master ID
}- Description: TCP connection test, checks connectivity and latency to target address
- Authentication: Requires API Key
- Parameters:
target(required): Target address in formathost:port
- Response:
{ "target": "example.com:80", "connected": true, "latency": 45, "error": null } - Example:
GET /api/tcping?target=fast.com:443
- Description: Get OpenAPI 3.1.1 specification
- Authentication: No authentication required
- Response: JSON format API specification
- Description: Swagger UI documentation interface
- Authentication: No authentication required
- Response: HTML format interactive documentation
Instance URLs must follow these formats:
server://<bind_address>:<bind_port>/<target_host>:<target_port>?<parameters>
Examples:
server://0.0.0.0:8080/localhost:3000- Listen on port 8080, forward to local port 3000server://0.0.0.0:9090/localhost:8080?tls=1&mode=1- TLS-enabled server, force reverse mode
client://<server_host>:<server_port>/<local_host>:<local_port>?<parameters>
Examples:
client://example.com:8080/localhost:3000- Connect to remote server, listen locally on port 3000client://remote.example.com:443/localhost:22?mode=2&min=32- Through remote server, force dual-end mode
| Parameter | Description | Values | Default | Scope |
|---|---|---|---|---|
log |
Log level | none, debug, info, warn, error, event |
info |
Both |
tls |
TLS encryption level | 0(none), 1(self-signed), 2(certificate) |
0 |
Server only |
crt |
Certificate path | File path | None | Server only |
key |
Private key path | File path | None | Server only |
dns |
DNS cache duration | Time duration (e.g., 5m, 30s, 1h) |
5m |
Both |
sni |
Server Name Indication | Hostname | none |
Client dual-end handshake mode only |
lbs |
Load balancing strategy | 0(round-robin), 1(optimal-latency), 2(primary-backup) |
0 |
Both |
min |
Minimum pool capacity | Integer > 0 | 64 |
Client dual-end handshake mode only |
max |
Maximum pool capacity | Integer > 0 | 1024 |
Dual-end handshake mode |
mode |
Runtime mode control | 0(auto), 1(force mode 1), 2(force mode 2) |
0 |
Both |
type |
Connection pool type | 0(TCP), 1(QUIC), 2(WebSocket), 3(HTTP/2) |
0 |
Server only |
dial |
Source IP for outbound | IP address or auto |
auto |
Both |
read |
Read timeout duration | Time duration (e.g., 10m, 30s, 1h) |
0 |
Both |
rate |
Bandwidth rate limit | Integer (Mbps), 0=unlimited | 0 |
Both |
slot |
Connection slot count | Integer (1-65536) | 65536 |
Both |
proxy |
PROXY protocol support | 0(disabled), 1(enabled) |
0 |
Both |
block |
Protocol blocking | 0(disabled), 1(SOCKS), 2(HTTP), 3(TLS) |
0 |
Both |
notcp |
TCP support control | 0(enabled), 1(disabled) |
0 |
Both |
noudp |
UDP support control | 0(enabled), 1(disabled) |
0 |
Both |