Release candidate: 0.5.6, fixing config/app connections delayed by stale device tickets.
When a device lookup is still dialing a relay after 250 ms, bind opening tries ready relays and then the existing bounded seed fallback. The lookup continues within its original deadline and remains available for destinations reachable only through a cold, non-seed relay. Cancellation prevents late tunnel opens. This applies to both API and Native transport without changing application data or authorization. A live config read that previously took 21.7 seconds completed in 2.2–3.0 seconds with the original seven-second Service deadline.
First-start identity creation supports Android app storage, where SELinux can
deny hard links. The fallback uses a private directory lock and atomic rename
of the complete, flushed key file. Concurrent starters reuse the winning identity;
existing or corrupt identities are never replaced. A stale creation lock fails
closed after five seconds. If startup crashed before creating keys.json, stop
all writers and remove only the empty keys.json.lock directory before retrying.
Never delete an existing identity to resolve a connection error.
If all connected routes return not found, tunnel opening tries at most three
additional configured seed relays. Each trial stays available until the caller
registers its tunnel; idle RTT pruning can then resume. Existing connection and
RPC deadlines bound each attempt. This recovers stale destination tickets on
clients with a small warm-relay budget. Transport selection and publisher access
checks are preserved; application data is never replayed.
Native TCP relay selection now checks the allocated data socket before accepting a relay. An unreachable socket closes that allocation and releases its lease, then tries the other available relays while the application socket stays paused. Cancellation closes late allocations. There is no API downgrade or replay of application data; failures after authentication still end the stream.
On 2026-09-09, the live disposable-peer benchmark through eu1.prenet.diode.io
passed Native TCP twice (1 MiB upload-plus-echo, 60.32 and 73.56 Mbps, not one-way
capacity). The OrendaService application test still failed on its automatically
selected route: allocated data-port timeouts and a handshake timeout. A successful
control connection or local relay test does not qualify a public Native route.
This branch is unreleased; installed diodejs 0.5.4 does not contain this retry fix.
Connected relays rank by measured RTT, with recently failed probes demoted. An older RTT sample triggers a background refresh without automatically losing to a slower, newer sample. Slow destination tickets are reconciled even when their relay already has a warm connection. Existing tunnels stay on their relay.
Run node scripts/benchmark-network.js --help for live seed discovery, handshake
timings, and API TCP/TLS/UDP and Native TCP/UDP echo measurements. The benchmark
uses disposable identities and a temporary echo service restricted to its own
client. --routing reproduces score aging while measuring the actual selected
data path. See the September performance report
for results, historical comparisons, limitations, and reproduction commands.
Native transport (transport: "native") uses portopen2 and the existing
authenticated, encrypted native wire protocol. It runs with Node's built-in
TCP/UDP/streams/crypto APIs and the existing cryptography packages' JavaScript
fallbacks; the transport choice does not require a native addon.
Native TCP now uses bounded stream backpressure in both directions, preserves half-close responses and final buffered bytes, and flushes its TLS handshake before releasing the handshake channel. Native opens can retry another relay without switching transport or replaying application data. TCP frame processing avoids repeated copies of fragmented ciphertext. Existing identity signatures, publish whitelists, authenticated encryption, and UDP replay checks remain.
Native supports TCP and UDP. API remains the default and handles the library's TLS protocol. Select the transport explicitly; application protocols such as RDP and SQL are carried inside the selected stream.
Run node scripts/benchmark-native-framing.js for the controlled receive-codec
benchmark. It compares copying/decryption with 0.5.2 and does not measure public
network performance. Native integration tests run with addons disabled using
node --require ./test/fixtures/no-native-addons.cjs --test test/nativeTcp.integration.test.js.
API TCP and TLS streams use an ordered send window capped at 256 KiB and 16 unacknowledged frames per direction. This avoids waiting a full relay round trip for every write while preserving Node stream backpressure. Frames remain below the 65,535-byte wire limit; failed stream data is never replayed. Graceful EOF drains queued data on both endpoints before closing the remote ref. Errors, relay disconnects, access revocation, and explicit disposal still tear down the session immediately.
Slow local readers apply receive pressure at 256 KiB of queued data and release it below 128 KiB, keeping the existing 1 MiB queue limit. API tunnels multiplex one relay TCP socket, so a paused reader can briefly delay other tunnels on that relay. Every blocked consumer must drain or close before reads resume. A reader making no queue progress for the I/O timeout (10 seconds by default) is closed; progressing readers refresh that deadline. Disconnects discard the old relay's queued frames and cannot resume a replacement socket.
The transport uses JavaScript with Node's built-in streams, TCP, and TLS. No new
native addon or portopen2 transport is required. Run
node scripts/benchmark-api-tls.js for a local real-TLS comparison with simulated
40/80 ms relay acknowledgment latency. Its results are not public-network speed
measurements. node --test test/apiTls.integration.test.js checks client-first
binary traffic, backpressure, EOF in both directions, and asynchronous failures
for API TCP and TLS.
To verify the existing crypto dependencies' JavaScript fallbacks, run
node --require ./test/fixtures/no-native-addons.cjs --test test/apiTls.integration.test.js.
This blocks loading .node addons while exercising the same real TCP/TLS tests.
diodejs is a JavaScript client for interacting with the Diode network. It provides functionalities to bind and publish ports, send RPC commands, and handle responses.
npm install diodejsWarnings and errors are always written to stderr. Set LOG=true to also enable
bounded persistent logging in logs/diodejs.log; the default retention is five
files of at most 2 MiB each. Use DIODE_LOG_DIRECTORY,
DIODE_LOG_MAX_BYTES, and DIODE_LOG_MAX_FILES to reduce those limits or
change the directory. Set DEBUG=true together with LOG=true to enable debug
output.
Can also use .env files
Connection retry behavior can be configured via environment variables:
| Environment Variable | Description | Default |
|---|---|---|
| DIODE_MAX_RETRIES | Maximum number of reconnection attempts | Infinity |
| DIODE_RETRY_DELAY | Initial delay between retries (ms) | 1000 |
| DIODE_MAX_RETRY_DELAY | Maximum delay between retries (ms) | 30000 |
| DIODE_AUTO_RECONNECT | Whether to automatically reconnect | true |
| DIODE_TICKET_BYTES_THRESHOLD | Bytes threshold for ticket updates | 4194304 (4 MiB) |
| DIODE_TICKET_UPDATE_INTERVAL | Time interval for ticket updates (ms) | 30000 (30s) |
Example .env file:
DIODE_MAX_RETRIES=10
DIODE_RETRY_DELAY=2000
DIODE_MAX_RETRY_DELAY=20000
DIODE_AUTO_RECONNECT=true
DIODE_TICKET_BYTES_THRESHOLD=512000
DIODE_TICKET_UPDATE_INTERVAL=30000
These settings can also be configured programmatically on the relay connections managed by DiodeClientManager:
const { DiodeClientManager } = require('diodejs');
async function main() {
const client = new DiodeClientManager({ keyLocation: './db/keys.json' });
await client.connect();
for (const connection of client.getConnections()) {
connection.setReconnectOptions({
maxRetries: 10,
retryDelay: 2000,
maxRetryDelay: 20000,
autoReconnect: true
});
connection.setTicketBatchingOptions({
threshold: 512000,
interval: 30000
});
}
}
main();Fleet contract selection is configured on the manager:
const { DiodeClientManager } = require('diodejs');
async function main() {
const client = new DiodeClientManager({
keyLocation: './db/keys.json',
fleetContract: '0x1111111111111111111111111111111111111111',
});
await client.connect();
// Applies on the next ticket generated by each active relay connection.
client.setFleetContract('0x2222222222222222222222222222222222222222');
}
main();If fleetContract is omitted, tickets continue using the default contract 0x6000000000000000000000000000000000000000.
You can connect to multiple Diode relays and automatically route binds to the relay where the target device is connected.
DiodeClientManager ranks relays by observed ping latency, excluding DNS, TLS, and ticket setup time. connect() resolves as soon as one relay completes its authenticated handshake. Remaining candidate measurements continue in the background, persist relay scores to disk, and improve the preferred relay for control-plane RPC calls. Idle pruning waits for startup coverage and preserves active tunnels and pending tunnel opens.
When neither host nor hosts is specified, the manager starts from the default seed pool, any discovery-provider candidates, built-in dio_network candidates, and previously successful non-provider relays saved in relay-scores.json. Without live network discovery it probes all default seeds once. When live network discovery returns usable relays, coverage uses a region-diverse seed bootstrap subset plus the bounded discovery sample, then continues measuring the remaining seeds while keeping region diversity in the warm set. If you pass host or hosts, startup stays constrained to those configured relays unless you explicitly opt into using the discovery provider alongside them.
const { DiodeClientManager, BindPort } = require('diodejs');
async function main() {
// Connect to default relay pool (pre-net defaults)
const client = new DiodeClientManager({ keyLocation: './db/keys.json' });
await client.connect();
const bind = new BindPort(client, {
3003: { targetPort: 8080, deviceIdHex: '0x...', protocol: 'tcp' }
});
bind.bind();
}You can tune relay selection if needed:
const client = new DiodeClientManager({
keyLocation: './db/keys.json',
relaySelection: {
startupConcurrency: 3,
minReadyConnections: 2,
probeTimeoutMs: 1200,
connectionTimeoutMs: 5000,
targetConnectTimeoutMs: 10000,
warmConnectionBudget: 3,
probeAllInitialCandidates: true,
continueProbingUntestedSeeds: true,
regionDiverseSeedOrdering: true,
discoveryProviderTimeoutMs: 1500,
backgroundProbeIntervalMs: 300000,
slowRelayThresholdMs: 250,
slowDeviceRetryTtlMs: 5000,
scoreCachePath: './db/relay-scores.json'
}
});Routing note: the initiating client can prefer a better local relay, but it cannot override the relay encoded in the remote device ticket. Best results come when both clients use the manager's relay ranking so each side reconnects toward a closer relay over time.
Discovery note: startup discovery sources are now configured/seed, discoveryProvider, built-in live dio_network discovery, cached non-provider/non-network relays, and target-on-demand relay resolution from getNode(serverId). Provider and network membership are not cached independently; only RTT/history is persisted in relay-scores.json.
Example discovery provider:
const fs = require('fs/promises');
const client = new DiodeClientManager({
keyLocation: './db/keys.json',
relaySelection: {
discoveryProvider: async () => {
const data = JSON.parse(await fs.readFile('./relays.json', 'utf8'));
return data;
},
networkDiscovery: {
endpoint: 'wss://prenet.diode.io:8443/ws',
startupProbeCount: 2,
backgroundBatchSize: 12
}
}
});discoveryProvider(context) may return either relay strings such as 'relay.example.com:41046' or objects like { host, port, priority, region, metadata }. The callback receives a read-only context object with:
defaultPortkeyLocationexplicitHostexplicitHostsinitialHostsknownRelayScores
knownRelayScores contains the manager's current score snapshot for previously seen relays. Provider membership itself is not cached across runs; only relay score history is persisted.
Built-in network discovery uses the Diode JSON-RPC websocket endpoint and requests dio_network. It is enabled by default only when neither host nor hosts is set. Only connected server nodes are considered, and private/unroutable addresses are filtered unless includePrivateAddresses is enabled. When discovery succeeds, startup keeps the seed safety baseline by probing one seed per region before adding the configured discovery sample, instead of blocking on every seed before ready.
If you provide a host, only that relay is used initially (similar to -diodeaddrs):
const client = new DiodeClientManager({
host: 'us2.prenet.diode.io',
port: 41046,
keyLocation: './db/keys.json'
});
await client.connect();Here's a quick example to get you started with RPC functions using DiodeRPC Class
const { DiodeClientManager, DiodeRPC, makeReadable } = require('diodejs');
async function main() {
const host = 'eu2.prenet.diode.io';
const port = 41046;
const keyLocation = './db/keys.json'; // Optional, defaults to './db/keys.json'
const client = new DiodeClientManager({ host, port, keyLocation });
await client.connect();
const connection = client.getConnections()[0];
if (!connection) {
throw new Error('No relay connection available');
}
// Configure reconnection (optional - overrides environment variables)
connection.setReconnectOptions({
maxRetries: Infinity, // Unlimited reconnection attempts
retryDelay: 1000, // Initial delay of 1 second
maxRetryDelay: 30000, // Maximum delay of 30 seconds
autoReconnect: true // Automatically reconnect on disconnection
});
// Configure ticket batching (optional - overrides environment variables)
connection.setTicketBatchingOptions({
threshold: 512000, // Bytes threshold for ticket updates
interval: 30000 // Time interval for ticket updates
});
// Listen for reconnection events (optional)
connection.on('reconnecting', (info) => {
console.log(`Reconnecting... Attempt #${info.attempt} in ${info.delay}ms`);
});
connection.on('reconnected', () => {
console.log('Successfully reconnected!');
});
connection.on('reconnect_failed', () => {
console.log('Failed to reconnect after maximum attempts');
});
const rpc = new DiodeRPC(connection);
try {
const ping = await rpc.ping();
console.log('Ping:', ping);
const blockPeak = await rpc.getBlockPeak();
console.log('Current Block Peak:', blockPeak);
const blockHeader = await rpc.getBlockHeader(blockPeak);
console.log('Block Header:', makeReadable(blockHeader));
} catch (error) {
console.error('RPC Error:', error);
} finally {
client.close();
}
}
main();Here's a quick example to get you started with port forwarding using the BindPort class.
const { DiodeClientManager, BindPort } = require('diodejs');
async function main() {
const host = 'eu2.prenet.diode.io';
const port = 41046;
const keyLocation = './db/keys.json';
const client = new DiodeClientManager({ host, port, keyLocation });
await client.connect();
// Multiple or single port binding with configuration object
const portsConfig = {
3002: {
targetPort: 80,
deviceIdHex: "5365baf29cb7ab58de588dfc448913cb609283e2",
protocol: "tls" // Optional - defaults to TLS if not specified
},
3003: {
targetPort: 443,
deviceIdHex: "0x5365baf29cb7ab58de588dfc448913cb609283e2",
protocol: "tcp", // Can be "tls", "tcp", or "udp"
transport: "native" // Optional - "api" (default) or "native" for portopen2 (tcp/udp only)
}
};
const portForward = new BindPort(client, portsConfig);
portForward.bind();
// You can also dynamically add ports with protocol specification
portForward.addPort(3004, 8080, "5365baf29cb7ab58de588dfc448913cb609283e2", "udp");
portForward.removePort(3003);
}
main();const { DiodeClientManager, BindPort } = require('diodejs');
async function main() {
const host = 'eu2.prenet.diode.io';
const port = 41046;
const keyLocation = './db/keys.json';
const client = new DiodeClientManager({ host, port, keyLocation });
await client.connect();
// Legacy method - single port binding (defaults to TLS protocol)
const portForward = new BindPort(client, 3002, 80, "5365baf29cb7ab58de588dfc448913cb609283e2");
portForward.bind();
}
main();Here's a quick example to get you started with publishing ports using the PublishPort class:
const { DiodeClientManager, PublishPort } = require('diodejs');
async function main() {
const host = 'us2.prenet.diode.io';
const port = 41046;
const keyLocation = './db/keys.json';
const client = new DiodeClientManager({ host, port, keyLocation });
await client.connect();
// Option 1: Simple array of ports (all public)
const publishedPorts = [8080, 3000];
// Option 2: Object with port configurations for public/private access control
const publishedPortsWithConfig = {
8080: { mode: 'public' }, // Public port on 127.0.0.1, accessible by any device
8081: { mode: 'public', host: '192.168.1.10' }, // Forward to another reachable host
3000: {
mode: 'private',
host: 'backend.internal',
whitelist: [
'0x1234567890abcdef1234567890abcdef12345678',
'0x9876543210fedcba9876543210fedcba98765432'
] // Only these 20-byte EVM device addresses can connect
}
};
// certPath parameter is maintained for backward compatibility but not required
const publishPort = new PublishPort(client, publishedPortsWithConfig);
// When permanently stopping publication, release sessions and listeners.
// clearPorts() only changes the live port set; close() is terminal.
// publishPort.close();
}
main();-
Constructor:
new DiodeClientManager(options)options.host(string, optional): Single relay host (with or without port). If provided, only this relay is used initially.options.port(number, optional): Port to use whenoptions.hosthas no port. Defaults to41046.options.hosts(string[] or comma-separated string, optional): Explicit relay list.options.keyLocation(string, optional): Key storage path (default:./db/keys.json).options.fleetContract(string, optional): Fleet contract used for relay tickets. Must be a 20-byte EVM address hex string. Defaults to0x6000000000000000000000000000000000000000.options.deviceCacheTtlMs(number, optional): Cache TTL for device relay resolution (default:30000).options.relaySelection(object, optional): Relay ranking and probing options.enabled(boolean, optional): Enables smart relay ranking. Defaults totrue.startupConcurrency(number, optional): Parallel startup probe limit. Defaults to3, allowing one seed from each default region to begin together.minReadyConnections(number, optional): Minimum target connection count for the startup probe set. Defaults to2.probeTimeoutMs(number, optional): Ping timeout for relay probes. Defaults to1200.connectionTimeoutMs(number, optional): DNS/TLS/ticket setup timeout for startup and background relay probes. Defaults to5000.targetConnectTimeoutMs(number, optional): Setup timeout for a relay resolved from a target device ticket. Defaults to10000.warmConnectionBudget(number, optional): Maximum number of idle control relays to keep after startup coverage completes. Defaults to3.probeAllInitialCandidates(boolean, optional): Probes all initial configured/default candidates once before final startup ranking is trusted. Defaults totrue.continueProbingUntestedSeeds(boolean, optional): Continues probing untested candidates after startup coverage completes. Defaults totrue.regionDiverseSeedOrdering(boolean, optional): Interleaves seed regions during first-pass probing and warm retention. Defaults totrue.discoveryProvider(function, optional): Async or sync callback that returns extra relay candidates as strings or{ host, port, priority, region, metadata }objects. The callback receives{ defaultPort, keyLocation, explicitHost, explicitHosts, initialHosts, knownRelayScores }.discoveryProviderTimeoutMs(number, optional): Timeout fordiscoveryProviderresults. Defaults to1500.useProviderWithExplicitHost(boolean, optional): Allows provider candidates whenoptions.hostis set. Defaults tofalse.useProviderWithExplicitHosts(boolean, optional): Allows provider candidates whenoptions.hostsis set. Defaults tofalse.networkDiscovery(object, optional): Built-in live directory discovery via the JSON-RPC websocket endpoint.enabled(boolean, optional): Enables live network discovery when no explicithostorhostsis configured. Defaults totruein default mode.endpoint(string, optional): Directory endpoint. Defaults towss://prenet.diode.io:8443/ws.method(string, optional): JSON-RPC method name. Defaults todio_network.timeoutMs(number, optional): Timeout for the discovery websocket request. Defaults to1500.startupProbeCount(number, optional): Number of discovered network relays added to the startup coverage probe set. Defaults to2.backgroundBatchSize(number, optional): Number of additional discovered relays measured in the background queue per run. Defaults to12.includePrivateAddresses(boolean, optional): Allows private or unroutable discovery results to be used. Defaults tofalse.
backgroundProbeIntervalMs(number, optional): Minimum interval before background refresh probes are retried for a relay. Defaults to300000.slowRelayThresholdMs(number, optional): RTT threshold used to shorten target relay cache entries. Defaults to250.slowDeviceRetryTtlMs(number, optional): TTL used for slow target relays. Defaults to5000.deviceRelayReconciliation(object, optional): Bounded fallback that re-resolves a device through alternate connected control relays when the initially resolved target relay is much slower than the current control relay baseline.enabled(boolean, optional): Enables reconciliation. Defaults totrue.maxControlRelays(number, optional): Maximum number of alternate connected control relays queried per reconciliation attempt. Defaults to2.timeoutMs(number, optional): Per-control-relay timeout for reconciliation lookups. Defaults toprobeTimeoutMs.minLatencyDeltaMs(number, optional): Minimum RTT gap between the target relay and the control relay baseline before reconciliation triggers. Defaults to150.slowdownFactor(number, optional): Minimum multiple by which the target relay RTT must exceed the control relay baseline before reconciliation triggers. Defaults to4.
scoreCachePath(string|null, optional): Relay score cache file. Defaults to./db/relay-scores.jsonnext tokeyLocation. Set tonullto disable persistence.
-
Methods:
connect(): Returns a promise that resolves with the manager when the first relay completes its TLS and ticket handshake. Remaining seed/provider/network measurements continue in the background; idle pruning waits for the required coverage to finish. Failed latency measurements do not discard an otherwise authenticated relay. Rejects if no relay can connect.setFleetContract(address): Updates the fleet contract used for future ticket generation on managed connections. Accepts a 20-byte EVM address hex string and returns the manager instance.getNearestConnection(): Returns the preferred connected relay. With relay selection enabled, this is the lowest-latency scored relay.getConnectionForDevice(deviceId): Resolves and returns a relay connection for the device. Returns a promise. Concurrent requests for the same device share the ticket/node lookup and relay handshake; each bind still opens its own tunnel.getConnections(): Returns a list of active connections.close(): Closes all managed connections.
Connections returned by getConnections() or getConnectionForDevice() emit reconnecting, reconnected, and reconnect_failed events, and support setReconnectOptions(...) and setTicketBatchingOptions({ threshold, interval }).
-
Constructor:
new DiodeRPC(connection)connection(object): A relay connection fromDiodeClientManager.getConnections()orDiodeClientManager.getConnectionForDevice().
-
Methods:
getBlockPeak(): Retrieves the current block peak. Returns a promise.getBlockHeader(index): Retrieves the block header for a given index. Returns a promise.getBlock(index): Retrieves the block for a given index. Returns a promise.getObject(deviceId, options): Retrieves a device ticket object. Returns a promise.getNode(nodeId, options): Retrieves relay node information. Returns a promise.ping(options): Sends a ping command. Returns a promise.portOpen(deviceId, port, flags, options): Opens a port on the device. Returns a promise.portOpen2(deviceId, port, flags, options): Opens a native relay port on the device (TCP/UDP). Returns the server relay port.portSend(ref, data, options): Sends data to the device. Returns a promise.portClose(ref, options): Closes an API-relayed port. Returns a promise.portClose2(physicalPort, options): Closes a native relay port. Returns a promise.sendError(sessionId, ref, error): Sends an error response. Returns a promise.sendResponse(sessionId, ref, response): Sends a response. Returns a promise.getEpoch(): Retrieves the current epoch. Returns a promise.parseTimestamp(blockHeader): Parses the timestamp from a block header. Returns a number.
For the RPC methods that accept options, pass { timeoutMs, signal } to set a per-call deadline or an AbortSignal. Commands use a 15-second default deadline and reject with typed errors such as DIODE_COMMAND_TIMEOUT, DIODE_COMMAND_ABORTED, DIODE_DISCONNECTED, or DIODE_RPC_ERROR. Port open/close failures reject instead of resolving as an undefined success; callers should handle those promise rejections.
-
Constructors:
Legacy Constructor:
new BindPort(connection, localPort, targetPort, deviceIdHex)connection(DiodeClientManager): An instance ofDiodeClientManager.localPort(number): The local port to bind.targetPort(number): The target port on the device.deviceIdHex(string): The device ID in hexadecimal format (with or without '0x' prefix).
New Constructor:
new BindPort(connection, portsConfig)connection(DiodeClientManager): An instance ofDiodeClientManager.portsConfig(object): A configuration object where keys are local ports and values are objects with:targetPort(number): The target port on the device.deviceIdHex(string): The device ID in hexadecimal format (with or without '0x' prefix).protocol(string, optional): The protocol to use ("tls", "tcp", or "udp"). Defaults to "tls".transport(string, optional): The relay transport to use ("api" or "native"). Defaults to "api". Native usesportopen2and supports TCP/UDP only.
-
Methods:
bind(): Binds all configured local ports to their target ports on the devices.addPort(localPort, targetPort, deviceIdHex, protocol, transport): Adds a new port binding configuration.protocol(string, optional): The protocol to use. Can be "tls", "tcp", or "udp". Defaults to "tls".transport(string, optional): The relay transport to use ("api" or "native"). Defaults to "api".
removePort(localPort): Removes a port binding configuration.bindSinglePort(localPort): Binds a single local port to its target.closeAllServers(): Closes all active server instances.dispose(): Permanently closes servers, active tunnels, and shared manager listeners. Use this when replacing or discarding aBindPortinstance.
-
Constructor:
new PublishPort(connection, publishedPorts, _certPath)connection(DiodeClientManager): An instance ofDiodeClientManager.publishedPorts(array|object): Either:- An array of ports to publish (all public mode)
- An object mapping ports to their configuration:
{ port: { mode: 'public'|'private', whitelist: ['0x123...'], host: '127.0.0.1' } }
_certPath(string): Has no functionality and maintained for backward compatibility.
-
Methods:
addPort(port, config): Adds a new port to publish. Config is optional and defaults to public mode.port(number): The port number to publish.config(object): Optional configuration withmode('public'|'private'),whitelistarray, andhoststring.host(string, optional): Target IP or hostname for the published service. Defaults to127.0.0.1.- Private whitelists accept complete 20-byte
0xEVM addresses; address matching is case-insensitive and stored in lowercase.
removePort(port): Removes a published port.port(number): The port number to remove.
addPorts(ports): Adds multiple ports at once (equivalent to the constructor's publishedPorts parameter).ports(array|object): Either an array of port numbers or an object mapping ports to their configurations.
getPublishedPorts(): Returns a plain object with all published ports and their configurations.clearPorts(): Removes all published ports. Returns the number of ports that were cleared.startListening(): Starts listening for unsolicited messages.stopListening(): Pauses unsolicited-message handling without closing active sessions.close(): Permanently removes listeners and closes all API/native sessions. A closed instance cannot be restarted; create a newPublishPortinstead.handlePortOpen(sessionIdRaw, messageContent): Handles port open requests.handlePortSend(sessionIdRaw, messageContent): Handles port send requests.handlePortClose(sessionIdRaw, messageContent): Handles port close requests.