cluster
js/cluster.ts
fino:cluster - public API for cluster participation.
Cluster membership uses WebTransport over HTTP/3. This module establishes the peer view consumed by cluster services; realm allocation remains owned by the reactor scheduler.
Learn more:
- WebTransport: https://www.w3.org/TR/webtransport/ node joins the cluster in one of two roles:
startCluster({ port }) - Start a seed server on the given port AND
participate as a worker. The calling node becomes both the coordinator
and an execution target. This is the entry point for the first node.
joinCluster({ seed }) - Connect to an existing seed node and join its
membership view.
Only one cluster connection per process is supported. Calling either function when already connected throws.
Current release scope uses one trusted seed node. Seed election, cluster authentication, and distributed scheduler coordination remain future work.
import { startCluster, leaveCluster } from 'fino:cluster';
await startCluster({ port: 9999, nodeId: 'seed-a', tls: { cert: './cert.pem', key: './key.pem' } });
leaveCluster();
Interfaces
interface StartClusterOptions {
Options for starting the first cluster seed node.
import { startCluster, type StartClusterOptions } from 'fino:cluster';
const opts: StartClusterOptions = { port: 9999, nodeId: 'seed-a' };
await startCluster(opts);
Properties
port: number
TCP/UDP port the seed HTTP/3 WebTransport server will listen on.
The port must be available on the local host. There is no default because the first cluster node must advertise a stable address to workers.
import { startCluster } from 'fino:cluster';
await startCluster({ port: 9999, tls: { cert: './cert.pem', key: './key.pem' } });
hostname?: string
Interface address the seed server binds.
Defaults to all interfaces (0.0.0.0, or :: when an IPv6 hostname
selects the IPv6 family). The seed also joins itself as a worker; for
that self-connection wildcard binds (0.0.0.0, ::) are mapped to the
matching loopback address, and bare IPv6 literals are bracketed.
nodeId?: string
Optional node identifier.
When omitted, the seed uses seed-{port}. Choose a stable ID if logs or
cluster diagnostics need to correlate restarts.
import { startCluster } from 'fino:cluster';
await startCluster({ port: 9999, nodeId: 'primary-seed' });
tls: {
cert: string;
key: string;
}
TLS certificate and key files for the seed's HTTPS/HTTP/3 server.
cert and key are filesystem paths to PEM files, not inline PEM text.
TLS is required — WebTransport rides on HTTP/3 over QUIC, so there is no
plaintext mode. For certificates not signed by a system-trusted CA,
joining workers should trust the issuing CA via tls.ca or pin the
certificate via serverCertificateHashes.
import { startCluster } from 'fino:cluster';
await startCluster({ port: 9999, tls: { cert: './cert.pem', key: './key.pem' } });
path?: string
URL path of the cluster WebTransport endpoint on the seed server.
Defaults to /__fino_cluster. A missing leading slash is added. Workers
must connect to this exact path; WebTransport sessions requested on any
other path are rejected with a 404, and plain HTTP requests receive a
placeholder response.
h3?: true | {
quic?: Record<string, unknown>;
}
HTTP/3 listener configuration forwarded to the underlying server.
HTTP/3 is always enabled — the cluster transport requires it — so the
only useful form is an object whose quic field tunes the QUIC
transport (timeouts, flow control, and similar low-level knobs).
Omitting this or passing true uses the defaults.
interface JoinClusterOptions {
Options for joining an existing cluster seed.
import { joinCluster, type JoinClusterOptions } from 'fino:cluster';
const opts: JoinClusterOptions = { seed: 'https://127.0.0.1:9999/__fino_cluster' };
await joinCluster(opts);
Properties
seed: string | URL
HTTPS WebTransport URL of the seed node.
The URL must include the https:// scheme and a reachable host and port.
When the path is omitted, /__fino_cluster is used.
import { joinCluster } from 'fino:cluster';
await joinCluster({ seed: 'https://seed.example.test:9999/__fino_cluster' });
nodeId?: string
Optional worker node identifier.
When omitted, a short random worker-* ID is generated. Provide a stable
value for predictable logs or cluster placement diagnostics.
import { joinCluster } from 'fino:cluster';
await joinCluster({ seed: 'https://127.0.0.1:9999/__fino_cluster', nodeId: 'worker-a' });
tls?: {
ca?: string;
rejectUnauthorized?: boolean;
}
TLS verification settings for the connection to the seed.
ca is a filesystem path to a PEM CA bundle trusted for the seed's
certificate — use it when the seed's certificate is not signed by a
system-trusted CA. rejectUnauthorized: false skips verification
entirely; avoid it outside local development, since it permits
man-in-the-middle attacks — prefer ca or serverCertificateHashes.
import { joinCluster } from 'fino:cluster';
await joinCluster({
seed: 'https://seed.example.test:9999/__fino_cluster',
tls: { ca: './cluster-ca.pem' },
});
quic?: Record<string, unknown>
QUIC transport tuning forwarded to the WebTransport session.
Low-level knobs (timeouts, flow control, and similar) applied to the QUIC connection under the WebTransport session. Most deployments should omit this and use the defaults.
serverCertificateHashes?: readonly WebTransportHash[]
Certificate pinning hashes for the seed's certificate.
Follows the WebTransport serverCertificateHashes model: the connection
is accepted when the seed's certificate matches one of the given hashes,
bypassing CA-based validation. Useful for self-signed deployments where
distributing a CA bundle is impractical.
import { joinCluster } from 'fino:cluster';
const certSha256 = new Uint8Array(32); // SHA-256 digest of the seed certificate
await joinCluster({
seed: 'https://127.0.0.1:9999/__fino_cluster',
serverCertificateHashes: [{ algorithm: 'sha-256', value: certSha256 }],
});
Functions
async function startCluster(opts: StartClusterOptions): Promise<void>
Start the cluster seed server and participate as a worker on this node.
The seed is the current membership hub. Realm placement is not performed by this API; the scheduler will consume membership when distributed allocation is added.
This call returns immediately after the seed starts listening. The event loop keeps the server alive as long as there are connected peers.
Throws if this process is already connected to a cluster. The function
resolves with void after the seed transport is listening and the local
worker client has connected to it.
import { startCluster, leaveCluster } from 'fino:cluster';
await startCluster({ port: 9999, nodeId: 'seed-a' });
leaveCluster();
async function joinCluster(opts: JoinClusterOptions): Promise<void>
Connect to an existing seed and register this node as a worker.
After this call the node receives membership updates from the seed.
Throws if this process is already connected or if the seed URL cannot be
reached, and throws a TypeError when the seed URL does not use the
https: scheme. The returned promise resolves once the worker transport
is connected and the client loop has started.
import { joinCluster, leaveCluster } from 'fino:cluster';
await joinCluster({ seed: 'https://127.0.0.1:9999/__fino_cluster', nodeId: 'worker-a' });
leaveCluster();
function leaveCluster(): void
Disconnect from the cluster.
The call is synchronous and idempotent. It stops the active worker client and seed server, if present, then clears module-level cluster state.
import { startCluster, leaveCluster } from 'fino:cluster';
await startCluster({ port: 9999 });
leaveCluster();