Overview
psys monitors network processes and connections in real-time using Linux’s ss command-line utility. Data is automatically refreshed every 5 seconds to provide live updates.
Data Collection
psys uses two ss commands to gather network information:
Listening Ports (ss -tlnp)
let ssListen : string ;
try {
ssListen = execSync ( "ss -tlnp 2>/dev/null" , { encoding: "utf8" });
} catch {
return { listeners: [], connections: [] };
}
The -tlnp flags stand for: t cp, l istening sockets, n umeric addresses (no DNS lookup), p rocess information.
Established Connections (ss -tnp)
let ssEstab : string ;
try {
ssEstab = execSync ( "ss -tnp 2>/dev/null" , { encoding: "utf8" });
} catch {
return { listeners , connections };
}
The -tnp flags capture: t cp, n umeric addresses, p rocess information for established connections.
Parsing ss Output
psys uses regular expressions to parse the ss command output:
Listener Regex
const SS_LISTEN_RE = /LISTEN \s + \d + \s + \d + \s + ( \S + ) \s + \S + (?: \s + users: \(\( " ( [ ^ " ] + ) ",pid= ( \d + ) ,fd= \d + \)\) ) ? / ;
This captures:
Local address and port
Process name
PID (process ID)
Established Connection Regex
const SS_ESTAB_RE = /ESTAB \s + \d + \s + \d + \s + ( \S + ) \s + ( \S + ) \s + users: \(\( " ( [ ^ " ] + ) ",pid= ( \d + ) ,fd= \d + \)\) / ;
This captures:
Local address and port
Remote (peer) address and port
Process name
PID
Data Structure
The collected data is structured into two main types:
Listener Type
export type Listener = {
pid : number ;
processName : string ;
serviceLabel ?: string ; // Display name (e.g., "Redis" or container name)
containerName ?: string ; // Set when Docker container
processIconType ?: string ; // Icon type: node, redis, mongo, etc.
address : string ;
addressDescription ?: string ; // Human-readable address description
port : number ;
cmd ?: string ; // Full command line
};
Connection Type
export type Connection = {
fromPid : number ;
fromProcessName : string ;
fromAddress : string ;
fromPort : number ;
toAddress : string ;
toPort : number ;
toLabel ?: string ; // Friendly name for the target
};
psys reads additional process information from the /proc filesystem:
Process Name
function getProcessName ( pid : number ) : string {
try {
const commPath = join ( "/proc" , String ( pid ), "comm" );
if ( existsSync ( commPath )) {
return readFileSync ( commPath , "utf8" ). trim ();
}
} catch {
// ignore
}
return `pid: ${ pid } ` ;
}
Command Line
function getProcessCmd ( pid : number ) : string | undefined {
try {
const path = join ( "/proc" , String ( pid ), "cmdline" );
if ( existsSync ( path )) {
const raw = readFileSync ( path , "utf8" );
return raw . replace ( / \0 / g , " " ). trim (). slice ( 0 , 80 );
}
} catch {
// ignore
}
return undefined ;
}
Reading from /proc requires appropriate permissions. psys gracefully handles permission errors.
Process Icon Detection
psys automatically detects the type of process and assigns appropriate icons:
function getProcessIconType (
processName : string ,
containerName : string | undefined ,
port : number
) : string | undefined {
const name = processName . toLowerCase ();
const container = ( containerName ?? "" ). toLowerCase ();
if ( name . includes ( "node" ) || name === "mainthread" ) return "node" ;
if ( name . includes ( "next" )) return "next" ;
if ( name . includes ( "redis" )) return "redis" ;
if ( name . includes ( "mongo" )) return "mongo" ;
if ( name . includes ( "postgres" ) || name . includes ( "psql" )) return "postgres" ;
if ( name . includes ( "mysql" ) || name . includes ( "mariadb" )) return "mysql" ;
if ( name . includes ( "apache" ) || name . includes ( "httpd" )) return "apache" ;
if ( name . includes ( "ssh" ) || name . includes ( "sshd" )) return "ssh" ;
// Check container name
if ( container . includes ( "redis" )) return "redis" ;
if ( container . includes ( "mongo" )) return "mongo" ;
if ( container . includes ( "postgres" )) return "postgres" ;
if ( container . includes ( "mysql" )) return "mysql" ;
// Fall back to port-based detection
return PORT_ICON_TYPE [ port ] ?? "generic" ;
}
Supported Icon Types
Node.js node, mainthread processes
Redis redis processes or port 6379
MongoDB mongo processes or port 27017
PostgreSQL postgres, psql processes or port 5432
MySQL mysql, mariadb processes or port 3306
Apache apache, httpd processes or port 80
SSH ssh, sshd processes or port 22
Generic All other processes
Known Service Detection
When the process name is unknown ("?"), psys falls back to port-based service detection:
function knownListenerService ( port : number ) : string | undefined {
const known : Record < number , string > = {
22 : "SSH" ,
53 : "DNS (systemd-resolved)" ,
80 : "Apache / HTTP" ,
443 : "HTTPS" ,
3000 : "Node/Express" ,
3001 : "Node/Express" ,
3002 : "psys (this app)" ,
631 : "CUPS (printing)" ,
6379 : "Redis" ,
27017 : "MongoDB" ,
5432 : "PostgreSQL" ,
3306 : "MySQL" ,
5672 : "RabbitMQ" ,
9200 : "Elasticsearch" ,
};
return known [ port ];
}
Address Interpretation
psys provides human-readable descriptions for listening addresses:
function getAddressDescription ( addr : string ) : string {
const a = addr . trim ();
if ( a === "0.0.0.0" ) return "Listening on all IPv4 interfaces" ;
if ( a === "127.0.0.1" ) return "Localhost only (IPv4)" ;
if ( a === "[::1]" || a === "::1" || a === "::" ) return "Localhost or all IPv6" ;
if ( a === "127.0.0.53" ) return "systemd-resolved (local DNS)" ;
if ( a . startsWith ( "127." ) || a . startsWith ( "10." ) ||
a . startsWith ( "192.168." ) || a . startsWith ( "172." ))
return "Local network (IPv4)" ;
if ( a . startsWith ( "[" ) && a . includes ( "]" )) return "IPv6 address" ;
return "Other interface" ;
}
Auto-Refresh Polling
The dashboard automatically refreshes data every 5 seconds:
useEffect (() => {
fetchData ();
const t = setInterval ( fetchData , 5000 );
return () => clearInterval ( t );
}, [ fetchData ]);
You can manually trigger a refresh at any time using the Refresh button in the header.