Skip to content

API

The Server class handles incoming HTTP requests and manages WebSocket connections to agents.

new Server(defaultHandler, options)
  • defaultHandler — a function that returns a Response when no agent is available to handle the request.
  • options:
    • strategy (optional) — agent selection strategy. Default "first". One of "first", "last", "random", "round-robin", "last-used" (the agent that most recently completed a request/response cycle), or "most-recent" (the agent that most recently connected).
    • secret — a string used for authentication between the server and agents. Required unless allowUnauthenticatedAgents is set — the constructor throws otherwise, since without a secret the server has nothing to validate an agent handshake against and would accept any agent unverified.
    • allowUnauthenticatedAgents (optional) — set to true to explicitly opt out of the secret requirement and accept any agent handshake with no verification. Default false.
    • log (optional) — an integer log level (0–4). Default 0.
  • addConnection(connection) — registers a WebSocket-like connection (anything implementing send/addEventListener/close) as a candidate agent connection and starts consuming its messages. Returns a Promise resolving to the connection.
  • removeConnection(connection) — unregisters a connection. Any request still in flight against it is rejected instead of hanging forever.
  • removeConnectionById(id) / removeConnectionByIndex(index)
  • getConnectionById(id) / getConnectionByIndex(index) — return undefined if not found.
  • fetch(request) — handles an incoming HTTP request, returns a Promise resolving to a Response.
  • setStrategy(newStrategy) — throws on an unrecognized value.
  • listen(port) — Node only. Starts an HTTP server and a WebSocket server on port: HTTP requests are handed to fetch() and WebSocket connections are registered with addConnection() (and removed with removeConnection() when they close). Returns a Promise that resolves once the port is bound; throws if the server is already listening. The Node req/res to Request/Response conversion is @johnhenry/webwire’s toWebRequest()/writeWebResponse(), with X-Forwarded-Host taking priority over Host for the URL the agent sees. Repeated response headers (for example several Set-Cookie) are written as separate header lines. A request that cannot be turned into a Request, or a handler error, gets a real error response instead of a hung socket.
  • close() — closes the WebSocket and HTTP servers started by listen(); throws if not listening.
  • listening — true while listen()’s servers are running.
  • strategy — gets or sets the current agent selection strategy (setting validates the same way as setStrategy).
  • fetch — a bound version of the fetch method, usable directly with HTTP server libraries.

The secret option is a single shared secret compared (in constant time) against whatever every agent sends in its handshake — there’s no per-agent identity, rotation, or session/token model. That’s an intentional simplification for now, not an oversight; if you need per-agent credentials or anything more sophisticated, put this behind your own auth layer (a reverse proxy or VPN in front of the WebSocket port) rather than expecting dialback to provide it.

secret is required precisely because omitting it isn’t a safe default — a Server with no secret configured would accept a handshake from any agent with no verification at all. If that’s genuinely what you want (local development, or a deployment secured entirely at the network layer), pass allowUnauthenticatedAgents: true explicitly so it’s visible in the code that authentication was deliberately skipped, not merely forgotten.

If a shared secret genuinely isn’t enough — you need to tell agents apart, not just confirm they’re some trusted agent — see the dialback/browsermesh transport, which swaps this model for real per-agent Ed25519 identity.

The package ships TypeScript declarations for Server, Agent, and their options (Server’s defaultHandler and Agent’s abort option may return a Response or a Promise<Response>). The dialback/browsermesh subpath ships none.

The Agent class connects to a Server and handles proxied requests.

new Agent(address, options)
  • address — a string WebSocket address of the server to connect to (or whatever address shape a custom transport expects).
  • options:
    • reconnect (optional) — milliseconds to wait before reconnecting if the connection is lost.
    • log (optional) — integer log level (0–4).
    • abort (optional) — a function returning a Response when a request is aborted.
    • secret (optional) — authentication string, matched against the server’s own secret.
    • transport (optional) — (address) => Promise<Connection>. When provided, used instead of new WebSocket(address) to establish the connection — see the dialback/browsermesh transport.
  • serve(handler) — sets the request handler function. handler accepts a Request and returns a Promise resolving to a Response.

  • close() — closes the connection to the server.

  • connection — a Promise resolving to the established connection; await it to know the handshake has finished.
  • serve — a bound version of the serve method.

Creates a WebSocket connection from an HTTP request. Primarily used in Deno environments.

import { Server } from "npm:@johnhenry/dialback";
const server = new Server(() => new Response("no responder", { status: 500 }));
Deno.serve({ port: 8082 }, (req) => {
if (req.headers.get("upgrade") !== "websocket") {
return server.fetch(req);
}
const { socket: connection, response } = Deno.upgradeWebSocket(req);
server.addConnection(connection);
connection.addEventListener("close", () => server.removeConnection(connection));
return response;
});

The Agent side is unchanged from Node.