API
Server
Section titled “Server”The Server class handles incoming HTTP requests and manages WebSocket
connections to agents.
Constructor
Section titled “Constructor”new Server(defaultHandler, options)defaultHandler— a function that returns aResponsewhen 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 unlessallowUnauthenticatedAgentsis 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 totrueto explicitly opt out of thesecretrequirement and accept any agent handshake with no verification. Defaultfalse.log(optional) — an integer log level (0–4). Default0.
Methods
Section titled “Methods”addConnection(connection)— registers a WebSocket-like connection (anything implementingsend/addEventListener/close) as a candidate agent connection and starts consuming its messages. Returns aPromiseresolving 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)— returnundefinedif not found.fetch(request)— handles an incoming HTTP request, returns aPromiseresolving to aResponse.setStrategy(newStrategy)— throws on an unrecognized value.listen(port)— Node only. Starts an HTTP server and a WebSocket server onport: HTTP requests are handed tofetch()and WebSocket connections are registered withaddConnection()(and removed withremoveConnection()when they close). Returns aPromisethat resolves once the port is bound; throws if the server is already listening. The Nodereq/restoRequest/Responseconversion is@johnhenry/webwire’stoWebRequest()/writeWebResponse(), withX-Forwarded-Hosttaking priority overHostfor the URL the agent sees. Repeated response headers (for example severalSet-Cookie) are written as separate header lines. A request that cannot be turned into aRequest, or a handler error, gets a real error response instead of a hung socket.close()— closes the WebSocket and HTTP servers started bylisten(); throws if not listening.
Properties
Section titled “Properties”listening—truewhilelisten()’s servers are running.strategy— gets or sets the current agent selection strategy (setting validates the same way assetStrategy).fetch— a bound version of thefetchmethod, usable directly with HTTP server libraries.
A note on authentication
Section titled “A note on authentication”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.
Constructor
Section titled “Constructor”new Agent(address, options)address— a string WebSocket address of the server to connect to (or whatever address shape a customtransportexpects).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 aResponsewhen a request is aborted.secret(optional) — authentication string, matched against the server’s ownsecret.transport(optional) —(address) => Promise<Connection>. When provided, used instead ofnew WebSocket(address)to establish the connection — see thedialback/browsermeshtransport.
Methods
Section titled “Methods”-
serve(handler)— sets the request handler function.handleraccepts aRequestand returns aPromiseresolving to aResponse. -
close()— closes the connection to the server.
Properties
Section titled “Properties”connection— aPromiseresolving to the established connection; await it to know the handshake has finished.serve— a bound version of theservemethod.
Utility functions
Section titled “Utility functions”upgradeWebSocket(req)
Section titled “upgradeWebSocket(req)”Creates a WebSocket connection from an HTTP request. Primarily used in Deno environments.
Usage with Deno
Section titled “Usage with Deno”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.