boltgame.js
v1.1.0
Published
Standalone Node.js headless host library for BOLT and HaxBall rooms.
Maintainers
Readme
boltgame.js
Standalone Node.js 24+ headless host library for BOLT and HaxBall rooms.
Built directly on top of the reconstructed @bolt/core game engine and native node-datachannel WebRTC data channels. It runs entirely in Node.js: no Chromium process or open BOLT Headless browser page is required.
Features
- ⚡ Zero-Token Hosting: Host public or unlisted rooms on the BOLT network instantly without solving captchas or fetching tokens.
- 🔄 HaxBall-Compatible Runtime: Replace the
haxball.jspackage/import while retaining standard room configuration, callbacks, and methods. Pass a HaxBall token to publish the same host on HaxBall and BOLT, or omit it for BOLT-only hosting. - 🚀 Native BOLT Features (
BOLTInit): Built-in support for match statistics tracking, vote-kick moderation, autonomous balancing bots, captain picking, voluntary AFK, and custom ratings (room.bolt). - 🛡️ Guarded Native WebRTC: Safe, high-performance WebRTC data channels backed by
libdatachannelwith process crash protections. - 🏢 Multi-Room Concurrency: Host multiple independent rooms within a single Node.js process without global state conflicts.
- 📦 Dual ESM & CommonJS: Works out of the box with
importandrequire()across modern Node.js runtimes.
Installation
Requires Node.js 24 or newer.
npm install boltgame.jsQuickstart
1. Drop-In Replacement for haxball.js (HBInit)
import BoltGameJS from 'boltgame.js';
// Exact haxball.js syntax:
const HBInit = await BoltGameJS();
const room = HBInit({
roomName: 'My BOLT Headless Room',
maxPlayers: 12,
public: true,
noPlayer: true,
});
room.onRoomLink = (link) => {
console.log('Room link:', link);
};
room.onPlayerJoin = (player) => {
console.log(`${player.name} joined the room!`);
room.sendAnnouncement(`Welcome to the room, ${player.name}!`, player.id, 0x00ff00);
};Or using direct named import:
import { HBInit } from 'boltgame.js';
const room = HBInit({
roomName: 'My BOLT Room',
maxPlayers: 12,
public: true,
noPlayer: true,
});2. Enhanced BOLT Room with Custom Ratings & Features (BOLTInit)
import BoltGameJS from 'boltgame.js';
const { BOLTInit } = await BoltGameJS();
const room = BOLTInit({
roomName: 'BOLT Pro 3v3',
maxPlayers: 12,
public: true,
noPlayer: true,
bolt: {
statistics: true, // Live match statistics tracking
voteKick: true, // Peer-channel vote-kick moderation
afk: true, // Voluntary !afk queue
captain: true, // Automatic captain picks
bots: true, // Autonomous balancing bots
ratingLabel: 'ROOM ELO',
},
});
room.onPlayerJoin = (player) => {
// Set custom in-game rating for player displayed in BOLT client UI
room.bolt?.setRating(player.id, 1500);
};3. Legacy HaxBall Room (Direct HaxBall Network Hosting)
import BoltGameJS from 'boltgame.js';
const HBInit = await BoltGameJS();
const room = HBInit({
roomName: 'Haxball Room',
maxPlayers: 16,
public: false,
noPlayer: true,
token: 'THR1-AAAA...', // HaxBall headless token from haxball.com/headlesstoken
});4. Existing Room Policies with Dual BOLT + HaxBall Listing
Use BOLTInit and explicitly disable every built-in policy that your room
already owns. The room.bolt presentation API remains available.
import { BOLTInit } from 'boltgame.js';
const room = BOLTInit({
roomName: 'My Production Room',
maxPlayers: 16,
public: true,
noPlayer: true,
token: process.env.HAXBALL_TOKEN,
bolt: {
afk: false,
bots: false,
captain: false,
voteKick: false,
statistics: false,
},
});
// Keep the room's existing AFK, captain, moderation, stats, ELO, and level code.
room.onPlayerJoin = (player) => {
// Optional display-only state for BOLT clients:
room.bolt?.setRating(player.id, getExistingRating(player.auth));
};The token registers one room with HaxBall; BOLT lists and joins that same room. This is not two synchronized room instances. Keep the Node.js process running for as long as the room should remain online.
Configuration
BoltGameJS(config) accepts an optional configuration object. The BOLT site and
the BOLT platform API live on different origins, so they are configured
separately:
| Option | Env fallback | Default | Used for |
|---|---|---|---|
| apiEndpoint | BOLT_PLATFORM_API_ORIGIN | https://spain.api.boltgame.io | Registering the room (/rs/api/host) |
| endpoint | BOLT_PLATFORM_WEB_ORIGIN | https://boltgame.io | /room/<token> links and the signaling Origin header |
| proxy | — | — | HTTP/HTTPS proxy for master and WebSocket connections |
| sourceIp | BOLT_ROOM_SOURCE_IP | — | Source IP for outgoing sockets |
| debug | — | false | Verbose logs |
Neither is required — the defaults host on the public BOLT network. If you run
your own single-origin deployment, set endpoint (or BOLT_PLATFORM_ORIGIN)
alone and it is used for both.
const HBInit = await BoltGameJS({
endpoint: 'http://localhost:8090', // web app
apiEndpoint: 'http://localhost:8091', // platform server
});Network Path
Gameplay data channels connect directly between each player and the Node.js
room process on your VPS. BOLT and HaxBall are used for directory and WebRTC
signaling, not as gameplay relays. BOLT's optional bolt presentation channel
also runs directly over the peer connection. Dual listing therefore adds no
BOLT server hop to gameplay, although exact latency still depends on the
client's normal ICE and network route.
License
Proprietary / Closed Source. Copyright (c) 2026 BOLT (boltgame.io). All rights reserved. See LICENSE for terms.
