HexBot is a modular Internet Relay Chat bot for Node.js, written in TypeScript. Designed for reliability and extensibility, HexBot runs on any IRC network and can be deployed in seconds using Docker.
- Event bind system — register handlers for IRC events with
bind(type, flags, mask, handler). - Flag-based permissions — owner/master/op/voice flags with hostmask matching and optional NickServ verification
- Hot-reloadable plugins — load, unload, and reload plugins at runtime without restarting the bot
- DCC CHAT console — remote admin party line with real flag enforcement
- Bot linking — hub-and-leaf multi-bot networking with permission sync, command relay, and ban sharing
- Flood protection — token-bucket outgoing queue and per-user input rate limiting
- Docker-ready — multi-stage build, healthcheck, bind-mount config and plugins
Requires Node.js 24+ and pnpm.
git clone https://github.com/jstnmthw/hexbot.git && cd hexbot
pnpm install
cp config/bot.example.json config/bot.json
cp config/plugins.example.json config/plugins.json
cp config/bot.env.example config/bot.env && chmod 600 config/bot.env
# Edit config/bot.json for your server, nick, owner hostmask, and plugins.
# Put secrets (NickServ password, etc.) in config/bot.env.
pnpm dev # start with interactive REPLpnpm start runs without the REPL. Use --config <path> to specify an alternate config file.
For a more detailed walkthrough, see the Getting Started guide.
Secret values never live in bot.json. Each secret field is named via a _env suffix — the loader reads the named environment variable at startup and fails loudly if a required secret is missing. pnpm start / pnpm dev auto-load config/bot.env. Default env vars (defined in config/bot.env.example): HEX_NICKSERV_PASSWORD, HEX_BOTLINK_PASSWORD, HEX_CHANMOD_RECOVERY_PASSWORD, HEX_PROXY_PASSWORD, HEX_GEMINI_API_KEY. The HEX_ prefix namespaces these so they won't collide with other services on the host. Plugin configs may declare their own <field>_env fields; the loader resolves them before the plugin sees its config.
Each bot instance has its own config, plugin overrides, env file, and database. The recommended layout groups configs by network:
config/
├── libera/
│ ├── chanbot.json # bot config
│ ├── chanbot-plugins.json # plugin overrides for this bot
│ ├── chanbot.env # per-bot secrets (gitignored)
│ └── rpgbot.json
└── rizon/
├── enforcer.json
└── enforcer.env
data/libera-chanbot.db # per-bot database
data/libera-rpgbot.db
data/rizon-enforcer.db
Launch each instance with its own env file and config:
tsx --env-file=config/libera/chanbot.env src/index.ts --config=config/libera/chanbot.json
tsx --env-file=config/rizon/enforcer.env src/index.ts --config=config/rizon/enforcer.jsonThe config/<network>/<bot-name>.json layout is a convention — --config= accepts any path. Link bots together via the botlink block in each bot's config. See docs/BOTLINK.md. A full worked example lives under config/examples/multi-bot/, and a docker-compose snippet for running multiple bots is in docs/multi-instance/docker-compose.yml.
Plugins live in plugins/<name>/ and are auto-discovered on startup. Any plugin directory containing an index.ts is loaded automatically — no config entry required. To disable a plugin, add it to config/plugins.json with "enabled": false. Use plugins.json to override config, restrict channels, or disable specific plugins.
| Plugin | Commands | Description |
|---|---|---|
| 8ball | !8ball <question> |
Magic 8-ball responses |
| chanmod | !op, !deop, !halfop, !dehalfop, !voice, !devoice, !kick, !ban, !unban, !kickban, !bans |
Channel protection: auto-op/halfop/voice on join, mode enforcement, bitch/punish/enforcebans, rejoin/revenge, timed bans |
| ctcp | (automatic) | Replies to CTCP VERSION, PING, and TIME requests |
| flood | (automatic) | Inbound flood protection: rate limiting, join/part spam, nick-change spam; escalating enforcement |
| greeter | (automatic) | Greets users on channel join |
| help | !help [command] |
Lists available commands or shows help for a specific command |
| seen | !seen <nick> |
Tracks when a user was last active |
| topic | !topic <theme> <text>, !topic preview <theme> <text>, !topics |
Set channel topics with IRC color-coded theme borders |
See plugins/README.md for the full plugin authoring guide, bind types, config patterns, and a complete example.
The bot's dot-commands (. prefix) provide administration via the REPL, IRC, or DCC CHAT:
| Command | Flags | Description |
|---|---|---|
.help [cmd] |
- |
List commands or show help for one |
.status |
+o |
Connection info, uptime, bind/user counts |
.say <target> <msg> |
+o |
Send a message to a channel or user |
.msg <target> <msg> |
+o |
Send a PRIVMSG to any target |
.join <channel> |
+o |
Join a channel |
.part <channel> |
+o |
Part a channel |
.invite <#channel> <nick> |
+o |
Invite a user to a channel |
.flags [handle] [+flags [#chan]] |
+n|+m |
View/set user flags (no args = flag legend) |
.adduser <handle> <hostmask> <flags> |
+n |
Add a bot user |
.deluser <handle> |
+n |
Remove a bot user |
.users |
+o |
List all bot users |
.chanset <#chan> [key] [value] |
+m |
View or set per-channel plugin settings |
.chaninfo <#chan> |
+o |
Show all per-channel settings for a channel |
.binds [plugin] |
+o |
List active event binds |
.plugins |
- |
List loaded plugins |
.load <name> |
+n |
Load a plugin |
.unload <name> |
+n |
Unload a plugin |
.reload <name> |
+n |
Reload a plugin |
Available when bot linking is enabled in bot.json. See docs/BOTLINK.md for setup.
| Command | Flags | Description |
|---|---|---|
.botlink <status|disconnect|reconnect> |
+m |
Bot link status and management |
.bots |
+m |
List all linked bots |
.bottree |
+m |
Show botnet topology tree |
.relay <botname> |
+m |
Relay DCC session to a remote bot |
.bot <botname> <command> |
+m |
Execute a command on a remote bot |
.bsay <botname|*> <target> <msg> |
+m |
Send a message via another linked bot |
.bannounce <message> |
+m |
Broadcast to all console sessions across bots |
.whom |
- |
Show all console users across linked bots |
Available inside a DCC CHAT session. See docs/DCC.md for setup.
| Command | Description |
|---|---|
.console |
Show who is on the console |
.quit |
Disconnect from the DCC session |
| Flag | Role | Access |
|---|---|---|
n |
Owner | Full access; implies all other flags |
m |
Master | User management |
o |
Op | Channel commands, bot admin |
v |
Voice | Reserved for plugin use |
d |
Deop | Suppress auto-op/halfop on join; auto-voice if also +v |
Flags can be set globally or per-channel. The owner defined in bot.json is bootstrapped automatically on startup.
- SOCKS5 proxy — tunnel the IRC connection through a SOCKS5 proxy (Tor, SSH dynamic forward, etc.); configure via
proxyinbot.json - DCC CHAT / party line — users connect directly via DCC CHAT for an admin party-line session; configure via
dccinbot.json(see docs/DCC.md) - Bot linking — hub-and-leaf multi-bot networking with permission sync, command relay, and shared ban lists (see docs/BOTLINK.md)
- IRC CASEMAPPING — reads the server's
CASEMAPPINGISUPPORT token and applies correct nick/channel folding (rfc1459,strict-rfc1459, orascii) throughout all core modules and the plugin API (api.ircLower()) - IRCv3 identity caps — negotiates
extended-join,account-notify, andchghostfor a live nick-to-account map; privileged commands can require NickServ verification before executing (configure viaidentity.require_acc_forinbot.json). SASL PLAIN and SASL EXTERNAL (CertFP) both supported. - Channel takeover protection — detects unauthorized mass deop/mode changes and responds with configurable escalation including ChanServ akick
- Persistent channel rejoin — automatically rejoins channels after kick, netsplit, or other forced parts
git clone https://github.com/jstnmthw/hexbot.git && cd hexbot
cp config/bot.example.json config/bot.json
cp config/plugins.example.json config/plugins.json
# Edit both files for your server, nick, and owner hostmask
docker compose up -d
docker compose logs -fPlugins and config live on the host filesystem via bind mounts. Edit a plugin file and run .reload <name> in IRC to pick up changes — no rebuild needed.
For non-Docker production use, pnpm start runs the bot directly via tsx.
pnpm test # run tests (vitest)
pnpm test:watch # watch mode
pnpm test:coverage # with coverage report
pnpm lint # eslint
pnpm typecheck # tsc --noEmit
pnpm format # prettier
pnpm check # typecheck + lint + test in one pass| Document | Description |
|---|---|
| Getting Started | Setup walkthrough, first steps, first plugin |
| Design Document | Architecture and design decisions |
| Plugin API | Full plugin API reference |
| Plugin Authoring | How to write plugins |
| DCC CHAT | Remote admin setup and usage |
| Bot Linking | Hub-and-leaf multi-bot networking |
| Security | Security guidelines and threat model |
| Changelog | Release history |