A modern IRC bouncer for Undernet, Libera.Chat, Rizon, EvilNet, IRCnet and any other network. This wiki explains everything it does and how to use it, from installation to the API.
xBNC is an IRC bouncer: a program that stays connected to your IRC networks around the clock, even when you close your client or switch off your computer. When you come back, it gives you what you missed, exactly once on each device.
You install it on a server of your own (a VPS, a Raspberry Pi, a computer at home), use it with your usual IRC client and configure everything from a web panel, or from IRC with /msg *status. It is a single program written in Rust, with the panel built in.
24native modules, configured per network
18official WebAssembly plugins
2+languages: English and Spanish built in, and any other with a translation file
What it includes
Bouncer: permanent connections to several networks per user, with verified TLS, SASL PLAIN and EXTERNAL, automatic reconnection with growing delays and flood protection towards the server.
Several devices at once: what you write on one appears on the others, and each one keeps its own account of what it has read.
History: channels and private messages with full-text search, filters, TXT and JSON export, general and per-channel retention, and per-user quotas.
Full IRCv3 with servers and clients (complete list); classic clients see plain old IRC.
Web chat in the panel, a mentions view and read markers shared between devices.
Notifications of mentions and private messages by Telegram, Discord, e-mail, a web address of your own or browser and phone push notifications.
24 modules (services login, automatic away, auto op, FiSH encryption, detached channels, scheduled tasks…) and 18 plugins already installed (trivia, polls, reminders, weather…).
Multi-user: five roles, quotas, suspension, audit log, two-step verification, passkeys, OpenID Connect and LDAP.
Data: SQLite or PostgreSQL, active/standby high availability, backups from the panel or the command line, migration from another bouncer.
Administration: web panel, REST API with tokens, WebSocket, Prometheus metrics with a Grafana dashboard, alerts in the panel, over IRC and by webhook.
Security: Argon2id, encrypted credentials, brute-force blocking, CSRF, CSP, an unprivileged service, sandboxed plugins and continuous fuzzing.
Your IRC client no longer connects to the network but to xBNC. xBNC keeps one connection per network that is not cut when you close the client, and presents itself to each device as if it were that network's server.
You connect with the server password user/network@device:password. The device name (@laptop, @phone) is optional, but it lets each device keep its own account of what it has read.
xBNC brings you up to date: it joins your channels for you, sends the topic and the user list, and plays back the messages that arrived since your last connection on that device, without repeating what you already saw.
While you are away, xBNC stays in the channels, stores the history, answers CTCP, marks you as away and runs your modules and plugins.
All your devices stay in sync: what you write on the phone appears on the laptop, and read markers are shared.
What you get when you connect
xBNC's welcome with the parameters (ISUPPORT) of the real network.
The network's MOTD (unless you turn on Hide the MOTD), followed by xBNC's name and version.
The channels you are in, with their topic and user list.
The messages this device has not seen. If the client does not support server-time, each message starts with the date and time in brackets.
If you missed private messages or mentions, a summary from *status (module Automatic away).
If the network is disconnected, a notice from *status with the reason.
This server's message of the day, if a superadministrator has set one (/msg *status setmotd).
History without duplicates
Each device (the @name of the login, or default if you give none) has a cursor per network: the last message delivered to it. Two connections with the same name share the cursor.
When it connects it receives the messages after its cursor, up to playback_max (1000 by default). A new device receives the most recent new_client_playback messages (100 by default).
Cursors are saved on disconnection and every 5 seconds, and never go backwards. If the process stops abruptly, at most a few seconds are repeated.
Clients with draft/chathistory get no automatic playback: they ask for what they need with CHATHISTORY (up to chathistory_max, 500 by default).
The connection to each network
States: disconnected, registering and connected. During registration the IRCv3 capabilities, SASL and the nick (with an alternative if it is taken) are negotiated.
Maximum registration time: 60 seconds. If the server is silent for 90 seconds it is sent a PING, and after 180 the connection is considered lost.
Automatic reconnection with delays growing from 5 seconds to 5 minutes, with a random ±20 % variation so that thousands of networks do not come back in the same second. If the network says you are reconnecting too fast, xBNC waits at least 2 minutes.
Output to the server goes through a rate limiter (a burst of 9 lines, then 2 per second) so the network does not throw you out for flooding.
The server's TLS certificate is always verified against the system's root certificates. For a network with its own or an expired certificate you can trust that particular certificate by its SHA-256 fingerprint (/msg *status addfingerprint); there is no option to turn verification off.
Each network can have backup servers (/msg *status addserver). If connecting to a server fails, xBNC tries the next one a second later; after going through the whole list it goes back to the growing delays. If a server that was connected drops the connection, xBNC first goes back to that same server.
The local address used to connect (vhost) can be set per network or per user (/msg *status setbindhost, administrators only).
If a line is not UTF-8 it is read as Windows-1252, so accented letters from old clients show correctly.
Virtual users
xBNC adds “users” whose names start with *. They do not exist on the network: they are the way to talk to xBNC from any client. *status, *notes, *watch, *crypt, *ignore and one per plugin (*trivia, *remind…). See IRC commands.
Linux with systemd: Debian 12, Ubuntu 22.04 or later, Fedora, Rocky or Alma.
Rust 1.89 or later (rustup.rs) and a C compiler: sudo apt install build-essential or sudo dnf install gcc.
1 CPU and 512 MB of RAM for personal use. Measured: 2,000 users with their networks and clients in about 150 MB (see Performance).
Ports: 6697 (IRC with TLS), 6667 (IRC without TLS, local network only), 8443 (panel). With Let's Encrypt, also 80; with Nginx, 443.
The web panel comes already built inside the source code: Node.js and npm are not needed.
Install
# 1. Install Rust (once, as a normal user)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# 2. Build xBNC (as a normal user)
unzip xbnc-1.0.0.zip && cd xbnc
cargo build --release
# 3. Install the service
sudo ./scripts/install.sh
Run in a terminal without options, the installer asks whether you want to use a domain, the e-mail address for Let's Encrypt and whether you want the panel at https://yourdomain with Nginx. At the end it shows the panel address and the one-time code to create your account:
Panel: https://bnc.example.org:8443
IRC: bnc.example.org, port 6697 (SSL)
Code to create your account: K7QM-2XPA-9DWE
Open the panel, enter the code and create the first administrator (it will be a superadministrator). To see the address and ports again: sudo xbncd info; to see the code again: sudo xbncd setup-code.
What the installer does
Checks the system, the built binary and, if you use a domain, that its DNS points to this machine and which ports you must open on your router.
Creates the unprivileged system user xbnc and the folders with their permissions.
Installs the binary in /usr/local/bin/xbncd and the systemd service with isolation measures (ProtectSystem=strict, NoNewPrivileges, no capabilities, a system call filter).
Creates the certificate: self-signed for the IP (or the domain), or from Let's Encrypt with automatic renewal. Renewal reloads xBNC (and Nginx, if used).
Writes /etc/xbnc/xbnc.toml with the version in its header, and creates the database and the secret key.
With --nginx, installs and configures Nginx: redirection from port 80, the panel on 443, WebSockets and uploads of plugins and backups.
Opens the ports in ufw or firewalld and starts the service.
Installer options
Option
What it does
--domain NAME
Use a domain (bnc.example.org) instead of the IP.
--letsencrypt EMAIL
With --domain: a free Let's Encrypt certificate with automatic renewal. Needs port 80 reachable from the internet.
--nginx
With --domain and --letsencrypt: installs and configures Nginx to serve the panel at https://DOMAIN (port 443).
--ip ADDRESS
IP to use if the one detected is not right (several interfaces or a VPN).
--language es|en
Language of the installer and default language of xBNC.
--behind-proxy
Panel on 127.0.0.1:8080, to publish it with your own proxy.
--web-port N
Panel port (8443 by default).
--irc-port N
IRC port with SSL (6697 by default).
--plain-port N|off
IRC port without SSL, local network only (6667 by default); off removes it.
--no-firewall
Do not open ports in ufw or firewalld.
--reconfigure
Redo the configuration and the certificate of an existing installation (keeps a copy of the previous one). Does not touch users, networks or history.
--binary PATH
Binary to install (target/release/xbncd by default).
--no-start
Install without starting the service.
--non-interactive
Ask nothing (by default it asks when run in a terminal).
Examples
# By IP, with a self-signed certificate
sudo ./scripts/install.sh
# With a domain and Let's Encrypt, panel at https://bnc.example.org:8443
sudo ./scripts/install.sh --domain bnc.example.org --letsencrypt you@example.org
# The same, with the panel at https://bnc.example.org behind Nginx
sudo ./scripts/install.sh --domain bnc.example.org --letsencrypt you@example.org --nginx
# Move an existing installation to a domain
sudo ./scripts/install.sh --reconfigure --domain bnc.example.org --letsencrypt you@example.org
# Change the detected IP
sudo ./scripts/install.sh --reconfigure --ip 192.168.1.50
Where everything goes
Path
Contents
/usr/local/bin/xbncd
The program.
/etc/xbnc/xbnc.toml
Process configuration (root:xbnc, 0640).
/etc/xbnc/tls/
TLS certificate: fullchain.pem and privkey.pem (root:xbnc, 0750).
/etc/xbnc/locales/
Extra languages and translation corrections (see Translating xBNC).
/var/lib/xbnc/
Database and secret key (xbnc:xbnc, 0700). Back up secret.key: without it the stored passwords cannot be decrypted.
/var/lib/xbnc/logs/
Log files of the log module (folders 0700, files 0600).
/var/lib/xbnc/plugins/
Installed plugins (0600). In dev/, those of development mode.
/var/lib/xbnc/backups/
Backups made from the panel.
The service runs as the unprivileged user xbnc. sudo systemctl status xbnc, sudo systemctl restart xbnc; sudo systemctl reload xbnc makes it re-read networks, channels and users changed from the command line, and the language files. The log: sudo journalctl -u xbnc -f.
Using it away from home
Forward port 6697 on your router to the xBNC machine and connect to your public IP or to a dynamic DNS name (DuckDNS and the like). Port 6667 need not be opened: xBNC refuses it from outside.
If you do not want to open the panel (8443) to the internet, manage it through an SSH tunnel: ssh -L 8443:127.0.0.1:8443 user@your-public-ip and open https://localhost:8443.
With a domain pointing to your home, reinstall with --reconfigure --domain … --letsencrypt … to get rid of the certificate warnings.
Panel behind your own Nginx
If you already run Nginx and prefer to configure it yourself, install with --domain bnc.example.org --behind-proxy and use the included example:
# Nginx reverse proxy for the xBNC web panel.
# Copy to /etc/nginx/sites-available/xbnc, adjust server_name and the
# certificate paths, then: ln -s ../sites-available/xbnc /etc/nginx/sites-enabled/
# In xbnc.toml: [web] address = "127.0.0.1:8080", public_url = "https://bnc.example.org",
# secure_cookies = true, trust_proxy = true.
server {
listen 80;
listen [::]:80;
server_name bnc.example.org;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name bnc.example.org;
ssl_certificate /etc/letsencrypt/live/bnc.example.org/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/bnc.example.org/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
# Plugins are up to 4 MB.
client_max_body_size 8m;
# Restoring a backup uploads the whole archive.
location = /api/v1/backups/restore {
client_max_body_size 4g;
proxy_request_buffering off;
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 3600s;
}
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSockets: live dashboard, web chat, plugin log
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 3600s;
}
}
# Needed once per Nginx configuration (e.g. in /etc/nginx/conf.d/upgrade.conf):
# map $http_upgrade $connection_upgrade { default upgrade; '' close; }
In xbnc.toml, with the panel behind Nginx: address = "127.0.0.1:8080", public_url = "https://bnc.example.org", secure_cookies = true and trust_proxy = true.
Certificate from another authority
Copy your certificate and key to /etc/xbnc/tls/fullchain.pem and /etc/xbnc/tls/privkey.pem (owner root:xbnc, the key with permissions 0640) and restart with sudo systemctl restart xbnc. The certificate is read at start; reload is not enough.
Docker
mkdir -p tls
cp /path/fullchain.pem /path/privkey.pem tls/
# Edit XBNC_HOSTNAME and XBNC_PUBLIC_URL in docker-compose.yml
docker compose up -d --build
docker compose logs xbnc | grep "setup code"
Open https://bnc.example.org:8443/, enter the code and create your account. The command line: docker compose exec xbnc xbncd user add admin --admin. The data lives in the xbnc-data volume. The container runs as the unprivileged user xbnc (uid 10001), with a read-only file system, no capabilities and no-new-privileges.
Variable
Use
XBNC_HOSTNAME
Server name (only on the first start).
XBNC_TLS_CERT, XBNC_TLS_KEY
Paths of the certificate and key inside the container; they turn on TLS on 6697 (IRC) and 8443 (panel).
XBNC_INSECURE=1
Plain text on 6667 (IRC) and 8080 (panel), for testing only.
unzip xbnc-new.zip && cd xbnc # or git pull
cargo build --release
sudo ./scripts/update.sh
update.sh checks the new binary and the configuration, makes a backup, stops the service, swaps the binary and starts it. If it does not start, it goes back to the previous binary and to the database from before the update (a new binary may have upgraded the schema, and an old one refuses to open a newer schema). Nothing is ever updated on its own. With --binary PATH it installs another binary.
If you also want to redo the configuration (for example, to move to a domain), run sudo ./scripts/install.sh --reconfigure afterwards.
Uninstalling
sudo ./scripts/uninstall.sh # removes the service and the binary; keeps /etc/xbnc and /var/lib/xbnc
sudo ./scripts/uninstall.sh --purge # also deletes the configuration, the data and the xbnc user
Create your account in the panel with the one-time code. The first account is a superadministrator.
Add a network in Networks → Add network. The wizard has four steps: choose the network (the well-known ones come ready), your nick and name, the identification (SASL, X, Q…) and the channels.
Connect your client. The network page has the card “Connect your IRC client” with your exact details and the steps for HexChat, mIRC, WeeChat and irssi, with buttons to copy them.
Turn on modules and plugins in Modules. Choose the network at the top; with many networks, “Copy to other networks” repeats the configuration.
Choose your time zone and language in Your account: the time zone is used in the log files and in scheduled tasks.
Turn on two-step verification and, if you like, a passkey, in Your account.
Networks ready in the wizard
Network
Server
Port
TLS
Identification
Description
Undernet
irc.undernet.org
6667
no
X with Login on Connect
Classic network with all kinds of channels
Libera.Chat
irc.libera.chat
6697
yes
SASL
Free software communities and open source projects
Any client that lets you set the server, port and server password works. No plugin is needed.
What you want
Server password
A user with a single network
alice:password
Choose the network
alice/undernet:password
Choose the network and name the device
alice/undernet@laptop:password
The same login in another order
alice@laptop/undernet:password
Server: your domain or IP (sudo xbncd info shows it).
Port: 6697 with SSL from anywhere; 6667 without SSL only from your local network.
Password: the same one you use for the panel. You can also put alice/undernet@laptop in the user field (USER) and the password alone in PASS, or sign in with SASL PLAIN from the client.
One connection is one network: for two networks, create two servers in your client (alice/undernet and alice/libera). Clients with soju.im/bouncer-networks (Goguma, gamja, Halloy) see all your networks in a single connection.
Device: the @device part is optional but recommended. Each device (@laptop, @phone, @work) gets its own pending history.
If you installed by IP, the certificate is self-signed: turn on “accept invalid certificates” in your client.
Network List → Add: "xBNC Undernet"
Edit → Server: bnc.example.org/6697
Tick "Use SSL for all the servers on this network"
Login method: Server password (/PASS password)
Password: alice/undernet@hexchat:your-password
With a self-signed certificate: tick "Accept invalid SSL certificates"
The quickest way, in any window:
/server bnc.example.org +6697 alice/undernet@mirc:your-password
The + before the port turns on SSL. With a self-signed certificate, mIRC asks
the first time: click Accept. To save it: File → Select server → Add, with
the same details.
/server add xbnc-undernet bnc.example.org/6697 -tls
/set irc.server.xbnc-undernet.password "alice/undernet@weechat:your-password"
/set irc.server.xbnc-undernet.capabilities "server-time,message-tags,batch,draft/chathistory,echo-message"
/connect xbnc-undernet
With a self-signed certificate: /set irc.server.xbnc-undernet.tls_verify off
/network add -nick alice xbnc-undernet
/server add -tls -network xbnc-undernet -password alice/undernet@irssi:your-password bnc.example.org 6697
/connect xbnc-undernet
With a self-signed certificate, add -notls_verify to /server add
Goguma (Android) and Palaver (iOS) support bouncers: add the server bnc.example.org, port 6697 with TLS, user alice and your password. Goguma shows all your networks in a single connection and asks for the history with CHATHISTORY. To get notifications with the app closed, use xBNC's notifications (push, Telegram…), or install the panel as an app.
KVIrc, Konversation, Textual, Halloy, The Lounge and Quassel (as a client of a normal IRC network) work with the same details: server, port with TLS and server password. Clients with draft/chathistory (Goguma, gamja, Kiwi IRC, Halloy) ask for the history themselves and get no automatic playback.
If you get the port or SSL wrong
xBNC spots the usual mistakes and tells you in the client itself:
What you see in the client
What is happening
What to do
Closing link: this port uses SSL/TLS. Enable SSL in your IRC client, or connect to port 6667 without SSL.
You connect to 6697 without SSL.
Turn on SSL or use 6667 from home.
Closing link: this port only accepts connections from the local network...
You use 6667 from outside your network.
Use 6697 with SSL.
Certificate error or “invalid certificate”
Self-signed certificate (installation by IP).
Accept the certificate or move to a domain with Let's Encrypt.
464 Password incorrect
Wrong user, network or password.
Check user/network@device:password.
Choose a network by logging in as user/<network>
You have several networks and did not say which.
Add /network to the user.
ERROR :Too many failed login attempts
Your IP is blocked after failed attempts.
Wait 15 minutes or ask an administrator to unblock it in Security.
When the client connects with SSL to a port without SSL it cannot show any message: the service log says so (client sent TLS to a plain-text port).
Networks without SASL (Undernet, QuakeNet)
Some networks do not use SASL: you log in with a services bot, X on Undernet or Q on QuakeNet. xBNC does it for you every time it connects. Turn on the Services login module (in the wizard, the Account step) with your account and password. If you prefer to do it by hand, write the commands in Settings → Commands on connect, one per line, as in your client:
They are sent before joining channels, so your IP is already hidden (+x) when you join, and they are stored encrypted.
Client compatibility
The automatic tests use a standard IRC client, a client with every IRCv3 capability and a classic client without any. HexChat, mIRC, WeeChat and irssi have been used by hand with xBNC; the other clients in the list follow the same standards but have not been checked one by one. If something does not work with a particular client, report it with the client's name and version (see Testing the beta).
Lines sent after connecting and before joining the channels (see below).
Channels
Saved channels, with their key (encrypted). A /join or /part from the client is saved on its own.
Retention per channel
How long a channel's history is kept: the server setting, nothing, forever or N days.
Backup servers
Other servers of the same network that xBNC tries if the current one fails. With /msg *status addserver (see IRC commands).
Trusted certificates
SHA-256 fingerprints of server certificates accepted even if they do not validate (self-signed or expired). With /msg *status addfingerprint.
Local address (vhost)
The IP of this server used to connect to the network. With /msg *status setbindhost (administrators only).
Disabled channels and order
A disabled channel stays saved but is not joined; the order is the joining order when connecting. With /msg *status disablechan and movechan.
Commands on connect
They are written as in your client, one per line (up to 20, of 510 characters each), and stored encrypted because they often contain passwords. $nick is replaced by your current nick. Allowed: /msg (or /privmsg, /query), /notice, /quote (or /raw) and any IRC command with a slash (/mode, /away…). A line without a slash is sent as it is.
To identify with X, NickServ, Q or AuthServ the Services login module is better: it waits for the network to confirm the login before joining the channels, and on Undernet it uses Login on Connect.
Undernet
Undernet uses the X service and has neither SASL nor TLS. Turn on Services login with your X account: with Login on Connect the account travels in the server password (+x! account password), so your real IP is never seen and you join the channels already as account.users.undernet.org.
*** Your host is now y2k.users.undernet.org
* y2k has joined #xbnc
*statusxBNC 1.0.0 Made by y2k
With Invite and unban, if you cannot join one of your channels, xBNC asks X for INVITE or UNBAN.
Ignore uses SILENCE: messages from the people you ignore do not even reach xBNC.
Nicks have at most 12 characters; the away nick is shortened automatically.
Many Spanish-speaking channels use Latin-1: xBNC always understands it, and with Character encoding you can write to them in Latin-1.
The connection to Undernet is unencrypted because the network offers no TLS; the X password travels in clear text to the server, as with any client.
Libera.Chat and freenode
Identification by SASL: put your account and password in the network configuration. On Libera you can also use a certificate (Client certificate) with SASL EXTERNAL: generate the certificate in the panel, connect identified, type /msg NickServ CERT ADD and reconnect. If your nick is taken when connecting, Services login with “Recover my nick” sends REGAIN (Libera) or GHOST (freenode).
Rizon
NickServ (Anope), by SASL or with Services login. ChanServ handles invitations and unbans.
EvilNet
X3 services: you identify with AuthServ (AUTH account password) and mode +x hides your host (numeric 396). Choose AuthServ in Services login, or “Automatic”.
QuakeNet
The Q service (AUTH to Q@CServe.quakenet.org) and +x. Services login does it for you; invite and unban use INVITE and UNBANME.
IRCnet and EFnet
They have no NickServ: anyone could take that nick. That is why, in “Automatic”, xBNC never sends your services password on these networks and says so in *status and in the log. IRCnet offers optional SASL with accounts from sasl.ircnet.com. To keep op without services, use Get op back when left alone.
OFTC and other networks with NickServ
Services login with NickServ, or commands on connect. For the client certificate, OFTC uses the SHA-1 fingerprint and Libera the SHA-512 one: the panel shows all three fingerprints (SHA-1, SHA-256 and SHA-512).
You type them in your client as /msg *service command. The answers come in your language.
*status
It is the main service: everything you can do with a network in the panel can also be done here. /msg *status help lists the commands grouped and with their names in bold, only those your account may use; /msg *status help <command> explains one with an example, and /msg *status help <word> searches (for example help channel). Names are not case-sensitive, and the names used by other bouncers are accepted too (ListChans, ListMods, AddTrustedServerFingerprint…), listed under each command as “Also accepted”.
*statusxBNC *status commands
*statusWrite them as /msg *status <command>. Details and an example: help <command>.
*statusChannels
*statuschannels Your channels: users, modes and state
*statusnicks <#channel> Everyone in a channel, with their rank
*statustopics The topic of every channel
Unless stated otherwise, any account may use a command. The others say who may: accounts allowed to make changes (every role except “limited”), administrators (administrator and superadministrator) or superadministrators. Administrative actions are recorded in the audit log.
General
Command
What it does
help [command]
This list, or the details of one command. Without a command, lists every command you can use. With one, shows how it works and an example. You can also search: help chan. Example: /msg *status help addserver
version
xBNC version. Example: /msg *status version
uptime
How long xBNC and this network have been running. Example: /msg *status uptime
language <code>
Your language, also for the web panel. Changes the language of xBNC's messages, the plugins and the web panel at once. Without a code it lists the installed languages. Example: /msg *status language en
seen
When you were last connected. Example: /msg *status seen
showmotd
This server's message of the day. Example: /msg *status showmotd
clients [user]
Devices connected to this network. Administrators can name a user to see all of theirs. Example: /msg *status clients Also accepted: listclients
Networks and connection
Command
What it does
status
State of this network: server, TLS, nick, capabilities. Example: /msg *status status
networks
Your networks and their state. Example: /msg *status networks Also accepted: listnetworks
addnetwork <name> <server> [[+]port]
Add a network to your account. A + before the port means TLS. Without a port: 6697 with TLS. Your nick is the one of this network. Then connect with user/name. Example: /msg *status addnetwork libera irc.libera.chat +6697
delnetwork <name> confirm
Delete one of your networks and its history. Asks for confirmation: repeat the command adding "confirm". It cannot be undone. Example: /msg *status delnetwork test confirm
setnick <nick>
Change the nick saved for this network. Example: /msg *status setnick y2k Only accounts allowed to make changes (not the limited role).
connect
Connect to the network again. Example: /msg *status connect Only accounts allowed to make changes (not the limited role).
disconnect
Disconnect and stay disconnected. xBNC stays disconnected from this network until you use connect. Example: /msg *status disconnect Only accounts allowed to make changes (not the limited role).
jump [server]
Reconnect, to the next server or to the one you name. Without a server, goes to the next one of the list (servers). With one, connects to it. Example: /msg *status jump irc2.undernet.org Only accounts allowed to make changes (not the limited role).
Servers, certificates and addresses
Command
What it does
servers
Servers of this network (main and backups). If a server fails, xBNC tries the next one in turn. Example: /msg *status servers Also accepted: listservers
addserver <server> [[+]port] [password]
Add a backup server. A + before the port means TLS. Without a port: the same as the main server. Up to 20. Example: /msg *status addserver irc2.undernet.org 6667 Only accounts allowed to make changes (not the limited role).
delserver <server> [port]
Remove a server. If you remove the main server, the first backup becomes the main one. Example: /msg *status delserver irc2.undernet.org Only accounts allowed to make changes (not the limited role).
addfingerprint <SHA-256>
Trust a server certificate by its fingerprint. For servers with a self-signed or expired certificate. 64 hex digits, with or without colons. Applies on the next connection. Example: /msg *status addfingerprint 3F:9A:...:C2:1E Also accepted: addtrustedserverfingerprint Only accounts allowed to make changes (not the limited role).
delfingerprint <SHA-256>
Stop trusting a certificate. Example: /msg *status delfingerprint 3F:9A:...:C2:1E Also accepted: deltrustedserverfingerprint Only accounts allowed to make changes (not the limited role).
fingerprints
Trusted certificates of this network. Example: /msg *status fingerprints Also accepted: listtrustedserverfingerprints
showbindhost
Local address used to connect. Example: /msg *status showbindhost
setbindhost <IP>
Local address for this network (vhost). One of this server's IP addresses. Applies on the next connection (jump). Example: /msg *status setbindhost 203.0.113.25 Administrators only.
clearbindhost
Use the automatic address for this network. Example: /msg *status clearbindhost Administrators only.
setuserbindhost <IP>
Default local address for all your networks. A network's own address (setbindhost) takes priority. Example: /msg *status setuserbindhost 2001:db8::25 Administrators only.
clearuserbindhost
Remove your default local address. Example: /msg *status clearuserbindhost Administrators only.
Channels
Command
What it does
channels
Your channels: users, modes and state. Example: /msg *status channels Also accepted: listchans
nicks <#channel>
Everyone in a channel, with their rank. Example: /msg *status nicks #xbnc Also accepted: listnicks
topics
The topic of every channel. Example: /msg *status topics
enablechan <#channels>
Join saved channels that were disabled. Accepts several channels and the wildcards * and ?. Example: /msg *status enablechan #xbnc #xbnc-dev Only accounts allowed to make changes (not the limited role).
disablechan <#channels>
Leave channels but keep them saved. They are not joined again until enablechan. Accepts * and ?. Example: /msg *status disablechan #xbnc* Only accounts allowed to make changes (not the limited role).
attach <#channels>
Show detached channels again. Example: /msg *status attach #xbnc
detach <#channels>
Hide channels from your client (they keep recording). A mention brings the channel back. Accepts * and ?. Example: /msg *status detach #xbnc Only accounts allowed to make changes (not the limited role).
movechan <#channel> <position>
Change a channel's place in the order. The order is the one in which xBNC joins and lists your channels. Example: /msg *status movechan #xbnc 1 Only accounts allowed to make changes (not the limited role).
swapchans <#channel1> <#channel2>
Swap the places of two channels. Example: /msg *status swapchans #xbnc #xbnc-dev Only accounts allowed to make changes (not the limited role).
History (buffers)
Command
What it does
playbuffer <#channel|nick> [lines]
Play back the history of a conversation. Sends you the last lines (50 by default, up to 1000). Example: /msg *status playbuffer #xbnc 100
clearbuffer <#channel|nick>
Delete the history of a conversation. Accepts * and ?. Example: /msg *status clearbuffer bob Also accepted: clearhistory
clearallbuffers
Delete all the history of this network. Example: /msg *status clearallbuffers
clearallchannelbuffers
Delete the history of every channel. Example: /msg *status clearallchannelbuffers
clearallquerybuffers
Delete the history of every private conversation. Example: /msg *status clearallquerybuffers
setbuffer <#channel> <days|forever|none|default>
How long a channel's history is kept. forever: never deleted; none: not stored; default: the server setting; or a number of days. Example: /msg *status setbuffer #xbnc 90 Only accounts allowed to make changes (not the limited role).
Modules and plugins
Command
What it does
modules
Modules and plugins of this network and their state. Example: /msg *status modules Also accepted: listmods
availmods
Every module and plugin, with what it does. Example: /msg *status availmods Also accepted: listavailmods
enable <module|plugin>
Turn on a module or plugin on this network. Example: /msg *status enable away Also accepted: loadmod Only accounts allowed to make changes (not the limited role).
disable <module|plugin>
Turn it off. Example: /msg *status disable away Also accepted: unloadmod Only accounts allowed to make changes (not the limited role).
reloadmod <module|plugin>
Restart a module or plugin on this network. Example: /msg *status reloadmod trivia Also accepted: updatemod Only accounts allowed to make changes (not the limited role).
get <module|plugin>
Show its settings. Example: /msg *status get away
set <module> <setting> <value>
Change a setting. For yes/no settings: on/off. See the names with get. Example: /msg *status set away delay 60 Only accounts allowed to make changes (not the limited role).
addline <module> <setting> <text>
Add a line to a list setting. Example: /msg *status addline watch patterns *xbnc* Only accounts allowed to make changes (not the limited role).
dellines <module> <setting>
Empty a list setting. Example: /msg *status dellines watch patterns Only accounts allowed to make changes (not the limited role).
Administration
Command
What it does
users
Every account and whether it is connected. Example: /msg *status users Also accepted: listusers Administrators only.
allnetworks
Every network of every user, with its state. Example: /msg *status allnetworks Also accepted: listallusernetworks Administrators only.
traffic
Traffic and messages since xBNC started. Example: /msg *status traffic Administrators only.
broadcast <message>
Send a notice to every connected user. Example: /msg *status broadcast Restart at 23:00 Administrators only.
Move a network to another user. The network reconnects as the new user, with its history. Example: /msg *status movenetwork ana libera bob Administrators only.
rehash
Re-read users, networks, channels and languages. For changes made with the xbncd command line. Changes in xbnc.toml need a restart. Example: /msg *status rehash Also accepted: reload Administrators only.
listports
Ports xBNC listens on. Ports are set in xbnc.toml (or with the installer) and need a restart. Example: /msg *status listports Administrators only.
saveconfig
Save the settings. Not needed: xBNC saves every change at once. Example: /msg *status saveconfig Administrators only.
setmotd <text>
Set this server's message of the day. Users receive it from *status when they connect. Up to 20 lines. Example: /msg *status setmotd Welcome to xBNC Superadministrators only.
addmotd <text>
Add a line to the message of the day. Example: /msg *status addmotd Rules: /msg *status help Superadministrators only.
clearmotd
Remove the message of the day. Example: /msg *status clearmotd Superadministrators only.
restart [message]
Restart xBNC. Every network reconnects. The message, if any, is sent to everyone first. Example: /msg *status restart Updating Superadministrators only.
shutdown [message]
Stop xBNC completely. It stays stopped until it is started again on the server (systemctl start xbnc). Example: /msg *status shutdown Maintenance Superadministrators only.
Notes on these commands
Backup servers. If connecting to a server fails, xBNC tries the next one on the list a second later, and after going through the whole list it waits as usual again (5 s, 10 s, 20 s… up to 5 min). If a server that was connected drops the connection, xBNC first goes back to that same server. Passwords of backup servers are stored encrypted. jump alone moves to the next one; jump server or jump server:port, to a particular one.
Trusted certificates. For networks with a self-signed or expired certificate, or one with another name. xBNC still checks everything else about TLS and accepts only that particular certificate (its SHA-256 fingerprint). To see a server's fingerprint: openssl s_client -connect irc.example.org:6697 </dev/null 2>/dev/null | openssl x509 -noout -fingerprint -sha256
Local address (vhost). Administrators only, because it uses the server's IP addresses. xBNC checks that the address belongs to the machine before saving it. The network's address takes priority over the user's. It applies on the next connection.
Disabled channels. They stay saved with their key and settings, but xBNC does not join them when connecting. The panel marks them as “Disabled”. enablechan joins them straight away.
Channel order. It is the order in which xBNC joins when connecting and in which channels and the panel list them. New channels go at the end.
Wildcards.enablechan, disablechan, attach, detach and clearbuffer accept several names separated by spaces or commas, and the wildcards * and ?: disablechan #xbnc* #xbnc-dev.
Message of the day. A superadministrator sets it and every user receives it, as a notice from *status, each time a client connects. Up to 20 lines of 400 characters. Shown with showmotd.
Notices to everyone.broadcast sends “[Administrator notice] text” to every running network; every connected client receives it. restart and shutdown with a message send it first.
Restarting and stopping.restart restarts xBNC inside the same process (the networks reconnect). shutdown stops it completely and systemd does not start it again: it has to be started with sudo systemctl start xbnc.
Ports and xbnc.toml. Ports and the rest of xbnc.toml are not changed from IRC: listports shows them and rehash re-reads users, networks, channels and languages (the same as sudo systemctl reload xbnc). saveconfig exists out of habit: xBNC saves every change at once.
*notes
Command
What it does
list
Your notes.
get <key>
Show a note.
add <key> <text>
Save a note (up to 400 characters).
del <key>
Delete a note.
They are the same notes as in Your account → Notes.
*watch
Matches of the Watch words module arrive here. /msg *watch help reminds you how to configure it.
*crypt
Command
What it does
setkey <#channel|nick> [cbc:]<key>
Save the FiSH key of a channel or private conversation (cbc: for CBC mode). Stored encrypted.
delkey <#channel|nick>
Delete a key.
keyx <nick>
Agree on a key in private with DH1080 (the other person needs FiSH).
list
Conversations with a key.
Type +p before a message to send it unencrypted. See Message encryption.
*ignore
Command
What it does
add <nick or mask>
Ignore someone (a nick, or nick!user@host with *).
del <nick or mask>
Stop ignoring.
list
List of ignored people.
One service per plugin
Each active plugin answers on *id (*trivia, *remind, *poll…). With help or any text it does not understand, it explains how it is used. If a plugin has nothing to say, xBNC answers with its name, version, description and usage.
Client commands
Command
What it does
/quote DETACH #channel
Detaches a channel: it keeps recording, but does not appear in your client.
/quote ATTACH #channel or /join #channel
Shows a detached channel again, with the latest messages.
/quote <alias> [arguments]
Runs an alias if your client does not send commands it does not know.
Live state of your networks (updated every 2 seconds) and, for administrators, memory, CPU, traffic, connections, blocked IPs, active alerts and new version notices.
Chat
Web IRC client: channels and private conversations of all your networks, with history. The panel counts as one of your devices.
Mentions
Everything said to you in private or where your nick was written, newest first.
Networks
Add (four-step wizard) and edit networks; control (connect, disconnect, reconnect); channels; connected devices (with a button to disconnect one); history with search and export; commands on connect; client certificate; the card “Connect your IRC client”.
Modules
Modules and plugins of each network. A network selector that works with any number of networks, and “Copy to other networks” (modules and plugins, except the certificate).
Plugins
Official and remote catalogue, one-click installation or uploading a .wasm, permissions and approved sites, turning them on or off on the server, uninstalling, live log and development mode (superadministrators).
Users
New accounts, roles, quotas of networks, devices and history, suspension, password reset, usage of each account and import from another bouncer.
Security
Sign-in attempts (IRC, panel and tokens), blocked IPs with a button to unblock them, open sessions and an audit log of every action.
Settings
History, limits, access protection, default language, alerts, backups, updates and restarting the service.
Your account
Language, time zone, notifications, password, two-step verification, passkeys, linked external accounts, notes, API tokens, test report and open sessions.
Live settings
What you change in Settings is stored in the database, applies without restarting and takes priority over xbnc.toml. The ↺ icon next to a field goes back to the value of xbnc.toml.
Group
Settings
Default
History
Days of retention (0: forever); messages played back on reconnection; messages for a new device; CHATHISTORY limit.
30 days; 1000; 100; 500
Limits
Total connections; devices per user; networks per user; queue per client (lines before a slow client is disconnected). Each user can have exceptions in Users.
1000; 10; 10; 4096
Access protection
Failed attempts before blocking; window; length of the block; time to complete the login.
5; 600 s; 900 s; 30 s
General
Default language for people who have not chosen one in Your account. It is saved and applied when clicked, also to whoever changes it.
Automatic yes/no, every how many hours (1–720) and how many to keep (1–365).
no; 24 h; 7
What is read at start (ports, certificates, database, secret key, the panel itself and the log level) is changed in xbnc.toml and needs sudo systemctl restart xbnc. The page says so. Superadministrators also have a button to restart the service.
Accessibility and use on the phone
Keyboard navigation with visible focus; dialogs close with Escape.
Respects the system's “reduce motion”.
On narrow screens the side bar opens with the menu button.
The theme (light, or dark in black, yellow and grey) follows the system the first time; the moon or sun button changes it and remembers it in that browser.
It can be installed as an app (PWA) to get push notifications.
In English and Spanish, and in any language installed as a translation file (see Translating xBNC). The language is changed on the sign-in page, in Your account or (the server default) in Settings, and applies at once to the panel and to xBNC's messages on IRC, also with the client connected. From IRC it is changed with /msg *status language en.
xBNC stores the messages of channels and private conversations (and, with the Events module, joins, parts, kicks, nick changes, topics and modes). Each message carries the server's time if the network sends it.
Searching
In Networks → (network) → History, or with the API. The search uses a full-text index:
It finds whole words and word beginnings: deplo finds “deployment”.
It ignores case and accents: cafe finds “Café”.
Several words must all appear, in any order. In quotes, the exact phrase is searched: "out of service".
If nothing matches by word, it also looks for pieces in the middle of a word: ployment finds “deployment”.
It combines with the conversation, sender and date filters.
Export and delete
It exports to TXT or JSON (everything, or one conversation). A conversation or the whole history of the network is deleted from the panel, with /msg *status clearbuffer (or clearallbuffers, clearallchannelbuffers, clearallquerybuffers) or with the API.
Retention and quotas
General:Settings → History, days of retention (30 by default; 0, forever).
Per channel: in the channels of each network, or with /msg *status setbuffer: the server setting, store nothing, forever or N days.
Per user: an administrator can set a history space quota (max_history_mb) in Users; usage shows on each user's page.
Space: about 150 bytes per stored message, including the search index.
Log files
Besides the history, the Log files module writes daily text files in the classic IRC format to /var/lib/xbnc/logs/user/network/#channel/YYYY-MM-DD.log, with times in your time zone.
In Your account → Notifications, xBNC tells you about mentions and private messages.
Option
What it does
Default
Turn on notifications
Main switch.
no
Only when I have no client connected
No notice if you have an IRC client or the panel chat open.
yes
When someone writes my nick in a channel
Mentions.
yes
When I get a private message
Private messages.
yes
Channel
What you need
Browser and phone (push)
Click “Turn on in this device” from the panel open on that device. On the phone, install the panel as an app first. It can be turned on on several devices.
Telegram
Create a bot with @BotFather, write to it once and enter its token and your chat id (@userinfobot tells you). The panel never shows the full token again.
Your address. The administrator has to configure the [smtp] section of xbnc.toml.
Your own web address
An https URL that receives a POST with the notification in JSON.
“Send a test notification” sends one to every configured channel. So as not to flood you, xBNC groups notifications: if many arrive in a row, those not sent are counted and the next notification says how many were skipped.
Format of the notification at your own web address
{
"network": "undernet",
"network_id": 1,
"conversation": "#xbnc",
"from": "bob",
"text": "y2k, what time is the meeting tomorrow?",
"private": false,
"ts": 1762851240000,
"skipped": 0
}
With push, tapping the notification opens the panel chat in that conversation.
Modules are native xBNC features, built into the program, that are turned on and configured per network: in the panel (Modules), with the API or from IRC with /msg *status. Changes apply at once, without reconnecting. Every setting is validated (types, ranges, lengths, no control characters). Passwords and keys are stored encrypted and the panel never shows them again: to change them, type them again.
/msg *status modules list and state
/msg *status enable keepnick turn on (disable to turn off)
/msg *status get away show settings
/msg *status set away delay 60 change a setting
/msg *status addline watch patterns *xbnc* add a line to a list
/msg *status dellines watch patterns empty a list
/msg *status availmods every module and plugin, with what it does
Setting types: yes/no (in *status, true, on or 1 for yes and false, off or 0 for no), number (with a minimum and maximum), text, list (one entry per line), choice (one of the values given) and secret (stored encrypted).
Connection and account
Services login
servicesnew in 1.0
Logs you in to X (Undernet), NickServ, Q or AuthServ when connecting and hides your IP before joining channels.
On Undernet it uses Login on Connect: the account goes in the server password and your real IP is never shown. If the network already has a server password, it logs in with /msg X login after connecting. Channels wait until the network confirms the login or the time set below passes. The password is stored encrypted and never shown again.
Setting
Type
Default
Detail
Service
service
choice
auto
Options: auto Automatic, by network, x X (Undernet), nickserv NickServ (Libera, freenode, Rizon…), q Q (QuakeNet), authserv AuthServ (EvilNet, GameSurge).
Account
account
text
empty
Your account name on the service. With NickServ it can be empty if it is your nick.
Password
password
secret
empty
Hide my IP (mode +x)
hide_host
yes/no
yes
On X, Q and AuthServ. Other networks do it themselves.
Log in while connecting (Login on Connect)
login_on_connect
yes/no
yes
Undernet only: the real IP is never shown.
Wait before joining channels (seconds)
wait
number (0–120)
15
Until the network confirms the login. 0: do not wait.
Get my nick back if it is taken
recover_nick
yes/no
yes
With NickServ: asks for the nick (REGAIN or GHOST) after logging in.
Example
Connecting to irc.undernet.org:6667 with Login on Connect…
*** Your host is now y2k.users.undernet.org
* y2k has joined #xbnc
From IRC: /msg *status enable services and /msg *status get services
Invite and unban
invitenew in 1.0
If you cannot join a channel (+i, +k, +l or a ban), asks X or ChanServ to invite or unban you and joins again.
It tries at most three times every ten minutes per channel. You need access in the channel for the service to listen.
Setting
Type
Default
Detail
Channel service
service
choice
auto
Options: auto Automatic, by network, x X (Undernet), chanserv ChanServ, q Q (QuakeNet).
Ask to remove my ban
unban
yes/no
yes
Join when invited to one of my channels
join_invites
yes/no
yes
Example
#xbnc: cannot join channel (+i)
*statusAsking X to let you into #xbnc (+i).
* y2k has joined #xbnc
From IRC: /msg *status enable invite and /msg *status get invite
Client certificate (CertFP)
cert
Identifies you on the network with a certificate instead of a password. Enables SASL EXTERNAL.
Generate a certificate, add its fingerprint to your network account and reconnect. On Libera.Chat: connect identified and type /msg NickServ CERT ADD. Only works with TLS.
Setting
Type
Default
Detail
Use SASL EXTERNAL if the network offers it
sasl_external
yes/no
yes
Example
y2k/msg NickServ CERT ADD
NickServAdded fingerprint 3f9a…c21e to your fingerprint list.
From IRC: /msg *status enable cert and /msg *status get cert
Keep nick
keepnickon by default
If your nick is taken when connecting, xBNC takes it back as soon as it is free.
Checks every 30 seconds whether your nick is free and takes it back as soon as whoever holds it disconnects. If you change nick by hand from your client, it pauses until the next connection.
Example
*statusYour nick y2k is taken; connected as y2k_. I will take it back as soon as it is free.
* y2k_ is now known as y2k
From IRC: /msg *status enable keepnick and /msg *status get keepnick
Channels
Sticky channels
stickychan
Channels you cannot leave by mistake: xBNC blocks /part and rejoins if you leave.
Setting
Type
Default
Detail
Channels
channels
list
empty
One per line.
Example
#xbnc
#xbnc-dev
If you type /part #xbnc, xBNC does not leave the channel.
From IRC: /msg *status enable stickychan and /msg *status get stickychan
Rejoin after a kick
kickrejoin
Rejoins the channel if you are kicked. Stops trying if you are kicked more than 3 times in a minute.
Setting
Type
Default
Detail
Wait before rejoining (seconds)
delay
number (0–600)
10
Example
* bob has kicked you from #xbnc (test)
* y2k has joined #xbnc
From IRC: /msg *status enable kickrejoin and /msg *status get kickrejoin
Automatic op and voice
autoop
Gives op, halfop or voice to the people you choose when they join your channels (if you have op).
Setting
Type
Default
Detail
Wait before giving the mode (seconds)
delay
number (0–30)
3
People
entries
list
empty
One per line: mode (o, h or v), mask and, optionally, comma-separated channels (or *) and a key. With a key, xBNC sends a challenge and only gives the mode if the other person answers with the same key from their own xBNC.
Example
o *!*@home.example.org #xbnc
v friend!*@* *
o bob!*@* #xbnc shared-key
From IRC: /msg *status enable autoop and /msg *status get autoop
Get op back when left alone
autocyclenew in 1.0
If you are left alone in a channel without op, leaves and joins again to get it. Never in +i or keyed channels.
Setting
Type
Default
Detail
Only in these channels
channels
list
empty
One per line. Empty: in all of them.
Example
* bob has left #xbnc
*statusYou are alone in #xbnc without op: leaving and joining again.
* y2k has joined #xbnc (with op)
From IRC: /msg *status enable autocycle and /msg *status get autocycle
Detached channels
detachnew in 1.0
Channels that keep recording everything but do not appear in your client. They come back if someone mentions you.
From your client: /quote DETACH #channel to detach and /join #channel to see it again. The latest messages are shown when it comes back.
Setting
Type
Default
Detail
Channels
channels
list
empty
One per line. * works (for example #*-bots).
Show the channel if someone mentions my nick
on_mention
yes/no
yes
Show the channel if one of these nicks writes
nicks
list
empty
One per line.
Example
/quote DETACH #xbnc detaches the channel (it keeps recording)
/quote ATTACH #xbnc or /join #xbnc: shows it again
If someone writes your nick in #xbnc, the channel comes back with the latest messages.
From IRC: /msg *status enable detach and /msg *status get detach
Hide flooding channels
flooddetachnew in 1.0
If a channel suddenly fills with messages, it is hidden for a while so your client does not freeze. It keeps recording.
Setting
Type
Default
Detail
Messages
messages
number (3–1000)
25
How many messages…
…in how many seconds
seconds
number (1–600)
10
Quiet seconds before showing it again
calm
number (10–3600)
60
Example
Messages: 25
…in how many seconds: 10
Quiet seconds before showing it again: 60
From IRC: /msg *status enable flooddetach and /msg *status get flooddetach
Messages
Automatic away
awayon by default
Marks you away when you close all your clients and, when you come back, sums up what you missed.
Setting
Type
Default
Detail
Away message
message
text
empty
Empty: “Away” in your language.
Wait before marking you away (seconds)
delay
number (0–3600)
60
When I come back, tell me about private messages and mentions received
summary
yes/no
yes
Nick while you are away
nick
text
empty
Empty: unchanged. {nick} is your nick, for example {nick}
Example
Away message: Back soon
Nick while away: {nick}|away
When you return, *status sums up: “While you were away: 3 private messages (bob, carol) and 2 mentions in #xbnc”.
From IRC: /msg *status enable away and /msg *status get away
Watch words
watchon by default
Copies messages containing what you care about to the *watch conversation, even if you were not connected.
Setting
Type
Default
Detail
Also tell me when someone mentions my nick
mentions
yes/no
no
Patterns
patterns
list
empty
One per line. * matches any text: *deploy* finds “the deploy failed”. You can add a channel or nick after it to narrow it down.
Example
*deploy*
*xbnc* #xbnc
*urgent* bob
Matches arrive in the *watch conversation.
From IRC: /msg *status enable watch and /msg *status get watch
Aliases
aliason by default
Your own commands: you type /ghost bob and xBNC sends /msg NickServ GHOST bob.
Setting
Type
Default
Detail
Aliases
aliases
list
empty
One per line: name and command. $1, $2… are the arguments, $1- all of them from the first, and $nick your nick.
Example
ghost /msg NickServ GHOST $1
op /mode $1 +o $nick
greet /msg $1 hi, $1, how are you?
In your client: /ghost y2k (or /quote ghost y2k if your client does not send it)
From IRC: /msg *status enable alias and /msg *status get alias
Ignore
ignorenew in 1.0
Messages from people you ignore never reach any client or the history. On Undernet it uses SILENCE so they are not even sent.
On IRC: /msg *ignore add nick, /msg *ignore del nick and /msg *ignore list.
Setting
Type
Default
Detail
Ignore these
masks
list
empty
One per line: a nick or a nick!user@host mask (* works).
Use SILENCE when the network allows it
silence
yes/no
yes
Example
/msg *ignore add pest
/msg *ignore add *!*@spam.example
/msg *ignore list
/msg *ignore del pest
From IRC: /msg *status enable ignore and /msg *status get ignore
Remove colours
stripcontrolsnew in 1.0
Removes colours, bold, underline and blinking from messages, also in the history.
Setting
Type
Default
Detail
Only in these channels
channels
list
empty
One per line. Empty: in all of them.
Example
Only in these channels:
#xbnc
Before: \x0304,01red\x03 \x02bold\x02 After: red bold
From IRC: /msg *status enable stripcontrols and /msg *status get stripcontrols
Character encoding
charsetnew in 1.0
Accents show correctly even when a channel uses Latin-1, and you can write in Latin-1 where needed.
xBNC always understands Latin-1 messages that are not UTF-8. This module picks the exact table and the channels or private chats where your messages are sent in that encoding (for people with very old clients).
Setting
Type
Default
Detail
Legacy encoding
legacy
choice
windows-1252
Options: windows-1252 Windows-1252 (the most common), iso-8859-15 ISO-8859-15 (with €), iso-8859-1 ISO-8859-1 (Latin-1).
Send in that encoding to
send_legacy
list
empty
Channels or nicks, one per line. * for all. Empty: always UTF-8.
Example
Legacy encoding: Windows-1252
Send in that encoding to:
#xbnc
grandpa
From IRC: /msg *status enable charset and /msg *status get charset
Message encryption (FiSH)
cryptnew in 1.0
Encrypts your messages in channels and private chats with shared keys, compatible with FiSH for mIRC, HexChat and irssi.
On IRC: /msg *crypt setkey #channel key, /msg *crypt keyx nick to agree a key in private and +p before a message to send it unencrypted. Keys are stored encrypted. FiSH protects from the network's servers; it is not a substitute for end-to-end encryption.
Setting
Type
Default
Detail
Keys
keys
secret list
empty
One per line: channel or nick and key (cbc: first for CBC mode). They are stored encrypted: write all of them again to change them.
Mark for decrypted messages
prefix
text
empty
For example [encrypted] . Empty: no mark.
Accept key exchanges (DH1080)
key_exchange
yes/no
yes
When someone proposes a key in private.
Example
/msg *crypt setkey #xbnc cbc:a-long-key
/msg *crypt keyx bob agree on a key in private (DH1080)
/msg *crypt list
/msg *crypt delkey #xbnc
+p hello sends this message unencrypted
From IRC: /msg *status enable crypt and /msg *status get crypt
Scheduled tasks
schedulenew in 1.0
Commands at a fixed time or every so often, in your time zone.
Pick the time zone in Your account. Tasks only run while the network is connected.
Setting
Type
Default
Detail
Tasks
tasks
list
empty
One per line: time and command (08:00 /away Working), days and time (mon-fri 18:00 /msg #channel bye) or interval (every 30m /msg #channel still here). Days: mon tue wed thu fri sat sun, weekdays, weekend.
Example
mon-fri 08:00 /away Working
mon-fri 15:00 /away
weekend 10:30 /msg #xbnc Good morning!
every 2h /msg #xbnc-dev still here
From IRC: /msg *status enable schedule and /msg *status get schedule
CTCP replies
ctcpon by default
Answers VERSION, PING and TIME when no client is connected, stops CTCP floods and lets you hide your version.
Setting
Type
Default
Detail
CTCP requests allowed every 10 seconds
flood_limit
number (1–100)
5
VERSION reply
version
text
empty
Empty: your client's, or xBNC's if none is connected.
CTCPs to ignore
block
list
empty
One per line, for example TIME or FINGER.
Example
CTCP requests allowed every 10 seconds: 5
Reply to VERSION: mIRC v7.77
CTCP to ignore:
FINGER
TIME
From IRC: /msg *status enable ctcp and /msg *status get ctcp
Hide the MOTD
block_motd
Does not show the server's message of the day to your clients.
Example
No settings. When on, your clients do not receive the server's message of the day.
From IRC: /msg *status enable block_motd and /msg *status get block_motd
History and DCC
Events in the history
eventson by default
Keeps joins, parts, kicks, nick changes, topics and modes next to the messages.
Modern clients get them as real events; the rest as messages from *events when the history is played back.
Setting
Type
Default
Detail
Keep joins, parts and quits
joins
yes/no
yes
In big channels they make a lot of noise.
Keep kicks, nick changes, topics and modes
other
yes/no
yes
Example
With “Store kicks, nick changes, topics and modes” on, when the history is played back
you also see: “bob changed the topic to: Meeting at 20:00”.
From IRC: /msg *status enable events and /msg *status get events
Log files
log
Writes every conversation to daily text files, in the classic IRC log format.
The files stay on the server, in the logs folder next to the database (for example /var/lib/xbnc/logs/user/network/#channel/2026-11-08.log). Times are in your time zone (Your account).
Setting
Type
Default
Detail
Delete files older than (days)
retention_days
number (0–3650)
0
0 keeps them forever.
Include events (joins, parts, kicks…)
events
yes/no
yes
Example
/var/lib/xbnc/logs/y2k/undernet/#xbnc/2026-11-11.log
[18:30:12] <bob> hello everyone
[18:30:20] * carol has joined
From IRC: /msg *status enable log and /msg *status get log
Replies to whoever asked
route_replieson by default
Replies to WHOIS, WHO, LIST, NAMES or ban lists go only to the device that asked, not to all of them.
Example
If you run /whois bob from your phone, the answer reaches only the phone,
not the laptop that is also connected.
From IRC: /msg *status enable route_replies and /msg *status get route_replies
DCC through xBNC
dcc
DCC file transfers and chats go through xBNC: the other person does not see your IP and they work even behind a router.
xBNC opens a port for each transfer and joins the two connections; nothing is stored on disk. On a server with a public IP it works with no setup; behind NAT, set the public IP in [dcc] public_ip in xbnc.toml and open the [dcc] ports in the firewall.
Setting
Type
Default
Detail
Largest transfer (MB)
max_size_mb
number (0–1000000)
4096
0 = no limit.
Seconds to accept a transfer
wait
number (30–900)
180
Allow transfers from private addresses
private_addresses
yes/no
no
When off, xBNC does not connect to internal networks (10.x, 192.168.x…) even if someone on IRC asks.
Example
# xbnc.toml, only if the server is behind a router (NAT):
[dcc]
public_ip = "203.0.113.10"
ports = "50000-50100"
From IRC: /msg *status enable dcc and /msg *status get dcc
Plugins add features for the whole channel. They run in sandboxed WebAssembly: they cannot read files or see other networks, they have CPU and memory limits, and they only do what their permissions allow, which are shown before installing them. Those that go out to the internet can only connect to approved sites.
The 18 official plugins come installed: xBNC installs the missing ones on the first start and after each update, and updates those with an old version. They stay off on every network. One that a superadministrator uninstalls on purpose does not come back until the next version of xBNC (or until it is installed again from Plugins → Catalogue).
A superadministrator can install others from the remote catalogue or by uploading a .wasm file, and approves the websites of plugins that go out to the internet (“Sites it may connect to”): link titles, weather and news.
Each user turns them on in their networks from Modules, in the Plugins section, and adjusts their settings.
They answer in your xBNC language (or the one of their “Language” setting) and also work with your own messages. The official plugins speak English and Spanish; in another language they answer in English.
Permission
What it allows
send
Send messages and notices.
raw
IRC commands such as KICK, MODE or INVITE. Never QUIT, PASS, USER, NICK, CAP, AUTHENTICATE, OPER, SERVER or SQUIT.
timer
Receive an event every minute (or every 5–60 seconds if the plugin asks for it).
presence
See joins, parts, kicks, nick changes and the user list of the channels.
http
Request https pages, only from the domains it declares (up to 10) and that the superadministrator approves.
Trivia
trivia 1.0.0new in 1.0
A quiz game for your channels, with hints, points and a ranking.
How to use it
In a channel: !trivia to start, !trivia stop, !trivia top (also as !trivial). Answer by writing in the channel. /msg *trivia add Question? | answer adds your own questions; list and del N manage them.
Example
carol!trivia
y2kTrivia starts! 10 questions; answer by writing in the channel.
y2kQuestion 1/10: What is the capital of Japan?
bobtokyo
y2kCorrect, bob! It was “Tokyo”. You have 1 point.
Setting
Default
Detail
Command
trigger
!trivia
Questions per game
rounds
10
Seconds to answer
seconds
40
A hint appears halfway.
Only in these channels
channels
empty
One per line. Empty: in every channel.
Language of the answers
lang
empty
Empty: your language in xBNC. Or es / en.
Permissions: send messages, timer. Help from IRC: /msg *trivia help
Welcome
greet 1.0.0new in 1.0
Greets people who join your channels, with a different message the first time and personal greetings.
How to use it
Automatic. /msg *greet test #channel nick shows what would be said. {nick} and {channel} are replaced in the messages.
Example
* carol has joined #xbnc
y2kWelcome to #xbnc, carol! It's your first time here.
Setting
Default
Detail
First time
first
Welcome to {channel}, {nick}! It's your first time here.
For people who have never been in the channel. {nick} and {channel} are replaced.
When they come back
message
Hi {nick}, welcome back to {channel}.
Empty: returning people are not greeted.
Personal greetings
personal
empty
One per line: nick and message. Replaces the usual one for that person.
Greet privately (notice)
notice
no
Off: the greeting is written in the channel.
Minutes before greeting the same person again
cooldown
30
Never greet
ignore
empty
Nicks, one per line (bots, services).
Only in these channels
channels
empty
One per line. Empty: in every channel.
Language of the answers
lang
empty
Empty: your language in xBNC. Or es / en.
Permissions: send messages, joins and parts. Help from IRC: /msg *greet help
Link titles
urltitle 1.0.0new in 1.0
Shows the title of YouTube, Vimeo, SoundCloud, Dailymotion and Spotify links, Wikipedia articles and GitHub repositories pasted in your channels.
How to use it
Automatic. An administrator approves each site in the panel (Plugins → «Sites it may connect to»).
Example
boblook at this https://www.youtube.com/watch?v=…
y2kYouTube: How to set up an IRC bouncer in 5 minutes
Setting
Default
Detail
Seconds between titles in a channel
cooldown
5
Also for your own links
own
yes
Only in these channels
channels
empty
One per line. Empty: in every channel.
Language of the answers
lang
empty
Empty: your language in xBNC. Or es / en.
Permissions: send messages, web access to approved sites. Help from IRC: /msg *urltitle help
Definitions
factoids 1.0.0new in 1.0
The channel's memory: short answers to frequent questions that you and your editors keep up to date.
How to use it
In a channel: !def word (or ?word), !def word = text, !def -word, !defs. Here: /msg *factoids word.
Example
y2k!def rules = Be respectful and do not flood.
carol?rules
y2krules: Be respectful and do not flood.
Setting
Default
Detail
Command
trigger
!def
Answer ?word too
question_mark
yes
Who else can change definitions
editors
empty
Nicks, one per line. You always can.
Only in these channels
channels
empty
One per line. Empty: in every channel.
Language of the answers
lang
empty
Empty: your language in xBNC. Or es / en.
Permissions: send messages. Help from IRC: /msg *factoids help
Weather
weather 1.0.0new in 1.0
The weather now and today's forecast for any city, from Open-Meteo (free, no key).
How to use it
In a channel: !weather London (also !tiempo or !clima). Here: /msg *weather Paris.
Example
bob!weather London
y2kLondon (United Kingdom): 14 °C, clear sky, wind 9 km/h, humidity 60 %. Today between 9 and 16 °C.
Setting
Default
Detail
Command
trigger
!weather
Default city
city
empty
Used when no city is written.
Only in these channels
channels
empty
One per line. Empty: in every channel.
Language of the answers
lang
empty
Empty: your language in xBNC. Or es / en.
Permissions: send messages, web access to approved sites. Help from IRC: /msg *weather help
News
news 1.0.0new in 1.0
Posts new entries from RSS and Atom feeds (news sites, blogs, YouTube channels, GitHub releases) in your channels.
How to use it
Set the feeds in the panel: one per line, "#channel https://...". /msg *news list, /msg *news check.
Example
y2k[Hacker News] Show HN: an IRC bouncer written in Rust — https://…
Setting
Default
Detail
Feeds
feeds
empty
One per line: channel and address, for example #xbnc https://hnrss.org/frontpage. An administrator approves the sites.
Minutes between checks
interval
15
Language of the answers
lang
empty
Empty: your language in xBNC. Or es / en.
Permissions: send messages, timer, web access to approved sites. Help from IRC: /msg *news help
Karma
karma 1.0.0new in 1.0
Points between people: nick++ and nick--, with a ranking.
How to use it
In a channel: nick++, nick--, !karma nick, !karma top. Here: /msg *karma nick.
Example
bobcarol++
y2kcarol has 1 karma.
carol!karma top
Setting
Default
Detail
Only in these channels
channels
empty
One per line. Empty: in every channel.
Language of the answers
lang
empty
Empty: your language in xBNC. Or es / en.
Permissions: send messages. Help from IRC: /msg *karma help
Quotes
quotes 1.0.0new in 1.0
Memorable lines of the channel: save them, find them and pull a random one.
How to use it
In a channel: !quote, !quote 12, !quote search word, !quote add text, !quote del 12 (also as !cita).
Example
bob!quote add today is a great day
y2kQuote 1 saved.
carol!quote 1
y2k#1: today is a great day
Setting
Default
Detail
Command
trigger
!quote
Anybody can add quotes
anyone_adds
yes
Off: only you and the editors.
Editors
editors
empty
Nicks who can add and delete quotes, one per line.
Only in these channels
channels
empty
One per line. Empty: in every channel.
Language of the answers
lang
empty
Empty: your language in xBNC. Or es / en.
Permissions: send messages. Help from IRC: /msg *quotes help
Forbidden words
badwords 1.0.0new in 1.0
Warns, kicks and finally bans people who write forbidden words in your channels (you need op).
How to use it
Set the words in the panel. /msg *badwords test text tells whether a text would be caught.
Example
carolyou are a dummy
y2kcarol: that word is not allowed here. Next time, kick.
Setting
Default
Detail
Forbidden words
words
empty
One per line. Case and accents do not matter; a * at the end matches the start of a word.
Ban length (minutes)
ban_minutes
10
0: the ban stays.
Kick reason
reason
empty
Warn privately (notice)
warn_private
no
Never act on
exempt
empty
Nicks, one per line.
Only in these channels
channels
empty
One per line. Empty: in every channel.
Language of the answers
lang
empty
Empty: your language in xBNC. Or es / en.
Permissions: send messages, IRC commands (kick, ban), timer. Help from IRC: /msg *badwords help
Games
games 1.0.0new in 1.0
Hangman, magic 8 ball and rock-paper-scissors for your channels.
How to use it
In a channel: !8ball question, !rps rock, !hangman and !g letter (or the whole word) to play.
Example
bob!rps rock
y2krock against scissors: you win, bob!
bob!8ball will it work?
y2kThe 8 ball says: “Ask again later”.
Setting
Default
Detail
Only in these channels
channels
empty
One per line. Empty: in every channel.
Language of the answers
lang
empty
Empty: your language in xBNC. Or es / en.
Permissions: send messages. Help from IRC: /msg *games help
Dice
dice 1.1.0
Rolls dice in channels and private messages: !dado, !dado 2d6, !dado d20.
How to use it
In a channel or private message: !dado (one six-sided die), !dado 2d6, !dado d20. Also !roll and !dice.
/msg *dice 3d8 rolls for you alone.
Example
bob!roll 2d6
y2kbob rolls 2 6-sided dice: 3 and 5 (total 8).
Setting
Default
Detail
Extra command
trigger
!roll
!dado, !dados, !roll and !dice always work.
Most dice per roll
max_dice
10
Answer in private messages
in_queries
yes
Language of the answers
lang
empty
Empty: your language in xBNC. Or es / en.
Permissions: send messages. Help from IRC: /msg *dice help
Polls
poll 1.1.0
!poll Question? | option | option, then !vote N. Closes on its own and announces the winner.
How to use it
In a channel: !poll Pizza or sushi? | pizza | sushi (up to 10 options) — vote with !vote 1 — !poll shows the count — !poll end closes it (who started it, or you). It closes on its own after the minutes in the settings.
Example
carol!poll Coffee or tea? | coffee | tea
bob!vote 2
*pollVote recorded in #xbnc: 2) tea. You can change it by voting again.
Setting
Default
Detail
Command
trigger
!poll
Vote command
vote_trigger
!vote
Minutes until it closes
minutes
60
Other people can start polls
public
yes
Off: only you start them; everyone can vote.
Language of the answers
lang
empty
Empty: your language in xBNC. Or es / en.
Permissions: send messages, timer. Help from IRC: /msg *poll help
Reminders
remind 1.1.0
Reminds you (or anyone in the channel) after a while or at a time: !remind 10m take the pizza out, !remind 18:30 meeting.
How to use it
In a channel: !remind 10m take the pizza out — !remind 2 hours call Bob — !remind 1h30m … — !remind 18:30 meeting — !remind list — !remind clear. Also !recuerda.
/msg *remind 10m … reminds you privately. Reminders are checked every minute.
Example
bob!remind in 10 minutes take the pizza out
y2kbob: OK, I will remind you in 10 minutes.
y2kbob: take the pizza out
Setting
Default
Detail
Extra command
trigger
!remind
!remind and !recuerda always work.
Other people can set reminders
public
yes
Off: only your own and /msg *remind.
Pending reminders per person
max_per_nick
10
Time zone for times like 18:30
timezone
Europe/Madrid
Europe/Madrid, Europe/London, UTC or a difference such as +1.
Language of the answers
lang
empty
Empty: your language in xBNC. Or es / en.
Permissions: send messages, timer. Help from IRC: /msg *remind help
Tell
tell 1.1.0
!tell nick message: delivers it in the channel when that person speaks or joins.
How to use it
In a channel: !tell bob the build is fixed — bob gets it in the channel as soon as he writes something or joins. Works for you and for anyone in the channel.
Example
y2k!tell carol the build is fixed
y2ky2k: OK, I will tell carol as soon as she speaks or joins the channel.
* carol has joined #xbnc
y2kcarol: y2k left you a message 5 minutes ago: the build is fixed
Setting
Default
Detail
Command
trigger
!tell
Deliver when they join
on_join
yes
Off: only when they write something.
Messages waiting per person
max_per_nick
5
Language of the answers
lang
empty
Empty: your language in xBNC. Or es / en.
Permissions: send messages, joins and parts. Help from IRC: /msg *tell help
Autoresponder
autoresponder 1.1.0
Answers on its own when someone writes a keyword, and (optionally) private messages while you are away.
How to use it
Write the rules in the settings, one per line: keyword = answer. {nick} is who wrote and {channel} the channel. The rule * = text answers any private message.
/msg *autoresponder shows the rules; /msg *autoresponder test <text> tries them.
Example
bobhi, anyone?
y2kHi bob! I am not here right now, leave me your message.
Setting
Default
Detail
Rules
rules
empty
One per line: keyword = answer. Example: hola = Hello {nick}! — * = answer to any private message.
Seconds before the same rule answers again
cooldown
60
Also answer private messages
in_queries
yes
Only when no client is connected
only_when_away
no
So it answers for you while you are away.
Only in these channels
channels
empty
One per line. Empty: every channel.
Language of the messages
lang
empty
Empty: your language in xBNC. Or es / en.
Permissions: send messages. Help from IRC: /msg *autoresponder help
Channel stats
chanstats 1.1.0
!stats: messages in the channel and who talks most. !stats nick: one person's count.
How to use it
In a channel: !stats shows the messages and who talks most — !stats bob shows one person. /msg *chanstats #channel shows it to you privately. Counts are saved every minute.
Example
bob!stats
y2k#xbnc: 1204 messages since 3 Nov 2026. Most active: carol 312, bob 280, y2k 199.
Setting
Default
Detail
Command
trigger
!stats
People in the ranking
top
5
Answer other people
public
yes
Off: only your own !stats.
Only these channels
channels
empty
One per line. Empty: every channel.
Language of the answers
lang
empty
Empty: your language in xBNC. Or es / en.
Permissions: send messages, timer. Help from IRC: /msg *chanstats help
Seen
seen 2.1.0
!seen nick: when someone was last seen and what they were doing, or which channels they are in right now.
How to use it
In a channel: !seen bob — when bob was last seen and whether he is here now. /msg *seen bob, or /msg *seen stats.
Example
bob!seen carol
y2kcarol was last seen 2 hours ago, in #xbnc saying “off to dinner”.
Setting
Default
Detail
Command
trigger
!seen
What people write before the nick.
Answer other people in channels
public
yes
Off: only answers you (your own !seen and /msg *seen).
Seconds between answers in a channel
cooldown
5
Show what they last said
show_text
yes
Off: only says where and when.
Answer only in these channels
channels
empty
One per line. Empty: in every channel. Activity is recorded in all of them.
Language of the answers
lang
empty
Empty: your language in xBNC. Or es / en.
Permissions: send messages, joins and parts. Help from IRC: /msg *seen help
Anti-flood
antiflood 1.1.0
Warns, kicks or bans people who flood or repeat lines in your channels (you must be a channel operator).
How to use it
Works by itself in the channels you list in the settings (you must be a channel operator there). /msg *antiflood shows its settings.
Example
spama
spama
spama
* spam was kicked from #xbnc by y2k (flood)
Setting
Default
Detail
Channels to protect
channels
empty
One per line. It does nothing until you list some.
Most lines allowed
lines
5
In this many seconds
seconds
4
Same line repeated
repeats
3
Times in a row; 0 turns it off.
What to do
action
kick
warn (warn first, kick the second time), kick or ban.
Ban length in minutes
ban_minutes
10
0: the ban is not lifted.
Kick reason
reason
empty
Never act on these nicks
exempt
empty
One per line.
Language of the messages
lang
empty
Empty: your language in xBNC. Or es / en.
Permissions: IRC commands (kick, ban), send messages, timer. Help from IRC: /msg *antiflood help
Everything, including system settings, plugins, backups, updates, import and managing other administrators.
admin
Manages users (except administrators and superadministrators), every network and security; sees the system.
moderator
Sees users and the audit log, and can suspend and reactivate normal and limited users.
user
Manages their own networks, channels and history. It is the default role.
limited
Uses networks an administrator prepares for them; manages channels and history, but does not add, edit or delete networks.
What each role can do
Permission
superadmin
admin
moderator
user
limited
Edit system settings, install plugins, backups, import, update
yes
—
—
—
—
See the system, manage users, security and every network
yes
yes
—
—
—
See users and the audit log, suspend users
yes
yes
yes
—
—
Manage their own networks
yes
yes
yes
yes
—
Use their networks, channels, history, chat, modules and plugins
yes
yes
yes
yes
yes
An administrator only manages roles below their own; nobody can change their own role or promote themselves, and there is always at least one superadministrator. Users do not see other people's networks (the API answers 404).
Accounts
User names: 1 to 32 characters A-Z a-z 0-9 . _ -, not case-sensitive. Passwords: at least 10 characters.
Per-user quotas: networks (max_networks), devices (max_clients) and history space in MB (max_history_mb). Empty: the server's general value.
Suspending an account closes its access and disconnects its networks. Deleting it also removes its networks and history.
Changing or resetting the password closes the account's other panel sessions.
Ways to sign in to the panel
User and password, with optional two-step verification (TOTP, RFC 6238, with a QR code) using Google Authenticator, Aegis, 1Password or another compatible app. The secret is stored encrypted; a wrong code counts as a failed attempt. IRC clients still sign in with the password only: the protocol has no second step.
Passkeys (WebAuthn): sign in with the device's fingerprint, face or PIN. They are added and removed in Your account.
OpenID Connect (Google, Microsoft, Keycloak, Authentik…) and LDAP, if the administrator configures them in xbnc.toml (see Configuration). An external account is linked and unlinked in Your account.
Security measures
Area
Measure
Passwords
Argon2id (m = 19 MiB, t = 2, p = 1). A user that does not exist costs the same to check, so as not to reveal which accounts exist. At most one computation per CPU at a time, so a flood of attempts cannot exhaust the memory.
Stored credentials
SASL and server passwords, channel keys, commands on connect, services passwords, FiSH keys, TOTP secrets and certificate keys, encrypted with XChaCha20-Poly1305. The key lives outside the database (secret.key, permissions 0600 checked at start).
TLS
rustls, TLS 1.2 and 1.3 only. Towards the networks the certificate is verified with the system store, with no option to turn it off. A trusted certificate (addfingerprint) accepts only that exact SHA-256 fingerprint, and the signatures of the TLS handshake are still checked.
Plain text
Forbidden on public addresses unless insecure = true is set explicitly. The default port without TLS only accepts the local network.
Brute force
IP blocked after 5 failures in 10 minutes for 15 minutes (configurable), shared between IRC (PASS and SASL), the panel and API tokens. At most 16 unauthenticated IRC connections per IP.
Logs
Passwords, AUTHENTICATE, OPER and messages to services (NickServ IDENTIFY, X LOGIN, Q AUTH…) are never logged, at any level.
Input
Lines limited to 8703 bytes; any CR or LF inside a line cuts it, to prevent command injection; nicks, names and texts of modules and plugins validated.
Resources
Limits on total connections and on clients and networks per user; bounded queues per client and towards the server; maximum registration time.
SQL
Every query uses bound parameters.
Privileges
The service runs as the unprivileged xbnc user, with systemd isolation. In Docker: unprivileged user, read-only, no capabilities.
Panel: sessions
A random 256-bit token in an HttpOnly, SameSite=Strict and Secure cookie; the database only holds its SHA-256. Sliding expiry (12 h by default).
Panel: CSRF and origin
CSRF token required on every request that changes something; requests and WebSockets with another site's Origin are refused.
Panel: headers
Strict CSP without external or inline scripts, X-Frame-Options: DENY, nosniff, Referrer-Policy: no-referrer, HSTS with TLS and Cache-Control: no-store on the API.
Panel: abuse
20 requests per second per IP, bodies up to 256 KB (4 MB for plugins), 64 connections per IP and 4,096 in total, closing after 2 minutes without traffic and 10 s for the TLS handshake. With trust_proxy the real IP behind Nginx is used.
First access
The first account can only be created with a one-time code that the service writes to its log.
Secrets in the API
Passwords are never returned; only whether they exist.
API tokens
256 bits with the xbnc_ prefix; only their SHA-256 is stored. Scope, expiry, last use and IP visible, immediate revocation.
Modules
Built into the program: no external code is loaded. Auto op: *!*@* is refused; with a key, a random challenge answered with HMAC-SHA256 and compared in constant time.
Log files
Paths built only from sanitised names (a channel cannot leave its folder), folders 0700 and files 0600.
Plugins
WebAssembly in the Wasmi interpreter, with fuel (CPU) per event, 16 MB of memory, no access to files or a network of their own. Actions checked against the permissions. Only a superadministrator installs, seeing the SHA-256 fingerprint; a file that changes after installation is not loaded. Five failures in a row stop it.
DCC
Offers from IRC cannot make xBNC connect to internal networks or to ports below 1024 (unless allowed). For your own sends it only connects back to your device's IP. Each port accepts a single connection, with a timeout, a size limit and closing when idle.
Services login
On networks without NickServ (IRCnet, EFnet) automatic mode never sends the password.
Translations
Language files are data, not code: an IRC text with line breaks or control characters, or with placeholders different from the English ones, is ignored. Files of 2 MB at most and 100 languages.
FiSH
Key exchange accepts 5 requests per minute and 100 keys per session; decrypted text cannot break lines.
Webhooks
http(s) only, no user in the URL or control characters, certificate verified, no redirects followed, 10 s at most.
Import
Superadministrators only; it never grants that role; the preview returns no secrets.
Audit log
Every action of the panel, of IRC and of the command line is recorded with date, author and detail (Security → Audit, or xbncd audit).
Fuzzing
IRC parser, DCC, importer, search, manifests and plugin execution, every week in CI with earlier failures as regressions.
Recommendations
Use a real certificate (Let's Encrypt) and do not publish plain-text ports.
Keep backups encrypted: they contain the secret key.
Every connection to the networks leaves from the server's IP: if a user misbehaves on a network, the server's IP may end up banned.
Turn on two-step verification or a passkey on administration accounts.
To report a vulnerability, follow SECURITY.md: do not publish it; use the repository's private advisory. Answer within 3 days at most, assessment within 7 and a fix within 90 at most. The complete threat model is in Threat model.
/etc/xbnc/xbnc.toml only holds process settings: users, networks and channels are in the database. The installer creates it for you. After editing it: sudo xbncd check-config and sudo systemctl restart xbnc. An unknown key is an error, so that a typo does not go unnoticed. What you set in Settings in the panel takes priority over [history] and [limits].
General
Key
Default
What it does
hostname
xbnc
Server name the clients see.
language
en
Default language of the IRC messages and of the panel: en, es or any language installed in locales_dir. Each user can choose their own. The installer sets the one you choose.
locales_dir
/etc/xbnc/locales
Folder of extra languages and corrections (one <code>.json file per language, see Translating xBNC). Read at start and on reload. The XBNC_LOCALES_DIR environment variable takes priority.
secret_key_file
required
Path of the 32-byte key that encrypts the credentials. Created by xbncd init with permissions 0600. Back it up: without it they cannot be decrypted.
[database]
Key
Default
What it does
path
required
SQLite file. Its folder is also the data folder (plugins, log files, backups).
url
—
PostgreSQL instead of SQLite: postgres://user:password@host/db. Add ?sslmode=require to encrypt the connection.
ha
false
High availability (needs url): several nodes share the database; one works and the others wait to take over.
[[listen]] (one per port)
Key
Default
What it does
address
required
Address and port: 0.0.0.0:6697 or [::]:6697.
tls_cert
—
Certificate chain (PEM). Required unless insecure, lan_only or a loopback address.
tls_key
—
Private key (PEM).
insecure
false
Allow plain text on a public address (not recommended).
lan_only
false
Local network clients only (loopback, private, link-local, CGNAT/Tailscale, IPv6 ULA). The rest get an explanatory ERROR. May go without TLS.
[web]
Key
Default
What it does
address
required
Address of the panel and the API, for example 0.0.0.0:8443 or 127.0.0.1:8080. Without [web], there is no panel.
tls_cert, tls_key
—
Certificate of the panel.
insecure
false
Unencrypted HTTP on a public address (only behind a TLS proxy on a trusted network).
secure_cookies
true with TLS
Marks the session cookie as Secure. Set it to true behind a TLS proxy.
public_url
—
Public URL of the panel (https://bnc.example.org). Used to check the Origin of requests and as the base for OpenID Connect.
session_hours
12
Session length in hours (sliding).
trust_proxy
false
Trust X-Forwarded-For and X-Real-IP from a proxy on loopback, so blocking uses the real IP.
[history]
Key
Default
What it does
retention_days
30
Delete messages older than N days. 0, forever.
playback_max
1000
Maximum messages played back on reconnection.
new_client_playback
100
Messages for a device that had never connected.
chathistory_max
500
Maximum limit accepted in CHATHISTORY.
[limits]
Key
Default
What it does
max_connections
1000
IRC client connections in total.
max_clients_per_user
10
Devices connected per user.
max_networks_per_user
10
Networks per user.
login_max_failures
5
Failures before an IP is blocked.
login_window_seconds
600
Window in which failures are counted.
login_block_seconds
900
Length of the block.
registration_timeout_seconds
30
Time for a client to complete its login.
client_queue
4096
Lines queued per client before disconnecting it as slow (when it comes back it gets what was pending).
[log]
Key
Default
What it does
level
info
Filter: error, warn, info, debug, trace, or per module (info,xbncd=debug). The RUST_LOG variable takes priority. Credentials are masked at every level.
[dcc]
Key
Default
What it does
public_ip
the one xBNC uses towards the network
Address announced on IRC. Needed behind NAT. If the connection to the network is IPv6, the public IPv4 is announced.
ports
any free one
Range for the tunnels, for example 50000-50100 (open it in the firewall and the router).
[smtp] (e-mail notifications)
Key
Default
What it does
host
required
Mail server.
port
587
Port.
security
starttls
starttls, tls (port 465) or none (local relays only).
username, password
—
Credentials.
from
required
Sender, for example xBNC <xbnc@example.org>.
[oidc] (OpenID Connect)
Key
Default
What it does
name
—
Text of the button (Google, Keycloak…).
issuer
required
URL of the issuer; its /.well-known/openid-configuration is read.
client_id, client_secret
required
Details of the application registered with the provider. The return address is https://YOUR-PANEL/api/v1/auth/oidc/callback (uses public_url).
scopes
openid profile email
Scopes requested.
username_claim
preferred_username
Claim used as the name of automatically created accounts.
auto_create
false
Create the xBNC account the first time someone signs in.
link_existing
false
Link with a local account of the same name (only with providers you control).
default_role
user
Role of the accounts created.
allowed_groups
[] (everyone)
If given, only members of one of these groups may sign in.
groups_claim
groups
Claim with the groups.
[ldap]
Key
Default
What it does
url
required
ldaps://host:636, or ldap://host:389 with starttls = true.
starttls
false
Use STARTTLS.
bind_dn
required
DN used to bind; {{username}} is replaced by the name typed.
ca_file
system certificates
PEM file with the authority of the LDAP server.
auto_create, link_existing, default_role
false, false, user
As in [oidc].
[updates]
Key
Default
What it does
check
true
Check once a day whether there is a new version (and when an administrator asks). Downloads nothing.
repository
—
GitHub repository with the releases (owner/name).
url
—
Or any https URL answering {{"version", "url", "notes", "packages"}}.
public_key
—
Ed25519 public key of the releases. With it, and xBNC installed from a package, a superadministrator can update from the panel.
Every command accepts -c PATH (or the XBNC_CONFIG variable; /etc/xbnc/xbnc.toml by default), -h for help and -V for the version. Those that write to the database are run as the service user: sudo -u xbnc xbncd … (in Docker, docker compose exec xbnc xbncd …). Afterwards, sudo systemctl reload xbnc so the service applies the changes to users, networks and channels. Every action is recorded in the audit log.
Command
What it does
Options
xbncd run
Runs the bouncer in the foreground (the service does this). SIGHUP (systemctl reload) re-reads networks, channels, users and languages.
—
xbncd init
Creates the database and the secret key.
—
xbncd check-config
Checks the configuration file and exits.
—
xbncd info
Shows the panel address and how clients connect.
—
xbncd setup-code
Shows the one-time code to create the first administrator.
—
xbncd user add NAME
Creates a user. The password is asked twice, read from standard input with --password-stdin or from XBNC_PASSWORD.
--role superadmin|admin|moderator|user|limited (user by default); --admin (shortcut for --role superadmin); --password-stdin
xbncd user list
Lists the users.
—
xbncd user passwd NAME
Changes the password.
--password-stdin
xbncd user role NAME ROLE
Changes the role.
—
xbncd user enable|disable NAME
Reactivates or suspends an account (after a reload, its networks disconnect).
—
xbncd user reset-2fa NAME
Removes two-step verification (lost phone).
—
xbncd user delete NAME --yes
Deletes the user, their networks and their history.
--yes (confirm)
xbncd network add USER NAME --host SERVER
Adds a network. SASL and server passwords are asked for or read from XBNC_SASL_PASSWORD and XBNC_SERVER_PASSWORD.
Puts the manifest (JSON) inside a compiled plugin.
-o, --output (required)
xbncd plugin catalog-keygen -o KEY
To publish a catalogue: creates the signing key (keep it safe).
-o, --output
xbncd plugin catalog-entry FILE.wasm URL
To publish a catalogue: prints the index entry of a plugin published at URL (https://).
—
xbncd plugin catalog-sign --key KEY INDEX
To publish a catalogue: signs the index (writes INDEX.sig).
-k, --key
xbncd backup DESTINATION
Consistent copy of the database, with xBNC running.
—
xbncd migrate-to-postgres URL
Copies the SQLite database to a new, empty PostgreSQL one. Stop xBNC first; then set database.url.
—
xbncd import PATH
Brings users, networks, channels and modules from another bouncer (its data folder, with configs/ and users/, or its configuration file). Without --apply nothing is written.
--apply; --logs (also the log files); --utc-offset ZONE (+02:00 or Europe/Madrid; +00:00 by default)
xbncd import-logs USER NETWORK FOLDER
Imports text logs (one folder per channel with YYYY-MM-DD.log files) into a network's history. Can be repeated without duplicating.
--utc-offset ZONE
xbncd audit
Latest administrative actions.
--limit N (50)
xbncd release keygen
To publish releases: creates the signing key pair. The public key goes in [updates] public_key.
--out FILE (xbnc-release.key)
xbncd release sign FILES…
Writes FILE.sig next to each package. The private key is read from XBNC_RELEASE_KEY or from --key.
--key
xbncd release verify --public-key KEY FILES…
Checks the .sig signatures.
—
xbncd apply-update
Installs an update requested from the panel (run as root by the xbnc-update service; checks the signature again).
—
xbncd locales check FILES…
Checks language files against English: missing texts, texts xBNC does not use, broken {0} or <b> tags. Does not need the configuration.
--missing (also lists what is missing)
xbncd locales list
Languages this server offers, with the share translated of each.
—
Examples
sudo xbncd info
sudo xbncd setup-code
sudo -u xbnc xbncd user add alice
sudo -u xbnc xbncd user add carla --role moderator
echo 'a-long-password' | sudo -u xbnc xbncd user add bob --password-stdin
sudo -u xbnc xbncd user role carla admin
sudo -u xbnc xbncd user reset-2fa alice
sudo -u xbnc xbncd network add alice libera --host irc.libera.chat --sasl-user alice
sudo -u xbnc xbncd network add alice undernet --host irc.undernet.org --port 6667 --no-tls
sudo -u xbnc xbncd channel add alice libera '#xbnc' --key
sudo -u xbnc xbncd backup /var/lib/xbnc/copy.db
sudo -u xbnc xbncd import /home/old/.bouncer --logs --utc-offset Europe/London # preview
sudo -u xbnc xbncd import /home/old/.bouncer --logs --utc-offset Europe/London --apply # import
xbncd locales check pt.json --missing
sudo systemctl reload xbnc
Scripts
Script
Use
scripts/install.sh
Installs or reconfigures the service (see Installation).
scripts/update.sh [--binary PATH]
Updates the binary with a backup first and automatic rollback.
scripts/backup.sh [FOLDER] [--keep N]
Live backup: database, secret key and /etc/xbnc in a 0600 .tar.gz (in /var/backups/xbnc, keeps 14).
In Settings → Backups (superadministrators): make a backup now, download it, delete it, restore it and schedule automatic backups (every N hours, keeping the last M). Each backup is a .tar.gz with the database (a live copy with an integrity check), the secret key and the installed plugins, stored in /var/lib/xbnc/backups/.
Restoring leaves the backup staged (one can also be uploaded from your computer) and it is applied when xBNC restarts; until then it can be cancelled. Backups contain the secret key: keep them encrypted.
Backups from the command line
sudo ./scripts/backup.sh # in /var/backups/xbnc, keeps 14
sudo ./scripts/backup.sh /mnt/backups --keep 30
# Every night at 3:30
echo '30 3 * * * root /path/to/xbnc/scripts/backup.sh >/dev/null' | sudo tee /etc/cron.d/xbnc-backup
# Restore (stops the service, keeps the current files as *.pre-restore-DATE, restores and starts)
sudo ./scripts/restore.sh /var/backups/xbnc/xbnc-20261108T033000Z.tar.gz
sudo ./scripts/restore.sh ARCHIVE --with-config # also /etc/xbnc
# Only the database
sudo -u xbnc xbncd backup /var/lib/xbnc/copy.db
The target database must be empty. With ?sslmode=require the connection is encrypted. Full-text search works the same with SQLite and PostgreSQL.
High availability
With ha = true, several servers run xBNC against the same PostgreSQL database. The one holding a PostgreSQL lock works and the others wait. If the active one goes down, its connection closes, the lock is released and another takes over within seconds. An active node that loses the database stops at once, so there are never two at the same time. Clients must be able to reach the active node (a floating IP or a DNS name that changes).
Migrating from another bouncer
xBNC brings over what you had in your previous bouncer if it uses the classic block configuration format (<User name>, <Network name>, <Chan #channel>).
What
How it ends up in xBNC
Users
With their name. Administrators become admin (never superadmin). Users that already exist in xBNC are not touched.
Passwords
Those stored with salted SHA-256 keep working and move to Argon2id the first time each user signs in. Those in another format (MD5) get a new random password, shown only once.
Networks
Server, port, TLS, server password, nick, ident and real name. Disabled networks stay disabled. If there were several servers, the first is used (add the others afterwards as backup servers with /msg *status addserver). IPv6 servers are supported.
Channels
With their keys (encrypted). Disabled ones are not imported.
SASL, NickServ and commands on connect
SASL goes to the network; the NickServ password and the commands on connect go to “Commands on connect” (encrypted). They need the data folder.
Client certificate
Imported if it is in the data folder and its key matches.
Modules
The xBNC equivalent is turned on (table below).
Logs
The daily text files go into each network's history, with search. Can be repeated without duplicating.
From the panel (Users → Import from another bouncer, superadministrators): choose the .conf file or paste its contents, click “See what will be imported” (nothing is written) and then “Import”. From the panel only the configuration is read; SASL, NickServ, commands on connect, certificates and logs are in the data folder and are imported on the server.
# Preview: changes nothing
sudo -u xbnc xbncd import /path/to/the/folder
# Import, with the logs (which were in London time)
sudo -u xbnc xbncd import /path/to/the/folder --apply --logs --utc-offset Europe/London
sudo systemctl reload xbnc
# Logs of one particular network, separately
sudo -u xbnc xbncd import-logs alice libera /path/to/logs/libera --utc-offset America/New_York
The logs folder has one sub-folder per channel or conversation (#xbnc/, bob/) with YYYY-MM-DD.log files. Messages, actions (* nick …), notices (-nick- …) and joins, parts, quits, kicks, nick changes, topics and modes are recognised. With a named time zone (Europe/London) each day's daylight saving time is applied.
Module of the previous bouncer
In xBNC
keepnick
Keep nick
simple_away, away, autoaway, awaynick
Automatic away (with the delay and message of simple_away)
autoop
Automatic op and voice (add the entries again: the syntax is different)
Any other module shows in the preview as “no equivalent”. After importing: networks with several servers use only the first; networks without TLS are flagged; if you used the same ports, stop the previous bouncer before starting xBNC; and configure your clients with the user/network format.
xBNC checks these conditions every 30 seconds. They are adjusted in Settings → Alerts (0 turns each one off):
Alert
Fires when
Default
network_down
A network has been trying to connect for more than N minutes without success (those you disconnect do not count). Up to 10,080 (a week).
15 minutes
failed_logins
There are N or more failed sign-ins (IRC, panel and tokens) in the last 10 minutes.
30
memory
The process memory goes over N MB.
off
database
The database goes over N MB.
off
When an alert starts and when it is resolved:
It appears (or disappears) on the panel's Home page for administrators.
It is recorded in the audit log (action alert).
With “Notify administrators over IRC” (on by default), every connected administrator and superadministrator gets a notice from *status in their language.
With a webhook (only a superadministrator configures it), xBNC sends a POST with JSON. It has 10 seconds to answer with 2xx; if it fails, it is noted in the service log.
{
"source": "xbnc",
"server": "bnc.example.org",
"state": "firing",
"alert": "network_down",
"subject": "alice/libera",
"message": "Network alice/libera has been disconnected for more than 15 minutes",
"since": 1762600000000,
"text": "[xBNC bnc.example.org] firing: Network alice/libera ...",
"content": "[xBNC bnc.example.org] firing: Network alice/libera ..."
}
state is firing or resolved. The text and content fields make it work as it is with the incoming webhooks of Slack and Mattermost (text) and Discord (content).
Prometheus metrics
GET /api/v1/metrics in Prometheus format, with an administrator's session or a token with the “metrics” scope:
Lines of the log module dropped because the disk could not keep up.
xbnc_dcc_relays_total
counter
DCC sends and chats relayed by xBNC.
xbnc_dcc_bytes_total
counter
DCC bytes relayed by xBNC.
xbnc_alerts_firing{alert}
gauge
Active alerts by type.
The same counters are shown on IRC with /msg *status traffic (administrators).
Grafana and Prometheus rules
In packaging/monitoring/:
grafana-dashboard.json: a dashboard ready to import with connected and disconnected networks, IRC clients, users, memory, uptime, messages per second, traffic, networks by state, logins, reconnections, storage, history and active alerts.
Everything the panel does is in the API. Base: /api/v1, at the same address as the panel. JSON format; errors are {"error": "code", "message": "text"} with the right HTTP status. The OpenAPI 3.1 specification is at GET /api/v1/openapi.json (and in docs/openapi.json); a test checks that it documents every route.
API tokens
Create them in Your account → API tokens. The full token (xbnc_ and 43 characters) is only shown when created: xBNC stores its SHA-256. Send it in the header Authorization: Bearer xbnc_…; with a token no CSRF is needed.
Scope
Can
read
GET requests only.
write
Everything your account can do, except signing out, changing the password, two-step verification, passkeys and creating or revoking tokens (those need a panel session and answer 403 session_required).
metrics
GET /metrics only.
They expire after 30, 90 or 365 days, or never; at most 50 per user. The list shows the prefix, the last use and the last IP. A wrong token counts as a failed attempt (the same blocks as IRC and the panel). Creating and revoking is recorded in the audit log.
Panel session
POST /auth/login with {"username", "password"} (and totp_code if the account has two-step verification). The answer brings the xbnc_session cookie and, in the body, csrf.
Send the cookie with every request, and with POST, PUT, PATCH and DELETE the header X-CSRF-Token with that value (GET /auth/me returns it again).
Requests with an Origin header from another site are refused; curl and scripts do not send it.
Examples
B=https://bnc.example.org:8443/api/v1
T="Authorization: Bearer xbnc_..."
# Networks and their state
curl -s -H "$T" $B/networks
# Search the history of network 1
curl -s -H "$T" "$B/networks/1/history?target=%23xbnc&q=meeting&limit=20"
# Reconnect a network (write token)
curl -s -X POST -H "$T" -H 'Content-Type: application/json' -d '{"action":"jump"}' $B/networks/1/control
# Turn on a module
curl -s -X PUT -H "$T" -H 'Content-Type: application/json' \
-d '{"enabled":true,"config":{"delay":60,"summary":true}}' $B/networks/1/modules/away
# Languages and the Spanish panel texts (no session needed)
curl -s $B/i18n
curl -s $B/i18n/es
# With a session instead of a token: create a user and give them a network
curl -s -c jar -H 'Content-Type: application/json' \
-d '{"username":"admin","password":"your-password"}' $B/auth/login > login.json
CSRF=$(python3 -c 'import json;print(json.load(open("login.json"))["csrf"])')
curl -s -b jar -H "X-CSRF-Token: $CSRF" -H 'Content-Type: application/json' \
-d '{"username":"bruno","password":"a-long-password","role":"user","max_networks":3}' $B/users
curl -s -b jar -H "X-CSRF-Token: $CSRF" -H 'Content-Type: application/json' \
-d '{"user_id":2,"name":"libera","host":"irc.libera.chat","port":6697,"tls":true,"nick":"bruno",
"sasl_username":"bruno","sasl_password":"secret","channels":["#xbnc","#xbnc-dev"]}' $B/networks
All routes
Method
Route
What it does
Who
GET
/health
Health check.
no session
GET
/i18n
Installed languages (code, name, share translated, translators) and the server's default language.
no session
GET
/i18n/{code}
Panel texts in one language: English text → translation.
no session
GET, POST
/setup
First setup with the one-time code.
no session
POST
/auth/login
Sign in: {username, password, totp_code}. Returns the xbnc_session cookie and the csrf field.
no session
POST
/auth/logout
Close the session.
session
GET
/auth/me
Your account, your permissions and the CSRF token.
session or token
GET
/auth/methods
Ways of signing in the server offers (passkeys, OpenID Connect, LDAP).
no session
GET
/auth/oidc/start
Redirects to the OpenID Connect provider.
no session
GET
/auth/oidc/callback
Return from the OpenID Connect provider.
no session
POST
/auth/passkey/options
Starts signing in with a passkey (WebAuthn challenge).
no session
POST
/auth/passkey
Signs in with the passkey's answer.
no session
GET
/dashboard
Summary of the Home page (global for administrators).
session or token
GET
/presets
Well-known networks for the wizard.
session or token
GET, POST
/networks
List (?all=true: every network, administrators) and create networks: {user_id, name, host, port, tls, nick, username, realname, server_password, sasl_username, sasl_password, channels, on_connect}.
create: user or above
GET, PATCH, DELETE
/networks/{id}
Show with live state, edit (an omitted secret is kept, "" deletes it) and delete.
owner or admin
POST
/networks/{id}/control
{action: connect | disconnect | jump}.
owner or admin
GET, POST
/networks/{id}/channels
Channels, with disabled and position (joining order); add {name, key}.
owner or admin
DELETE
/networks/{id}/channels/{name}
Remove a channel (encode # as %23).
owner or admin
PUT
/networks/{id}/channels/{name}/history
Retention of the channel: {retention_days: null (server setting) | -1 (do not store) | 0 (forever) | N days}.
owner or admin
DELETE
/networks/{id}/clients/{client}
Disconnect a device.
owner or admin
GET
/networks/{id}/conversations
Conversations with their number of messages.
owner or admin
GET, DELETE
/networks/{id}/history
Search: target, q, sender, before_id, from, to, limit (up to 500), newest first. DELETE deletes (?target= one conversation).
owner or admin
GET
/networks/{id}/history/export
Download: ?format=txt|json and ?target=.
owner or admin
GET
/networks/{id}/chat
WebSocket of the panel chat: the panel as one more IRC client. Messages {line: "PRIVMSG #xbnc :hello"} in both directions.
owner only, session
GET, PUT
/networks/{id}/on-connect
Commands on connect: {commands: "/msg X login …"}; empty deletes them.
owner or admin
GET, PUT, DELETE
/networks/{id}/cert
Client certificate: SHA-1/256/512 fingerprints; upload {certificate, private_key} in PEM; delete. The key is never returned.
owner or admin
POST
/networks/{id}/cert/generate
Generate a new certificate.
owner or admin
GET
/modules
Module catalogue: ids, settings with type and limits.
session or token
GET
/networks/{id}/modules
State of a network's modules (secrets empty, plus secrets_set).
owner or admin
PUT
/networks/{id}/modules/{module}
{enabled, config}: validated and applied at once. An empty secret keeps the stored one; null deletes it.
owner or admin
GET
/networks/{id}/plugins
Plugins of a network.
owner or admin
PUT
/networks/{id}/plugins/{plugin}
{enabled, config}.
owner or admin
GET
/mentions
Messages that mention you, newest first (?before=ms&limit=).
session or token
GET
/notes
Your notes.
session or token
PUT, DELETE
/notes/{key}
Save {text} (up to 400 characters) or delete a note.
session or token
PUT
/me/language
{language: code of an installed language (en, es…) | ""} (empty: the server's).
Test report: what each of your networks supports and how xBNC handled it (no secrets or messages).
session or token
GET, POST
/tokens
Your tokens; create {name, scope: read | write | metrics, expires_days: 0–3650} (0: never expires). The token only comes in the answer.
session only
DELETE
/tokens/{id}
Revoke a token.
session only
GET
/sessions
Panel sessions (?all=true: all of them, administrators).
session
DELETE
/sessions/{id}
Close a session.
session
GET, POST
/users
List and create {username, password, role, max_networks, max_clients}.
see: moderator; create: admin
GET, PATCH, DELETE
/users/{id}
Show, change {role, enabled, max_networks, max_clients, max_history_mb} (null removes the limit) and delete.
depends on the role
POST
/users/{id}/password
Reset the password (closes their sessions).
admin
GET
/users/{id}/usage
Networks, clients and history space used against their quotas.
moderator
GET
/security
Sign-ins and blocked IPs.
moderator
DELETE
/security/blocked/{ip}
Unblock an IP.
admin
GET
/audit
Audit log.
moderator
GET, PUT
/settings
Live settings.
see: admin; edit: superadmin
GET
/system
Version, database, listeners and counters.
admin
POST
/system/restart
Restart xBNC (the service starts it again).
superadmin
GET
/alerts
Active alerts.
admin
GET
/metrics
Metrics in Prometheus format.
admin or metrics token
GET
/ws
WebSocket of the Home page: a {type: "stats"} message every 2 seconds with the state of the networks and, for administrators, memory, CPU, clients, connections, blocked IPs and counters; {type: "logout"} when the session ends.
session only
GET, POST
/backups
Backups, staged restore and settings of the automatic ones; make a backup now.
superadmin
GET, DELETE
/backups/{name}
Download or delete a backup.
superadmin
POST
/backups/{name}/restore
Stage the restore of a backup on the server.
superadmin
POST, DELETE
/backups/restore
Upload a backup (.tar.gz) and stage it; cancel the staged restore.
superadmin
GET
/plugins
Installed plugins.
session or token
POST
/plugins
Install or update: the body is the .wasm (application/wasm, up to 4 MB).
superadmin, session
POST
/plugins/inspect
Check a .wasm without installing it.
superadmin, session
PATCH, DELETE
/plugins/{id}
{enabled} on the server and approved domains; uninstall.
superadmin, session
GET
/plugins/catalog
Catalogue: official plugins and signed remote catalogue.
session or token
POST
/plugins/catalog/{id}
One-click install from the catalogue.
superadmin, session
GET, PUT
/plugins/dev
Development mode: state and folder; turn it on or off.
superadmin, session
GET
/plugins/logs
WebSocket with the plugins' log: last lines and new ones.
superadmin, session
GET
/updates
Installed and latest published version, whether it can be updated from the panel and the result of the last update.
admin
POST
/updates/check
Check now whether there is a new version.
admin, session
POST
/updates/install
Download, verify and install the signed package of the latest version.
superadmin, session
POST
/import/preview
Preview of the import: {config: "file contents"}. Writes nothing.
superadmin, session
POST
/import/apply
Import.
superadmin, session
“owner or admin”: the owner of the network or an administrator. “session only”: not accepted with a token. Passwords are never returned: networks say has_sasl_password and has_server_password.
With [updates] repository (or url) in xbnc.toml, xBNC checks the latest published version once a day and says so on the Home page and in Settings → Updates. On its own it downloads and installs nothing.
Updating
In this beta, xBNC is installed from the source code: it is updated with scripts/update.sh (see Installation).
Updating from the panel (package installations)
xBNC also includes one-click updates for when it is installed from signed .deb or .rpm packages. With [updates] public_key, Settings → Updates shows “Update now” to superadministrators. xBNC downloads the package and its .sig signature, checks the Ed25519 signature and leaves it in /var/lib/xbnc/updates/. The system service xbnc-update checks everything again as root without trusting that folder (it verifies with the key in /etc/xbnc/xbnc.toml, which xBNC cannot change, and refuses equal or older versions), installs it with dpkg or rpm and xBNC restarts. A package without a signature, signed with another key or modified is never installed. Without public_key, or without a package installation, the panel explains how to update by hand.
Publishing signed releases
# Once: create the key (the private one is secret; the public one goes in [updates] public_key)
xbncd release keygen --out xbnc-release.key
# For each release: sign the packages (writes FILE.sig)
XBNC_RELEASE_KEY="$(cat xbnc-release.key)" xbncd release sign xbnc_1.0.0_amd64.deb xbnc-1.0.0-1.x86_64.rpm
# Check
xbncd release verify --public-key "BASE64..." xbnc_1.0.0_amd64.deb
The repository includes a workflow that builds and signs .deb and .rpm packages and a container image (.github/workflows/release.yml). It needs the key as a repository secret (XBNC_RELEASE_KEY) and the public key as a variable (XBNC_RELEASE_PUBLIC_KEY). During the beta xBNC is published only as a source .zip, so the workflow only runs when it is started by hand from the Actions tab.
Publishing on GitHub
scripts/publish-github.sh publishes the source code, a release with the .zip and its checksum, and the wiki as a website of its own (GitHub Pages), from the maintainer's computer and with the maintainer's GitHub account. The complete guide is docs/PUBLISHING.md.
A plugin is a WebAssembly file with a JSON manifest inside. There are SDKs for C (sdk/c, with the 18 official plugins as examples and the xbnc_util.h helper) and for Rust (sdk/rust). Any language that compiles to WebAssembly without WASI works if it follows the interface.
Manifest
Field
Required
What it is
id
yes
1–32 characters a-z 0-9 _ -, starting with a letter. Cannot be status, notes, watch, events, xbnc, bnc, plugin or plugins. It is the name of its service (*id).
name
yes
Visible name, 1–60 characters.
version
yes
Version, 1–20 characters.
description
no
What it does.
usage
no
How it is used (up to 1000 characters). The panel shows it under “How to use it” and xBNC answers with it if the plugin does not answer a command.
author
no
Author.
homepage
no
Page of the plugin (https://).
abi
no
Interface version (1).
permissions
no
List of send, raw, timer, presence and http.
http_domains
with http
1 to 10 domains it asks permission to connect to.
timer_secs
no
With timer: receive the timer every 5–60 seconds instead of every minute.
settings
no
Up to 20 settings: {key, type (bool, int, text or lines), label, help, default, min, max}. The panel shows them as a form.
translations
no
By language code (es, pt, fr…): name, description, usage and, per setting, label and help. The texts at the root of the manifest are the base, in English.
Reserves len bytes for the event and returns their address.
xbnc_on_event
(ptr: i32, len: i32) -> i64
Receives the event (UTF-8 JSON) and returns (address << 32) | length of the answer (JSON), or 0 if it does not answer.
And it can only import from the xbnc module (neither WASI nor anything else):
Import
What it does
log(ptr, len)
Writes a line to the log (at most 10 per event and 500 characters).
now_ms() -> i64
Milliseconds since 1970.
kv_get, kv_set, kv_del, kv_list
Key-value store of the plugin on that network: keys up to 200 bytes, values up to 64 KB, 16 MB and 200,000 keys in total, up to 2,000 writes per event (saved together).
Every minute, or every timer_secs (timer permission).
—
join, part, kick, quit, nick, names
Movements in the channels (presence permission).
the data of each movement
http
The answer to a web request (http permission).
id, status, body, error
Common fields: network, nick (your current nick), connected, clients (connected clients), lang (your language code: en, es or any installed language), config (its settings) and state (what it saved).
Answer
{
"actions": [
{ "reply": "text in the same conversation" },
{ "tell": "text for you only, as *id" },
{ "privmsg": { "target": "#channel", "text": "..." } },
{ "notice": { "target": "nick", "text": "..." } },
{ "raw": "MODE #channel +o bob" },
{ "http": { "id": "weather", "method": "GET", "url": "https://api.open-meteo.com/..." } }
],
"state": { "whatever you want to keep": 1 }
}
Single-line texts. Actions without permission or badly formed are dropped and noted in the log.
state replaces the plugin's saved state on that network; it comes with every event and survives restarts. The instance is kept between events while the network is running.
raw can never send QUIT, PASS, USER, NICK, CAP, AUTHENTICATE, OPER, SERVER or SQUIT.
http: ('id', 'url', 'method', 'body'). https only, to approved domains; methods GET, POST, PUT and DELETE; body up to 16 KB (text or JSON); up to 3 requests at a time and 30 per minute per plugin and network. The answer arrives as an http event with id, status, body (up to 256 KB) and, if it failed, error.
Limits
Limit
Value
File size
4 MB
Memory
16 MB
CPU per event
about 20 million instructions (50 million when starting)
Answer
64 KB and 20 actions; 5 messages per event
Saved state
64 KB
Key-value store
16 MB and 200,000 keys per plugin and network
Failures in a row before it stops on a network
5
C SDK
sdk/c/xbnc_plugin.h takes care of the interface. Main functions:
xbnc_kv_get, xbnc_kv_set, xbnc_kv_del, xbnc_kv_list (and the _str variants)
Key-value store.
xbnc_log(text)
Write to the log.
sdk/c/xbnc_util.h adds what the official plugins use: language of the answer (u_lang), reading commands with aliases (u_command), walking the store (u_next_key), dates, time zones with European daylight saving time and text building.
cd sdk/c
clang --target=wasm32 -O2 -nostdlib -fno-builtin -I. \
-Wl,--no-entry -Wl,--export-dynamic -Wl,--strip-debug -Wl,-z,stack-size=65536 \
-o hello.raw.wasm hello.c
xbncd plugin pack hello.raw.wasm hello.json -o hello.wasm
xbncd plugin check hello.wasm
sudo -u xbnc xbncd plugin install hello.wasm
# The official examples: make (or make bundle to copy them to the catalogue)
It needs clang and wasm-ld (LLVM 15 or later) and llvm-objcopy (or xbncd plugin pack).
Rust SDK
The xbnc-plugin crate (sdk/rust) provides the Plugin trait, the Event type (with kind(), text(), conversation(), is_channel(), outgoing(), config_str(), state()…), Response (with reply(), tell(), state()…) and the plugin! macro. Included example (sdk/rust/examples/stats.rs):
//! Example plugin: counts messages per channel and answers "!stats".
//! The counts survive restarts thanks to the persisted state.
//!
//! Build: cargo build -p xbnc-plugin --release --target wasm32-unknown-unknown --example stats
//! then: xbncd plugin pack target/wasm32-unknown-unknown/release/examples/stats.wasm \
//! sdk/rust/examples/stats.json -o stats.wasm
use xbnc_plugin::{plugin, Event, Plugin, Response};
#[derive(Default)]
struct Stats;
impl Plugin for Stats {
fn on_event(&mut self, ev: &Event) -> Response {
let mut r = Response::new();
let mut counts = ev.state().as_object().cloned().unwrap_or_default();
match ev.kind() {
"message" if ev.is_channel() && !ev.outgoing() => {
let chan = ev.conversation().to_lowercase();
let n = counts.get(&chan).and_then(|v| v.as_u64()).unwrap_or(0) + 1;
counts.insert(chan, n.into());
if ev.text().trim() == ev.config_str("trigger") {
r.reply(format!("{} messages counted in {}", n, ev.conversation()));
}
r.state(counts.into());
}
"command" => {
if counts.is_empty() {
r.tell("Nothing counted yet.");
}
let mut v: Vec<_> = counts.iter().collect();
v.sort_by_key(|e| std::cmp::Reverse(e.1.as_u64()));
for (chan, n) in v.into_iter().take(10) {
r.tell(format!("{chan}: {n}"));
}
}
_ => {}
}
r
}
}
plugin!(Stats);
Development mode
In Plugins → Development (superadministrators): with the mode on, xBNC watches /var/lib/xbnc/plugins/dev/. A .wasm saved there is checked and installed as soon as it changes and every network reloads it. If you put name.json next to name.wasm, it packs it for you (no need for xbncd plugin pack). A new plugin is turned on in the networks of whoever turned the mode on. The results and what the plugin writes are shown in the page's live log.
Besides the official ones, xBNC can offer the plugins of a signed remote catalogue ([catalog] in xbnc.toml). The index is a JSON {"plugins": [{"id", "version", "url", "sha256", "size", "manifest"}]} and its Ed25519 signature in base64 is published in index.sig. Each plugin is checked against the signed SHA-256 before it is installed.
xbncd plugin catalog-keygen -o catalogue.key # once; the public key goes in [catalog] public_key
xbncd plugin catalog-entry hello.wasm https://plugins.example.org/hello.wasm # entry for the index
xbncd plugin catalog-sign --key catalogue.key index.json # writes index.json.sig
xBNC can speak any language. Every text it shows lives in one file per language: the web panel, the sign-in page and everything it says over IRC (*status, its help, notices and module messages). You translate one file and the language appears in every language selector of xBNC, without programming or compiling anything.
The files
File
What it is
locales/en.json
English. It is the base: it holds every text of xBNC.
locales/es.json
Spanish.
locales/<code>.json
Any other language: pt.json, fr.json, pt-BR.json…
English and Spanish are built into the program. Other languages, and corrections, go in /etc/xbnc/locales/ (locales_dir in xbnc.toml, or the XBNC_LOCALES_DIR variable). xBNC reads them at start and on reload (sudo systemctl reload xbnc or /msg *status rehash).
{
"language": {
"code": "pt",
"name": "Português",
"english_name": "Portuguese",
"locale": "pt-PT",
"translators": ["Your name or nick"]
},
"irc": {
"connected_to": "Ligado a {0} como {1}.",
"help.group.channels": "Canais"
},
"panel": {
"Joined": "Dentro",
"Delete {username}": "Apagar {username}"
}
}
Part
What it is
language
code: the language code (two or three letters, with a region if needed: pt, pt-BR); the file has the same name. name: the name of the language in the language itself, as shown in the selectors. locale: for dates and numbers. translators: shown by xbncd locales list.
irc
What xBNC says over IRC. The key on the left is a name; translate only the text on the right.
panel
The web panel. The key on the left is the English text; translate only the text on the right.
Step by step
Copy en.json as <code>.json (for example pt.json).
Change the language part: code, name, English name, locale and your name.
Translate the texts on the right. Never change the keys on the left.
Check the file: xbncd locales check pt.json (add --missing to see what is left).
Try it: copy it to /etc/xbnc/locales/ and reload xBNC. It appears straight away on the sign-in page, in Your account, in Settings and with /msg *status language pt.
Send it to the project so it is included in the next version.
Rules for the texts
In the English text
What to do
{0}, {1}… (IRC)
Values xBNC fills in (a nick, a number…). Keep all of them; you can move them inside the sentence.
{name}, {count}… (panel)
The same: keep them with exactly the same name.
<b>…</b>, <u>…</u> (IRC)
Bold and underline in the IRC client. Keep them around the same words, each <b> with its </b>.
Commands and settings (/msg *status help, set away delay 60)
Do not translate them: they are what people type.
$1, $nick
Variables of the alias module: keep them as they are.
Disabled|network state (panel key)
What follows the | is a hint about where the text is shown (for gender, for example). Translate only the text, without the hint.
Plurals
Texts ending in .one are used with 1 and those ending in .other with any other number (unit.network.one = “{{0}} network”).
Line breaks
Not allowed in IRC texts.
A text that breaks these rules is ignored and shown in English; xbncd locales check reports it as a problem. This also makes it impossible for a language file to inject IRC commands (line breaks are forbidden).
Correcting a built-in language
A file for a language that already exists (for example es.json in /etc/xbnc/locales/) only needs the texts you want to change: the rest stays as it is.
What a file does not translate
Plugin replies. Their name, description and settings are translated in their manifest (translations). What a plugin answers in a channel is written in the plugin itself; in a language it does not include, it answers in English. The official plugins speak English and Spanish.
The installer speaks English and Spanish.
The server log is in English.
Translating this wiki
The wiki is a project of its own, separate from the app (xbnc-wiki), so that anyone can read it without installing xBNC. It is generated with build.py from one content file per language (content/en.py is the base) and the xBNC source code. To translate it, copy en.py as <code>.py and translate the texts between quotes without touching the HTML tags or calls such as code(…). The tables of modules, plugins and commands translate themselves with xBNC's language files. When the wiki is built, each language appears in the selector at the top.
For developers
In the panel, texts are written in English inside t("…"), tk("…") or tx("…"). npm run i18n:check (in web/) lists what is missing from en.json, and node scripts/i18n-check.mjs --sync adds it.
In the server, the text is added to the irc section of locales/en.json and used with self.t("key", &[]), tr!(self, "key", arg1, arg2) or i18n::tr(lang, "key", &[…]). The tests check that every key used exists and that es.json translates all of them with the same placeholders.
The complete guide is in TRANSLATING.md, in the source code.
xBNC negotiates with each server the capabilities it offers and xBNC wants, and offers each client its own. Each client only gets what it asked for: a classic client receives the usual messages (for example, the short JOIN and only the highest rank in the user list).
With the servers
Capability
What for
server-time
Real time of each message.
message-tags
Tags on messages.
multi-prefix
Every rank of a user (@+), not only the highest.
extended-join
Joins carry the account and the real name.
away-notify
Notices when someone goes away or comes back.
account-notify
Notices when someone identifies or stops being identified.
account-tag
Each message says the account of whoever sent it.
chghost
Changes of user or host without leaving and joining.
userhost-in-names
The user list with nick!user@host.
invite-notify
Invitations of other people to your channels.
setname
Real name changes.
cap-notify
The server adds or removes capabilities on the fly.
sasl
SASL identification (PLAIN and EXTERNAL towards the server; PLAIN from the clients).
With the clients
Capability
What for
server-time
Real time of each message.
message-tags
Tags on messages.
echo-message
Your own messages come back confirmed by the server.
batch
Groups of messages (history, playback).
sasl
SASL identification (PLAIN and EXTERNAL towards the server; PLAIN from the clients).
draft/chathistory
The client asks for the history it needs (CHATHISTORY).
draft/event-playback
The history includes joins, parts, kicks and nick changes as real events.
draft/read-marker
Read markers shared between devices.
soju.im/bouncer-networks
All your networks in a single connection.
soju.im/bouncer-networks-notify
Notices of changes in your networks.
extended-join
Joins carry the account and the real name.
away-notify
Notices when someone goes away or comes back.
account-notify
Notices when someone identifies or stops being identified.
account-tag
Each message says the account of whoever sent it.
chghost
Changes of user or host without leaving and joining.
multi-prefix
Every rank of a user (@+), not only the highest.
userhost-in-names
The user list with nick!user@host.
invite-notify
Invitations of other people to your channels.
setname
Real name changes.
cap-notify
The server adds or removes capabilities on the fly.
With CAP LS 302, sasl=PLAIN is announced. If the server does not support a capability a client asks for, xBNC still offers it when it can build it itself (for example, the extended JOIN with the data it knows). The test report records, per network, what the server offers (caps_offered), what xBNC turned on (caps_acked) and what each client asked for.
In Your account → Test report → Download report (or GET /api/v1/diagnostics) you download a JSON file with what is needed to diagnose a problem with a network, without passwords, messages, other people's nicks or the IPs of your devices.
Field
Contents
report, format
xbnc-diagnostics, format version 1.
version, brand
xBNC version and “xBNC 1.0.0 Made by y2k”.
os, arch, database
System, architecture and database (sqlite or postgresql).
started_at, generated_at
When xBNC started and when the report was generated.
timezone, language
Your time zone and language.
counters
Server counters (traffic, messages, reconnections, logins).
networks[]
Per network: name, host, port, tls, enabled, whether it has SASL or a server password, saved_channels, active modules and plugins, and live.
networks[].live
state, server, network_name, connected_since, last_error, caps_offered, caps_acked, isupport, services_logged_in, host_hidden, channels_joined and clients (device and capabilities of each one).
xBNC is a single program (xbncd) with the panel built in. The code is split into crates/xbnc-proto (IRC protocol without I/O: parser and serialiser with IRCv3 tags, prefixes, casemapping, ISUPPORT and CTCP) and crates/xbncd (service, CLI, API and panel), plus the plugin SDKs in sdk/, the panel (React and TypeScript) in web/ and the translations in locales/.
How it works inside
Each network of each user is an independent task that exclusively owns its connection to the server, the state of the network and the list of clients. Nobody else touches that state: there are no locks on the path of the messages.
Each client is a reading task and a writing task, with a bounded queue. If a client does not read, it is disconnected instead of piling up memory; when it comes back it gets what was pending.
The panel, the API and the command line never touch the IRC connections: they write to the database and send orders to the network tasks (state, control, join, part, reload, disconnect a client, alert, broadcast).
Every database access goes through a separate thread pool. The messages of every network go through a single writer thread that stores everything waiting (up to 512) in one transaction, keeping each network's order.
Modules are code of the program itself, called at specific points (server line, client command, timer, connection, client joining or leaving). Plugins receive their events from the network task and run in the blocking thread pool.
When a message arrives: it is parsed, the state is updated (nick, channels, members, modes, topic), it is stored with the server's time and an identifier, and it is sent to each client adapted to its capabilities.
Texts are looked up in the loaded languages (built in, plus the files of locales_dir) by key, with English as the fallback.
Modules of the code
Module (crates/xbncd/src)
What it does
main.rs
The xbncd command: running the service and the command line (users, networks, backups, import, languages, releases, updates).
config
Reading and validating xbnc.toml.
db, sql
Storage: versioned migrations, users, networks, channels, messages, cursors, audit log and backups; sql gives SQLite (rusqlite) and PostgreSQL the same interface.
crypto
Argon2id, XChaCha20-Poly1305 encryption of the stored credentials and a Secret type that is never printed.
auth
The login format, password checks, the brute-force limiter and the masking of lines in the logs.
util
IRC line codec (length limit, lossy UTF-8, protection against CR/LF injection) and time helpers.
network/
The task of each network of each user: connection to the IRC server, state, history and attached clients. mods.rs and extra.rs hold the behaviour of the modules, status.rs and status_more.rs the *status, *notes and *watch commands, plugins.rs delivers events to plugins, dcc.rs decides which DCC offers go through xBNC and readmark.rs keeps the read markers.
downstream, bouncer, events
Client connections: registration, CAP, SASL, PASS and relaying to the network task; soju.im/bouncer-networks; notices to clients when a network changes.
modules
Catalogue of the modules, schema and validation of their settings and the typed configuration per network.
perform
Turns client-style commands (/msg, /mode…) into IRC, for the commands on connect, aliases and scheduled tasks.
logs
Log files, written in a separate thread.
certs
Client certificates (generation, validation and fingerprints) and trusted server certificates.
fish
FiSH encryption: Blowfish ECB and CBC and the DH1080 key exchange.
i18n
Languages: the built-in English and Spanish and the files of locales_dir.
netinfo
Local network addresses and the address shown for 0.0.0.0.
server, ha
Shared context, the registry of network tasks, TLS listeners, maintenance and reloading; active/standby high availability with PostgreSQL.
rbac, settings
The five roles and their permissions; history settings and limits editable live (database over xbnc.toml).
totp, webauthn, sso
Two-step verification (RFC 6238), passkeys, OpenID Connect and LDAP.
tz
Time zones with daylight saving time, with the database built in.
metrics, alerts
Traffic and message counters, process memory and CPU; alert rules checked every 30 s, with notices in the panel, over IRC and by webhook.
notify, push
Notifications by Telegram, Discord, e-mail, your own web address and Web Push.
plugins, plugin_dev, catalog
WebAssembly plugins: manifest, validation, the Wasmi engine with limits, instances and actions; development mode; the built-in catalogue and the signed remote one.
dcc
Reading and rewriting DCC offers and the one-use relays.
http
A minimal HTTP/1.1 client (verified TLS, bounded size and time) for webhooks, notifications, plugins with web access and update checks.
updates, release
New version notices, downloading and checking packages, the xbncd apply-update helper and Ed25519 signatures.
backup
Backups from the panel and restoring them.
import
Migration from another bouncer: reading its configuration, module data, plan, applying it in one transaction and importing logs.
web/
HTTP server (axum): the /api/v1 API, WebSocket, sessions, CSRF, headers and the panel files built into the binary.
The panel (web/) is a React and TypeScript application built with Vite. web/dist is built into the binary, so xBNC stays a single file. The official plugins are built into the binary too (crates/xbncd/catalog/).
State machines
They are kept separate:
Connection to the network: Disconnected → Registering → Connected. While registering, xBNC negotiates the IRCv3 capabilities, SASL and the nick (with an alternative if it is taken). Times: 30 s to connect, 60 s to register, a PING after 90 s of silence and disconnection at 180 s. Reconnection with growing delays from 5 s to 5 minutes with ±20 % random variation, and at least 2 minutes after a throttling notice. If a server fails and the network has backup servers, the next one is tried after 1 second.
Client session: registration (CAP, PASS or SASL, NICK, USER) with a time limit, and then a session attached to a network.
User and bouncer: which network tasks exist and how many clients each user has.
The path of a message
The network task reads a line from the server and parses it.
It updates the state (nick, channels, members, modes, topic).
If it is a PRIVMSG, NOTICE or ACTION from someone (or an event, with the Events module), it stores it in messages with the server's time (server-time) or the local one, and gives it the identifier msgid = x<id>.
It sends it to each attached client adapted to its capabilities (removing the tags the client did not ask for) and moves each client's cursor forward.
What a client sends goes to the server, is stored as outgoing and is relayed to the user's other clients (and to the sender itself if it asked for echo-message).
History without duplicates
Each device (@device in the login, default if it is not given) has a cursor per network in client_cursors: the identifier of the last message delivered.
When it connects, it gets the messages after its cursor (up to playback_max, 1,000 by default). A new device gets the most recent new_client_playback (100 by default).
Cursors are saved when the device disconnects and every 5 seconds; they never go back.
Clients with draft/chathistory get no automatic playback: they ask for what they need with CHATHISTORY. Messages already read on another device (draft/read-marker) are not played back again.
Database schema
Table
Contents
Since migration
users
User, Argon2id hash, role, limits and quotas, encrypted TOTP secret, language, time zone, bind host, last login and last connection.
1 (2, 4, 8, 9, 10)
networks
Server, port, TLS, nick, SASL, commands on connect, client certificate, backup servers and trusted certificates; passwords and keys encrypted.
1 (3, 4, 10)
channels
Saved channels per network, with an encrypted key, retention, disabled and position.
1 (8, 10)
messages
History: network, conversation, time, sender, command, text, outgoing and mention.
Login attempts over IRC and in the panel (10,000 at most).
2
module_config
Modules per network: enabled and validated JSON settings.
4
notes
Each user's notes.
4
messages_fts
Full-text index of messages.text, kept in sync by triggers (SQLite; PostgreSQL uses its own text search).
5
api_tokens
API tokens: SHA-256 hash, prefix, scope, expiry, last use and IP.
6
plugins
Installed plugins: manifest, SHA-256, size, enabled on the server, approved web domains and the module itself.
7 (8)
plugin_config
Plugins per network: enabled, settings and saved state.
7
plugin_kv
Each plugin's key-value store per network.
8
notify_settings
Notification settings of each user.
8
push_subscriptions
Web Push subscriptions of each user's browsers.
8
read_markers
Last message read in each conversation.
8
passkeys
Passkeys: credential, public key, counter, name and last use.
8
user_identities
Outside identities (OpenID Connect, LDAP) linked to local users.
8
PostgreSQL has the same schema; it is supported from version 8 of the schema (PG_MIGRATIONS), and every new migration is added to both lists. Channel events (JOIN, PART…) are stored in messages with their command. Passwords imported from another bouncer with SHA-256 are kept as legacy-sha256$<hash>$<salt> until the first correct login, when they are replaced by Argon2id. Migrations are applied at start inside a transaction, and a binary refuses to open a newer schema than it knows.
What xBNC protects, from whom and with which measure at each point. It is the guide for the external audit and for reviewing each change.
What is protected
Asset
Why it matters
Passwords of the xBNC accounts
They give access to the networks, the history and, for administrators, the whole server.
Network credentials (SASL, server password, channel keys, commands on connect, services passwords, FiSH keys, certificates)
They allow impersonating the user on the networks.
History and logs
Private conversations and channels: personal information.
Panel sessions and API tokens
As good as the password while valid.
The secret key (secret.key)
Decrypts every stored credential.
The availability of the service
If it goes down, users lose messages and presence.
The reputation of the server's IP
Every connection leaves from it.
Who may attack
Actor
What they control
What they try
Anyone on the internet
Connections to the IRC, panel and DCC ports
Guess passwords, exhaust resources, exploit the parser.
Someone on an IRC network
Messages, CTCP, DCC, channel names and nicks
Inject commands, make xBNC connect to internal networks, flood the history.
A malicious IRC server
Everything coming through a network's connection
The above, plus malformed answers, huge lines and disconnection loops.
An xBNC user
Their account, networks, modules and plugins
Read other people's data, escalate to administrator, abuse the server.
An administrator
Other people's users and networks
Escalate to superadministrator, change global settings.
The author of a plugin
The code a superadministrator installs
Escape the sandbox, read data, act without permission.
The author of a language file
A file an administrator copies to locales_dir
Inject IRC commands or HTML through the texts (refused: they are validated and treated as plain text).
A malicious website
The browser of a signed-in user
CSRF, session theft, WebSocket hijacking.
Whoever gets the backups
The .tar.gz files
Credentials and conversations.
Out of scope: someone who is already root on the machine, the server provider and failures of the operating system or of the IRC networks.
IRC client connections (6697/6667)
Threat
Measure
Password brute force
Per-IP blocking shared with the panel and tokens; Argon2id; the same cost for users that do not exist.
Exhausting connections or memory
Global connection limit, 16 unauthenticated per IP, clients per user, registration time and bounded queues.
Huge or binary lines
8703-byte limit per line and lossy UTF-8.
Command injection with CR/LF
Any CR or LF cuts the line; texts of modules, plugins and language files are validated without control characters.
Plain text on the internet
Ports without TLS only accept the local network or require insecure = true.
Credentials in the logs
Masking of PASS, AUTHENTICATE, OPER and messages to services at every level.
Connections to the IRC networks
Threat
Measure
Server impersonation
TLS verified with the system store, with no option to turn it off; a trusted certificate only accepts its exact SHA-256 fingerprint.
Malformed answers
Own parser without unsafe, with tests and fuzzing.
History flooding
Configurable retention, optional events, bounded queues; the log module drops lines if the disk cannot keep up; hide flooding channels.
Disconnection loops
Growing delays with random variation and a minimum wait after throttling notices.
Being thrown out for flooding
Rate limiting on the output.
Theft of the services password
On networks without NickServ it is never sent in automatic mode.
DCC
Threat
Measure
Using the bouncer to reach internal networks (SSRF)
Offers to internal addresses or ports below 1024 refused unless the user allows them.
Making xBNC connect to a third party
For your own sends it only connects back to your device's IP.
Ports open indefinitely
One port per offer, a single connection, a timeout, closing when idle and a size limit.
Hijacking the transfer
When receiving, the port only accepts the client's IP.
Web panel and API
Threat
Measure
CSRF
CSRF token in a header, SameSite=Strict cookie and Origin check.
XSS
React escapes all content; CSP without external or inline scripts; IRC texts are never interpreted as HTML; translations are plain text.
Session theft
HttpOnly and Secure cookie; only the token's SHA-256 in the database; sliding expiry; changing the password closes the other sessions.
WebSocket hijacking
Origin check, the session revalidated every 30 s and no API tokens.
Privilege escalation
Permissions checked on the server in every route; nobody changes their own role; there is always a superadministrator.
Seeing other users' data
Other people's networks answer 404, not 403.
API abuse and slowloris
20 requests/s per IP, bounded bodies, 64 connections per IP and 4,096 in total, closing after 2 minutes without traffic, 10 s for the TLS handshake.
Leaked tokens
Scope, expiry, last use and IP visible, immediate revocation, only their SHA-256 stored.
Stolen first access
One-time code, only in the service log.
Webhook to internal sites
Only a superadministrator configures it; no redirects, no credentials in the URL, 10 s at most.
Plugins
Threat
Measure
Escaping the sandbox
Wasmi interpreter without JIT, module validated when loaded, no WASI nor imports other than xbnc's, advanced WebAssembly features turned off.
Blocking or crashing the server
Fuel per event, limited memory, execution that never grows the native stack, calls in separate threads, stopping after five failures.
Acting without permission
Declarative actions checked against the permissions; dangerous commands always forbidden; maximum messages per event; web access only to approved domains and with limits.
Reading other people's data
Each instance only receives the events of its network; state and store separated per network.
File changed after installation
Its SHA-256 is stored and it is not loaded if it does not match; files 0600.
Malicious plugin installed
Only a superadministrator installs, seeing permissions and fingerprint; recorded in the audit log; signed remote catalogue.
Import from another bouncer
Threat
Measure
File crafted to create a superadministrator
The import never grants that role.
Weak inherited passwords
Salted SHA-256 compared in constant time and replaced by Argon2id; formats that cannot be checked get a random password.
Abusive paths or sizes
File up to 4 MB, limited nesting, validated names; logs are only read from the folders given.
Leaks in the preview
The preview includes no secrets.
Updates from the panel
Threat
Measure
Fake package (compromised download server, DNS or network)
Ed25519 signature over the package name and its SHA-256; the public key is only in xbnc.toml, owned by root.
Going back to a version with known flaws
Equal or older versions than the installed one are refused.
A compromised xbnc process trying to install something as root
The helper (xbnc-update.service) trusts nothing in the service's folder: it copies the package to /run (0700), checks the signature and the version on that copy, never follows symbolic links, validates the file name and uses each request only once.
Fake update request
Only superadministrators, from a panel session (not with API tokens) and with CSRF; recorded in the audit log.
Endless downloads or redirects to plain text
200 MB and 10 minutes at most; https:// only, 5 redirects at most.
Data at rest and backups
Threat
Measure
Theft of the database
Network credentials encrypted with XChaCha20-Poly1305; the key outside the database, with 0600 permissions checked at start.
Theft of a backup
0600 file; it includes the key, so it must be stored encrypted.
Access from other users of the system
Service run as the unprivileged xbnc user, 0700 folders and systemd hardening.
Accepted risks
The history is stored unencrypted in the database: encrypting it would prevent search and playback. It is protected by the system's permissions.
IRC clients have no second factor: the bouncer password is their only protection (hence per-IP blocking and Argon2id).
A superadministrator can install a plugin with permission to send messages on behalf of whoever turns it on: it is a decision of trust that the panel shows before installing.
Every connection to the networks leaves from the server's IP.
FiSH is old cryptography (Blowfish, without integrity); decrypted messages are stored in the history like the rest.
Review
Each version reviews these tables: a new feature that opens a surface not listed here is added to them. The tests behind each measure are in crates/xbncd/tests/ and in the unit tests of each module; fuzzing covers the IRC parser, DCC, the imported configuration file, search, and the manifests and execution of plugins (see Test environment).
Internal review of version 0.7. The whole code was reviewed with these tables as a guide (it does not replace the external audit):
SQL queries: all with bound parameters; the parts built with format! are only fixed column lists and clauses.
API authorisation: every route checks the session or token and the permission; network routes check the owner. Fixed: PATCH /users/{id} answered 404 or 403 to accounts without permission depending on whether the user existed.
External processes: the only one is dpkg or rpm in the update helper, with fixed arguments and the package already checked.
File paths: logs, plugins and updates build paths only from validated names; the update helper never follows symbolic links.
Denial of service: added the limit of unauthenticated IRC connections per IP, the panel's connection limits and idle time, and the limit of simultaneous Argon2id calculations.
Plugins: the interpreter moved to the loop execution mode after fuzzing found that the chained-call mode could exhaust the native stack in unoptimised builds.
unsafe code: only one call to mallopt at start (memory allocator setting).
Dependencies:cargo-deny in CI (licences and security advisories).
Internal review of version 1.0. The new code of 1.0 was reviewed and three points were fixed before publishing: on networks without services the password is never sent to whoever uses the nick “NickServ”; the FiSH key exchange has a limit of requests and stored keys; decrypted text cannot break lines. Language files are validated (placeholders, tags, no control characters or line breaks) and a text that fails is shown in English.
Load test (cargo run --release --example loadtest -- --users 1000 --clients 1 --rate 2 --seconds 60): a simulated IRC server sends messages to every network at once (the worst case) and each client measures how long it takes to receive them, going through the history, modules and plugins. Machine: 2 vCPU Xeon at 2.1 GHz, 7 GB of RAM, network disk, default configuration (SQLite).
Users and networks
Clients
Messages/s delivered
CPU (of one core)
Memory
Latency p50 / p95 / p99
Every network back after an outage
500
500
492
5 %
52 MB
21 / 43 / 46 ms
6.2 s
500
500
4,847
43 %
56 MB
46 / 64 / 72 ms
6.2 s
1,000
1,000
1,951
17 %
88 MB
50 / 80 / 95 ms
6.2 s
1,000
2,000
1,968
11 %
114 MB
44 / 66 / 78 ms
6.2 s
2,000
2,000
1,968
19 %
153 MB
75 / 141 / 155 ms
6.3 s
No message was lost in any test.
Latency grows with the size of the burst: with 2,000 networks receiving the same message at once, the last one is delivered about 150 ms after the first. With real traffic, spread over time, it is a few milliseconds.
Memory: about 45 KB per connected network and about 30 KB more per client, not counting the history (on disk).
Client logins (Argon2id) go one at a time per CPU: 2,000 clients signing in together take about 27 s on 2 cores, without a memory spike.
Disk: about 150 bytes per stored message, including the search index.
Sizing: a small server (1–2 vCPU, 1 GB of RAM) serves hundreds of users with several networks and thousands of messages per second. With many users, check limits.max_connections, the service's LimitNOFILE (65,536 by default) and the history retention.
How it is measured
The test (crates/xbncd/examples/loadtest.rs) starts, in a single process:
a simulated IRC server with a channel #xbnc;
xBNC with N users, each one with its network connected to that server and joined to #xbnc;
one or more IRC clients per user connected to xBNC.
The server sends --rate messages per second to the channel, to every network at the same time (the worst case: simultaneous bursts). Each message carries the time it was sent, and each client measures how long it takes to arrive: IRC server → xBNC (stored in the history, modules, plugins) → client. At the end the server cuts every connection and the test measures how long it takes for every network to come back.
Memory and CPU are those of the whole process, so they include the simulated server and the test clients (xBNC alone uses a little less). The build is --release with the default configuration (history in SQLite with WAL).
Problems found and fixed with this test
Problem
Before
After
Memory after many simultaneous logins: glibc kept in each thread the 19 MB of every Argon2id calculation.
1,680 MB with 500 clients
55 MB (util::tune_allocator, a fixed mmap threshold)
Hundreds of Argon2id calculations at once (also useful against floods of login attempts).
No limit
One per CPU (crypto::with_hash_slot)
One SQLite transaction per stored message.
p50 70 ms and 13 % CPU with 500 networks
p50 21 ms and 5 % CPU: a single writer thread stores everything waiting in one transaction (Db::insert_message)
Still to do
Repeat the test on dedicated hardware and with several networks per user.
Measure with real IRC servers far away (the test uses a local one, without network latency).
To repeat it: cargo run --release --example loadtest -- --users 2000 --clients 1 --rate 1 --seconds 60. The options are --users, --clients (per user), --rate (messages per second to the channel) and --seconds.
testenv/ brings up xBNC with two different IRC servers (Ergo and InspIRCd) on an isolated network, all in plain text on purpose:
cd testenv
docker compose up -d --build
./setup.sh
# IRC: localhost:6667, password test/ergo@laptop:testpassword1
# Panel: http://localhost:8080 (user test, password testpassword1)
Do not publish those ports on a public interface.
Automatic tests
cargo test --workspace # every test
XBNC_TEST_POSTGRES=postgres://postgres@127.0.0.1/postgres cargo test --workspace # also with PostgreSQL
cargo clippy --workspace --all-targets -- -D warnings
cd web && npm run build && npm run i18n:check # panel and translations
xbncd locales check locales/*.json # language files
python3 ../xbnc-wiki/build.py --app . # the wiki (a separate project)
They cover the protocol, the units, the API against a real bouncer, IRC end to end, every *status command, the switch to a backup server, the trusted certificate with a real TLS handshake, a language added as a file, modules and plugins against a simulated IRC server, the panel, SSO and real IRC servers in CI (Ergo, InspIRCd, UnrealIRCd, Solanum and miniircd). Fuzzing (fuzz/) runs every week. testenv/ui/language_check.py checks in a real browser that every way of changing the language works.
Tests on real networks
The test plan per network, module and plugin is in testenv/real/PLAN.md. See Testing the beta.
The test sets
Set
Where
What it covers
Protocol (16)
crates/xbnc-proto
Parser and serialiser, tag escaping, prefixes, casemapping, ISUPPORT, CTCP.
Units of the service (142)
crates/xbncd/src
Configuration, Argon2id, encryption and key permissions, database and migrations (SQLite and PostgreSQL, and upgrading an old schema), history and search, cursors, sessions, backups, login, brute-force limiter, log masking, codec, CAP, roles, settings, TOTP (RFC 6238 vectors), metrics, tokens, cookies, origin, IP behind a proxy, OpenAPI coverage, HTTP→HTTPS redirect, module catalogue and validation, globs, mentions, aliases, logs, client and trusted certificates (with a real TLS handshake), FiSH, time zones, languages (every key used exists, Spanish complete, files checked and loaded), plugins (permissions, limits, fuzzing regressions), DCC, HTTP client, release signatures, the update helper and connection limits.
API (12)
tests/api.rs
First run with a one-time code, login, sessions, CSRF, origin, headers, brute force, networks, channels, history, export, isolation between users, roles, live settings, 2FA, panel, OpenAPI, modules, certificates, notes, language, tokens, metrics, alerts, import and plugins.
End to end (12)
tests/e2e.rs
The whole bouncer against a simulated IRC server (scenarios below).
Modules (16 + 12)
tests/modules.rs, tests/modules_v1.rs
Each module against the simulated server: away and summary, keep nick, replies to the device that asked, events, CTCP, sticky channels, aliases, auto op, rejoin after a kick, watch words, MOTD and logs; and the modules of 1.0: IRCv3 extras only for clients that asked, services login (X with the hidden host and Login on Connect), invite, ignore and remove colours, FiSH in both directions and DH1080, detached channels, flooding channels, autocycle, Latin-1 and scheduled tasks.
*status commands (12)
tests/status_commands.rs
Grouped help, other bouncers' command names, backup servers (added, jumped to, promoted and automatic switch), trusted fingerprints, bind hosts, disabled and ordered channels, buffers, MOTD and broadcast, permissions, networks from IRC and a language added as a file.
Plugins answering on the network and to the user, and each official plugin: seen, trivia, greet, factoids, karma, quotes, games, badwords, web access only to approved sites, reminders, tell, polls, channel stats, anti-flood, dice and autoresponder.
Panel (3)
tests/panel.rs
Chat in the panel, mentions and channel history, plugin development mode and the one-click catalogue.
SSO (3)
tests/sso.rs
Passkeys, OpenID Connect and LDAP.
PostgreSQL (1)
tests/postgres.rs
Moving an SQLite database to PostgreSQL (with XBNC_TEST_POSTGRES).
Real IRC server (1)
tests/real_ircd.rs
A real xBNC against a real IRC server (see below).
Rust SDK (3)
sdk/rust
Events and answers of the SDK.
End-to-end scenarios
On connecting, the client gets the welcome, the network's ISUPPORT, its channels, the topic and the list of users.
A message received with the client closed is played back on reconnecting, with its time; your own messages are not played back to the same device and a third connection gets no duplicates.
Two devices connected at once stay in sync; a new device gets the recent history.
CHATHISTORY LATEST, AFTER and errors, batch, the server's server-time kept, msgid and echo-message; capabilities that are not implemented are not announced.
Login with SASL PLAIN.
A wrong password is refused and the IP is blocked after three failures, even with the right password.
Unknown network: the error lists the networks available.
Server outage: the bouncer says so, reconnects with growing delays and joins the channels again.
*status, and PART and JOIN with a key saved (the key is stored encrypted).
Commands on connect are sent before joining channels.
Read markers are shared between devices.
soju.im/bouncer-networks for modern clients.
Real IRC servers
tests/real_ircd.rs connects a real xBNC to a real IRC server (variable XBNC_IRCD=host:port) and checks the list of users, messages in both directions, sync between two devices, playback without duplicates with the client closed, a nick change, leaving a channel and staying connected to the network:
XBNC_IRCD=127.0.0.1:6667 cargo test -p xbncd --test real_ircd -- --ignored
CI runs it against Ergo, InspIRCd, UnrealIRCd, Solanum and miniircd (testenv/ci/start-ircd.sh builds or installs each one with a minimal configuration).
Fuzzing
fuzz/ has six libFuzzer targets:
Target
What it receives
irc_message
Arbitrary IRC lines: parser, serialiser and the round trip.
dcc
DCC offers.
import_config
Configuration files of another bouncer.
search_query
Search queries against the full-text index.
plugin_manifest
WebAssembly files with arbitrary manifests.
plugin_runtime
Arbitrary WebAssembly modules loaded and run as plugins.
CI (.github/workflows/fuzz.yml) runs each target for 20 minutes every week, keeps the corpus between runs and first checks that the failures already found (fuzz/regressions/) are still fixed. The failures found so far, all fixed and with their test:
The parser accepted CR, LF or NUL inside a line.
The parser accepted commands that are not alphanumeric (@ @).
DCC offers accepted control characters.
Embedding a manifest overflowed with sections of impossible size.
A plugin with a long loop exhausted the process's native stack in builds where the compiler does not turn the interpreter's calls into jumps: the interpreter now uses the loop execution mode, which never grows the stack.
Load tests
cargo run --release --example loadtest -- --users 2000 --rate 1 --seconds 60. Results and method in Performance.
The panel in a browser
The panel has been gone through in Chrome with Playwright against a real bouncer with a real IRC client connected: first run, network wizard, channels, history and search, users, security, settings, 2FA, modules, plugins (installation with the permissions preview, per-network settings), import from another bouncer, alerts, updates, backups, chat, the sign-in page, the light and dark themes and the phone, in English and in Spanish, with no errors in the console apart from the expected 401 before signing in. testenv/ui/language_check.py URL USER PASSWORD repeats in a real browser every way of changing the language (sign-in page, Your account, Settings, a language added as a file) and checks that no text is left in another language.
Manual checklist
Use it with testenv/ or with a real installation:
The service starts and stops cleanly (systemctl start and stop xbnc; the log shows xBNC stopped).
An administrator and several users are created with the command line.
A user with two networks is connected to both at once.
A classic client (HexChat, WeeChat, irssi or mIRC) connects with user/network@device:password.
With the client closed, someone else writes in the channel and in private; on reconnecting those messages arrive exactly once.
Two clients of the same user see what the other one writes.
docker compose restart ergo (or cutting the network): xBNC says so, reconnects and joins the channels again.
/msg *status status shows the right state.
Five wrong passwords block the IP for 15 minutes.
scripts/backup.sh and scripts/restore.sh bring back users, networks and history.
journalctl -u xbnc contains no passwords after logging in, SASL and /msg NickServ IDENTIFY.
From the panel: create a user, add a network with the wizard, join a channel and see the history.
An ordinary user does not see Users, Security or Settings, nor other people's networks.
Turn on two-step verification and sign in with the code.
Change the retention in Settings and check in System that it applies without restarting.
Memory use with 1 user and 2 networks (systemctl status xbnc): note it down to compare in future versions.
Invalid IRC server certificate (self-signed or expired) or missing root certificates.
sudo apt install ca-certificates; check the host and the TLS port. If the network uses its own certificate, trust it with /msg *status addfingerprint FINGERPRINT.
*status: registration timed out
Wrong port (TLS against a port without TLS or the other way round).
Check the network's port and TLS (Undernet: 6667 without TLS).
SASL authentication failed
Wrong SASL account or password.
Correct them in the network configuration.
SASL is configured but this server does not offer SASL PLAIN
The network does not support SASL.
Remove SASL and use Services login.
Your host is trying to (re)connect too fast -- throttled
The network slows down repeated reconnections from your IP.
Wait; xBNC waits at least 2 minutes. Do not click Connect several times.
On Undernet I join the channels with my real IP
No login before joining.
Turn on Services login with “Log in when connecting” and a wait of a few seconds.
The login with X, Q or AuthServ does not work
Wrong services account or password.
Check Services login; the reason arrives through *status.
On IRCnet it does not identify
IRCnet has no NickServ; xBNC does not send the password on purpose.
Use SASL from sasl.ircnet.com on the network, or nothing.
The nick ends in “_”
The nick was taken when connecting.
Turn on Keep nick or type /nick your-nick.
xBNC keeps changing my nick
Keep nick goes back to the saved nick.
/msg *status setnick new, or turn the module off.
I see “�” instead of accented letters
Rare: xBNC converts Latin-1 to UTF-8.
If it is the other person who cannot see your accents, add their channel or nick in Character encoding.
The client certificate does not identify
The fingerprint is not in your account, the network does not use TLS or you did not reconnect.
Add the fingerprint (SHA-512 on Libera, SHA-1 on OFTC) and reconnect.
Modules and plugins
Symptom
Likely cause
Solution
/msg *plugin only shows “Usage: MSG <nick> <message>”
That is your client's help: you wrote nothing after the name.
/msg *poll help.
An alias does nothing
Your client does not send commands it does not know.
/quote name arguments.
The autoop module does not give op
You do not have op, the mask does not match or the other person does not answer the challenge.
Check the real user@host with /whois, and the key.
A plugin with web access does not answer
Its sites are not approved.
A superadministrator approves them in Plugins (“Sites it may connect to”).
A plugin has stopped on a network
It failed five times in a row.
Look at the Plugins log; turn it off and on again after fixing it.
No log files appear
The log module is off or there are no permissions on /var/lib/xbnc/logs.
Turn it on; the service log says if it cannot write.
Scheduled tasks run at another time
Your time zone is not set.
Your account → Time zone.
Panel and service
Symptom
Likely cause
Solution
The panel does not load (connection refused)
[web] is missing or it listens on 127.0.0.1.
sudo xbncd info; to redo the configuration, sudo ./scripts/install.sh --reconfigure.
“The page was out of date”
Old CSRF token after signing in again.
Reload the page.
“Request refused because it came from another site”
public_url does not match the browser's address.
Correct public_url or use that address.
After signing in you are back at the sign-in page
Secure cookie over HTTP without TLS.
Use HTTPS or localhost (or secure_cookies = false, for testing only).
Everyone gets blocked behind Nginx
Without trust_proxy, everything comes from 127.0.0.1.
trust_proxy = true and the X-Forwarded-For header in Nginx.
Version 1.0.0 is being tested on real networks (Undernet, Libera.Chat, freenode, Rizon, EvilNet, IRCnet and others) before it is declared stable.
Install it and add your networks.
Follow the test plan in testenv/real/PLAN.md: one section per network, module and plugin.
If something fails, download the test report from Your account.
Send it with what you did, what you expected, what happened, the network, the client, the approximate time, the version (/msg *status version) and the warnings of the log (sudo journalctl -u xbnc --since "1 hour ago" | grep -E "WARN|ERROR"; review it before sending it).
If the project is on GitHub, use the “Test report” issue template. Security flaws are not made public: follow SECURITY.md.
Known limits
Undernet and QuakeNet do not offer TLS: the connection to those networks is unencrypted.
FiSH is old cryptography (Blowfish, without integrity): it protects from the network's servers, it is no replacement for end-to-end encryption. Decrypted messages are stored in your history like the rest.
Some clients do not send commands they do not know: aliases need /quote.
DCC between an IPv6-only client and an IPv4-only one is not translated.
Search by pieces of words is only used when there are no whole-word matches.
If the process stops abruptly, a few seconds of history may be repeated.
In a language added as a file, the replies of the official plugins come in English (they speak English and Spanish).
In this beta installation is from the source code; no .deb or .rpm packages are published.
Still to be checked on real networks: login with X, NickServ, Q and AuthServ; invite and unban; plugins with web access; FiSH with real clients; installer with real systemd and Let's Encrypt; DCC between networks; SASL EXTERNAL; importer with real installations.
Un bouncer IRC moderno para Undernet, Libera.Chat, Rizon, EvilNet, IRCnet y cualquier red. Esta wiki explica todo lo que hace y cómo usarlo, de la instalación a la API.
xBNC es un bouncer IRC: un programa que se queda conectado a tus redes IRC las 24 horas, aunque cierres el cliente o apagues el ordenador. Cuando vuelves, te entrega lo que te perdiste, una sola vez en cada dispositivo.
Se instala en un servidor propio (un VPS, una Raspberry Pi, un ordenador de casa), se usa con tu cliente IRC de siempre y se configura entero desde un panel web, o desde IRC con /msg *status. Es un único programa escrito en Rust, con el panel incluido dentro.
24módulos nativos configurables por red
18plugins oficiales en WebAssembly
2+idiomas: inglés y español incluidos, y cualquier otro con un fichero de traducción
Qué incluye
Bouncer: conexión permanente a varias redes por usuario, con TLS verificado, SASL PLAIN y EXTERNAL, reconexión automática con espera progresiva y protección anti-flood hacia el servidor.
Varios dispositivos a la vez: lo que escribes en uno aparece en los demás, y cada uno lleva su propia cuenta de lo que ha leído.
Historial: canales y privados con búsqueda de texto completo, filtros, exportación en TXT y JSON, retención general y por canal, y cuotas por usuario.
IRCv3 completo con los servidores y con los clientes (lista completa); los clientes clásicos ven el IRC de siempre.
Chat web en el panel, vista de menciones y marcadores de lectura compartidos entre dispositivos.
Avisos de menciones y privados por Telegram, Discord, correo, una dirección web propia o notificaciones push del navegador y el móvil.
24 módulos (login en servicios, ausente automático, op automático, cifrado FiSH, canales desacoplados, tareas programadas…) y 18 plugins ya instalados (trivial, encuestas, recordatorios, el tiempo…).
Multiusuario: cinco roles, cuotas, suspensión, auditoría, verificación en dos pasos, passkeys, OpenID Connect y LDAP.
Datos: SQLite o PostgreSQL, alta disponibilidad activo/en espera, copias de seguridad desde el panel o la terminal, migración desde otro bouncer.
Administración: panel web, API REST con tokens, WebSocket, métricas para Prometheus con panel de Grafana, alertas por panel, IRC y webhook.
Seguridad: Argon2id, credenciales cifradas, bloqueo de fuerza bruta, CSRF, CSP, servicio sin privilegios, plugins aislados y fuzzing continuo.
Tu cliente IRC ya no se conecta a la red, sino a xBNC. xBNC mantiene una conexión por red que no se corta cuando cierras el cliente, y se presenta ante cada dispositivo como si fuera el servidor de esa red.
Te conectas con la contraseña de servidor usuario/red@dispositivo:contraseña. El nombre del dispositivo (@portatil, @movil) es opcional, pero permite que cada uno lleve su propia cuenta de lo que ha leído.
xBNC te pone al día: entra contigo en tus canales, te envía el tema y la lista de usuarios, y reproduce los mensajes que llegaron desde tu última conexión en ese dispositivo, sin repetir lo que ya viste.
Mientras no estás, xBNC sigue en los canales, guarda el historial, responde a CTCP, te marca como ausente y ejecuta tus módulos y plugins.
Todos tus dispositivos están sincronizados: lo que escribes en el móvil aparece en el portátil, y los marcadores de lectura se comparten.
Qué recibes al conectar
La bienvenida de xBNC con los parámetros (ISUPPORT) de la red real.
El MOTD de la red (salvo que actives Ocultar el MOTD), seguido del nombre y la versión de xBNC.
Los canales en los que estás, con su tema y su lista de usuarios.
Los mensajes que este dispositivo no ha visto. Si el cliente no admite server-time, cada mensaje lleva delante la fecha y la hora entre corchetes.
Si te perdiste privados o menciones, un resumen de *status (módulo Ausente automático).
Si la red está desconectada, un aviso de *status con el motivo.
El mensaje del día de este servidor, si un superadministrador lo ha puesto (/msg *status setmotd).
Historial sin duplicados
Cada dispositivo (el @nombre del login, o default si no pones ninguno) tiene un cursor por red: el último mensaje que se le entregó. Dos conexiones con el mismo nombre comparten cursor.
Al conectar recibe los mensajes posteriores a su cursor, hasta playback_max (1000 por defecto). Un dispositivo nuevo recibe los new_client_playback más recientes (100 por defecto).
Los cursores se guardan al desconectar y cada 5 segundos, y nunca retroceden. Si el proceso se corta de golpe, como mucho se repiten unos segundos.
Los clientes con draft/chathistory no reciben reproducción automática: piden lo que necesitan con CHATHISTORY (hasta chathistory_max, 500 por defecto).
Conexión con cada red
Estados: desconectada, registrándose y conectada. Durante el registro se negocian las capacidades IRCv3, SASL y el nick (con alternativa si está ocupado).
Tiempo máximo de registro: 60 segundos. Si el servidor calla 90 segundos se le envía PING, y a los 180 se da la conexión por perdida.
Reconexión automática con espera progresiva de 5 segundos a 5 minutos, con una variación aleatoria del ±20 % para que miles de redes no vuelvan en el mismo segundo. Si la red avisa de que reconectas demasiado rápido, xBNC espera al menos 2 minutos.
La salida hacia el servidor pasa por un control de ritmo (ráfaga de 9 líneas y después 2 por segundo) para que la red no te expulse por flood.
El certificado TLS del servidor se verifica siempre con los certificados raíz del sistema. Para una red con certificado propio o caducado se puede confiar en ese certificado concreto por su huella SHA-256 (/msg *status addfingerprint); no existe opción para desactivar la verificación.
Cada red puede tener servidores de reserva (/msg *status addserver). Si la conexión a un servidor falla, xBNC prueba el siguiente al segundo; tras recorrer la lista entera vuelve a la espera progresiva. Si un servidor ya conectado corta la conexión, xBNC vuelve primero a ese mismo servidor.
La dirección local de salida (vhost) se puede fijar por red o por usuario (/msg *status setbindhost, solo administradores).
Si una línea no es UTF-8 se interpreta como Windows-1252, así que las tildes de los clientes antiguos se ven bien.
Usuarios virtuales
xBNC añade «usuarios» que empiezan por *. No existen en la red: son la forma de hablar con xBNC desde cualquier cliente. *status, *notes, *watch, *crypt, *ignore y uno por cada plugin (*trivia, *remind…). Ver Comandos IRC.
Linux con systemd: Debian 12, Ubuntu 22.04 o posterior, Fedora, Rocky o Alma.
Rust 1.89 o posterior (rustup.rs) y un compilador de C: sudo apt install build-essential o sudo dnf install gcc.
1 CPU y 512 MB de RAM para uso personal. Medido: 2 000 usuarios con sus redes y clientes en unos 150 MB (ver Rendimiento).
Puertos: 6697 (IRC con TLS), 6667 (IRC sin TLS, solo red local), 8443 (panel). Con Let's Encrypt, también el 80; con Nginx, el 443.
El panel web viene ya compilado dentro del código: no hace falta Node.js ni npm.
Instalar
# 1. Instalar Rust (una vez, como usuario normal)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# 2. Compilar xBNC (como usuario normal)
unzip xbnc-1.0.0.zip && cd xbnc
cargo build --release
# 3. Instalar el servicio
sudo ./scripts/install.sh
Ejecutado en una terminal y sin opciones, el instalador te pregunta si quieres usar un dominio, el correo para Let's Encrypt y si quieres el panel en https://tudominio con Nginx. Al terminar muestra la dirección del panel y el código de un solo uso para crear tu cuenta:
Panel: https://bnc.ejemplo.org:8443
IRC: bnc.ejemplo.org, puerto 6697 (SSL)
Código para crear tu cuenta: K7QM-2XPA-9DWE
Abre el panel, escribe el código y crea el primer administrador (será superadministrador). Para volver a ver la dirección y los puertos: sudo xbncd info; para volver a ver el código: sudo xbncd setup-code.
Qué hace el instalador
Comprueba el sistema, el binario compilado y, si usas dominio, que el DNS apunta a esta máquina y qué puertos tienes que abrir en el router.
Crea el usuario del sistema xbnc, sin privilegios, y las carpetas con sus permisos.
Instala el binario en /usr/local/bin/xbncd y el servicio de systemd con medidas de aislamiento (ProtectSystem=strict, NoNewPrivileges, sin capacidades, filtro de llamadas al sistema).
Crea el certificado: propio para la IP (o el dominio), o de Let's Encrypt con renovación automática. La renovación recarga xBNC (y Nginx, si lo usa).
Escribe /etc/xbnc/xbnc.toml con la versión en la cabecera, crea la base de datos y la clave secreta.
Con --nginx, instala y configura Nginx: redirección del puerto 80, el panel en el 443, WebSockets y subida de plugins y copias de seguridad.
Abre los puertos en ufw o firewalld y arranca el servicio.
Opciones del instalador
Opción
Qué hace
--domain NOMBRE
Usar un dominio (bnc.ejemplo.org) en lugar de la IP.
--letsencrypt CORREO
Con --domain: certificado gratuito de Let's Encrypt con renovación automática. Necesita el puerto 80 accesible desde internet.
--nginx
Con --domain y --letsencrypt: instala y configura Nginx para servir el panel en https://DOMINIO (puerto 443).
--ip DIRECCIÓN
IP que usar si la detectada no es la correcta (varias interfaces o VPN).
--language es|en
Idioma del instalador y predeterminado de xBNC.
--behind-proxy
Panel en 127.0.0.1:8080, para publicarlo con tu propio proxy.
--web-port N
Puerto del panel (8443 por defecto).
--irc-port N
Puerto IRC con SSL (6697 por defecto).
--plain-port N|off
Puerto IRC sin SSL, solo red local (6667 por defecto); off lo quita.
--no-firewall
No abrir puertos en ufw o firewalld.
--reconfigure
Rehacer la configuración y el certificado de una instalación existente (guarda una copia de la anterior). No toca usuarios, redes ni historial.
--binary RUTA
Binario que instalar (target/release/xbncd por defecto).
--no-start
Instalar sin arrancar el servicio.
--non-interactive
No preguntar nada (por defecto pregunta si se ejecuta en una terminal).
Ejemplos
# Por IP, con certificado propio
sudo ./scripts/install.sh
# Con dominio y Let's Encrypt, panel en https://bnc.ejemplo.org:8443
sudo ./scripts/install.sh --domain bnc.ejemplo.org --letsencrypt tu@correo.org
# Igual, pero con el panel en https://bnc.ejemplo.org detrás de Nginx
sudo ./scripts/install.sh --domain bnc.ejemplo.org --letsencrypt tu@correo.org --nginx
# Pasar una instalación existente a dominio
sudo ./scripts/install.sh --reconfigure --domain bnc.ejemplo.org --letsencrypt tu@correo.org
# Cambiar la IP detectada
sudo ./scripts/install.sh --reconfigure --ip 192.168.1.50
Dónde queda cada cosa
Ruta
Contenido
/usr/local/bin/xbncd
El programa.
/etc/xbnc/xbnc.toml
Configuración del proceso (root:xbnc, 0640).
/etc/xbnc/tls/
Certificado TLS: fullchain.pem y privkey.pem (root:xbnc, 0750).
/etc/xbnc/locales/
Idiomas adicionales y correcciones de traducción (ver Traducir xBNC).
/var/lib/xbnc/
Base de datos y clave secreta (xbnc:xbnc, 0700). Haz copia de secret.key: sin ella no se pueden descifrar las contraseñas guardadas.
/var/lib/xbnc/logs/
Registros en ficheros del módulo log (carpetas 0700, ficheros 0600).
/var/lib/xbnc/plugins/
Plugins instalados (0600). En dev/, los del modo desarrollo.
/var/lib/xbnc/backups/
Copias de seguridad hechas desde el panel.
El servicio corre como el usuario sin privilegios xbnc. sudo systemctl status xbnc, sudo systemctl restart xbnc; sudo systemctl reload xbnc hace que relea redes, canales y usuarios cambiados con la terminal, y los ficheros de idioma. El registro: sudo journalctl -u xbnc -f.
Usarlo desde fuera de casa
Abre en el router el puerto 6697 hacia la máquina de xBNC y conéctate a tu IP pública o a un nombre de DNS dinámico (DuckDNS y similares). El 6667 no hace falta abrirlo: desde fuera xBNC lo rechaza.
Si no quieres abrir el panel (8443) a internet, administra con un túnel SSH: ssh -L 8443:127.0.0.1:8443 usuario@tu-ip-publica y abre https://localhost:8443.
Con un dominio que apunte a tu casa, reinstala con --reconfigure --domain … --letsencrypt … para quitar los avisos de certificado.
Panel detrás de tu propio Nginx
Si ya tienes Nginx y prefieres configurarlo tú, instala con --domain bnc.ejemplo.org --behind-proxy y usa el ejemplo incluido:
# Nginx reverse proxy for the xBNC web panel.
# Copy to /etc/nginx/sites-available/xbnc, adjust server_name and the
# certificate paths, then: ln -s ../sites-available/xbnc /etc/nginx/sites-enabled/
# In xbnc.toml: [web] address = "127.0.0.1:8080", public_url = "https://bnc.example.org",
# secure_cookies = true, trust_proxy = true.
server {
listen 80;
listen [::]:80;
server_name bnc.example.org;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name bnc.example.org;
ssl_certificate /etc/letsencrypt/live/bnc.example.org/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/bnc.example.org/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
# Plugins are up to 4 MB.
client_max_body_size 8m;
# Restoring a backup uploads the whole archive.
location = /api/v1/backups/restore {
client_max_body_size 4g;
proxy_request_buffering off;
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 3600s;
}
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSockets: live dashboard, web chat, plugin log
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 3600s;
}
}
# Needed once per Nginx configuration (e.g. in /etc/nginx/conf.d/upgrade.conf):
# map $http_upgrade $connection_upgrade { default upgrade; '' close; }
En xbnc.toml, con el panel detrás de Nginx: address = "127.0.0.1:8080", public_url = "https://bnc.ejemplo.org", secure_cookies = true y trust_proxy = true.
Certificado de otra autoridad
Copia tu certificado y tu clave a /etc/xbnc/tls/fullchain.pem y /etc/xbnc/tls/privkey.pem (propietario root:xbnc, la clave con permisos 0640) y reinicia con sudo systemctl restart xbnc. El certificado se lee al arrancar; reload no basta.
Docker
mkdir -p tls
cp /ruta/fullchain.pem /ruta/privkey.pem tls/
# Edita XBNC_HOSTNAME y XBNC_PUBLIC_URL en docker-compose.yml
docker compose up -d --build
docker compose logs xbnc | grep "setup code"
Abre https://bnc.ejemplo.org:8443/, escribe el código y crea tu cuenta. La terminal: docker compose exec xbnc xbncd user add admin --admin. Los datos viven en el volumen xbnc-data. El contenedor se ejecuta como el usuario sin privilegios xbnc (uid 10001), con sistema de ficheros de solo lectura, sin capacidades y con no-new-privileges.
Variable
Uso
XBNC_HOSTNAME
Nombre del servidor (solo en el primer arranque).
XBNC_TLS_CERT, XBNC_TLS_KEY
Rutas del certificado y la clave dentro del contenedor; activan TLS en el 6697 (IRC) y el 8443 (panel).
XBNC_INSECURE=1
Texto plano en el 6667 (IRC) y el 8080 (panel), solo para pruebas.
unzip xbnc-nueva.zip && cd xbnc # o git pull
cargo build --release
sudo ./scripts/update.sh
update.sh comprueba el binario nuevo y la configuración, hace una copia de seguridad, para el servicio, cambia el binario y lo arranca. Si no arranca, vuelve al binario anterior y a la base de datos de antes de actualizar (un binario nuevo puede haber actualizado el esquema, y uno antiguo se niega a abrir un esquema más nuevo). Nunca se actualiza nada solo. Con --binary RUTA instala otro binario.
Si además quieres rehacer la configuración (por ejemplo, para pasar a dominio), ejecuta después sudo ./scripts/install.sh --reconfigure.
Desinstalar
sudo ./scripts/uninstall.sh # quita el servicio y el binario; conserva /etc/xbnc y /var/lib/xbnc
sudo ./scripts/uninstall.sh --purge # borra también la configuración, los datos y el usuario xbnc
Crea tu cuenta en el panel con el código de un solo uso. La primera cuenta es superadministrador.
Añade una red en Redes → Añadir red. El asistente tiene cuatro pasos: elegir la red (las conocidas vienen preparadas), tu nick y nombre, la identificación (SASL, X, Q…) y los canales.
Conecta tu cliente. La página de la red tiene la tarjeta «Conecta tu cliente IRC» con tus datos exactos y los pasos para HexChat, mIRC, WeeChat e irssi, con botones para copiar.
Activa módulos y plugins en Módulos. Elige la red arriba; con muchas redes, «Copiar a otras redes» repite la configuración.
Elige tu zona horaria e idioma en Tu cuenta: la zona se usa en los registros y en las tareas programadas.
Activa la verificación en dos pasos y, si quieres, una passkey, en Tu cuenta.
Redes preparadas en el asistente
Red
Servidor
Puerto
TLS
Identificación
Descripción
Undernet
irc.undernet.org
6667
no
X con Login on Connect
Red clásica con canales de todo tipo
Libera.Chat
irc.libera.chat
6697
sí
SASL
Comunidades de software libre y proyectos de código abierto
Sirve cualquier cliente que permita indicar servidor, puerto y contraseña de servidor. No hace falta ningún plugin.
Qué quieres
Contraseña de servidor
Usuario con una sola red
alicia:contraseña
Elegir red
alicia/undernet:contraseña
Elegir red e identificar el dispositivo
alicia/undernet@portatil:contraseña
El mismo login en otro orden
alicia@portatil/undernet:contraseña
Servidor: tu dominio o IP (sudo xbncd info lo muestra).
Puerto: 6697 con SSL desde cualquier sitio; 6667 sin SSL solo desde tu red local.
Contraseña: la misma con la que entras al panel. También puedes poner alicia/undernet@portatil en el campo de usuario (USER) y la contraseña sola en PASS, o entrar con SASL PLAIN desde el cliente.
Una conexión equivale a una red: para dos redes, crea dos servidores en tu cliente (alicia/undernet y alicia/libera). Los clientes con soju.im/bouncer-networks (Goguma, gamja, Halloy) ven todas tus redes en una sola conexión.
Dispositivo: la parte @dispositivo es opcional pero recomendable. Cada dispositivo (@portatil, @movil, @trabajo) recibe su propio historial pendiente.
Si instalaste por IP, el certificado es propio: activa en tu cliente «aceptar certificados no válidos».
Lista de redes → Añadir: «xBNC Undernet»
Editar → Servidor: bnc.ejemplo.org/6697
Marca «Usar SSL para todos los servidores»
Método de inicio de sesión: Contraseña del servidor (/PASS)
Contraseña: alicia/undernet@hexchat:tu-contraseña
Con certificado propio: marca «Aceptar certificados SSL no válidos»
Lo más rápido, en cualquier ventana:
/server bnc.ejemplo.org +6697 alicia/undernet@mirc:tu-contraseña
El + delante del puerto activa SSL. Con certificado propio, mIRC pregunta la
primera vez: pulsa Aceptar. Para guardarlo: Archivo → Seleccionar servidor →
Añadir, con los mismos datos.
/server add xbnc-undernet bnc.ejemplo.org/6697 -tls
/set irc.server.xbnc-undernet.password "alicia/undernet@weechat:tu-contraseña"
/set irc.server.xbnc-undernet.capabilities "server-time,message-tags,batch,draft/chathistory,echo-message"
/connect xbnc-undernet
Con certificado propio: /set irc.server.xbnc-undernet.tls_verify off
Goguma (Android) y Palaver (iOS) admiten bouncers: añade el servidor bnc.ejemplo.org, puerto 6697 con TLS, usuario alicia y tu contraseña. Goguma muestra todas tus redes en una sola conexión y pide el historial con CHATHISTORY. Para recibir avisos con la aplicación cerrada, usa los avisos de xBNC (push, Telegram…), o instala el panel como aplicación.
KVIrc, Konversation, Textual, Halloy, The Lounge y Quassel (como cliente de una red IRC normal) funcionan con los mismos datos: servidor, puerto con TLS y contraseña de servidor. Los clientes con draft/chathistory (Goguma, gamja, Kiwi IRC, Halloy) piden el historial directamente y no reciben reproducción automática.
Si te equivocas de puerto o de SSL
xBNC detecta los errores habituales y te lo dice en el propio cliente:
Lo que ves en el cliente
Qué pasa
Qué hacer
Closing link: this port uses SSL/TLS. Enable SSL in your IRC client, or connect to port 6667 without SSL.
Te conectas al 6697 sin SSL.
Activa SSL o usa el 6667 desde casa.
Closing link: this port only accepts connections from the local network...
Usas el 6667 desde fuera de tu red.
Usa el 6697 con SSL.
Error de certificado o «certificado no válido»
Certificado propio (instalación por IP).
Acepta el certificado o pasa a un dominio con Let's Encrypt.
464 Password incorrect
Usuario, red o contraseña incorrectos.
Revisa usuario/red@dispositivo:contraseña.
Choose a network by logging in as usuario/<red>
Tienes varias redes y no indicaste cuál.
Añade /red al usuario.
ERROR :Too many failed login attempts
Tu IP está bloqueada por fallos.
Espera 15 minutos o pide a un administrador que la desbloquee en Seguridad.
Cuando el cliente se conecta con SSL a un puerto sin SSL no puede mostrar ningún mensaje: lo indica el registro del servicio (client sent TLS to a plain-text port).
Redes sin SASL (Undernet, QuakeNet)
Algunas redes no usan SASL: te identificas con un bot de servicios, X en Undernet o Q en QuakeNet. xBNC lo hace por ti cada vez que se conecta. Activa el módulo Login en servicios (en el asistente, el paso Cuenta) con tu cuenta y tu contraseña. Si prefieres hacerlo a mano, escribe los comandos en Configuración → Comandos al conectar, uno por línea, como en tu cliente:
Se envían antes de entrar en los canales, así tu IP ya está oculta (+x) cuando entras, y se guardan cifrados.
Compatibilidad de clientes
Los tests automáticos usan un cliente IRC estándar, un cliente con todas las capacidades IRCv3 y un cliente clásico sin ninguna. HexChat, mIRC, WeeChat e irssi se han usado a mano con xBNC; el resto de clientes de la lista siguen los mismos estándares pero no se han comprobado uno a uno. Si algo no funciona con un cliente concreto, avisa con su nombre y versión (ver Probar la beta).
Líneas que se envían tras conectar y antes de entrar en los canales (ver abajo).
Canales
Canales guardados, con su clave (cifrada). Un /join o /part desde el cliente se guarda solo.
Retención por canal
Cuánto se guarda el historial de un canal: lo del servidor, nada, para siempre o N días.
Servidores de reserva
Otros servidores de la misma red que xBNC prueba si falla el actual. Con /msg *status addserver (ver Comandos IRC).
Certificados de confianza
Huellas SHA-256 de certificados de servidor que se aceptan aunque no validen (certificado propio o caducado). Con /msg *status addfingerprint.
Dirección local (vhost)
La IP de este servidor con la que se conecta a la red. Con /msg *status setbindhost (solo administradores).
Canales desactivados y orden
Un canal desactivado sigue guardado pero no se entra en él; el orden es el de entrada al conectar. Con /msg *status disablechan y movechan.
Comandos al conectar
Se escriben como en tu cliente, uno por línea (hasta 20 y 510 caracteres cada uno), y se guardan cifrados porque suelen llevar contraseñas. $nick se cambia por tu nick actual. Se admiten /msg (o /privmsg, /query), /notice, /quote (o /raw) y cualquier comando IRC con barra (/mode, /away…). Una línea sin barra se envía tal cual.
Para identificarte con X, NickServ, Q o AuthServ es mejor el módulo Login en servicios: espera a que la red confirme el inicio de sesión antes de entrar en los canales y en Undernet usa Login on Connect.
Undernet
Undernet usa el servicio X y no tiene SASL ni TLS. Activa Login en servicios con tu cuenta de X: con Login on Connect la cuenta viaja en la contraseña del servidor (+x! cuenta contraseña), así que tu IP real no llega a verse nunca y entras en los canales ya con cuenta.users.undernet.org.
*** Tu host es ahora y2k.users.undernet.org
* y2k ha entrado en #xbnc
*statusxBNC 1.0.0 Made by y2k
Con Invitación y desbaneo, si no puedes entrar en un canal tuyo, xBNC pide a X INVITE o UNBAN.
Ignorar usa SILENCE: los mensajes de quien ignoras ni siquiera llegan a xBNC.
Los nicks tienen 12 caracteres como máximo; el nick de ausente se recorta solo.
Muchos canales hispanos usan Latin-1: xBNC lo entiende siempre, y con Codificación puedes escribirles en Latin-1.
La conexión con Undernet va sin cifrar porque la red no ofrece TLS; la contraseña de X viaja en claro hasta el servidor, igual que con cualquier cliente.
Libera.Chat y freenode
Identificación por SASL: pon tu cuenta y contraseña en la configuración de la red. En Libera también puedes usar un certificado (Certificado de cliente) con SASL EXTERNAL: genera el certificado en el panel, conéctate identificado, escribe /msg NickServ CERT ADD y reconecta. Si tu nick está ocupado al conectar, Login en servicios con «Recuperar mi nick» envía REGAIN (Libera) o GHOST (freenode).
Rizon
NickServ (Anope), por SASL o con Login en servicios. ChanServ atiende las invitaciones y los desbaneos.
EvilNet
Servicios X3: te identificas con AuthServ (AUTH cuenta contraseña) y el modo +x oculta tu host (numérico 396). Elige AuthServ en Login en servicios, o «Automático».
QuakeNet
Servicio Q (AUTH a Q@CServe.quakenet.org) y +x. Login en servicios lo hace por ti; la invitación y el desbaneo usan INVITE y UNBANME.
IRCnet y EFnet
No tienen NickServ: cualquiera podría ponerse ese nick. Por eso, en «Automático», xBNC nunca envía tu contraseña de servicios en estas redes y lo avisa en *status y en el registro. IRCnet ofrece SASL opcional con cuentas de sasl.ircnet.com. Para conservar op sin servicios, usa Recuperar op al quedarte solo.
OFTC y otras redes con NickServ
Login en servicios con NickServ, o comandos al conectar. Para el certificado de cliente, OFTC usa la huella SHA-1 y Libera la SHA-512: el panel muestra las tres huellas (SHA-1, SHA-256 y SHA-512).
Se escriben desde tu cliente con /msg *servicio comando. Las respuestas salen en tu idioma.
*status
Es el servicio principal: todo lo que se hace en el panel con una red se puede hacer también aquí. /msg *status help lista los comandos agrupados y con el nombre en negrita, solo los que tu cuenta puede usar; /msg *status help <comando> explica uno con un ejemplo, y /msg *status help <palabra> busca (por ejemplo help canal). Los nombres no distinguen mayúsculas, y se aceptan también los nombres que usan otros bouncers (ListChans, ListMods, AddTrustedServerFingerprint…), indicados en cada comando como «También vale».
*statusxBNC: comandos de *status
*statusSe escriben como /msg *status <comando>. Detalles y un ejemplo: help <comando>.
*statusCanales
*statuschannels Tus canales: usuarios, modos y estado
*statusnicks <#canal> Quién está en un canal, con su rango
*statustopics El tema de cada canal
Si no se indica otra cosa, el comando lo puede usar cualquier cuenta. Los demás indican quién: las cuentas con permiso de cambios (todos los roles menos «limitado»), los administradores (administrador y superadministrador) o los superadministradores. Las acciones de administración quedan en la auditoría.
General
Comando
Qué hace
help [comando]
Esta lista, o los detalles de un comando. Sin comando, lista todos los que puedes usar. Con uno, explica cómo funciona y da un ejemplo. También busca: help canal. Ejemplo: /msg *status help addserver
version
Versión de xBNC. Ejemplo: /msg *status version
uptime
Cuánto llevan en marcha xBNC y esta red. Ejemplo: /msg *status uptime
language <código>
Tu idioma, también para el panel web. Cambia al momento el idioma de los mensajes de xBNC, de los plugins y del panel web. Sin código, lista los idiomas instalados. Ejemplo: /msg *status language en
seen
Cuándo te conectaste por última vez. Ejemplo: /msg *status seen
showmotd
El mensaje del día de este servidor. Ejemplo: /msg *status showmotd
clients [usuario]
Dispositivos conectados a esta red. Los administradores pueden indicar un usuario para ver todos los suyos. Ejemplo: /msg *status clients También vale: listclients
Redes y conexión
Comando
Qué hace
status
Estado de esta red: servidor, TLS, nick, capacidades. Ejemplo: /msg *status status
networks
Tus redes y su estado. Ejemplo: /msg *status networks También vale: listnetworks
addnetwork <nombre> <servidor> [[+]puerto]
Añadir una red a tu cuenta. Un + delante del puerto significa TLS. Sin puerto: 6697 con TLS. Tu nick será el de esta red. Después conéctate con usuario/nombre. Ejemplo: /msg *status addnetwork libera irc.libera.chat +6697
delnetwork <nombre> confirmar
Borrar una de tus redes y su historial. Pide confirmación: repite el comando añadiendo «confirmar». No se puede deshacer. Ejemplo: /msg *status delnetwork pruebas confirmar
setnick <nick>
Cambiar el nick guardado de esta red. Ejemplo: /msg *status setnick y2k Solo cuentas con permiso de cambios (no el rol limitado).
connect
Volver a conectar a la red. Ejemplo: /msg *status connect Solo cuentas con permiso de cambios (no el rol limitado).
disconnect
Desconectar y quedarse desconectado. xBNC se queda desconectado de esta red hasta que uses connect. Ejemplo: /msg *status disconnect Solo cuentas con permiso de cambios (no el rol limitado).
jump [servidor]
Reconectar, al siguiente servidor o al que digas. Sin servidor, pasa al siguiente de la lista (servers). Con uno, conecta a ese. Ejemplo: /msg *status jump irc2.undernet.org Solo cuentas con permiso de cambios (no el rol limitado).
Servidores, certificados y direcciones
Comando
Qué hace
servers
Servidores de esta red (principal y de reserva). Si un servidor falla, xBNC prueba el siguiente por turnos. Ejemplo: /msg *status servers También vale: listservers
addserver <servidor> [[+]puerto] [contraseña]
Añadir un servidor de reserva. Un + delante del puerto significa TLS. Sin puerto: el del servidor principal. Hasta 20. Ejemplo: /msg *status addserver irc2.undernet.org 6667 Solo cuentas con permiso de cambios (no el rol limitado).
delserver <servidor> [puerto]
Quitar un servidor. Si quitas el principal, el primero de reserva pasa a ser el principal. Ejemplo: /msg *status delserver irc2.undernet.org Solo cuentas con permiso de cambios (no el rol limitado).
addfingerprint <SHA-256>
Confiar en el certificado de un servidor por su huella. Para servidores con certificado propio o caducado. 64 dígitos hexadecimales, con o sin dos puntos. Se aplica en la próxima conexión. Ejemplo: /msg *status addfingerprint 3F:9A:...:C2:1E También vale: addtrustedserverfingerprint Solo cuentas con permiso de cambios (no el rol limitado).
delfingerprint <SHA-256>
Dejar de confiar en un certificado. Ejemplo: /msg *status delfingerprint 3F:9A:...:C2:1E También vale: deltrustedserverfingerprint Solo cuentas con permiso de cambios (no el rol limitado).
fingerprints
Certificados de confianza de esta red. Ejemplo: /msg *status fingerprints También vale: listtrustedserverfingerprints
showbindhost
Dirección local con la que se conecta. Ejemplo: /msg *status showbindhost
setbindhost <IP>
Dirección local para esta red (vhost). Una de las direcciones IP de este servidor. Se aplica en la próxima conexión (jump). Ejemplo: /msg *status setbindhost 203.0.113.25 Solo administradores.
clearbindhost
Usar la dirección automática en esta red. Ejemplo: /msg *status clearbindhost Solo administradores.
setuserbindhost <IP>
Dirección local por defecto para todas tus redes. La dirección propia de una red (setbindhost) tiene prioridad. Ejemplo: /msg *status setuserbindhost 2001:db8::25 Solo administradores.
clearuserbindhost
Quitar tu dirección local por defecto. Ejemplo: /msg *status clearuserbindhost Solo administradores.
Canales
Comando
Qué hace
channels
Tus canales: usuarios, modos y estado. Ejemplo: /msg *status channels También vale: listchans
nicks <#canal>
Quién está en un canal, con su rango. Ejemplo: /msg *status nicks #xbnc También vale: listnicks
topics
El tema de cada canal. Ejemplo: /msg *status topics
enablechan <#canales>
Entrar en canales guardados que estaban desactivados. Admite varios canales y los comodines * y ?. Ejemplo: /msg *status enablechan #xbnc #xbnc-dev Solo cuentas con permiso de cambios (no el rol limitado).
disablechan <#canales>
Salir de canales pero dejarlos guardados. No se vuelve a entrar en ellos hasta enablechan. Admite * y ?. Ejemplo: /msg *status disablechan #xbnc* Solo cuentas con permiso de cambios (no el rol limitado).
attach <#canales>
Volver a mostrar canales desacoplados. Ejemplo: /msg *status attach #xbnc
detach <#canales>
Ocultar canales del cliente (siguen grabando). Una mención trae el canal de vuelta. Admite * y ?. Ejemplo: /msg *status detach #xbnc Solo cuentas con permiso de cambios (no el rol limitado).
movechan <#canal> <posición>
Cambiar la posición de un canal en el orden. El orden es en el que xBNC entra en tus canales y los lista. Ejemplo: /msg *status movechan #xbnc 1 Solo cuentas con permiso de cambios (no el rol limitado).
swapchans <#canal1> <#canal2>
Intercambiar la posición de dos canales. Ejemplo: /msg *status swapchans #xbnc #xbnc-dev Solo cuentas con permiso de cambios (no el rol limitado).
Historial (buffers)
Comando
Qué hace
playbuffer <#canal|nick> [líneas]
Reproducir el historial de una conversación. Te envía las últimas líneas (50 por defecto, hasta 1000). Ejemplo: /msg *status playbuffer #xbnc 100
clearbuffer <#canal|nick>
Borrar el historial de una conversación. Admite * y ?. Ejemplo: /msg *status clearbuffer bob También vale: clearhistory
clearallbuffers
Borrar todo el historial de esta red. Ejemplo: /msg *status clearallbuffers
clearallchannelbuffers
Borrar el historial de todos los canales. Ejemplo: /msg *status clearallchannelbuffers
clearallquerybuffers
Borrar el historial de todos los privados. Ejemplo: /msg *status clearallquerybuffers
setbuffer <#canal> <días|siempre|nada|servidor>
Cuánto tiempo se guarda el historial de un canal. siempre: no se borra; nada: no se guarda; servidor: lo general; o un número de días. Ejemplo: /msg *status setbuffer #xbnc 90 Solo cuentas con permiso de cambios (no el rol limitado).
Módulos y plugins
Comando
Qué hace
modules
Módulos y plugins de esta red y su estado. Ejemplo: /msg *status modules También vale: listmods
availmods
Todos los módulos y plugins, con lo que hacen. Ejemplo: /msg *status availmods También vale: listavailmods
enable <módulo|plugin>
Activar un módulo o plugin en esta red. Ejemplo: /msg *status enable away También vale: loadmod Solo cuentas con permiso de cambios (no el rol limitado).
disable <módulo|plugin>
Desactivarlo. Ejemplo: /msg *status disable away También vale: unloadmod Solo cuentas con permiso de cambios (no el rol limitado).
reloadmod <módulo|plugin>
Reiniciar un módulo o plugin en esta red. Ejemplo: /msg *status reloadmod trivia También vale: updatemod Solo cuentas con permiso de cambios (no el rol limitado).
get <módulo|plugin>
Ver sus ajustes. Ejemplo: /msg *status get away
set <módulo> <ajuste> <valor>
Cambiar un ajuste. Para ajustes sí/no: on/off. Mira los nombres con get. Ejemplo: /msg *status set away delay 60 Solo cuentas con permiso de cambios (no el rol limitado).
addline <módulo> <ajuste> <texto>
Añadir una línea a un ajuste de lista. Ejemplo: /msg *status addline watch patterns *xbnc* Solo cuentas con permiso de cambios (no el rol limitado).
dellines <módulo> <ajuste>
Vaciar un ajuste de lista. Ejemplo: /msg *status dellines watch patterns Solo cuentas con permiso de cambios (no el rol limitado).
Administración
Comando
Qué hace
users
Todas las cuentas y si están conectadas. Ejemplo: /msg *status users También vale: listusers Solo administradores.
allnetworks
Todas las redes de todos los usuarios, con su estado. Ejemplo: /msg *status allnetworks También vale: listallusernetworks Solo administradores.
traffic
Tráfico y mensajes desde que arrancó xBNC. Ejemplo: /msg *status traffic Solo administradores.
broadcast <mensaje>
Enviar un aviso a todos los usuarios conectados. Ejemplo: /msg *status broadcast Reinicio a las 23:00 Solo administradores.
Pasar una red a otro usuario. La red se reconecta como el nuevo usuario, con su historial. Ejemplo: /msg *status movenetwork ana libera bob Solo administradores.
rehash
Releer usuarios, redes, canales e idiomas. Para cambios hechos con la terminal (xbncd). Los cambios en xbnc.toml necesitan reiniciar. Ejemplo: /msg *status rehash También vale: reload Solo administradores.
listports
Puertos en los que escucha xBNC. Los puertos se configuran en xbnc.toml (o con el instalador) y necesitan reiniciar. Ejemplo: /msg *status listports Solo administradores.
saveconfig
Guardar la configuración. No hace falta: xBNC guarda cada cambio al momento. Ejemplo: /msg *status saveconfig Solo administradores.
setmotd <texto>
Poner el mensaje del día de este servidor. Los usuarios lo reciben de *status al conectar. Hasta 20 líneas. Ejemplo: /msg *status setmotd Bienvenidos a xBNC Solo superadministradores.
addmotd <texto>
Añadir una línea al mensaje del día. Ejemplo: /msg *status addmotd Normas: /msg *status help Solo superadministradores.
clearmotd
Quitar el mensaje del día. Ejemplo: /msg *status clearmotd Solo superadministradores.
restart [mensaje]
Reiniciar xBNC. Todas las redes se reconectan. El mensaje, si lo hay, se envía antes a todos. Ejemplo: /msg *status restart Actualizando Solo superadministradores.
shutdown [mensaje]
Parar xBNC por completo. Queda parado hasta que se arranque de nuevo en el servidor (systemctl start xbnc). Ejemplo: /msg *status shutdown Mantenimiento Solo superadministradores.
Notas sobre estos comandos
Servidores de reserva. Si la conexión a un servidor falla, xBNC prueba el siguiente de la lista al segundo, y tras recorrerla entera vuelve a esperar como siempre (5 s, 10 s, 20 s… hasta 5 min). Si un servidor ya conectado corta la conexión, xBNC vuelve primero a ese mismo servidor. Las contraseñas de los servidores de reserva se guardan cifradas. jump sin nada pasa al siguiente; jump servidor o jump servidor:puerto, a uno concreto.
Certificados de confianza. Para redes con certificado propio, caducado o con otro nombre. xBNC sigue comprobando todo lo demás de TLS y solo acepta ese certificado concreto (su huella SHA-256). Para ver la huella de un servidor: openssl s_client -connect irc.ejemplo.org:6697 </dev/null 2>/dev/null | openssl x509 -noout -fingerprint -sha256
Dirección local (vhost). Solo administradores, porque usa las direcciones IP del servidor. xBNC comprueba que la dirección pertenece a la máquina antes de guardarla. La de la red tiene prioridad sobre la del usuario. Se aplica en la siguiente conexión.
Canales desactivados. Siguen guardados con su clave y su configuración, pero xBNC no entra en ellos al conectar. El panel los marca como «Desactivado». enablechan entra en ellos al momento.
Orden de los canales. Es el orden en el que xBNC entra al conectar y en el que los lista channels y el panel. Los canales nuevos van al final.
Comodines.enablechan, disablechan, attach, detach y clearbuffer aceptan varios nombres separados por espacios o comas, y los comodines * y ?: disablechan #xbnc* #xbnc-dev.
Mensaje del día. Lo pone un superadministrador y lo recibe cada usuario, como aviso de *status, cada vez que conecta un cliente. Hasta 20 líneas de 400 caracteres. Se ve con showmotd.
Avisos a todos.broadcast envía «[Aviso del administrador] texto» a cada red en marcha; lo reciben todos los clientes conectados. restart y shutdown con mensaje lo envían antes.
Reiniciar y parar.restart reinicia xBNC dentro del mismo proceso (las redes se reconectan). shutdown lo para del todo y systemd no lo vuelve a arrancar: hay que hacerlo con sudo systemctl start xbnc.
Puertos y xbnc.toml. Los puertos y el resto de xbnc.toml no se cambian desde IRC: listports los muestra y rehash relee usuarios, redes, canales e idiomas (lo mismo que sudo systemctl reload xbnc). saveconfig existe por costumbre: xBNC guarda cada cambio al momento.
*notes
Comando
Qué hace
list
Tus notas.
get <clave>
Ver una nota.
add <clave> <texto>
Guardar una nota (hasta 400 caracteres).
del <clave>
Borrar una nota.
Son las mismas notas que en Tu cuenta → Notas.
*watch
Aquí llegan las coincidencias del módulo Vigilar palabras. /msg *watch help recuerda cómo configurarlo.
*crypt
Comando
Qué hace
setkey <#canal|nick> [cbc:]<clave>
Guardar la clave FiSH de un canal o privado (cbc: para el modo CBC). Se guarda cifrada.
delkey <#canal|nick>
Borrar una clave.
keyx <nick>
Acordar una clave en privado con DH1080 (la otra persona necesita FiSH).
list
Conversaciones con clave.
Escribe +p delante de un mensaje para enviarlo sin cifrar. Ver Cifrado de mensajes.
*ignore
Comando
Qué hace
add <nick o máscara>
Ignorar a alguien (nick, o nick!usuario@host con *).
del <nick o máscara>
Dejar de ignorar.
list
Lista de ignorados.
Un servicio por plugin
Cada plugin activo responde en *id (*trivia, *remind, *poll…). Con ayuda o cualquier texto que no entienda, explica cómo se usa. Si un plugin no tiene nada que decir, xBNC responde con su nombre, versión, descripción y uso.
Comandos de cliente
Comando
Qué hace
/quote DETACH #canal
Desacopla un canal: sigue guardando, pero no aparece en tu cliente.
/quote ATTACH #canal o /join #canal
Vuelve a mostrar un canal desacoplado, con los últimos mensajes.
/quote <alias> [argumentos]
Ejecuta un alias si tu cliente no envía comandos que no conoce.
Estado en vivo de tus redes (se actualiza cada 2 segundos) y, para administradores, memoria, CPU, tráfico, conexiones, IPs bloqueadas, alertas activas y aviso de versiones nuevas.
Chat
Cliente IRC web: canales y privados de todas tus redes, con historial. El panel cuenta como uno de tus dispositivos.
Menciones
Todo lo que te han dicho en privado o donde han escrito tu nick, de más nuevo a más antiguo.
Redes
Añadir (asistente de cuatro pasos) y editar redes; control (conectar, desconectar, reconectar); canales; dispositivos conectados (con botón para desconectar uno); historial con búsqueda y exportación; comandos al conectar; certificado de cliente; tarjeta «Conecta tu cliente IRC».
Módulos
Módulos y plugins de cada red. Selector de red que sirve con cualquier número de redes y «Copiar a otras redes» (módulos y plugins, salvo el certificado).
Plugins
Catálogo oficial y remoto, instalación con un clic o subiendo un .wasm, permisos y sitios aprobados, activar o desactivar en el servidor, desinstalar, registro en directo y modo desarrollo (superadministradores).
Usuarios
Altas, roles, cuotas de redes, dispositivos e historial, suspensión, restablecer contraseña, uso de cada cuenta e importación desde otro bouncer.
Seguridad
Intentos de acceso (IRC, panel y tokens), IPs bloqueadas con botón para desbloquear, sesiones abiertas y auditoría de todas las acciones.
Ajustes
Historial, límites, protección del acceso, idioma por defecto, alertas, copias de seguridad, actualizaciones y reinicio del servicio.
Tu cuenta
Idioma, zona horaria, avisos, contraseña, verificación en dos pasos, passkeys, cuentas externas vinculadas, notas, tokens de API, informe para pruebas y sesiones abiertas.
Ajustes en caliente
Lo que se cambia en Ajustes se guarda en la base de datos, se aplica sin reiniciar y tiene prioridad sobre xbnc.toml. El icono ↺ junto a un campo vuelve al valor de xbnc.toml.
Grupo
Ajustes
Por defecto
Historial
Días de retención (0: para siempre); mensajes reproducidos al reconectar; mensajes para un dispositivo nuevo; límite de CHATHISTORY.
30 días; 1000; 100; 500
Límites
Conexiones totales; dispositivos por usuario; redes por usuario; cola por cliente (líneas antes de desconectar a un cliente lento). Cada usuario puede tener excepciones en Usuarios.
1000; 10; 10; 4096
Protección del acceso
Intentos fallidos antes de bloquear; ventana; duración del bloqueo; tiempo para completar el login.
5; 600 s; 900 s; 30 s
General
Idioma por defecto para quien no ha elegido uno en Tu cuenta. Se guarda y se aplica al pulsarlo, también a quien lo cambia.
Automáticas sí/no, cada cuántas horas (1–720) y cuántas conservar (1–365).
no; 24 h; 7
Lo que se lee al arrancar (puertos, certificados, base de datos, clave secreta, el propio panel y el nivel de registro) se cambia en xbnc.toml y necesita sudo systemctl restart xbnc. La página lo indica. Los superadministradores tienen además un botón para reiniciar el servicio.
Accesibilidad y uso en el móvil
Navegable con teclado, con foco visible; los diálogos se cierran con Escape.
Respeta «reducir movimiento» del sistema.
En pantallas estrechas la barra lateral se abre con el botón de menú.
El tema (claro, u oscuro en negro, amarillo y gris) sigue al sistema la primera vez; el botón de luna o sol lo cambia y lo recuerda en ese navegador.
Se puede instalar como aplicación (PWA) para tener las notificaciones push.
En inglés y en español, y en cualquier idioma instalado como fichero de traducción (ver Traducir xBNC). El idioma se cambia en la pantalla de entrada, en Tu cuenta o (el predeterminado del servidor) en Ajustes, y se aplica al momento al panel y a los mensajes de xBNC en IRC, también con el cliente conectado. Con /msg *status language en se cambia desde IRC.
xBNC guarda los mensajes de canales y privados (y, con el módulo Eventos, entradas, salidas, kicks, cambios de nick, temas y modos). Cada mensaje lleva la hora del servidor si la red la envía.
Buscar
En Redes → (red) → Historial, o con la API. La búsqueda usa un índice de texto completo:
Encuentra palabras enteras y comienzos de palabra: desplie encuentra «despliegue».
No distingue mayúsculas ni tildes: cancion encuentra «Canción».
Varias palabras tienen que aparecer todas, en cualquier orden. Entre comillas se busca la frase exacta: "fuera de servicio".
Si no hay coincidencias por palabra, busca también trozos del medio de una palabra: spliegue encuentra «despliegue».
Se combina con los filtros de conversación, emisor y fechas.
Exportar y borrar
Se exporta en TXT o JSON (todo, o una conversación). Se borra una conversación o todo el historial de la red desde el panel, con /msg *status clearbuffer (o clearallbuffers, clearallchannelbuffers, clearallquerybuffers) o con la API.
Retención y cuotas
General:Ajustes → Historial, días de retención (30 por defecto; 0, para siempre).
Por canal: en los canales de cada red, o con /msg *status setbuffer: lo del servidor, no guardar nada, para siempre o N días.
Por usuario: un administrador puede fijar una cuota de espacio de historial (max_history_mb) en Usuarios; el uso se ve en la ficha de cada usuario.
Espacio: unos 150 bytes por mensaje guardado, con el índice de búsqueda.
Registros en ficheros
Aparte del historial, el módulo Registros en ficheros escribe ficheros de texto diarios con el formato clásico de IRC en /var/lib/xbnc/logs/usuario/red/#canal/AAAA-MM-DD.log, con la hora en tu zona horaria.
En Tu cuenta → Avisos, xBNC te avisa de las menciones y los privados.
Opción
Qué hace
Por defecto
Activar avisos
Interruptor general.
no
Solo cuando no tengo ningún cliente conectado
No avisa si tienes un cliente IRC o el chat del panel abiertos.
sí
Cuando alguien escribe mi nick en un canal
Menciones.
sí
Cuando recibo un mensaje privado
Privados.
sí
Canal
Qué necesitas
Navegador y móvil (push)
Pulsa «Activar en este dispositivo» desde el panel abierto en ese dispositivo. En el móvil, instala antes el panel como aplicación. Se puede activar en varios dispositivos.
Telegram
Crea un bot con @BotFather, escríbele una vez y pon su token y tu id de chat (te lo dice @userinfobot). El panel no vuelve a mostrar el token completo.
Discord
Un webhook del canal: Ajustes del canal → Integraciones → Webhooks. Los avisos no mencionan a nadie.
Correo
Tu dirección. El administrador tiene que configurar la sección [smtp] de xbnc.toml.
Dirección web propia
Una URL https que recibe un POST con el aviso en JSON.
«Enviar un aviso de prueba» manda uno a todos los canales configurados. Para no inundarte, xBNC agrupa los avisos: si llegan muchos seguidos, los que no se envían se cuentan y el siguiente aviso indica cuántos se saltaron.
Formato del aviso en la dirección web propia
{
"network": "undernet",
"network_id": 1,
"conversation": "#xbnc",
"from": "bob",
"text": "y2k, ¿mañana a qué hora es la reunión?",
"private": false,
"ts": 1762851240000,
"skipped": 0
}
En push, al tocar el aviso se abre el chat del panel en esa conversación.
Los módulos son funciones nativas de xBNC, compiladas dentro del programa, que se activan y configuran por red: en el panel (Módulos), con la API o desde IRC con /msg *status. Los cambios se aplican al momento, sin reconectar. Toda la configuración se valida (tipos, rangos, longitudes, sin caracteres de control). Las contraseñas y claves se guardan cifradas y el panel no las vuelve a mostrar: para cambiarlas se escriben de nuevo.
/msg *status modules lista y estado
/msg *status enable keepnick activar (disable para desactivar)
/msg *status get away ver ajustes
/msg *status set away delay 60 cambiar un ajuste
/msg *status addline watch patterns *xbnc* añadir una línea a una lista
/msg *status dellines watch patterns vaciar una lista
/msg *status availmods todos los módulos y plugins, con lo que hacen
Tipos de ajuste: sí/no (en *status, true, on o 1 para sí y false, off o 0 para no), número (con mínimo y máximo), texto, lista (una entrada por línea), opción (uno de los valores indicados) y secreto (se guarda cifrado).
Conexión y cuenta
Login en servicios
servicesnuevo en 1.0
Te identifica con X (Undernet), NickServ, Q o AuthServ al conectar y oculta tu IP antes de entrar en los canales.
En Undernet usa Login on Connect: la cuenta va en la contraseña del servidor y tu IP real no llega a verse. Si la red ya tiene contraseña de servidor, se identifica con /msg X login después de conectar. Los canales esperan a que la red confirme el inicio de sesión o a que pase el tiempo indicado. La contraseña se guarda cifrada y no se vuelve a mostrar.
Ajuste
Tipo
Por defecto
Detalle
Servicio
service
opción
auto
Opciones: auto Automático según la red, x X (Undernet), nickserv NickServ (Libera, freenode, Rizon…), q Q (QuakeNet), authserv AuthServ (EvilNet, GameSurge).
Cuenta
account
texto
vacío
Tu nombre de cuenta en el servicio. Con NickServ puede quedar vacío si es tu nick.
Contraseña
password
secreto
vacío
Ocultar mi IP (modo +x)
hide_host
sí/no
sí
En X, Q y AuthServ. En el resto de redes lo hace la propia red.
Identificarse al conectar (Login on Connect)
login_on_connect
sí/no
sí
Solo Undernet: la IP real no llega a verse nunca.
Esperar antes de entrar en los canales (segundos)
wait
número (0–120)
15
Hasta que la red confirme el inicio de sesión. 0: no esperar.
Recuperar mi nick si está ocupado
recover_nick
sí/no
sí
Con NickServ: pide el nick (REGAIN o GHOST) al identificarte.
Ejemplo
Conectando a irc.undernet.org:6667 con Login on Connect…
*** Tu host es ahora y2k.users.undernet.org
* y2k ha entrado en #xbnc
Desde IRC: /msg *status enable services y /msg *status get services
Invitación y desbaneo
invitenuevo en 1.0
Si no puedes entrar en un canal (+i, +k, +l o ban), pide a X o ChanServ que te invite o te quite el ban y vuelve a entrar.
Lo intenta como mucho tres veces cada diez minutos por canal. Necesitas acceso en el canal para que el servicio te haga caso.
Ajuste
Tipo
Por defecto
Detalle
Servicio de canales
service
opción
auto
Opciones: auto Automático según la red, x X (Undernet), chanserv ChanServ, q Q (QuakeNet).
Pedir que me quiten el ban
unban
sí/no
sí
Entrar cuando me inviten a uno de mis canales
join_invites
sí/no
sí
Ejemplo
#xbnc: no puedes entrar (+i)
*statusPidiendo a X que te deje entrar en #xbnc (+i).
* y2k ha entrado en #xbnc
Desde IRC: /msg *status enable invite y /msg *status get invite
Certificado de cliente (CertFP)
cert
Te identifica en la red con un certificado en lugar de una contraseña. Permite SASL EXTERNAL.
Genera un certificado, añade su huella a tu cuenta de la red y reconecta. En Libera.Chat: conéctate identificado y escribe /msg NickServ CERT ADD. Solo funciona con TLS.
Ajuste
Tipo
Por defecto
Detalle
Usar SASL EXTERNAL si la red lo ofrece
sasl_external
sí/no
sí
Ejemplo
y2k/msg NickServ CERT ADD
NickServAdded fingerprint 3f9a…c21e to your fingerprint list.
Desde IRC: /msg *status enable cert y /msg *status get cert
Recuperar nick
keepnickactivo por defecto
Si tu nick está ocupado al conectar, xBNC lo recupera en cuanto quede libre.
Comprueba cada 30 segundos si tu nick está libre y lo recupera al momento cuando quien lo tiene se desconecta. Si cambias de nick a mano desde tu cliente, se pausa hasta la próxima conexión.
Ejemplo
*statusTu nick y2k está ocupado; conectado como y2k_. Lo recupero en cuanto quede libre.
* y2k_ ahora se llama y2k
Desde IRC: /msg *status enable keepnick y /msg *status get keepnick
Canales
Canales fijos
stickychan
Canales de los que no puedes salir por error: xBNC bloquea el /part y vuelve a entrar si sales.
Ajuste
Tipo
Por defecto
Detalle
Canales
channels
lista
vacío
Uno por línea.
Ejemplo
#xbnc
#xbnc-dev
Si escribes /part #xbnc, xBNC no sale del canal.
Desde IRC: /msg *status enable stickychan y /msg *status get stickychan
Volver tras un kick
kickrejoin
Vuelve a entrar en el canal si te echan. Deja de intentarlo si te echan más de 3 veces en un minuto.
Ajuste
Tipo
Por defecto
Detalle
Espera antes de volver a entrar (segundos)
delay
número (0–600)
10
Ejemplo
* bob te ha echado de #xbnc (prueba)
* y2k ha entrado en #xbnc
Desde IRC: /msg *status enable kickrejoin y /msg *status get kickrejoin
Op y voz automáticos
autoop
Da op, halfop o voz a las personas que elijas cuando entran en tus canales (si tú tienes op).
Ajuste
Tipo
Por defecto
Detalle
Espera antes de dar el modo (segundos)
delay
número (0–30)
3
Personas
entries
lista
vacío
Una por línea: modo (o, h o v), máscara y, si quieres, canales separados por comas (o *) y una clave. Con clave, xBNC envía un desafío y solo da el modo si la otra persona responde con la misma clave desde su propio xBNC.
Ejemplo
o *!*@casa.ejemplo.org #xbnc
v amigo!*@* *
o bob!*@* #xbnc clave-compartida
Desde IRC: /msg *status enable autoop y /msg *status get autoop
Recuperar op al quedarte solo
autocyclenuevo en 1.0
Si te quedas solo en un canal sin op, sales y vuelves a entrar para conseguirlo. Nunca en canales +i o con clave.
Ajuste
Tipo
Por defecto
Detalle
Solo en estos canales
channels
lista
vacío
Uno por línea. Vacío: en todos.
Ejemplo
* bob ha salido de #xbnc
*statusTe has quedado solo en #xbnc sin op: salgo y vuelvo a entrar.
* y2k ha entrado en #xbnc (con op)
Desde IRC: /msg *status enable autocycle y /msg *status get autocycle
Canales desacoplados
detachnuevo en 1.0
Canales que siguen guardando todo pero no aparecen en tu cliente. Vuelven a aparecer si te mencionan.
Desde tu cliente: /quote DETACH #canal para desacoplar y /join #canal para volver a verlo. Al volver se muestran los últimos mensajes.
Ajuste
Tipo
Por defecto
Detalle
Canales
channels
lista
vacío
Uno por línea. Admite * (por ejemplo #*-bots).
Mostrar el canal si mencionan mi nick
on_mention
sí/no
sí
Mostrar el canal si escribe alguno de estos nicks
nicks
lista
vacío
Uno por línea.
Ejemplo
/quote DETACH #xbnc desacopla el canal (sigue guardando)
/quote ATTACH #xbnc o /join #xbnc: vuelve a mostrarlo
Si alguien escribe tu nick en #xbnc, el canal reaparece con los últimos mensajes.
Desde IRC: /msg *status enable detach y /msg *status get detach
Ocultar canales inundados
flooddetachnuevo en 1.0
Si un canal se llena de mensajes de golpe, lo oculta un rato para que tu cliente no se congele. Se sigue guardando.
Ajuste
Tipo
Por defecto
Detalle
Mensajes
messages
número (3–1000)
25
Cuántos mensajes…
…en cuántos segundos
seconds
número (1–600)
10
Segundos de calma antes de volver a mostrarlo
calm
número (10–3600)
60
Ejemplo
Mensajes: 25
…en cuántos segundos: 10
Segundos de calma antes de volver a mostrarlo: 60
Desde IRC: /msg *status enable flooddetach y /msg *status get flooddetach
Mensajes
Ausente automático
awayactivo por defecto
Te marca como ausente cuando cierras todos tus clientes y, al volver, te resume lo que te perdiste.
Ajuste
Tipo
Por defecto
Detalle
Mensaje de ausencia
message
texto
vacío
Vacío: «Ausente» en tu idioma.
Esperar antes de marcarte ausente (segundos)
delay
número (0–3600)
60
Al volver, avisarme de los privados y menciones recibidos
summary
sí/no
sí
Nick mientras estás ausente
nick
texto
vacío
Vacío: no lo cambia. {nick} es tu nick, por ejemplo {nick}|away. Se recorta a lo que permita la red (12 caracteres en Undernet).
Ejemplo
Mensaje de ausencia: Vuelvo enseguida
Nick mientras estás ausente: {nick}|away
Al volver, *status te resume: «Mientras no estabas: 3 privados (bob, carol) y 2 menciones en #xbnc».
Desde IRC: /msg *status enable away y /msg *status get away
Vigilar palabras
watchactivo por defecto
Copia a la conversación *watch los mensajes que contienen lo que te interesa, aunque no estuvieras conectado.
Ajuste
Tipo
Por defecto
Detalle
Avisarme también cuando mencionen mi nick
mentions
sí/no
no
Patrones
patterns
lista
vacío
Uno por línea. * vale por cualquier texto: *despliegue* encuentra «el despliegue falló». Después puedes poner un canal o un nick para limitarlo.
Ejemplo
*despliegue*
*xbnc* #xbnc
*urgente* bob
Las coincidencias llegan a la conversación *watch.
Desde IRC: /msg *status enable watch y /msg *status get watch
Alias
aliasactivo por defecto
Tus propios comandos: escribes /ghost bob y xBNC envía /msg NickServ GHOST bob.
Ajuste
Tipo
Por defecto
Detalle
Alias
aliases
lista
vacío
Uno por línea: nombre y comando. $1, $2… son los argumentos, $1- todos desde el primero y $nick tu nick.
Ejemplo
ghost /msg NickServ GHOST $1
op /mode $1 +o $nick
saluda /msg $1 hola, $1, ¿qué tal?
En tu cliente: /ghost y2k (o /quote ghost y2k si tu cliente no lo envía)
Desde IRC: /msg *status enable alias y /msg *status get alias
Ignorar
ignorenuevo en 1.0
No recibes los mensajes de quien ignoras en ningún cliente ni en el historial. En Undernet usa SILENCE para que ni siquiera lleguen.
En IRC: /msg *ignore add nick, /msg *ignore del nick y /msg *ignore list.
Ajuste
Tipo
Por defecto
Detalle
Ignorar a
masks
lista
vacío
Uno por línea: un nick o una máscara nick!usuario@host (admite *).
Usar SILENCE si la red lo permite
silence
sí/no
sí
Ejemplo
/msg *ignore add pesado
/msg *ignore add *!*@spam.example
/msg *ignore list
/msg *ignore del pesado
Desde IRC: /msg *status enable ignore y /msg *status get ignore
Quitar colores
stripcontrolsnuevo en 1.0
Quita colores, negritas, subrayados y parpadeos de los mensajes, también en el historial.
Ajuste
Tipo
Por defecto
Detalle
Solo en estos canales
channels
lista
vacío
Uno por línea. Vacío: en todos.
Ejemplo
Solo en estos canales:
#xbnc
Antes: \x0304,01rojo\x03 \x02negrita\x02 Después: rojo negrita
Desde IRC: /msg *status enable stripcontrols y /msg *status get stripcontrols
Codificación de caracteres
charsetnuevo en 1.0
Las tildes y la ñ se ven bien aunque el canal use Latin-1, y puedes escribir en Latin-1 donde haga falta.
xBNC siempre entiende los mensajes en Latin-1 que no son UTF-8. Este módulo elige la tabla exacta y en qué canales o privados envías tus mensajes en esa codificación (para gente con clientes muy antiguos).
Ajuste
Tipo
Por defecto
Detalle
Codificación antigua
legacy
opción
windows-1252
Opciones: windows-1252 Windows-1252 (la más común), iso-8859-15 ISO-8859-15 (con €), iso-8859-1 ISO-8859-1 (Latin-1).
Enviar en esa codificación a
send_legacy
lista
vacío
Canales o nicks, uno por línea. * para todos. Vacío: siempre UTF-8.
Ejemplo
Codificación antigua: Windows-1252
Enviar en esa codificación a:
#xbnc
abuelo
Desde IRC: /msg *status enable charset y /msg *status get charset
Cifrado de mensajes (FiSH)
cryptnuevo en 1.0
Cifra tus mensajes en canales y privados con claves compartidas, compatible con FiSH de mIRC, HexChat e irssi.
En IRC: /msg *crypt setkey #canal clave, /msg *crypt keyx nick para acordar una clave en privado y +p delante de un mensaje para enviarlo sin cifrar. Las claves se guardan cifradas. FiSH protege de los servidores de la red, no sustituye al cifrado de extremo a extremo.
Ajuste
Tipo
Por defecto
Detalle
Claves
keys
lista secreta
vacío
Una por línea: canal o nick y clave (cbc: delante para el modo CBC). Se guardan cifradas: escribe todas otra vez para cambiarlas.
Marca para los mensajes descifrados
prefix
texto
vacío
Por ejemplo [cifrado] . Vacío: sin marca.
Aceptar intercambios de claves (DH1080)
key_exchange
sí/no
sí
Cuando alguien te propone una clave en privado.
Ejemplo
/msg *crypt setkey #xbnc cbc:una-clave-larga
/msg *crypt keyx bob acordar una clave en privado (DH1080)
/msg *crypt list
/msg *crypt delkey #xbnc
+p hola envía este mensaje sin cifrar
Desde IRC: /msg *status enable crypt y /msg *status get crypt
Tareas programadas
schedulenuevo en 1.0
Comandos a una hora fija o cada cierto tiempo, en tu zona horaria.
La zona horaria se elige en Tu cuenta. Las tareas solo se ejecutan mientras la red está conectada.
Ajuste
Tipo
Por defecto
Detalle
Tareas
tasks
lista
vacío
Una por línea: hora y comando (08:00 /away Trabajando), días y hora (lun-vie 18:00 /msg #canal me voy) o intervalo (cada 30m /msg #canal sigo aquí). Días: lun mar mie jue vie sab dom, laborables, finde.
Ejemplo
lun-vie 08:00 /away Trabajando
lun-vie 15:00 /away
finde 10:30 /msg #xbnc ¡Buenos días!
cada 2h /msg #xbnc-dev sigo aquí
Desde IRC: /msg *status enable schedule y /msg *status get schedule
Respuestas CTCP
ctcpactivo por defecto
Responde a VERSION, PING y TIME cuando no hay clientes conectados, frena las inundaciones de CTCP y permite ocultar tu versión.
Ajuste
Tipo
Por defecto
Detalle
Peticiones CTCP permitidas cada 10 segundos
flood_limit
número (1–100)
5
Respuesta a VERSION
version
texto
vacío
Vacío: la de tu cliente, o la de xBNC si no hay ninguno conectado.
CTCP que ignorar
block
lista
vacío
Uno por línea, por ejemplo TIME o FINGER.
Ejemplo
Peticiones CTCP permitidas cada 10 segundos: 5
Respuesta a VERSION: mIRC v7.77
CTCP que ignorar:
FINGER
TIME
Desde IRC: /msg *status enable ctcp y /msg *status get ctcp
Ocultar el MOTD
block_motd
No muestra a tus clientes el mensaje del día del servidor.
Ejemplo
Sin ajustes. Activado, tus clientes no reciben el mensaje del día del servidor.
Desde IRC: /msg *status enable block_motd y /msg *status get block_motd
Historial y DCC
Eventos en el historial
eventsactivo por defecto
Guarda entradas, salidas, kicks, cambios de nick, temas y modos junto a los mensajes.
Los clientes modernos los reciben como eventos reales; el resto, como mensajes de *events al reproducir el historial.
Ajuste
Tipo
Por defecto
Detalle
Guardar entradas, salidas y desconexiones
joins
sí/no
sí
En canales grandes generan mucho ruido.
Guardar kicks, cambios de nick, temas y modos
other
sí/no
sí
Ejemplo
Con «Guardar kicks, cambios de nick, temas y modos» activado, al reproducir el historial
verás también: «bob ha cambiado el tema a: Reunión a las 20:00».
Desde IRC: /msg *status enable events y /msg *status get events
Registros en ficheros
log
Escribe cada conversación en ficheros de texto diarios, con el formato clásico de los registros de IRC.
Los ficheros quedan en el servidor, en la carpeta logs junto a la base de datos (por ejemplo /var/lib/xbnc/logs/usuario/red/#canal/2026-11-08.log). Las horas están en tu zona horaria (Tu cuenta).
Ajuste
Tipo
Por defecto
Detalle
Borrar ficheros con más de (días)
retention_days
número (0–3650)
0
0 los conserva para siempre.
Incluir eventos (entradas, salidas, kicks…)
events
sí/no
sí
Ejemplo
/var/lib/xbnc/logs/y2k/undernet/#xbnc/2026-11-11.log
[18:30:12] <bob> hola a todos
[18:30:20] * carol ha entrado
Desde IRC: /msg *status enable log y /msg *status get log
Respuestas a quien pregunta
route_repliesactivo por defecto
Las respuestas a WHOIS, WHO, LIST, NAMES o listas de bans llegan solo al dispositivo que las pidió, no a todos.
Ejemplo
Si haces /whois bob desde el móvil, la respuesta llega solo al móvil,
no al portátil que también está conectado.
Desde IRC: /msg *status enable route_replies y /msg *status get route_replies
DCC a través de xBNC
dcc
Los envíos de ficheros y chats DCC pasan por xBNC: la otra persona no ve tu IP y funcionan aunque estés detrás de un router.
xBNC abre un puerto para cada envío y une las dos conexiones; no guarda nada en disco. En un servidor con IP pública funciona sin configurar nada; detrás de NAT, indica la IP pública en [dcc] public_ip de xbnc.toml y abre los puertos de [dcc] ports en el cortafuegos.
Ajuste
Tipo
Por defecto
Detalle
Tamaño máximo por envío (MB)
max_size_mb
número (0–1000000)
4096
0 = sin límite.
Segundos para aceptar un envío
wait
número (30–900)
180
Permitir envíos desde direcciones privadas
private_addresses
sí/no
no
Desactivado, xBNC no se conecta a redes internas (10.x, 192.168.x…) aunque alguien en IRC lo pida.
Ejemplo
# xbnc.toml, solo si el servidor está detrás de un router (NAT):
[dcc]
public_ip = "203.0.113.10"
ports = "50000-50100"
Desde IRC: /msg *status enable dcc y /msg *status get dcc
Los plugins añaden funciones para todo el canal. Se ejecutan en WebAssembly, aislados: no pueden leer ficheros ni ver otras redes, tienen límites de CPU y memoria, y solo hacen lo que permiten sus permisos, que se ven antes de instalarlos. Los que salen a internet solo pueden conectarse a los sitios aprobados.
Los 18 plugins oficiales vienen instalados: xBNC instala los que faltan al arrancar por primera vez y después de cada actualización, y actualiza los que tengan una versión antigua. Quedan desactivados en todas las redes. Uno que un superadministrador desinstala a propósito no vuelve hasta la siguiente versión de xBNC (o hasta que se instala de nuevo desde Plugins → Catálogo).
Un superadministrador puede instalar otros desde el catálogo remoto o subiendo un fichero .wasm, y aprueba los sitios web de los plugins que salen a internet («Sitios a los que puede conectarse»): títulos de enlaces, el tiempo y noticias.
Cada usuario los activa en sus redes desde Módulos, en la sección Plugins, y ajusta su configuración.
Contestan en tu idioma de xBNC (o en el del ajuste «Idioma») y funcionan también con tus propios mensajes. Los oficiales hablan inglés y español; en otro idioma responden en inglés.
Permiso
Qué permite
send
Enviar mensajes y avisos.
raw
Comandos IRC como KICK, MODE o INVITE. Nunca QUIT, PASS, USER, NICK, CAP, AUTHENTICATE, OPER, SERVER ni SQUIT.
timer
Recibir un evento cada minuto (o cada 5–60 segundos si el plugin lo pide).
presence
Ver entradas, salidas, kicks, cambios de nick y la lista de usuarios de los canales.
http
Pedir páginas https, solo a los dominios que declara (hasta 10) y que apruebe el superadministrador.
Trivial
trivia 1.0.0nuevo en 1.0
Juego de preguntas para tus canales, con pistas, puntos y ranking.
Cómo se usa
En un canal: !trivial para empezar, !trivial stop y !trivial top. Se responde escribiendo en el canal. /msg *trivia add ¿Pregunta? | respuesta añade preguntas propias; list y del N para gestionarlas.
Ejemplo
carol!trivial
y2k¡Empieza el trivial! 10 preguntas; responde escribiendo en el canal.
y2kPregunta 1/10: ¿Cuál es la capital de Japón?
bobtokio
y2k¡Correcto, bob! Era «Tokio». Llevas 1 punto.
Ajuste
Por defecto
Detalle
Comando
trigger
!trivia
Preguntas por partida
rounds
10
Segundos para responder
seconds
40
A mitad de tiempo sale una pista.
Solo en estos canales
channels
vacío
Uno por línea. Vacío: en todos.
Idioma de las respuestas
lang
vacío
Vacío: tu idioma en xBNC. O es / en.
Permisos: enviar mensajes, temporizador. Ayuda desde IRC: /msg *trivia ayuda
Bienvenida
greet 1.0.0nuevo en 1.0
Saluda a quien entra en tus canales, con un mensaje distinto la primera vez y saludos personales.
Cómo se usa
Automático. /msg *greet test #canal nick muestra qué se diría. En los mensajes se cambian {nick} y {channel}.
Ejemplo
* carol ha entrado en #xbnc
y2k¡Bienvenido a #xbnc, carol! Es tu primera vez por aquí.
Ajuste
Por defecto
Detalle
La primera vez
first
¡Bienvenido a {channel}, {nick}! Es tu primera vez por aquí.
Para quien nunca ha estado en el canal. Se cambian {nick} y {channel}.
Cuando vuelven
message
Hola {nick}, bienvenido de nuevo a {channel}.
Vacío: no se saluda a quien vuelve.
Saludos personales
personal
vacío
Uno por línea: nick y mensaje. Sustituye al habitual para esa persona.
Saludar en privado (notice)
notice
no
Desactivado: el saludo se escribe en el canal.
Minutos antes de volver a saludar a la misma persona
cooldown
30
No saludar nunca
ignore
vacío
Nicks, uno por línea (bots, servicios).
Solo en estos canales
channels
vacío
Uno por línea. Vacío: en todos.
Idioma de las respuestas
lang
vacío
Vacío: tu idioma en xBNC. O es / en.
Permisos: enviar mensajes, entradas y salidas. Ayuda desde IRC: /msg *greet ayuda
Títulos de enlaces
urltitle 1.0.0nuevo en 1.0
Muestra el título de los enlaces de YouTube, Vimeo, SoundCloud, Dailymotion y Spotify, artículos de Wikipedia y repositorios de GitHub que se pegan en tus canales.
Cómo se usa
Automático. Un administrador aprueba cada sitio en el panel (Plugins → «Sitios a los que puede conectarse»).
Ejemplo
bobmirad esto https://www.youtube.com/watch?v=…
y2kYouTube: Cómo montar un bouncer IRC en 5 minutos
Ajuste
Por defecto
Detalle
Segundos entre títulos en un canal
cooldown
5
También para tus propios enlaces
own
sí
Solo en estos canales
channels
vacío
Uno por línea. Vacío: en todos.
Idioma de las respuestas
lang
vacío
Vacío: tu idioma en xBNC. O es / en.
Permisos: enviar mensajes, acceso web a sitios aprobados. Ayuda desde IRC: /msg *urltitle ayuda
Definiciones
factoids 1.0.0nuevo en 1.0
La memoria del canal: respuestas cortas a preguntas frecuentes que tú y tus editores mantenéis al día.
Cómo se usa
En un canal: !def palabra (o ?palabra), !def palabra = texto, !def -palabra, !defs. Aquí: /msg *factoids palabra.
Ejemplo
y2k!def normas = Sé respetuoso y no hagas flood.
carol?normas
y2knormas: Sé respetuoso y no hagas flood.
Ajuste
Por defecto
Detalle
Comando
trigger
!def
Responder también a ?palabra
question_mark
sí
Quién más puede cambiar definiciones
editors
vacío
Nicks, uno por línea. Tú siempre puedes.
Solo en estos canales
channels
vacío
Uno por línea. Vacío: en todos.
Idioma de las respuestas
lang
vacío
Vacío: tu idioma en xBNC. O es / en.
Permisos: enviar mensajes. Ayuda desde IRC: /msg *factoids ayuda
El tiempo
weather 1.0.0nuevo en 1.0
El tiempo ahora y la previsión de hoy para cualquier ciudad, de Open-Meteo (gratis, sin clave).
Cómo se usa
En un canal: !tiempo Madrid o !weather London. Aquí: /msg *weather Sevilla.
Ejemplo
bob!tiempo Sevilla
y2kSevilla (España): 22 °C, cielo despejado, viento 9 km/h, humedad 40 %. Hoy entre 14 y 25 °C.
Ajuste
Por defecto
Detalle
Comando
trigger
!weather
Ciudad por defecto
city
vacío
Se usa cuando no se escribe ninguna.
Solo en estos canales
channels
vacío
Uno por línea. Vacío: en todos.
Idioma de las respuestas
lang
vacío
Vacío: tu idioma en xBNC. O es / en.
Permisos: enviar mensajes, acceso web a sitios aprobados. Ayuda desde IRC: /msg *weather ayuda
Noticias
news 1.0.0nuevo en 1.0
Publica en tus canales las novedades de fuentes RSS y Atom (periódicos, blogs, canales de YouTube, versiones de GitHub).
Cómo se usa
Configura las fuentes en el panel: una por línea, «#canal https://...». /msg *news list, /msg *news check.
Ejemplo
y2k[Hacker News] Show HN: un bouncer IRC escrito en Rust — https://…
Ajuste
Por defecto
Detalle
Fuentes
feeds
vacío
Una por línea: canal y dirección, por ejemplo #xbnc https://hnrss.org/frontpage. Un administrador aprueba los sitios.
Minutos entre comprobaciones
interval
15
Idioma de las respuestas
lang
vacío
Vacío: tu idioma en xBNC. O es / en.
Permisos: enviar mensajes, temporizador, acceso web a sitios aprobados. Ayuda desde IRC: /msg *news ayuda
Karma
karma 1.0.0nuevo en 1.0
Puntos entre personas: nick++ y nick--, con ranking.
Cómo se usa
En un canal: nick++, nick--, !karma nick, !karma top. Aquí: /msg *karma nick.
Ejemplo
bobcarol++
y2kcarol tiene 1 de karma.
carol!karma top
Ajuste
Por defecto
Detalle
Solo en estos canales
channels
vacío
Uno por línea. Vacío: en todos.
Idioma de las respuestas
lang
vacío
Vacío: tu idioma en xBNC. O es / en.
Permisos: enviar mensajes. Ayuda desde IRC: /msg *karma ayuda
Citas
quotes 1.0.0nuevo en 1.0
Frases memorables del canal: guárdalas, búscalas y saca una al azar.
Cómo se usa
En un canal: !cita, !cita 12, !cita buscar palabra, !cita añadir texto, !cita borrar 12 (o !quote add/search/del).
Ejemplo
bob!cita añadir hoy es un gran día
y2kCita 1 guardada.
carol!cita 1
y2k#1: hoy es un gran día
Ajuste
Por defecto
Detalle
Comando
trigger
!quote
Cualquiera puede añadir citas
anyone_adds
sí
Desactivado: solo tú y los editores.
Editores
editors
vacío
Nicks que pueden añadir y borrar citas, uno por línea.
Solo en estos canales
channels
vacío
Uno por línea. Vacío: en todos.
Idioma de las respuestas
lang
vacío
Vacío: tu idioma en xBNC. O es / en.
Permisos: enviar mensajes. Ayuda desde IRC: /msg *quotes ayuda
Palabras prohibidas
badwords 1.0.0nuevo en 1.0
Avisa, echa y al final banea a quien escribe palabras prohibidas en tus canales (necesitas op).
Cómo se usa
Pon las palabras en el panel. /msg *badwords test texto dice si un texto se detectaría.
Ejemplo
caroleres tontito
y2kcarol: esa palabra no está permitida aquí. La próxima vez, kick.
Ajuste
Por defecto
Detalle
Palabras prohibidas
words
vacío
Una por línea. Da igual mayúsculas y tildes; un * al final vale por el principio de una palabra.
Ahorcado, bola 8 mágica y piedra, papel o tijera para tus canales.
Cómo se usa
En un canal: !8ball pregunta, !ppt piedra (o !rps rock), !ahorcado (o !hangman) y !a letra para jugar.
Ejemplo
bob!ppt piedra
y2kpiedra contra tijera: ¡ganas, bob!
bob!8ball ¿funcionará?
y2kLa bola 8 dice: «Pregunta más tarde».
Ajuste
Por defecto
Detalle
Solo en estos canales
channels
vacío
Uno por línea. Vacío: en todos.
Idioma de las respuestas
lang
vacío
Vacío: tu idioma en xBNC. O es / en.
Permisos: enviar mensajes. Ayuda desde IRC: /msg *games ayuda
Dados
dice 1.1.0
Tira dados en canales y privados: !dado, !dado 2d6, !dado d20.
Cómo se usa
En un canal o privado: !dado (un dado de seis caras), !dado 2d6, !dado d20. También !roll y !dice.
/msg *dice 3d8 tira solo para ti.
Ejemplo
bob!dado 2d6
y2kbob tira 2 dados de 6 caras: 3 y 5 (total 8).
Ajuste
Por defecto
Detalle
Comando adicional
trigger
!roll
!dado, !dados, !roll y !dice funcionan siempre.
Máximo de dados por tirada
max_dice
10
Responder en privados
in_queries
sí
Idioma de las respuestas
lang
vacío
Vacío: tu idioma en xBNC. O es / en.
Permisos: enviar mensajes. Ayuda desde IRC: /msg *dice ayuda
Encuestas
poll 1.1.0
!poll ¿Pregunta? | opción | opción, y luego !vote N. Se cierra sola y anuncia la ganadora.
Cómo se usa
En un canal: !poll ¿Pizza o sushi? | pizza | sushi (hasta 10 opciones) — vota con !vote 1 — !poll muestra el recuento — !poll end la cierra (quien la creó, o tú). Se cierra sola pasados los minutos de los ajustes. También !encuesta y !votar.
Ejemplo
carol!poll ¿Café o té? | café | té
bob!vote 2
*pollVoto registrado en #xbnc: 2) té. Puedes cambiarlo votando otra vez.
Ajuste
Por defecto
Detalle
Comando
trigger
!poll
Comando para votar
vote_trigger
!vote
Minutos hasta que se cierra
minutes
60
Otras personas pueden crear encuestas
public
sí
Desactivado: solo las creas tú; votar puede todo el mundo.
Idioma de las respuestas
lang
vacío
Vacío: tu idioma en xBNC. O es / en.
Permisos: enviar mensajes, temporizador. Ayuda desde IRC: /msg *poll ayuda
Recordatorios
remind 1.1.0
Te lo recuerda (a ti o a cualquiera del canal) pasado un tiempo o a una hora: !recuerda 10m sacar la pizza, !recuerda 18:30 reunión.
Cómo se usa
En un canal: !recuerda 10m sacar la pizza — !recuerda 2 horas llamar a Bob — !recuerda 1h30m … — !recuerda 18:30 reunión — !recuerda lista — !recuerda borrar. También !remind.
/msg *remind 10m … te lo recuerda en privado. Los recordatorios se comprueban cada minuto.
Ejemplo
bob!recuerda en 10 minutos sacar la pizza
y2kbob: vale, te lo recuerdo dentro de 10 minutos.
y2kbob: sacar la pizza
Ajuste
Por defecto
Detalle
Comando adicional
trigger
!remind
!remind y !recuerda funcionan siempre.
Otras personas pueden crear recordatorios
public
sí
Desactivado: solo los tuyos y /msg *remind.
Recordatorios pendientes por persona
max_per_nick
10
Zona horaria para horas como 18:30
timezone
Europe/Madrid
Europe/Madrid, Europe/London, UTC o una diferencia como +1.
Idioma de las respuestas
lang
vacío
Vacío: tu idioma en xBNC. O es / en.
Permisos: enviar mensajes, temporizador. Ayuda desde IRC: /msg *remind ayuda
Mensajes diferidos
tell 1.1.0
!tell nick mensaje: se lo da en el canal cuando esa persona escriba o entre.
Cómo se usa
En un canal: !tell bob el build está arreglado — bob lo recibe en el canal en cuanto escriba algo o entre. Funciona para ti y para cualquiera del canal. También !avisa.
Ejemplo
y2k!tell carol el build está arreglado
y2ky2k: vale, se lo diré a carol en cuanto escriba o entre en el canal.
* carol ha entrado en #xbnc
y2kcarol: y2k te dejó un mensaje hace 5 minutos: el build está arreglado
Ajuste
Por defecto
Detalle
Comando
trigger
!tell
Entregar al entrar
on_join
sí
Desactivado: solo cuando escriba algo.
Mensajes esperando por persona
max_per_nick
5
Idioma de las respuestas
lang
vacío
Vacío: tu idioma en xBNC. O es / en.
Permisos: enviar mensajes, entradas y salidas. Ayuda desde IRC: /msg *tell ayuda
Respuestas automáticas
autoresponder 1.1.0
Responde solo cuando alguien escribe una palabra clave y, si quieres, a los privados mientras estás ausente.
Cómo se usa
Escribe las reglas en los ajustes, una por línea: palabra = respuesta. {nick} es quien escribió y {channel} el canal. La regla * = texto responde a cualquier privado.
/msg *autoresponder muestra las reglas; /msg *autoresponder test <texto> las prueba.
Ejemplo
bobhola, ¿alguien?
y2k¡Hola bob! Ahora mismo no estoy, déjame tu mensaje.
Ajuste
Por defecto
Detalle
Reglas
rules
vacío
Una por línea: palabra = respuesta. Ejemplo: hola = ¡Hola {nick}! — * = respuesta a cualquier privado.
Segundos antes de que la misma regla vuelva a responder
cooldown
60
Responder también a privados
in_queries
sí
Solo cuando no hay ningún cliente conectado
only_when_away
no
Así responde por ti mientras estás ausente.
Solo en estos canales
channels
vacío
Uno por línea. Vacío: en todos.
Idioma de los mensajes
lang
vacío
Vacío: tu idioma en xBNC. O es / en.
Permisos: enviar mensajes. Ayuda desde IRC: /msg *autoresponder ayuda
Estadísticas del canal
chanstats 1.1.0
!stats: mensajes del canal y quién habla más. !stats nick: lo de una persona.
Cómo se usa
En un canal: !stats muestra los mensajes y quién habla más — !stats bob, lo de una persona. /msg *chanstats #canal te lo muestra en privado. Los recuentos se guardan cada minuto. También !estadisticas y !top.
Ejemplo
bob!stats
y2k#xbnc: 1204 mensajes desde el 3 nov 2026. Más activos: carol 312, bob 280, y2k 199.
Ajuste
Por defecto
Detalle
Comando
trigger
!stats
Personas en la clasificación
top
5
Responder a otras personas
public
sí
Desactivado: solo tu propio !stats.
Solo en estos canales
channels
vacío
Uno por línea. Vacío: en todos.
Idioma de las respuestas
lang
vacío
Vacío: tu idioma en xBNC. O es / en.
Permisos: enviar mensajes, temporizador. Ayuda desde IRC: /msg *chanstats ayuda
Visto por última vez
seen 2.1.0
!seen nick: cuándo se vio a alguien por última vez y qué hacía, o en qué canales está ahora.
Cómo se usa
En un canal: !seen bob — cuándo se vio a bob por última vez y si está ahora. /msg *seen bob, o /msg *seen stats.
Ejemplo
bob!seen carol
y2kcarol fue visto por última vez hace 2 horas, en #xbnc diciendo «me voy a cenar».
Ajuste
Por defecto
Detalle
Comando
trigger
!seen
Lo que se escribe antes del nick.
Responder a otras personas en los canales
public
sí
Desactivado: solo te responde a ti (tu propio !seen y /msg *seen).
Segundos entre respuestas en un canal
cooldown
5
Mostrar lo último que dijo
show_text
sí
Desactivado: solo dice dónde y cuándo.
Responder solo en estos canales
channels
vacío
Uno por línea. Vacío: en todos. La actividad se registra en todos.
Idioma de las respuestas
lang
vacío
Vacío: tu idioma en xBNC. O es / en.
Permisos: enviar mensajes, entradas y salidas. Ayuda desde IRC: /msg *seen ayuda
Anti-flood
antiflood 1.1.0
Avisa, expulsa o banea a quien hace flood o repite mensajes en tus canales (tienes que ser operador del canal).
Cómo se usa
Funciona solo, en los canales que pongas en los ajustes (tienes que ser operador allí). /msg *antiflood muestra cómo está configurado.
Ejemplo
spama
spama
spama
* spam ha sido expulsado de #xbnc por y2k (flood)
Ajuste
Por defecto
Detalle
Canales que proteger
channels
vacío
Uno por línea. No hace nada hasta que pongas alguno.
Máximo de líneas
lines
5
En estos segundos
seconds
4
Misma línea repetida
repeats
3
Veces seguidas; 0 lo desactiva.
Qué hacer
action
kick
warn (avisar la primera vez y expulsar la segunda), kick o ban.
Todo, incluidos los ajustes del sistema, los plugins, las copias de seguridad, las actualizaciones, la importación y gestionar otros administradores.
admin
Gestiona usuarios (salvo administradores y superadministradores), todas las redes y la seguridad; ve el sistema.
moderator
Ve los usuarios y la auditoría, y puede suspender y reactivar usuarios normales y limitados.
user
Gestiona sus propias redes, canales e historial. Es el rol por defecto.
limited
Usa redes que le prepara un administrador; gestiona canales e historial, pero no añade, edita ni borra redes.
Qué puede hacer cada rol
Permiso
superadmin
admin
moderator
user
limited
Editar ajustes del sistema, instalar plugins, copias de seguridad, importar, actualizar
sí
—
—
—
—
Ver el sistema, gestionar usuarios, seguridad y todas las redes
sí
sí
—
—
—
Ver usuarios y auditoría, suspender usuarios
sí
sí
sí
—
—
Gestionar sus propias redes
sí
sí
sí
sí
—
Usar sus redes, canales, historial, chat, módulos y plugins
sí
sí
sí
sí
sí
Un administrador solo gestiona roles por debajo del suyo; nadie puede cambiarse su propio rol ni elevarse, y siempre queda al menos un superadministrador. Los usuarios no ven las redes de otros (la API responde 404).
Cuentas
Nombres de usuario: de 1 a 32 caracteres A-Z a-z 0-9 . _ -, sin distinguir mayúsculas. Contraseñas: al menos 10 caracteres.
Cuotas por usuario: redes (max_networks), dispositivos (max_clients) y espacio de historial en MB (max_history_mb). Vacío: lo general del servidor.
Suspender una cuenta cierra su acceso y desconecta sus redes. Borrarla elimina también sus redes e historial.
Cambiar o restablecer la contraseña cierra las demás sesiones del panel de esa cuenta.
Formas de entrar al panel
Usuario y contraseña, con verificación en dos pasos opcional (TOTP, RFC 6238, con QR) usando Google Authenticator, Aegis, 1Password u otra compatible. El secreto se guarda cifrado; un código incorrecto cuenta como intento fallido. Los clientes IRC siguen entrando solo con contraseña: el protocolo no admite un segundo paso.
Passkeys (WebAuthn): entrar con la huella, la cara o el PIN del dispositivo. Se añaden y se quitan en Tu cuenta.
OpenID Connect (Google, Microsoft, Keycloak, Authentik…) y LDAP, si el administrador los configura en xbnc.toml (ver Configuración). Una cuenta externa se vincula y desvincula en Tu cuenta.
Medidas de seguridad
Área
Medida
Contraseñas
Argon2id (m = 19 MiB, t = 2, p = 1). Un usuario que no existe cuesta lo mismo de comprobar, para no revelar qué cuentas existen. Como mucho un cálculo por CPU a la vez, para que una avalancha de intentos no agote la memoria.
Credenciales guardadas
Contraseñas SASL y de servidor, claves de canal, comandos al conectar, contraseñas de servicios, claves FiSH, secretos TOTP y claves de certificados, cifrados con XChaCha20-Poly1305. La clave está fuera de la base de datos (secret.key, permisos 0600 comprobados al arrancar).
TLS
rustls, solo TLS 1.2 y 1.3. Hacia las redes se verifica el certificado con el almacén del sistema, sin opción para desactivarlo. Un certificado de confianza (addfingerprint) solo acepta esa huella SHA-256 exacta, y se siguen comprobando las firmas del saludo TLS.
Texto plano
Prohibido en direcciones públicas salvo insecure = true explícito. El puerto sin TLS por defecto solo acepta la red local.
Fuerza bruta
Bloqueo de IP tras 5 fallos en 10 minutos durante 15 minutos (configurable), compartido entre IRC (PASS y SASL), el panel y los tokens de API. Máximo 16 conexiones IRC sin autenticar por IP.
Registros
Nunca se registran contraseñas, AUTHENTICATE, OPER ni mensajes a servicios (NickServ IDENTIFY, X LOGIN, Q AUTH…), en ningún nivel.
Entrada
Líneas limitadas a 8703 bytes; cualquier CR o LF dentro de una línea la corta, para impedir inyección de comandos; nicks, nombres y textos de módulos y plugins validados.
Recursos
Límites de conexiones totales, de clientes y redes por usuario; colas acotadas por cliente y hacia el servidor; tiempo máximo de registro.
SQL
Todas las consultas usan parámetros enlazados.
Privilegios
El servicio corre como xbnc sin privilegios, con aislamiento de systemd. En Docker: usuario sin privilegios, solo lectura, sin capacidades.
Panel: sesiones
Token aleatorio de 256 bits en cookie HttpOnly, SameSite=Strict y Secure; en la base de datos solo su SHA-256. Caducidad deslizante (12 h por defecto).
Panel: CSRF y origen
Token CSRF obligatorio en toda petición que cambia algo; peticiones y WebSockets con Origin de otro sitio rechazados.
Panel: cabeceras
CSP estricta sin scripts externos ni en línea, X-Frame-Options: DENY, nosniff, Referrer-Policy: no-referrer, HSTS con TLS y Cache-Control: no-store en la API.
Panel: abuso
20 peticiones por segundo por IP, cuerpos de hasta 256 KB (4 MB para plugins), 64 conexiones por IP y 4 096 en total, cierre tras 2 minutos sin tráfico y 10 s para el saludo TLS. Con trust_proxy se usa la IP real detrás de Nginx.
Primer acceso
La primera cuenta solo se crea con un código de un solo uso que el servicio escribe en su registro.
Secretos en la API
Las contraseñas nunca se devuelven; solo se indica si existen.
Tokens de API
256 bits con prefijo xbnc_; solo se guarda su SHA-256. Alcance, caducidad, último uso e IP visibles, revocación inmediata.
Módulos
Compilados dentro del programa: no se carga código externo. Op automático: *!*@* se rechaza; con clave, desafío aleatorio respondido con HMAC-SHA256 y comparado en tiempo constante.
Registros en ficheros
Rutas construidas solo con nombres saneados (un canal no puede salir de su carpeta), carpetas 0700 y ficheros 0600.
Plugins
WebAssembly en el intérprete Wasmi, con combustible (CPU) por evento, 16 MB de memoria, sin acceso a ficheros ni red propia. Acciones comprobadas contra los permisos. Solo un superadministrador instala, viendo la huella SHA-256; un fichero que cambia tras instalarse no se carga. Cinco fallos seguidos lo detienen.
DCC
Las ofertas de IRC no pueden hacer que xBNC se conecte a redes internas ni a puertos por debajo de 1024 (salvo permiso). En tus envíos solo se conecta de vuelta a la IP de tu dispositivo. Cada puerto acepta una sola conexión, con tiempo de espera, límite de tamaño y cierre por inactividad.
Login en servicios
En redes sin NickServ (IRCnet, EFnet) el modo automático nunca envía la contraseña.
Traducciones
Los ficheros de idioma son datos, no código: un texto de IRC con saltos de línea o caracteres de control, o con marcadores distintos de los del inglés, se ignora. Ficheros de 2 MB como máximo y 100 idiomas.
FiSH
El intercambio de claves admite 5 peticiones por minuto y 100 claves por sesión; el texto descifrado no puede romper líneas.
Webhooks
Solo http(s), sin usuario en la URL ni caracteres de control, certificado verificado, sin seguir redirecciones, 10 s como máximo.
Importación
Solo un superadministrador; nunca concede ese rol; la vista previa no devuelve secretos.
Auditoría
Cada acción del panel, de IRC y de la terminal queda registrada con fecha, autor y detalle (Seguridad → Auditoría, o xbncd audit).
Fuzzing
Parser IRC, DCC, importador, búsqueda, manifiestos y ejecución de plugins, cada semana en el CI con los fallos anteriores como regresiones.
Recomendaciones
Usa un certificado real (Let's Encrypt) y no publiques puertos en texto plano.
Guarda las copias de seguridad cifradas: contienen la clave secreta.
Todas las conexiones a las redes salen de la IP del servidor: si un usuario abusa en una red, la IP del servidor puede acabar bloqueada.
Activa la verificación en dos pasos o una passkey en las cuentas de administración.
Para avisar de una vulnerabilidad, sigue SECURITY.md: no la publiques; usa el aviso privado del repositorio. Respuesta en 3 días como máximo, evaluación en 7 y corrección en 90 como mucho. El modelo de amenazas completo está en Modelo de amenazas.
/etc/xbnc/xbnc.toml solo guarda ajustes del proceso: usuarios, redes y canales están en la base de datos. El instalador lo crea por ti. Tras editarlo: sudo xbncd check-config y sudo systemctl restart xbnc. Una clave desconocida es un error, para que una errata no pase desapercibida. Lo de Ajustes en el panel tiene prioridad sobre [history] y [limits].
General
Clave
Por defecto
Qué hace
hostname
xbnc
Nombre del servidor que ven los clientes.
language
en
Idioma por defecto de los mensajes de IRC y del panel: en, es o cualquier idioma instalado en locales_dir. Cada usuario puede elegir el suyo. El instalador pone el que elijas.
locales_dir
/etc/xbnc/locales
Carpeta de idiomas adicionales y correcciones (un fichero <código>.json por idioma, ver Traducir xBNC). Se lee al arrancar y al recargar. La variable de entorno XBNC_LOCALES_DIR tiene prioridad.
secret_key_file
obligatoria
Ruta de la clave de 32 bytes que cifra las credenciales. La crea xbncd init con permisos 0600. Haz copia: sin ella no se pueden descifrar.
[database]
Clave
Por defecto
Qué hace
path
obligatoria
Fichero SQLite. Su carpeta es también la de datos (plugins, registros, copias).
url
—
PostgreSQL en lugar de SQLite: postgres://usuario:clave@host/base. Añade ?sslmode=require para cifrar la conexión.
ha
false
Alta disponibilidad (necesita url): varios nodos comparten la base de datos; uno trabaja y los demás esperan para tomar el relevo.
[[listen]] (uno por puerto)
Clave
Por defecto
Qué hace
address
obligatoria
Dirección y puerto: 0.0.0.0:6697 o [::]:6697.
tls_cert
—
Cadena del certificado (PEM). Obligatoria salvo insecure, lan_only o una dirección de loopback.
tls_key
—
Clave privada (PEM).
insecure
false
Permitir texto plano en una dirección pública (desaconsejado).
lan_only
false
Solo clientes de la red local (loopback, privadas, enlace local, CGNAT/Tailscale, IPv6 ULA). El resto recibe un ERROR explicativo. Puede ir sin TLS.
[web]
Clave
Por defecto
Qué hace
address
obligatoria
Dirección del panel y la API, por ejemplo 0.0.0.0:8443 o 127.0.0.1:8080. Sin [web], no hay panel.
tls_cert, tls_key
—
Certificado del panel.
insecure
false
HTTP sin cifrar en una dirección pública (solo detrás de un proxy TLS en una red de confianza).
secure_cookies
true con TLS
Marca la cookie de sesión como Secure. Ponlo a true detrás de un proxy TLS.
public_url
—
URL pública del panel (https://bnc.ejemplo.org). Se usa para comprobar el Origin de las peticiones y como base de OpenID Connect.
session_hours
12
Duración de la sesión en horas (deslizante).
trust_proxy
false
Fiarse de X-Forwarded-For y X-Real-IP de un proxy en loopback, para que el bloqueo use la IP real.
[history]
Clave
Por defecto
Qué hace
retention_days
30
Borrar mensajes de más de N días. 0, para siempre.
playback_max
1000
Máximo de mensajes reproducidos al reconectar.
new_client_playback
100
Mensajes para un dispositivo que nunca se había conectado.
chathistory_max
500
Máximo limit aceptado en CHATHISTORY.
[limits]
Clave
Por defecto
Qué hace
max_connections
1000
Conexiones IRC de clientes en total.
max_clients_per_user
10
Dispositivos conectados por usuario.
max_networks_per_user
10
Redes por usuario.
login_max_failures
5
Fallos antes de bloquear una IP.
login_window_seconds
600
Ventana en la que se cuentan los fallos.
login_block_seconds
900
Duración del bloqueo.
registration_timeout_seconds
30
Tiempo para completar el login de un cliente.
client_queue
4096
Líneas en cola por cliente antes de desconectarlo por lento (al volver recupera lo pendiente).
[log]
Clave
Por defecto
Qué hace
level
info
Filtro: error, warn, info, debug, trace, o por módulo (info,xbncd=debug). La variable RUST_LOG tiene prioridad. Las credenciales se enmascaran en todos los niveles.
[dcc]
Clave
Por defecto
Qué hace
public_ip
la que usa xBNC hacia la red
Dirección que se anuncia en IRC. Necesaria detrás de NAT. Si la conexión con la red es IPv6, se anuncia la IPv4 pública.
ports
cualquiera libre
Rango para los túneles, por ejemplo 50000-50100 (ábrelo en el cortafuegos y el router).
[smtp] (avisos por correo)
Clave
Por defecto
Qué hace
host
obligatoria
Servidor de correo.
port
587
Puerto.
security
starttls
starttls, tls (puerto 465) o none (solo relés locales).
username, password
—
Credenciales.
from
obligatoria
Remitente, por ejemplo xBNC <xbnc@ejemplo.org>.
[oidc] (OpenID Connect)
Clave
Por defecto
Qué hace
name
—
Texto del botón (Google, Keycloak…).
issuer
obligatoria
URL del emisor; se lee su /.well-known/openid-configuration.
client_id, client_secret
obligatorias
Datos de la aplicación registrada en el proveedor. La dirección de retorno es https://TU-PANEL/api/v1/auth/oidc/callback (usa public_url).
scopes
openid profile email
Alcances que se piden.
username_claim
preferred_username
Dato que se usa como nombre de las cuentas creadas automáticamente.
auto_create
false
Crear la cuenta de xBNC la primera vez que alguien entra.
link_existing
false
Vincular con una cuenta local del mismo nombre (solo con proveedores que controles).
default_role
user
Rol de las cuentas creadas.
allowed_groups
[] (todos)
Si se indica, solo pueden entrar miembros de alguno de estos grupos.
groups_claim
groups
Dato con los grupos.
[ldap]
Clave
Por defecto
Qué hace
url
obligatoria
ldaps://host:636, o ldap://host:389 con starttls = true.
starttls
false
Usar STARTTLS.
bind_dn
obligatoria
DN con el que se entra; {{username}} se cambia por el nombre escrito.
ca_file
certificados del sistema
Fichero PEM con la autoridad del servidor LDAP.
auto_create, link_existing, default_role
false, false, user
Igual que en [oidc].
[updates]
Clave
Por defecto
Qué hace
check
true
Comprobar una vez al día si hay versión nueva (y cuando un administrador lo pide). No descarga nada.
repository
—
Repositorio de GitHub con las versiones (dueño/nombre).
url
—
O cualquier URL https que responda {{"version", "url", "notes", "packages"}}.
public_key
—
Clave pública Ed25519 de las versiones. Con ella, y xBNC instalado desde paquete, un superadministrador puede actualizar desde el panel.
[catalog] (catálogo remoto de plugins)
Clave
Por defecto
Qué hace
url
—
Dirección https:// del índice JSON.
public_key
—
Clave pública Ed25519 (base64) que firma el índice.
Todos los comandos aceptan -c RUTA (o la variable XBNC_CONFIG; por defecto /etc/xbnc/xbnc.toml), -h para la ayuda y -V para la versión. Los que escriben en la base de datos se ejecutan como el usuario del servicio: sudo -u xbnc xbncd … (en Docker, docker compose exec xbnc xbncd …). Después, sudo systemctl reload xbnc para que el servicio aplique los cambios de usuarios, redes y canales. Cada acción queda en la auditoría.
Comando
Qué hace
Opciones
xbncd run
Ejecuta el bouncer en primer plano (lo hace el servicio). SIGHUP (systemctl reload) relee redes, canales, usuarios e idiomas.
—
xbncd init
Crea la base de datos y la clave secreta.
—
xbncd check-config
Comprueba el fichero de configuración y termina.
—
xbncd info
Muestra la dirección del panel y cómo se conectan los clientes.
—
xbncd setup-code
Muestra el código de un solo uso para crear el primer administrador.
—
xbncd user add NOMBRE
Crea un usuario. La contraseña se pide dos veces, se lee de la entrada con --password-stdin o de XBNC_PASSWORD.
--role superadmin|admin|moderator|user|limited (user por defecto); --admin (atajo de --role superadmin); --password-stdin
xbncd user list
Lista los usuarios.
—
xbncd user passwd NOMBRE
Cambia la contraseña.
--password-stdin
xbncd user role NOMBRE ROL
Cambia el rol.
—
xbncd user enable|disable NOMBRE
Reactiva o suspende una cuenta (tras reload, sus redes se desconectan).
—
xbncd user reset-2fa NOMBRE
Quita la verificación en dos pasos (móvil perdido).
—
xbncd user delete NOMBRE --yes
Borra el usuario, sus redes y su historial.
--yes (confirmar)
xbncd network add USUARIO NOMBRE --host SERVIDOR
Añade una red. Las contraseñas SASL y de servidor se piden o se leen de XBNC_SASL_PASSWORD y XBNC_SERVER_PASSWORD.
--port (6697); --no-tls; --nick; --username; --realname; --server-password; --sasl-user CUENTA
xbncd network list USUARIO
Lista las redes de un usuario.
—
xbncd network enable|disable USUARIO NOMBRE
Activa o desactiva una red.
—
xbncd network delete USUARIO NOMBRE --yes
Borra una red.
--yes
xbncd channel add USUARIO RED CANAL
Guarda un canal para entrar automáticamente.
--key (pide la clave del canal)
xbncd channel list USUARIO RED
Lista los canales guardados.
—
xbncd channel delete USUARIO RED CANAL
Quita un canal guardado.
—
xbncd plugin install FICHERO.wasm
Instala o actualiza un plugin; cada usuario lo activa en sus redes.
--yes (aceptar los permisos que pide)
xbncd plugin list
Lista los plugins instalados.
—
xbncd plugin remove ID
Desinstala un plugin y borra sus ajustes en todas las redes.
Mete el manifiesto (JSON) dentro de un plugin compilado.
-o, --output (obligatoria)
xbncd plugin catalog-keygen -o CLAVE
Para publicar un catálogo: crea la clave de firma (guárdala bien).
-o, --output
xbncd plugin catalog-entry FICHERO.wasm URL
Para publicar un catálogo: imprime la entrada del índice de un plugin publicado en URL (https://).
—
xbncd plugin catalog-sign --key CLAVE ÍNDICE
Para publicar un catálogo: firma el índice (escribe ÍNDICE.sig).
-k, --key
xbncd backup DESTINO
Copia consistente de la base de datos, con xBNC en marcha.
—
xbncd migrate-to-postgres URL
Copia la base de datos SQLite a una PostgreSQL nueva y vacía. Para xBNC antes; después pon database.url.
—
xbncd import RUTA
Trae usuarios, redes, canales y módulos de otro bouncer (su carpeta de datos, con configs/ y users/, o su fichero de configuración). Sin --apply no escribe nada.
--apply; --logs (también los registros); --utc-offset ZONA (+02:00 o Europe/Madrid; +00:00 por defecto)
xbncd import-logs USUARIO RED CARPETA
Importa registros de texto (una carpeta por canal con ficheros AAAA-MM-DD.log) al historial de una red. Se puede repetir sin duplicar.
--utc-offset ZONA
xbncd audit
Últimas acciones administrativas.
--limit N (50)
xbncd release keygen
Para publicar versiones: crea el par de claves de firma. La pública va en [updates] public_key.
--out FICHERO (xbnc-release.key)
xbncd release sign FICHEROS…
Escribe FICHERO.sig junto a cada paquete. La clave privada se lee de XBNC_RELEASE_KEY o de --key.
--key
xbncd release verify --public-key CLAVE FICHEROS…
Comprueba las firmas .sig.
—
xbncd apply-update
Instala una actualización pedida desde el panel (lo ejecuta como root el servicio xbnc-update; vuelve a comprobar la firma).
—
xbncd locales check FICHEROS…
Comprueba ficheros de idioma contra el inglés: textos que faltan, textos que xBNC no usa, {0} o etiquetas <b> rotas. No necesita la configuración.
--missing (lista también lo que falta)
xbncd locales list
Idiomas que ofrece este servidor, con el porcentaje traducido de cada uno.
—
Ejemplos
sudo xbncd info
sudo xbncd setup-code
sudo -u xbnc xbncd user add alicia
sudo -u xbnc xbncd user add carla --role moderator
echo 'una-contraseña-larga' | sudo -u xbnc xbncd user add bob --password-stdin
sudo -u xbnc xbncd user role carla admin
sudo -u xbnc xbncd user reset-2fa alicia
sudo -u xbnc xbncd network add alicia libera --host irc.libera.chat --sasl-user alicia
sudo -u xbnc xbncd network add alicia undernet --host irc.undernet.org --port 6667 --no-tls
sudo -u xbnc xbncd channel add alicia libera '#xbnc' --key
sudo -u xbnc xbncd backup /var/lib/xbnc/copia.db
sudo -u xbnc xbncd import /home/antiguo/.bouncer --logs --utc-offset Europe/Madrid # vista previa
sudo -u xbnc xbncd import /home/antiguo/.bouncer --logs --utc-offset Europe/Madrid --apply # importar
sudo systemctl reload xbnc
Scripts
Script
Uso
scripts/install.sh
Instala o reconfigura el servicio (ver Instalación).
scripts/update.sh [--binary RUTA]
Actualiza el binario con copia previa y vuelta atrás automática.
scripts/backup.sh [CARPETA] [--keep N]
Copia en caliente: base de datos, clave secreta y /etc/xbnc en un .tar.gz 0600 (en /var/backups/xbnc, conserva 14).
En Ajustes → Copias de seguridad (superadministradores): crear una copia ahora, descargarla, borrarla, restaurarla y programar copias automáticas (cada N horas, conservando las últimas M). Cada copia es un .tar.gz con la base de datos (copia en caliente con comprobación de integridad), la clave secreta y los plugins instalados, guardado en /var/lib/xbnc/backups/.
Restaurar deja la copia preparada (también se puede subir una desde el ordenador) y se aplica cuando xBNC se reinicia; mientras tanto se puede cancelar. Las copias contienen la clave secreta: guárdalas cifradas.
Copias desde la terminal
sudo ./scripts/backup.sh # en /var/backups/xbnc, conserva 14
sudo ./scripts/backup.sh /mnt/copias --keep 30
# Cada noche a las 3:30
echo '30 3 * * * root /ruta/a/xbnc/scripts/backup.sh >/dev/null' | sudo tee /etc/cron.d/xbnc-backup
# Restaurar (para el servicio, guarda lo actual como *.pre-restore-FECHA, restaura y arranca)
sudo ./scripts/restore.sh /var/backups/xbnc/xbnc-20261108T033000Z.tar.gz
sudo ./scripts/restore.sh ARCHIVO --with-config # también /etc/xbnc
# Solo la base de datos
sudo -u xbnc xbncd backup /var/lib/xbnc/copia.db
La base de datos de destino tiene que estar vacía. Con ?sslmode=require la conexión va cifrada. La búsqueda de texto completo funciona igual con SQLite y con PostgreSQL.
Alta disponibilidad
Con ha = true, varios servidores ejecutan xBNC contra la misma base de datos PostgreSQL. El que tiene un bloqueo de PostgreSQL trabaja y los demás esperan. Si el activo cae, su conexión se cierra, el bloqueo se libera y otro toma el relevo en segundos. Un nodo activo que pierde la base de datos se detiene al momento, así que nunca hay dos a la vez. Los clientes tienen que poder llegar al nodo activo (una IP flotante o un DNS que cambie).
Migrar desde otro bouncer
xBNC trae lo que tenías en tu bouncer anterior si usa el formato clásico de configuración por bloques (<User nombre>, <Network nombre>, <Chan #canal>).
Qué
Cómo queda en xBNC
Usuarios
Con su nombre. Los administradores pasan a admin (nunca a superadmin). Los usuarios que ya existen en xBNC no se tocan.
Contraseñas
Las guardadas con SHA-256 con sal siguen funcionando y pasan a Argon2id la primera vez que cada usuario entra. Las de otro formato (MD5) reciben una contraseña nueva aleatoria, que se muestra una sola vez.
Redes
Servidor, puerto, TLS, contraseña de servidor, nick, ident y nombre real. Las redes desactivadas siguen desactivadas. Si había varios servidores, se usa el primero (añade los demás después como servidores de reserva con /msg *status addserver). Se admiten servidores IPv6.
Canales
Con sus claves (cifradas). Los desactivados no se importan.
SASL, NickServ y comandos al conectar
SASL pasa a la red; la contraseña de NickServ y los comandos al conectar, a «Comandos al conectar» (cifrados). Necesitan la carpeta de datos.
Certificado de cliente
Se importa si está en la carpeta de datos y su clave coincide.
Módulos
Se activa el equivalente de xBNC (tabla de abajo).
Registros
Los ficheros de texto diarios pasan al historial de cada red, con búsqueda. Se pueden repetir sin duplicar.
Desde el panel (Usuarios → Importar desde otro bouncer, superadministradores): elige el fichero .conf o pega su contenido, pulsa «Ver qué se importa» (no escribe nada) y después «Importar». Desde el panel solo se lee la configuración; SASL, NickServ, comandos al conectar, certificados y registros están en la carpeta de datos y se importan desde el servidor.
# Vista previa: no cambia nada
sudo -u xbnc xbncd import /ruta/a/la/carpeta
# Importar, con los registros (que estaban en hora de Madrid)
sudo -u xbnc xbncd import /ruta/a/la/carpeta --apply --logs --utc-offset Europe/Madrid
sudo systemctl reload xbnc
# Registros de una red concreta, aparte
sudo -u xbnc xbncd import-logs alicia libera /ruta/a/logs/libera --utc-offset America/New_York
La carpeta de registros tiene una subcarpeta por canal o conversación (#xbnc/, bob/) con ficheros AAAA-MM-DD.log. Se reconocen mensajes, acciones (* nick …), avisos (-nick- …) y entradas, salidas, desconexiones, kicks, cambios de nick, temas y modos. Con una zona horaria con nombre (Europe/Madrid) se aplica el horario de verano de cada día.
Módulo del bouncer anterior
En xBNC
keepnick
Recuperar nick
simple_away, away, autoaway, awaynick
Ausente automático (con el retraso y el mensaje de simple_away)
autoop
Op y voz automáticos (vuelve a añadir las entradas: la sintaxis es otra)
kickrejoin
Volver tras un kick (con su retraso)
stickychan
Canales fijos (con la lista)
buffextras
Eventos en el historial
log
Registros en ficheros
route_replies
Respuestas a quien pregunta
cert
Certificado de cliente
alias, watch
Alias y Vigilar palabras (vuelve a añadir las entradas)
Cualquier otro módulo aparece en la vista previa como «sin equivalente». Después de importar: las redes con varios servidores usan solo el primero; las redes sin TLS aparecen avisadas; si usabas los mismos puertos, para el bouncer anterior antes de arrancar xBNC; y configura tus clientes con el formato usuario/red.
xBNC comprueba cada 30 segundos estas condiciones. Se ajustan en Ajustes → Alertas (0 desactiva cada una):
Alerta
Se activa cuando
Por defecto
network_down
Una red lleva más de N minutos intentando conectar sin conseguirlo (las que desconectas tú no cuentan). Hasta 10 080 (una semana).
15 minutos
failed_logins
Hay N o más accesos fallidos (IRC, panel y tokens) en los últimos 10 minutos.
30
memory
La memoria del proceso supera N MB.
desactivada
database
La base de datos supera N MB.
desactivada
Cuando una alerta empieza y cuando se resuelve:
Aparece (o desaparece) en el Inicio del panel para los administradores.
Queda en la auditoría (acción alert).
Con «Avisar por IRC a los administradores» (activado por defecto), cada administrador y superadministrador conectado recibe un aviso de *status en su idioma.
Con un webhook (solo lo configura un superadministrador), xBNC envía un POST con JSON. Tiene 10 segundos para responder con 2xx; si falla, queda en el registro del servicio.
{
"source": "xbnc",
"server": "bnc.ejemplo.org",
"state": "firing",
"alert": "network_down",
"subject": "alicia/libera",
"message": "Network alicia/libera has been disconnected for more than 15 minutes",
"since": 1762600000000,
"text": "[xBNC bnc.ejemplo.org] firing: Network alicia/libera ...",
"content": "[xBNC bnc.ejemplo.org] firing: Network alicia/libera ..."
}
state es firing o resolved. Los campos text y content hacen que funcione tal cual con los webhooks entrantes de Slack y Mattermost (text) y de Discord (content).
Métricas para Prometheus
GET /api/v1/metrics en formato Prometheus, con la sesión de un administrador o un token de alcance «métricas»:
Líneas del módulo log descartadas porque el disco no daba abasto.
xbnc_dcc_relays_total
counter
Envíos y chats DCC pasados por xBNC.
xbnc_dcc_bytes_total
counter
Bytes de DCC pasados por xBNC.
xbnc_alerts_firing{alert}
gauge
Alertas activas por tipo.
Los mismos contadores se ven desde IRC con /msg *status traffic (administradores).
Grafana y reglas de Prometheus
En packaging/monitoring/:
grafana-dashboard.json: un panel listo para importar con redes conectadas y sin conectar, clientes IRC, usuarios, memoria, tiempo en marcha, mensajes por segundo, tráfico, redes por estado, logins, reconexiones, almacenamiento, historial y alertas activas.
Todo lo que hace el panel está en la API. Base: /api/v1, en la misma dirección que el panel. Formato JSON; los errores son {"error": "código", "message": "texto"} con el código HTTP adecuado. La especificación OpenAPI 3.1 está en GET /api/v1/openapi.json (y en docs/openapi.json); un test comprueba que documenta todas las rutas.
Tokens de API
Créalos en Tu cuenta → Tokens de API. El token completo (xbnc_ y 43 caracteres) solo se muestra al crearlo: xBNC guarda su SHA-256. Envíalo en la cabecera Authorization: Bearer xbnc_…; con token no hace falta CSRF.
Alcance
Puede
read
Solo peticiones GET.
write
Todo lo que puede tu cuenta, salvo cerrar sesión, cambiar la contraseña, la verificación en dos pasos, las passkeys y crear o revocar tokens (eso exige la sesión del panel y responde 403 session_required).
metrics
Solo GET /metrics.
Caducan a los 30, 90 o 365 días, o nunca; máximo 50 por usuario. La lista muestra el prefijo, el último uso y la última IP. Un token incorrecto cuenta como intento fallido (mismos bloqueos que IRC y el panel). Crear y revocar queda en la auditoría.
Sesión del panel
POST /auth/login con {"username", "password"} (y totp_code si la cuenta tiene verificación en dos pasos). La respuesta trae la cookie xbnc_session y, en el cuerpo, csrf.
Envía la cookie en cada petición, y en POST, PUT, PATCH y DELETE la cabecera X-CSRF-Token con ese valor (GET /auth/me lo devuelve otra vez).
Las peticiones con una cabecera Origin de otro sitio se rechazan; curl y los scripts no la envían.
Ejemplos
B=https://bnc.ejemplo.org:8443/api/v1
T="Authorization: Bearer xbnc_..."
# Redes y su estado
curl -s -H "$T" $B/networks
# Buscar en el historial de la red 1
curl -s -H "$T" "$B/networks/1/history?target=%23xbnc&q=reunión&limit=20"
# Reconectar una red (token write)
curl -s -X POST -H "$T" -H 'Content-Type: application/json' -d '{"action":"jump"}' $B/networks/1/control
# Activar un módulo
curl -s -X PUT -H "$T" -H 'Content-Type: application/json' \
-d '{"enabled":true,"config":{"delay":60,"summary":true}}' $B/networks/1/modules/away
# Con sesión en lugar de token: crear un usuario y darle una red
curl -s -c jar -H 'Content-Type: application/json' \
-d '{"username":"admin","password":"tu-contraseña"}' $B/auth/login > login.json
CSRF=$(python3 -c 'import json;print(json.load(open("login.json"))["csrf"])')
curl -s -b jar -H "X-CSRF-Token: $CSRF" -H 'Content-Type: application/json' \
-d '{"username":"bruno","password":"una-clave-larga","role":"user","max_networks":3}' $B/users
curl -s -b jar -H "X-CSRF-Token: $CSRF" -H 'Content-Type: application/json' \
-d '{"user_id":2,"name":"libera","host":"irc.libera.chat","port":6697,"tls":true,"nick":"bruno",
"sasl_username":"bruno","sasl_password":"secreto","channels":["#xbnc","#xbnc-dev"]}' $B/networks
Todas las rutas
Método
Ruta
Qué hace
Quién
GET
/health
Comprobación de vida.
sin sesión
GET
/i18n
Idiomas instalados (código, nombre, porcentaje traducido, traductores) y el idioma por defecto del servidor.
sin sesión
GET
/i18n/{code}
Textos del panel en un idioma: texto en inglés → traducción.
sin sesión
GET, POST
/setup
Instalación inicial con el código de un solo uso.
sin sesión
POST
/auth/login
Entrar: {username, password, totp_code}. Devuelve la cookie xbnc_session y el campo csrf.
sin sesión
POST
/auth/logout
Cerrar la sesión.
sesión
GET
/auth/me
Tu cuenta, tus permisos y el token CSRF.
sesión o token
GET
/auth/methods
Formas de entrar que ofrece el servidor (passkeys, OpenID Connect, LDAP).
Informe para pruebas: qué admite cada una de tus redes y cómo la ha tratado xBNC (sin secretos ni mensajes).
sesión o token
GET, POST
/tokens
Tus tokens; crear {name, scope: read | write | metrics, expires_days: 0–3650} (0: no caduca). El token solo viene en la respuesta.
solo sesión
DELETE
/tokens/{id}
Revocar un token.
solo sesión
GET
/sessions
Sesiones del panel (?all=true: todas, administradores).
sesión
DELETE
/sessions/{id}
Cerrar una sesión.
sesión
GET, POST
/users
Listar y crear {username, password, role, max_networks, max_clients}.
ver: moderator; crear: admin
GET, PATCH, DELETE
/users/{id}
Ver, cambiar {role, enabled, max_networks, max_clients, max_history_mb} (null quita el límite) y borrar.
según el rol
POST
/users/{id}/password
Restablecer la contraseña (cierra sus sesiones).
admin
GET
/users/{id}/usage
Redes, clientes y espacio de historial usados frente a sus cuotas.
moderator
GET
/security
Accesos e IPs bloqueadas.
moderator
DELETE
/security/blocked/{ip}
Desbloquear una IP.
admin
GET
/audit
Registro de auditoría.
moderator
GET, PUT
/settings
Ajustes en caliente.
ver: admin; editar: superadmin
GET
/system
Versión, base de datos, escuchas y contadores.
admin
POST
/system/restart
Reiniciar xBNC (el servicio lo vuelve a arrancar).
superadmin
GET
/alerts
Alertas activas.
admin
GET
/metrics
Métricas en formato Prometheus.
admin o token metrics
GET
/ws
WebSocket del Inicio: un mensaje {type: "stats"} cada 2 segundos con el estado de las redes y, para administradores, memoria, CPU, clientes, conexiones, IPs bloqueadas y contadores; {type: "logout"} si la sesión termina.
solo sesión
GET, POST
/backups
Copias, restauración preparada y ajustes de las automáticas; crear una copia ahora.
superadmin
GET, DELETE
/backups/{nombre}
Descargar o borrar una copia.
superadmin
POST
/backups/{nombre}/restore
Preparar la restauración de una copia del servidor.
superadmin
POST, DELETE
/backups/restore
Subir una copia (.tar.gz) y prepararla; cancelar la restauración preparada.
superadmin
GET
/plugins
Plugins instalados.
sesión o token
POST
/plugins
Instalar o actualizar: el cuerpo es el .wasm (application/wasm, hasta 4 MB).
superadmin, sesión
POST
/plugins/inspect
Comprobar un .wasm sin instalarlo.
superadmin, sesión
PATCH, DELETE
/plugins/{id}
{enabled} en el servidor y dominios aprobados; desinstalar.
superadmin, sesión
GET
/plugins/catalog
Catálogo: plugins oficiales y catálogo remoto firmado.
sesión o token
POST
/plugins/catalog/{id}
Instalar del catálogo con un clic.
superadmin, sesión
GET, PUT
/plugins/dev
Modo desarrollo: estado y carpeta; activar o desactivar.
superadmin, sesión
GET
/plugins/logs
WebSocket con el registro de los plugins: últimas líneas y las nuevas.
superadmin, sesión
GET
/updates
Versión instalada y última publicada, si se puede actualizar desde el panel y el resultado de la última actualización.
admin
POST
/updates/check
Comprobar ahora si hay versión nueva.
admin, sesión
POST
/updates/install
Descargar, verificar e instalar el paquete firmado de la última versión.
superadmin, sesión
POST
/import/preview
Vista previa de la importación: {config: "contenido del fichero"}. No escribe nada.
superadmin, sesión
POST
/import/apply
Importar.
superadmin, sesión
«dueño o admin»: el dueño de la red o un administrador. «solo sesión»: no se admite con token. Las contraseñas nunca se devuelven: las redes indican has_sasl_password y has_server_password.
Con [updates] repository (o url) en xbnc.toml, xBNC consulta una vez al día la última versión publicada y avisa en el Inicio y en Ajustes → Actualizaciones. Por sí solo no descarga ni instala nada.
Actualizar
En esta beta, xBNC se instala desde el código: se actualiza con scripts/update.sh (ver Instalación).
Actualizar desde el panel (instalaciones con paquete)
xBNC incluye también la actualización con un clic para cuando se instala desde paquetes .deb o .rpm firmados. Con [updates] public_key, Ajustes → Actualizaciones muestra «Actualizar ahora» a los superadministradores. xBNC descarga el paquete y su firma .sig, comprueba la firma Ed25519 y lo deja en /var/lib/xbnc/updates/. El servicio del sistema xbnc-update lo vuelve a comprobar todo como root sin fiarse de esa carpeta (verifica con la clave de /etc/xbnc/xbnc.toml, que xBNC no puede cambiar, y rechaza versiones iguales o más antiguas), lo instala con dpkg o rpm y xBNC se reinicia. Un paquete sin firma, firmado con otra clave o modificado no se instala nunca. Sin public_key, o sin instalación por paquete, el panel explica cómo actualizar a mano.
Publicar versiones firmadas
# Una vez: crear la clave (la privada, secreta; la pública va en [updates] public_key)
xbncd release keygen --out xbnc-release.key
# En cada versión: firmar los paquetes (escribe FICHERO.sig)
XBNC_RELEASE_KEY="$(cat xbnc-release.key)" xbncd release sign xbnc_1.0.0_amd64.deb xbnc-1.0.0-1.x86_64.rpm
# Comprobar
xbncd release verify --public-key "BASE64..." xbnc_1.0.0_amd64.deb
El repositorio incluye un flujo que compila y firma paquetes .deb y .rpm y una imagen de contenedor (.github/workflows/release.yml). Necesita la clave como secreto del repositorio (XBNC_RELEASE_KEY) y la clave pública como variable (XBNC_RELEASE_PUBLIC_KEY). Durante la beta xBNC se publica solo como un .zip con el código fuente, así que el flujo solo se ejecuta cuando se lanza a mano desde la pestaña Actions.
Publicar en GitHub
scripts/publish-github.sh publica el código fuente, una versión con el .zip y su suma de comprobación, y la wiki como sitio web propio (GitHub Pages), desde el ordenador de quien mantiene el proyecto y con su cuenta de GitHub. La guía completa está en docs/PUBLISHING.md (en inglés).
Un plugin es un fichero WebAssembly con un manifiesto JSON dentro. Hay SDK para C (sdk/c, con los 18 plugins oficiales como ejemplo y el ayudante xbnc_util.h) y para Rust (sdk/rust). Cualquier lenguaje que compile a WebAssembly sin WASI sirve si cumple la interfaz.
Manifiesto
Campo
Obligatorio
Qué es
id
sí
1–32 caracteres a-z 0-9 _ -, empezando por letra. No puede ser status, notes, watch, events, xbnc, bnc, plugin ni plugins. Es el nombre de su servicio (*id).
name
sí
Nombre visible, 1–60 caracteres.
version
sí
Versión, 1–20 caracteres.
description
no
Qué hace.
usage
no
Cómo se usa (hasta 1000 caracteres). El panel lo muestra en «Cómo se usa» y xBNC lo responde si el plugin no contesta a un comando.
author
no
Autor.
homepage
no
Página del plugin (https://).
abi
no
Versión de la interfaz (1).
permissions
no
Lista de send, raw, timer, presence y http.
http_domains
con http
De 1 a 10 dominios a los que pide permiso para conectarse.
timer_secs
no
Con timer: recibir el temporizador cada 5–60 segundos en lugar de cada minuto.
settings
no
Hasta 20 ajustes: {key, type (bool, int, text o lines), label, help, default, min, max}. El panel los muestra como formulario.
translations
no
Por código de idioma (es, pt, fr…): name, description, usage y, por ajuste, label y help. Los textos de la raíz del manifiesto son la base, en inglés.
Reserva len bytes para el evento y devuelve su dirección.
xbnc_on_event
(ptr: i32, len: i32) -> i64
Recibe el evento (JSON UTF-8) y devuelve (dirección << 32) | longitud de la respuesta (JSON), o 0 si no responde.
Y solo puede importar del módulo xbnc (ni WASI ni nada más):
Import
Qué hace
log(ptr, len)
Escribe una línea en el registro (máximo 10 por evento y 500 caracteres).
now_ms() -> i64
Milisegundos desde 1970.
kv_get, kv_set, kv_del, kv_list
Almacén clave-valor propio del plugin en esa red: claves de hasta 200 bytes, valores de hasta 64 KB, 16 MB y 200 000 claves en total, hasta 2 000 escrituras por evento (se guardan juntas).
Campos comunes: network, nick (tu nick actual), connected, clients (clientes conectados), lang (tu idioma), config (sus ajustes) y state (lo que guardó).
Respuesta
{
"actions": [
{ "reply": "texto en la misma conversación" },
{ "tell": "texto solo para ti, como *id" },
{ "privmsg": { "target": "#canal", "text": "..." } },
{ "notice": { "target": "nick", "text": "..." } },
{ "raw": "MODE #canal +o bob" },
{ "http": { "id": "tiempo", "method": "GET", "url": "https://api.open-meteo.com/..." } }
],
"state": { "lo que quieras guardar": 1 }
}
Textos de una sola línea. Las acciones sin permiso o mal formadas se descartan y quedan en el registro.
state sustituye el estado guardado del plugin en esa red; llega en cada evento y sobrevive a los reinicios. La instancia se mantiene entre eventos mientras la red sigue activa.
raw nunca puede enviar QUIT, PASS, USER, NICK, CAP, AUTHENTICATE, OPER, SERVER ni SQUIT.
http: ('id', 'url', 'method', 'body'). Solo https, a dominios aprobados; métodos GET, POST, PUT y DELETE; cuerpo de hasta 16 KB (texto o JSON); hasta 3 peticiones a la vez y 30 por minuto por plugin y red. La respuesta llega como evento http con id, status, body (hasta 256 KB) y, si falló, error.
Límites
Límite
Valor
Tamaño del fichero
4 MB
Memoria
16 MB
CPU por evento
unos 20 millones de instrucciones (50 millones al arrancar)
Respuesta
64 KB y 20 acciones; 5 mensajes por evento
Estado guardado
64 KB
Almacén clave-valor
16 MB y 200 000 claves por plugin y red
Fallos seguidos antes de detenerse en una red
5
SDK de C
sdk/c/xbnc_plugin.h resuelve la interfaz. Funciones principales:
xbnc_kv_get, xbnc_kv_set, xbnc_kv_del, xbnc_kv_list (y las variantes _str)
Almacén clave-valor.
xbnc_log(texto)
Escribir en el registro.
sdk/c/xbnc_util.h añade lo que usan los plugins oficiales: idioma de la respuesta (u_lang), lectura de comandos con alias (u_command), recorrido del almacén (u_next_key), fechas, zonas horarias con horario de verano europeo y construcción de textos.
cd sdk/c
clang --target=wasm32 -O2 -nostdlib -fno-builtin -I. \
-Wl,--no-entry -Wl,--export-dynamic -Wl,--strip-debug -Wl,-z,stack-size=65536 \
-o hola.raw.wasm hola.c
xbncd plugin pack hola.raw.wasm hola.json -o hola.wasm
xbncd plugin check hola.wasm
sudo -u xbnc xbncd plugin install hola.wasm
# Los ejemplos oficiales: make (o make bundle para copiarlos al catálogo)
Necesita clang y wasm-ld (LLVM 15 o posterior) y llvm-objcopy (o xbncd plugin pack).
SDK de Rust
El crate xbnc-plugin (sdk/rust) da el trait Plugin, el tipo Event (con kind(), text(), conversation(), is_channel(), outgoing(), config_str(), state()…), Response (con reply(), tell(), state()…) y la macro plugin!. Ejemplo incluido (sdk/rust/examples/stats.rs):
//! Example plugin: counts messages per channel and answers "!stats".
//! The counts survive restarts thanks to the persisted state.
//!
//! Build: cargo build -p xbnc-plugin --release --target wasm32-unknown-unknown --example stats
//! then: xbncd plugin pack target/wasm32-unknown-unknown/release/examples/stats.wasm \
//! sdk/rust/examples/stats.json -o stats.wasm
use xbnc_plugin::{plugin, Event, Plugin, Response};
#[derive(Default)]
struct Stats;
impl Plugin for Stats {
fn on_event(&mut self, ev: &Event) -> Response {
let mut r = Response::new();
let mut counts = ev.state().as_object().cloned().unwrap_or_default();
match ev.kind() {
"message" if ev.is_channel() && !ev.outgoing() => {
let chan = ev.conversation().to_lowercase();
let n = counts.get(&chan).and_then(|v| v.as_u64()).unwrap_or(0) + 1;
counts.insert(chan, n.into());
if ev.text().trim() == ev.config_str("trigger") {
r.reply(format!("{} messages counted in {}", n, ev.conversation()));
}
r.state(counts.into());
}
"command" => {
if counts.is_empty() {
r.tell("Nothing counted yet.");
}
let mut v: Vec<_> = counts.iter().collect();
v.sort_by_key(|e| std::cmp::Reverse(e.1.as_u64()));
for (chan, n) in v.into_iter().take(10) {
r.tell(format!("{chan}: {n}"));
}
}
_ => {}
}
r
}
}
plugin!(Stats);
Modo desarrollo
En Plugins → Desarrollo (superadministradores): con el modo activo, xBNC vigila /var/lib/xbnc/plugins/dev/. Un .wasm guardado ahí se comprueba e instala en cuanto cambia y todas las redes lo recargan. Si pones nombre.json junto a nombre.wasm, lo empaqueta solo (no hace falta xbncd plugin pack). Un plugin nuevo se activa en las redes de quien activó el modo. Los resultados y lo que escribe el plugin se ven en el registro en directo de la página.
Además de los oficiales, xBNC puede ofrecer los plugins de un catálogo remoto firmado ([catalog] en xbnc.toml). El índice es un JSON {"plugins": [{"id", "version", "url", "sha256", "size", "manifest"}]} y su firma Ed25519 en base64 se publica en índice.sig. Cada plugin se comprueba contra el SHA-256 firmado antes de instalarlo.
xbncd plugin catalog-keygen -o catalogo.key # una vez; la pública va en [catalog] public_key
xbncd plugin catalog-entry hola.wasm https://plugins.ejemplo.org/hola.wasm # entrada para el índice
xbncd plugin catalog-sign --key catalogo.key index.json # escribe index.json.sig
xBNC puede hablar cualquier idioma. Todos sus textos están en un fichero por idioma: el panel web, la pantalla de entrada y todo lo que dice por IRC (*status, su ayuda, los avisos y los mensajes de los módulos). Se traduce un fichero y el idioma aparece en todos los selectores de idioma de xBNC, sin programar ni compilar nada.
Los ficheros
Fichero
Qué es
locales/en.json
Inglés. Es la base: tiene todos los textos de xBNC.
locales/es.json
Español.
locales/<código>.json
Cualquier otro idioma: pt.json, fr.json, pt-BR.json…
El inglés y el español van dentro del programa. Los demás idiomas, y las correcciones, se ponen en /etc/xbnc/locales/ (locales_dir en xbnc.toml, o la variable XBNC_LOCALES_DIR). xBNC los lee al arrancar y al recargar (sudo systemctl reload xbnc o /msg *status rehash).
{
"language": {
"code": "pt",
"name": "Português",
"english_name": "Portuguese",
"locale": "pt-PT",
"translators": ["Tu nombre o nick"]
},
"irc": {
"connected_to": "Ligado a {0} como {1}.",
"help.group.channels": "Canais"
},
"panel": {
"Joined": "Dentro",
"Delete {username}": "Apagar {username}"
}
}
Parte
Qué es
language
code: código del idioma (dos o tres letras, con región si hace falta: pt, pt-BR); el fichero se llama igual. name: nombre del idioma en ese idioma, como sale en los selectores. locale: para fechas y números. translators: sale en xbncd locales list.
irc
Lo que xBNC dice por IRC. La clave de la izquierda es un nombre; solo se traduce el texto de la derecha.
panel
El panel web. La clave de la izquierda es el texto en inglés; solo se traduce el texto de la derecha.
Paso a paso
Copia en.json como <código>.json (por ejemplo, pt.json).
Cambia la parte language: código, nombre, nombre en inglés, locale y tu nombre.
Traduce los textos de la derecha. No cambies nunca las claves de la izquierda.
Comprueba el fichero: xbncd locales check pt.json (añade --missing para ver lo que falta).
Pruébalo: cópialo en /etc/xbnc/locales/ y recarga xBNC. Aparece al momento en la pantalla de entrada, en Tu cuenta, en Ajustes y con /msg *status language pt.
Envíalo al proyecto para incluirlo en la siguiente versión.
Reglas de los textos
En el texto en inglés
Qué hacer
{0}, {1}… (IRC)
Valores que pone xBNC (un nick, un número…). Mantenlos todos; puedes moverlos dentro de la frase.
{name}, {count}… (panel)
Igual: mantenlos con el mismo nombre exacto.
<b>…</b>, <u>…</u> (IRC)
Negrita y subrayado en el cliente IRC. Mantenlos alrededor de las mismas palabras, cada <b> con su </b>.
Comandos y ajustes (/msg *status help, set away delay 60)
No se traducen: es lo que la gente escribe.
$1, $nick
Variables del módulo de alias: se dejan tal cual.
Disabled|network state (clave del panel)
Lo que va tras la | es una pista de dónde sale el texto (para el género, por ejemplo). Se traduce solo el texto, sin la pista.
Plurales
Los textos terminados en .one se usan con 1 y los terminados en .other con cualquier otro número (unit.network.one = «{{0}} red»).
Saltos de línea
No se admiten en los textos de IRC.
Un texto que no cumple estas reglas se ignora y sale en inglés; xbncd locales check lo indica como problema. Esto impide también que un fichero de idioma pueda meter comandos IRC (los saltos de línea están prohibidos).
Corregir un idioma incluido
Un fichero de un idioma que ya existe (por ejemplo, es.json en /etc/xbnc/locales/) solo necesita los textos que quieras cambiar: el resto se queda como está.
Lo que no se traduce con un fichero
Las respuestas de los plugins. Su nombre, descripción y ajustes se traducen en su manifiesto (translations). Lo que un plugin responde en un canal está escrito en el propio plugin; en un idioma que no incluye, responde en inglés. Los plugins oficiales hablan inglés y español.
El instalador habla inglés y español.
El registro del servidor está en inglés.
Traducir esta wiki
La wiki es un proyecto propio, aparte de la app (xbnc-wiki), para que cualquiera pueda leerla sin instalar xBNC. Se genera con build.py a partir de un fichero de contenido por idioma (content/en.py es la base) y del código fuente de xBNC. Para traducirla, copia en.py como <código>.py y traduce los textos entre comillas sin tocar las etiquetas HTML ni las llamadas como code(…). Las tablas de módulos, plugins y comandos se traducen solas con los ficheros de idioma de xBNC. Al generar la wiki, cada idioma aparece en el selector de la cabecera.
Para desarrolladores
En el panel, los textos se escriben en inglés dentro de t("…"), tk("…") o tx("…"). npm run i18n:check (en web/) lista lo que falta en en.json, y node scripts/i18n-check.mjs --sync lo añade.
En el servidor, el texto se añade a la sección irc de locales/en.json y se usa con self.t("clave", &[]), tr!(self, "clave", arg1, arg2) o i18n::tr(lang, "clave", &[…]). Los tests comprueban que cada clave usada existe y que es.json las traduce todas con los mismos marcadores.
La guía completa, en inglés, está en TRANSLATING.md, en el código fuente; esta sección es su versión en español.
xBNC negocia con cada servidor las capacidades que ofrece y le interesan, y ofrece a cada cliente las suyas. A cada cliente solo le llega lo que ha pedido: un cliente clásico recibe los mensajes de siempre (por ejemplo, el JOIN corto y solo el rango más alto en la lista de usuarios).
Con los servidores
Capacidad
Para qué
server-time
Hora real de cada mensaje.
message-tags
Etiquetas en los mensajes.
multi-prefix
Todos los rangos de un usuario (@+), no solo el más alto.
extended-join
Las entradas llevan la cuenta y el nombre real.
away-notify
Avisos cuando alguien se marca como ausente o vuelve.
account-notify
Avisos cuando alguien se identifica o deja de estarlo.
account-tag
Cada mensaje indica la cuenta de quien lo envía.
chghost
Cambios de usuario o host sin salir y entrar.
userhost-in-names
La lista de usuarios con nick!usuario@host.
invite-notify
Invitaciones a otras personas en tus canales.
setname
Cambios de nombre real.
cap-notify
El servidor añade o quita capacidades en caliente.
sasl
Identificación SASL (PLAIN y EXTERNAL hacia el servidor; PLAIN desde los clientes).
Con los clientes
Capacidad
Para qué
server-time
Hora real de cada mensaje.
message-tags
Etiquetas en los mensajes.
echo-message
Tus propios mensajes vuelven confirmados por el servidor.
batch
Grupos de mensajes (historial, reproducción).
sasl
Identificación SASL (PLAIN y EXTERNAL hacia el servidor; PLAIN desde los clientes).
draft/chathistory
El cliente pide el historial que necesita (CHATHISTORY).
draft/event-playback
El historial incluye entradas, salidas, kicks y cambios de nick como eventos reales.
draft/read-marker
Marcadores de lectura compartidos entre dispositivos.
soju.im/bouncer-networks
Todas tus redes en una sola conexión.
soju.im/bouncer-networks-notify
Avisos de cambios en tus redes.
extended-join
Las entradas llevan la cuenta y el nombre real.
away-notify
Avisos cuando alguien se marca como ausente o vuelve.
account-notify
Avisos cuando alguien se identifica o deja de estarlo.
account-tag
Cada mensaje indica la cuenta de quien lo envía.
chghost
Cambios de usuario o host sin salir y entrar.
multi-prefix
Todos los rangos de un usuario (@+), no solo el más alto.
userhost-in-names
La lista de usuarios con nick!usuario@host.
invite-notify
Invitaciones a otras personas en tus canales.
setname
Cambios de nombre real.
cap-notify
El servidor añade o quita capacidades en caliente.
Con CAP LS 302 se anuncia sasl=PLAIN. Si el servidor no admite una capacidad que pide un cliente, xBNC la ofrece igual cuando puede construirla él (por ejemplo, el JOIN extendido con los datos que conoce). El informe para pruebas recoge, por red, lo que ofrece el servidor (caps_offered), lo que xBNC activó (caps_acked) y lo que pidió cada cliente.
En Tu cuenta → Informe para pruebas → Descargar informe (o GET /api/v1/diagnostics) se descarga un JSON con lo que hace falta para diagnosticar un problema con una red, sin contraseñas, mensajes, nicks de otras personas ni IPs de tus dispositivos.
Campo
Contenido
report, format
xbnc-diagnostics, versión 1 del formato.
version, brand
Versión de xBNC y «xBNC 1.0.0 Made by y2k».
os, arch, database
Sistema, arquitectura y base de datos (sqlite o postgresql).
started_at, generated_at
Cuándo arrancó xBNC y cuándo se generó el informe.
timezone, language
Tu zona horaria e idioma.
counters
Contadores del servidor (tráfico, mensajes, reconexiones, logins).
networks[]
Por red: name, host, port, tls, enabled, si tiene SASL o contraseña de servidor, saved_channels, modules y plugins activos, y live.
networks[].live
state, server, network_name, connected_since, last_error, caps_offered, caps_acked, isupport, services_logged_in, host_hidden, channels_joined y clients (dispositivo y capacidades de cada uno).
xBNC es un único programa (xbncd) con el panel incluido. El código se divide en crates/xbnc-proto (protocolo IRC sin E/S: parser y serializador con etiquetas IRCv3, prefijos, casemapping, ISUPPORT y CTCP) y crates/xbncd (servicio, CLI, API y panel), más los SDK de plugins en sdk/, el panel (React y TypeScript) en web/ y las traducciones en locales/.
Cómo trabaja por dentro
Cada red de cada usuario es una tarea independiente que posee en exclusiva su conexión con el servidor, el estado de la red y la lista de clientes. Nadie más toca ese estado: no hay bloqueos en el camino de los mensajes.
Cada cliente es una tarea de lectura y otra de escritura, con una cola acotada. Si un cliente no lee, se le desconecta en vez de acumular memoria; al volver recupera lo pendiente.
El panel, la API y la terminal nunca tocan las conexiones IRC: escriben en la base de datos y envían órdenes a las tareas de red (estado, control, entrar, salir, recargar, desconectar un cliente, alerta, aviso a todos).
Todo acceso a la base de datos va por un grupo de hilos aparte. Los mensajes de todas las redes pasan por un único hilo escritor que guarda en una sola transacción todo lo que espera (hasta 512), conservando el orden de cada red.
Los módulos son código del propio programa, llamados en puntos concretos (línea del servidor, comando de un cliente, temporizador, conexión, cliente que entra o sale). Los plugins reciben los eventos desde la tarea de red y se ejecutan en el grupo de hilos bloqueante.
Al llegar un mensaje: se interpreta, se actualiza el estado (nick, canales, miembros, modos, tema), se guarda con la hora del servidor y un identificador, y se envía a cada cliente adaptado a sus capacidades.
Los textos se buscan por clave en los idiomas cargados (los incluidos y los ficheros de locales_dir), con el inglés como respaldo.
Módulos del código
Módulo (crates/xbncd/src)
Qué hace
main.rs
El comando xbncd: el servicio y la línea de comandos (usuarios, redes, copias, importación, idiomas, versiones, actualizaciones).
config
Lectura y validación de xbnc.toml.
db, sql
Almacenamiento: migraciones versionadas, usuarios, redes, canales, mensajes, cursores, auditoría y copias; sql da a SQLite (rusqlite) y a PostgreSQL la misma interfaz.
crypto
Argon2id, cifrado XChaCha20-Poly1305 de las credenciales guardadas y un tipo Secret que nunca se imprime.
auth
El formato de login, la comprobación de contraseñas, el limitador de fuerza bruta y el enmascarado de líneas en los logs.
util
Codec de líneas IRC (límite de longitud, UTF-8 con pérdida, protección contra inyección de CR/LF) y funciones de tiempo.
network/
La tarea de cada red de cada usuario: conexión con el servidor IRC, estado, historial y clientes vinculados. mods.rs y extra.rs tienen el comportamiento de los módulos, status.rs y status_more.rs los comandos *status, *notes y *watch, plugins.rs entrega los eventos a los plugins, dcc.rs decide qué ofertas DCC pasan por xBNC y readmark.rs guarda las marcas de lectura.
downstream, bouncer, events
Conexiones de clientes: registro, CAP, SASL, PASS y reenvío a la tarea de la red; soju.im/bouncer-networks; avisos a los clientes cuando cambia una red.
modules
Catálogo de módulos, esquema y validación de sus ajustes y configuración tipada por red.
perform
Convierte comandos al estilo del cliente (/msg, /mode…) en IRC, para los comandos al conectar, los alias y las tareas programadas.
logs
Registros en ficheros, escritos en un hilo aparte.
certs
Certificados de cliente (generación, validación y huellas) y certificados de servidor de confianza.
fish
Cifrado FiSH: Blowfish ECB y CBC y el intercambio de claves DH1080.
i18n
Idiomas: inglés y español incluidos y los ficheros de locales_dir.
netinfo
Direcciones de la red local y la dirección que se muestra para 0.0.0.0.
server, ha
Contexto compartido, registro de tareas de red, puertos TLS, mantenimiento y recarga; alta disponibilidad activo/en espera con PostgreSQL.
rbac, settings
Los cinco roles y sus permisos; ajustes de historial y límites editables en caliente (base de datos sobre xbnc.toml).
totp, webauthn, sso
Verificación en dos pasos (RFC 6238), passkeys, OpenID Connect y LDAP.
tz
Zonas horarias con horario de verano, con la base de datos incluida.
metrics, alerts
Contadores de tráfico y mensajes, memoria y CPU del proceso; reglas de alerta comprobadas cada 30 s, con avisos en el panel, por IRC y por webhook.
notify, push
Avisos por Telegram, Discord, correo, una dirección web propia y Web Push.
plugins, plugin_dev, catalog
Plugins WebAssembly: manifiesto, validación, motor Wasmi con límites, instancias y acciones; modo desarrollo; el catálogo incluido y el remoto firmado.
dcc
Lectura y reescritura de ofertas DCC y los túneles de un solo uso.
http
Cliente HTTP/1.1 mínimo (TLS verificado, tamaño y tiempo acotados) para webhooks, avisos, plugins con acceso web y la comprobación de versiones.
updates, release
Aviso de versiones nuevas, descarga y comprobación de paquetes, el ayudante xbncd apply-update y las firmas Ed25519.
backup
Copias de seguridad desde el panel y su restauración.
import
Migración desde otro bouncer: lectura de su configuración, datos de módulos, plan, aplicación en una transacción e importación de registros.
web/
Servidor HTTP (axum): API /api/v1, WebSocket, sesiones, CSRF, cabeceras y los ficheros del panel incluidos en el binario.
El panel (web/) es una aplicación React y TypeScript compilada con Vite. web/dist se incluye en el binario, así que xBNC sigue siendo un único fichero. Los plugins oficiales también van dentro del binario (crates/xbncd/catalog/).
Máquinas de estado
Se mantienen separadas:
Conexión con la red: Disconnected → Registering → Connected. Durante el registro, xBNC negocia las capacidades IRCv3, SASL y el nick (con alternativa si está ocupado). Tiempos: 30 s para conectar, 60 s para registrarse, PING tras 90 s de silencio y desconexión a los 180 s. Reconexión con esperas crecientes de 5 s a 5 minutos con una variación aleatoria de ±20 %, y al menos 2 minutos tras un aviso de throttling. Si un servidor falla y la red tiene servidores de reserva, se prueba el siguiente al cabo de 1 segundo.
Sesión de cliente: registro (CAP, PASS o SASL, NICK, USER) con tiempo límite, y después una sesión vinculada a una red.
Usuario y bouncer: qué tareas de red existen y cuántos clientes tiene cada usuario.
El camino de un mensaje
La tarea de la red lee una línea del servidor y la analiza.
Actualiza el estado (nick, canales, miembros, modos, tema).
Si es un PRIVMSG, NOTICE o ACTION de alguien (o un evento, con el módulo Eventos), lo guarda en messages con la hora del servidor (server-time) o la local, y le da el identificador msgid = x<id>.
Lo envía a cada cliente vinculado adaptado a sus capacidades (quitando las tags que el cliente no pidió) y avanza el cursor de cada cliente.
Lo que envía un cliente va al servidor, se guarda como saliente y se reenvía al resto de clientes del usuario (y al propio emisor si pidió echo-message).
Historial sin duplicados
Cada dispositivo (@dispositivo en el login, default si no se indica) tiene un cursor por red en client_cursors: el identificador del último mensaje entregado.
Al conectar, recibe los mensajes posteriores a su cursor (hasta playback_max, 1.000 por defecto). Un dispositivo nuevo recibe los new_client_playback más recientes (100 por defecto).
Los cursores se guardan al desconectar el dispositivo y cada 5 segundos; nunca retroceden.
Los clientes con draft/chathistory no reciben reproducción automática: piden lo que necesitan con CHATHISTORY. Los mensajes ya leídos en otro dispositivo (draft/read-marker) no se vuelven a reproducir.
Esquema de la base de datos
Tabla
Contenido
Desde la migración
users
Usuario, hash Argon2id, rol, límites y cuotas, secreto TOTP cifrado, idioma, zona horaria, dirección de salida, último acceso y última conexión.
1 (2, 4, 8, 9, 10)
networks
Servidor, puerto, TLS, nick, SASL, comandos al conectar, certificado de cliente, servidores de reserva y certificados de confianza; contraseñas y claves cifradas.
1 (3, 4, 10)
channels
Canales guardados por red, con clave cifrada, retención, desactivado y posición.
1 (8, 10)
messages
Historial: red, conversación, hora, emisor, comando, texto, saliente y mención.
1 (8)
client_cursors
Último mensaje entregado por red y dispositivo.
1
audit_log
Acciones administrativas.
1
web_sessions
Sesiones del panel (hash del token, CSRF, IP, navegador, caducidad).
2
settings
Ajustes cambiados desde el panel.
2
login_events
Intentos de acceso por IRC y en el panel (10.000 como máximo).
2
module_config
Módulos por red: activo y ajustes en JSON validado.
4
notes
Notas de cada usuario.
4
messages_fts
Índice de texto completo de messages.text, mantenido con triggers (SQLite; PostgreSQL usa su propia búsqueda de texto).
5
api_tokens
Tokens de API: hash SHA-256, prefijo, alcance, caducidad, último uso e IP.
6
plugins
Plugins instalados: manifiesto, SHA-256, tamaño, activo en el servidor, dominios web aprobados y el propio módulo.
7 (8)
plugin_config
Plugins por red: activo, ajustes y estado guardado.
7
plugin_kv
Almacén clave-valor de cada plugin por red.
8
notify_settings
Ajustes de avisos de cada usuario.
8
push_subscriptions
Suscripciones Web Push de los navegadores de cada usuario.
8
read_markers
Último mensaje leído de cada conversación.
8
passkeys
Passkeys: credencial, clave pública, contador, nombre y último uso.
8
user_identities
Identidades externas (OpenID Connect, LDAP) vinculadas a usuarios locales.
8
PostgreSQL tiene el mismo esquema; se admite desde la versión 8 del esquema (PG_MIGRATIONS) y cada migración nueva se añade a las dos listas. Los eventos de canal (JOIN, PART…) se guardan en messages con su comando. Las contraseñas importadas de otro bouncer con SHA-256 se guardan como legacy-sha256$<hash>$<sal> hasta el primer acceso correcto, en el que se sustituyen por Argon2id. Las migraciones se aplican al arrancar dentro de una transacción, y un binario se niega a abrir un esquema más nuevo del que conoce.
Qué protege xBNC, de quién y con qué medida en cada punto. Es la guía para la auditoría externa y para revisar cada cambio.
Qué se protege
Activo
Por qué importa
Contraseñas de las cuentas de xBNC
Dan acceso a las redes, al historial y, a los administradores, al servidor entero.
Credenciales de red (SASL, contraseña de servidor, claves de canal, comandos al conectar, contraseñas de servicios, claves FiSH, certificados)
Permiten suplantar al usuario en las redes.
Historial y registros
Privados y canales: información personal.
Sesiones del panel y tokens de API
Equivalen a la contraseña mientras son válidos.
La clave secreta (secret.key)
Descifra todas las credenciales guardadas.
La disponibilidad del servicio
Si cae, los usuarios pierden mensajes y presencia.
La reputación de la IP del servidor
Todas las conexiones salen de ella.
Quién puede atacar
Actor
Qué controla
Qué intenta
Cualquiera en internet
Conexiones a los puertos de IRC, panel y DCC
Adivinar contraseñas, agotar recursos, explotar el parser.
Alguien en una red IRC
Mensajes, CTCP, DCC, nombres de canal y nick
Inyectar comandos, que xBNC se conecte a redes internas, saturar el historial.
Un servidor IRC malicioso
Todo lo que llega por la conexión de una red
Lo anterior, más respuestas malformadas, líneas enormes y desconexiones en bucle.
Un usuario de xBNC
Su cuenta, sus redes, módulos y plugins
Leer datos de otros, escalar a administrador, abusar del servidor.
Un administrador
Usuarios y redes de otros
Escalar a superadministrador, cambiar ajustes globales.
El autor de un plugin
El código que instala un superadministrador
Salir del aislamiento, leer datos, actuar sin permiso.
El autor de un fichero de idioma
Un fichero que un administrador copia en locales_dir
Meter comandos IRC o HTML con los textos (se rechaza: se validan y se tratan como texto plano).
Una web maliciosa
El navegador de un usuario con sesión
CSRF, robo de sesión, secuestro del WebSocket.
Quien accede a las copias de seguridad
Los .tar.gz
Credenciales y conversaciones.
Fuera de alcance: quien ya es root en la máquina, el proveedor del servidor y los fallos del sistema operativo o de las redes IRC.
Conexiones de clientes IRC (6697/6667)
Amenaza
Medida
Fuerza bruta de contraseñas
Bloqueo por IP compartido con el panel y los tokens; Argon2id; mismo coste para usuarios inexistentes.
Agotar conexiones o memoria
Límite global de conexiones, 16 sin autenticar por IP, clientes por usuario, tiempo de registro y colas acotadas.
Líneas enormes o binarias
Límite de 8703 bytes por línea y UTF-8 con pérdida.
Inyección de comandos con CR/LF
Cualquier CR o LF corta la línea; los textos de módulos, plugins y ficheros de idioma se validan sin caracteres de control.
Texto plano en internet
Los puertos sin TLS solo aceptan la red local o exigen insecure = true.
Credenciales en los registros
Enmascarado de PASS, AUTHENTICATE, OPER y mensajes a servicios en todos los niveles.
Conexiones a las redes IRC
Amenaza
Medida
Suplantación del servidor
TLS verificado con el almacén del sistema, sin opción para desactivarlo; un certificado de confianza solo acepta su huella SHA-256 exacta.
Respuestas malformadas
Parser propio sin unsafe, con tests y fuzzing.
Inundación del historial
Retención configurable, eventos opcionales, colas acotadas; el módulo log descarta líneas si el disco no da abasto; ocultar canales inundados.
Desconexiones en bucle
Espera progresiva con variación aleatoria y espera mínima tras avisos de throttling.
Expulsión por flood
Control de ritmo en la salida.
Robo de la contraseña de servicios
En redes sin NickServ nunca se envía en modo automático.
DCC
Amenaza
Medida
Usar el bouncer para llegar a redes internas (SSRF)
Ofertas hacia direcciones internas o puertos por debajo de 1024 rechazadas salvo permiso del usuario.
Hacer que xBNC se conecte a un tercero
En tus envíos solo se conecta de vuelta a la IP de tu dispositivo.
Puertos abiertos indefinidamente
Un puerto por oferta, una sola conexión, tiempo de espera, cierre por inactividad y límite de tamaño.
Robo de la transferencia
En la recepción, el puerto solo acepta la IP del cliente.
Panel web y API
Amenaza
Medida
CSRF
Token CSRF en cabecera, cookie SameSite=Strict y comprobación de Origin.
XSS
React escapa todo el contenido; CSP sin scripts externos ni en línea; los textos de IRC nunca se interpretan como HTML; las traducciones son texto plano.
Robo de sesión
Cookie HttpOnly y Secure; solo el SHA-256 del token en la base de datos; caducidad deslizante; cambiar la contraseña cierra las demás sesiones.
Secuestro del WebSocket
Comprobación de Origin, revalidación de la sesión cada 30 s y sin tokens de API.
Escalada de privilegios
Permisos comprobados en el servidor en cada ruta; nadie cambia su propio rol; siempre queda un superadministrador.
Ver datos de otros usuarios
Las redes ajenas responden 404, no 403.
Abuso de la API y slowloris
20 peticiones/s por IP, cuerpos acotados, 64 conexiones por IP y 4 096 en total, cierre tras 2 minutos sin tráfico, 10 s para el saludo TLS.
Tokens filtrados
Alcance, caducidad, último uso e IP visibles, revocación inmediata, solo su SHA-256 guardado.
Primer acceso robado
Código de un solo uso, solo en el registro del servicio.
Webhook hacia sitios internos
Solo lo configura un superadministrador; sin redirecciones, sin credenciales en la URL, 10 s como máximo.
Plugins
Amenaza
Medida
Salir del aislamiento
Intérprete Wasmi sin JIT, módulo validado al cargar, sin WASI ni más importaciones que las de xbnc, funciones avanzadas de WebAssembly desactivadas.
Bloquear o tumbar el servidor
Combustible por evento, memoria limitada, ejecución que nunca hace crecer la pila nativa, llamadas en hilos aparte, parada tras cinco fallos.
Actuar sin permiso
Acciones declarativas comprobadas contra los permisos; comandos peligrosos siempre prohibidos; máximo de mensajes por evento; acceso web solo a dominios aprobados y con límites.
Leer datos de otros
Cada instancia solo recibe los eventos de su red; estado y almacén separados por red.
Fichero cambiado tras instalarlo
Se guarda su SHA-256 y no se carga si no coincide; ficheros 0600.
Plugin malicioso instalado
Solo un superadministrador instala, viendo permisos y huella; queda en la auditoría; catálogo remoto firmado.
Importación desde otro bouncer
Amenaza
Medida
Fichero preparado para crear un superadministrador
La importación nunca concede ese rol.
Contraseñas débiles heredadas
SHA-256 con sal comparado en tiempo constante y sustituido por Argon2id; los formatos no comprobables reciben una contraseña aleatoria.
Rutas o tamaños abusivos
Fichero de hasta 4 MB, anidamiento limitado, nombres validados; los registros solo se leen de las carpetas indicadas.
Fugas en la vista previa
La vista previa no incluye secretos.
Actualizaciones desde el panel
Amenaza
Medida
Paquete falso (servidor de descargas, DNS o red comprometidos)
Firma Ed25519 sobre el nombre del paquete y su SHA-256; la clave pública solo está en xbnc.toml, propiedad de root.
Volver a una versión con fallos conocidos
Se rechazan las versiones iguales o anteriores a la instalada.
Un proceso xbnc comprometido que intenta instalar algo como root
El ayudante (xbnc-update.service) no confía en nada de la carpeta del servicio: copia el paquete a /run (0700), comprueba la firma y la versión sobre esa copia, nunca sigue enlaces simbólicos, valida el nombre del fichero y usa cada petición una sola vez.
Petición de actualización falsa
Solo superadministradores, desde una sesión del panel (no con tokens de API) y con CSRF; queda en la auditoría.
Descargas infinitas o redirecciones a texto plano
200 MB y 10 minutos como máximo; solo https://, 5 redirecciones como máximo.
Datos guardados y copias de seguridad
Amenaza
Medida
Robo de la base de datos
Credenciales de red cifradas con XChaCha20-Poly1305; la clave fuera de la base de datos, con permisos 0600 comprobados al arrancar.
Robo de una copia de seguridad
Fichero 0600; incluye la clave, así que hay que guardarlo cifrado.
Acceso desde otros usuarios del sistema
Servicio con el usuario xbnc sin privilegios, carpetas 0700 y hardening de systemd.
Riesgos aceptados
El historial se guarda sin cifrar en la base de datos: cifrarlo impediría la búsqueda y la reproducción. Lo protegen los permisos del sistema.
Los clientes IRC no tienen segundo factor: la contraseña del bouncer es su única protección (de ahí el bloqueo por IP y Argon2id).
Un superadministrador puede instalar un plugin con permiso para enviar mensajes en nombre de quien lo active: es una decisión de confianza que el panel muestra antes de instalar.
Todas las conexiones a las redes salen de la IP del servidor.
FiSH es criptografía antigua (Blowfish, sin integridad); los mensajes descifrados se guardan en el historial como el resto.
Revisión
Cada versión revisa estas tablas: una función nueva que abra una superficie que no esté aquí se añade a ellas. Los tests que respaldan cada medida están en crates/xbncd/tests/ y en los tests unitarios de cada módulo; el fuzzing cubre el parser IRC, DCC, el fichero de configuración importado, la búsqueda, y los manifiestos y la ejecución de plugins (ver Entorno de pruebas).
Revisión interna de la versión 0.7. Se revisó todo el código con estas tablas como guía (no sustituye a la auditoría externa):
Consultas SQL: todas con parámetros enlazados; las partes construidas con format! son solo listas de columnas y cláusulas fijas.
Autorización de la API: cada ruta comprueba la sesión o el token y el permiso; las rutas de redes comprueban el dueño. Corregido: PATCH /users/{id} respondía 404 o 403 a cuentas sin permiso según existiera el usuario.
Procesos externos: el único es dpkg o rpm en el ayudante de actualización, con argumentos fijos y el paquete ya comprobado.
Rutas de ficheros: registros, plugins y actualizaciones construyen rutas solo con nombres validados; el ayudante de actualización nunca sigue enlaces simbólicos.
Denegación de servicio: se añadieron el límite de conexiones IRC sin autenticar por IP, los límites de conexiones y el tiempo de inactividad del panel, y el límite de cálculos de Argon2id simultáneos.
Plugins: el intérprete pasó al modo de ejecución en bucle después de que el fuzzing encontrara que el modo de llamadas encadenadas podía agotar la pila nativa en compilaciones sin optimizar.
Código unsafe: solo una llamada a mallopt al arrancar (ajuste del asignador de memoria).
Dependencias:cargo-deny en el CI (licencias y avisos de seguridad).
Revisión interna de la versión 1.0. Se revisó el código nuevo de la 1.0 y se corrigieron tres puntos antes de publicar: en redes sin servicios nunca se envía la contraseña a quien use el nick «NickServ»; el intercambio de claves FiSH tiene límite de peticiones y de claves guardadas; el texto descifrado no puede romper líneas. Los ficheros de idioma se validan (marcadores, etiquetas, sin caracteres de control ni saltos de línea) y un texto que no pasa se muestra en inglés.
Prueba de carga (cargo run --release --example loadtest -- --users 1000 --clients 1 --rate 2 --seconds 60): un servidor IRC simulado envía mensajes a todas las redes a la vez (el peor caso) y cada cliente mide cuánto tarda en recibirlos, pasando por el historial, los módulos y los plugins. Máquina: 2 vCPU Xeon a 2,1 GHz, 7 GB de RAM, disco en red, configuración por defecto (SQLite).
Usuarios y redes
Clientes
Mensajes/s entregados
CPU (de un núcleo)
Memoria
Latencia p50 / p95 / p99
Todas las redes de vuelta tras una caída
500
500
492
5 %
52 MB
21 / 43 / 46 ms
6,2 s
500
500
4 847
43 %
56 MB
46 / 64 / 72 ms
6,2 s
1 000
1 000
1 951
17 %
88 MB
50 / 80 / 95 ms
6,2 s
1 000
2 000
1 968
11 %
114 MB
44 / 66 / 78 ms
6,2 s
2 000
2 000
1 968
19 %
153 MB
75 / 141 / 155 ms
6,3 s
Ningún mensaje se perdió en ninguna prueba.
La latencia sube con el tamaño de la ráfaga: con 2 000 redes recibiendo el mismo mensaje a la vez, el último se entrega unos 150 ms después que el primero. Con tráfico real, repartido en el tiempo, es de pocos milisegundos.
Memoria: unos 45 KB por red conectada y unos 30 KB más por cliente, sin contar el historial (en disco).
Los logins de clientes (Argon2id) van de uno en uno por CPU: 2 000 clientes entrando juntos tardan unos 27 s en 2 núcleos, sin disparar la memoria.
Disco: unos 150 bytes por mensaje guardado, con el índice de búsqueda.
Para dimensionar: un servidor pequeño (1–2 vCPU, 1 GB de RAM) atiende cientos de usuarios con varias redes y miles de mensajes por segundo. Con muchos usuarios, revisa limits.max_connections, LimitNOFILE del servicio (65 536 por defecto) y la retención del historial.
Cómo se mide
La prueba (crates/xbncd/examples/loadtest.rs) arranca en un solo proceso:
un servidor IRC simulado con un canal #xbnc;
xBNC con N usuarios, cada uno con su red conectada a ese servidor y dentro de #xbnc;
uno o varios clientes IRC por usuario conectados a xBNC.
El servidor envía --rate mensajes por segundo al canal, a todas las redes a la vez (el peor caso: ráfagas simultáneas). Cada mensaje lleva la hora de envío y cada cliente mide cuánto tarda en llegar: servidor IRC → xBNC (guardado en el historial, módulos, plugins) → cliente. Al final el servidor corta todas las conexiones y la prueba mide cuánto tardan todas las redes en volver.
La memoria y la CPU son las del proceso completo, así que incluyen el servidor simulado y los clientes de la prueba (xBNC solo consume algo menos). La compilación es --release con la configuración por defecto (historial en SQLite con WAL).
Problemas encontrados y corregidos con esta prueba
Problema
Antes
Después
Memoria tras muchas entradas simultáneas: glibc guardaba en cada hilo los 19 MB de cada cálculo de Argon2id.
1.680 MB con 500 clientes
55 MB (util::tune_allocator, umbral fijo de mmap)
Cientos de cálculos de Argon2id a la vez (también útil contra avalanchas de intentos de entrada).
Sin límite
Uno por CPU (crypto::with_hash_slot)
Una transacción de SQLite por mensaje guardado.
p50 70 ms y 13 % de CPU con 500 redes
p50 21 ms y 5 % de CPU: un único hilo escritor guarda en una sola transacción todo lo que espera (Db::insert_message)
Pendiente
Repetir la prueba en hardware dedicado y con varias redes por usuario.
Medir con servidores IRC reales a distancia (la prueba usa uno local, sin latencia de red).
Para repetirla: cargo run --release --example loadtest -- --users 2000 --clients 1 --rate 1 --seconds 60. Las opciones son --users, --clients (por usuario), --rate (mensajes por segundo al canal) y --seconds.
testenv/ levanta xBNC con dos servidores IRC distintos (Ergo e InspIRCd) en una red aislada, todo en texto plano a propósito:
cd testenv
docker compose up -d --build
./setup.sh
# IRC: localhost:6667, contraseña test/ergo@laptop:testpassword1
# Panel: http://localhost:8080 (usuario test, contraseña testpassword1)
No publiques esos puertos en una interfaz pública.
Tests automáticos
cargo test --workspace # todos los tests
XBNC_TEST_POSTGRES=postgres://postgres@127.0.0.1/postgres cargo test --workspace # también con PostgreSQL
cargo clippy --workspace --all-targets -- -D warnings
cd web && npm run build && npm run i18n:check # panel y traducciones
xbncd locales check locales/*.json # ficheros de idioma
python3 ../xbnc-wiki/build.py --app . # la wiki (proyecto aparte)
Incluyen el protocolo, las unidades, la API contra un bouncer real, IRC de extremo a extremo, todos los comandos de *status, el cambio a un servidor de reserva, el certificado de confianza con un saludo TLS real, un idioma añadido como fichero, módulos y plugins contra un servidor IRC simulado, el panel, SSO y servidores IRC reales en el CI (Ergo, InspIRCd, UnrealIRCd, Solanum y miniircd). El fuzzing (fuzz/) se ejecuta cada semana. testenv/ui/language_check.py comprueba en un navegador real que todas las formas de cambiar el idioma funcionan.
Pruebas en redes reales
El plan de pruebas por red, módulo y plugin está en testenv/real/PLAN.md. Ver Probar la beta.
Los conjuntos de tests
Conjunto
Dónde
Qué cubre
Protocolo (16)
crates/xbnc-proto
Parser y serializador, escapado de tags, prefijos, casemapping, ISUPPORT, CTCP.
Unitarios del servicio (142)
crates/xbncd/src
Configuración, Argon2id, cifrado y permisos de la clave, base de datos y migraciones (SQLite y PostgreSQL, y actualización de un esquema antiguo), historial y búsqueda, cursores, sesiones, copias, login, limitador de fuerza bruta, enmascarado de logs, codec, CAP, roles, ajustes, TOTP (vectores del RFC 6238), métricas, tokens, cookies, origen, IP tras un proxy, cobertura de OpenAPI, redirección HTTP→HTTPS, catálogo y validación de módulos, globs, menciones, alias, registros, certificados de cliente y de confianza (con un handshake TLS real), FiSH, zonas horarias, idiomas (todas las claves usadas existen, español completo, ficheros comprobados y cargados), plugins (permisos, límites, regresiones del fuzzing), DCC, cliente HTTP, firmas de versiones, el ayudante de actualización y límites de conexiones.
API (12)
tests/api.rs
Primera instalación con código de un solo uso, login, sesiones, CSRF, origen, cabeceras, fuerza bruta, redes, canales, historial, exportación, aislamiento entre usuarios, roles, ajustes en caliente, 2FA, panel, OpenAPI, módulos, certificados, notas, idioma, tokens, métricas, alertas, importación y plugins.
Extremo a extremo (12)
tests/e2e.rs
El bouncer completo contra un servidor IRC simulado (escenarios abajo).
Módulos (16 + 12)
tests/modules.rs, tests/modules_v1.rs
Cada módulo contra el servidor simulado: ausente y resumen, recuperar nick, respuestas al dispositivo que pregunta, eventos, CTCP, canales fijos, alias, op automático, volver tras un kick, vigilar palabras, MOTD y registros; y los módulos de la 1.0: extras IRCv3 solo para los clientes que los piden, login en servicios (X con la IP oculta y Login on Connect), invitación, ignorar y quitar colores, FiSH en los dos sentidos y DH1080, canales desacoplados, canales inundados, recuperar op, Latin-1 y tareas programadas.
Comandos *status (12)
tests/status_commands.rs
Ayuda agrupada, nombres de comandos de otros bouncers, servidores de reserva (añadir, saltar, promover y cambio automático), huellas de confianza, direcciones de salida, canales desactivados y ordenados, búferes, MOTD y avisos a todos, permisos, redes desde IRC y un idioma añadido como fichero.
Plugins respondiendo en la red y al usuario, y cada plugin oficial: seen, trivia, greet, factoids, karma, quotes, games, badwords, acceso web solo a sitios aprobados, recordatorios, tell, encuestas, estadísticas, antiflood, dados y respuestas automáticas.
Panel (3)
tests/panel.rs
Chat en el panel, menciones e historial de canal, modo desarrollo de plugins y el catálogo de un clic.
SSO (3)
tests/sso.rs
Passkeys, OpenID Connect y LDAP.
PostgreSQL (1)
tests/postgres.rs
Paso de una base de datos SQLite a PostgreSQL (con XBNC_TEST_POSTGRES).
Servidor IRC real (1)
tests/real_ircd.rs
Un xBNC real contra un servidor IRC real (ver abajo).
SDK de Rust (3)
sdk/rust
Eventos y respuestas del SDK.
Escenarios de extremo a extremo
Al conectarse, el cliente recibe la bienvenida, el ISUPPORT de la red, sus canales, el tema y la lista de usuarios.
Un mensaje recibido con el cliente cerrado se reproduce al reconectar, con su hora; los mensajes propios no se reproducen al mismo dispositivo y una tercera conexión no recibe duplicados.
Dos dispositivos conectados a la vez se sincronizan; un dispositivo nuevo recibe el historial reciente.
CHATHISTORY LATEST, AFTER y errores, batch, el server-time del servidor conservado, msgid y echo-message; no se anuncian capacidades no implementadas.
Login con SASL PLAIN.
Una contraseña incorrecta se rechaza y la IP se bloquea tras tres fallos, incluso con la contraseña correcta.
Red inexistente: el error indica las redes disponibles.
Caída del servidor: el bouncer avisa, reconecta con esperas crecientes y vuelve a entrar en los canales.
*status, y PART y JOIN con clave guardados (la clave se guarda cifrada).
Los comandos al conectar se envían antes de entrar en los canales.
Las marcas de lectura se comparten entre dispositivos.
soju.im/bouncer-networks para clientes modernos.
Servidores IRC reales
tests/real_ircd.rs conecta un xBNC real a un servidor IRC real (variable XBNC_IRCD=host:puerto) y comprueba la lista de usuarios, los mensajes en los dos sentidos, la sincronización entre dos dispositivos, la reproducción sin duplicados con el cliente cerrado, el cambio de nick, salir de un canal y seguir conectado a la red:
XBNC_IRCD=127.0.0.1:6667 cargo test -p xbncd --test real_ircd -- --ignored
El CI lo ejecuta contra Ergo, InspIRCd, UnrealIRCd, Solanum y miniircd (testenv/ci/start-ircd.sh compila o instala cada uno con una configuración mínima).
Fuzzing
fuzz/ tiene seis objetivos de libFuzzer:
Objetivo
Qué recibe
irc_message
Líneas IRC arbitrarias: parser, serializador y la vuelta atrás.
dcc
Ofertas DCC.
import_config
Ficheros de configuración de otro bouncer.
search_query
Consultas de búsqueda contra el índice de texto completo.
plugin_manifest
Ficheros WebAssembly con manifiestos arbitrarios.
plugin_runtime
Módulos WebAssembly arbitrarios cargados y ejecutados como plugins.
El CI (.github/workflows/fuzz.yml) ejecuta cada objetivo 20 minutos cada semana, conserva el corpus entre ejecuciones y antes comprueba que los fallos ya encontrados (fuzz/regressions/) siguen corregidos. Los fallos encontrados hasta ahora, todos corregidos y con su test:
El parser aceptaba CR, LF o NUL dentro de una línea.
El parser aceptaba comandos que no son alfanuméricos (@ @).
Las ofertas DCC aceptaban caracteres de control.
Incrustar un manifiesto desbordaba con secciones de tamaño imposible.
Un plugin con un bucle largo agotaba la pila nativa del proceso en compilaciones donde el compilador no convierte en saltos las llamadas del intérprete: ahora el intérprete usa el modo de ejecución en bucle, que nunca hace crecer la pila.
Pruebas de carga
cargo run --release --example loadtest -- --users 2000 --rate 1 --seconds 60. Resultados y método en Rendimiento.
El panel en un navegador
El panel se ha recorrido en Chrome con Playwright contra un bouncer real con un cliente IRC conectado de verdad: primera instalación, asistente de redes, canales, historial y búsqueda, usuarios, seguridad, ajustes, 2FA, módulos, plugins (instalación con la vista previa de permisos, ajustes por red), importación desde otro bouncer, alertas, actualizaciones, copias, chat, la pantalla de entrada, los temas claro y oscuro y el móvil, en inglés y en español, sin errores en la consola salvo el 401 esperado antes de entrar. testenv/ui/language_check.py URL USUARIO CONTRASEÑA repite en un navegador real todas las formas de cambiar el idioma (pantalla de entrada, Tu cuenta, Ajustes, un idioma añadido como fichero) y comprueba que no queda ningún texto en otro idioma.
Lista de verificación manual
Úsala con testenv/ o con una instalación real:
El servicio arranca y para limpiamente (systemctl start y stop xbnc; en el log aparece xBNC stopped).
Se crean un administrador y varios usuarios con la línea de comandos.
Un usuario con dos redes está conectado a las dos a la vez.
Un cliente clásico (HexChat, WeeChat, irssi o mIRC) se conecta con usuario/red@dispositivo:contraseña.
Con el cliente cerrado, otra persona escribe en el canal y por privado; al reconectar esos mensajes llegan una sola vez.
Dos clientes del mismo usuario ven lo que escribe el otro.
docker compose restart ergo (o cortar la red): xBNC avisa, reconecta y vuelve a los canales.
/msg *status status muestra el estado correcto.
Cinco contraseñas incorrectas bloquean la IP durante 15 minutos.
scripts/backup.sh y scripts/restore.sh recuperan usuarios, redes e historial.
journalctl -u xbnc no contiene contraseñas tras hacer login, SASL y /msg NickServ IDENTIFY.
Desde el panel: crear un usuario, añadirle una red con el asistente, entrar en un canal y ver el historial.
Un usuario normal no ve Usuarios, Seguridad ni Ajustes, ni las redes de otros.
Activar la verificación en dos pasos y entrar con el código.
Cambiar la retención en Ajustes y comprobar en Sistema que se aplica sin reiniciar.
Consumo de memoria con 1 usuario y 2 redes (systemctl status xbnc): anótalo para comparar en versiones futuras.
Certificado del servidor IRC no válido (propio o caducado) o faltan los certificados raíz.
sudo apt install ca-certificates; comprueba el host y el puerto TLS. Si la red usa un certificado propio, confía en él con /msg *status addfingerprint HUELLA.
*status: registration timed out
Puerto equivocado (TLS contra un puerto sin TLS o al revés).
Revisa el puerto y el TLS de la red (Undernet: 6667 sin TLS).
SASL authentication failed
Cuenta o contraseña SASL incorrectas.
Corrígelas en la configuración de la red.
SASL is configured but this server does not offer SASL PLAIN
La red no admite SASL.
Quita SASL y usa Login en servicios.
Your host is trying to (re)connect too fast -- throttled
La red frena las reconexiones seguidas desde tu IP.
Espera; xBNC aguarda al menos 2 minutos. No pulses Conectar varias veces.
En Undernet entro en los canales con mi IP real
Sin login antes de entrar.
Activa Login en servicios con «Identificarse al conectar» y una espera de unos segundos.
El login con X, Q o AuthServ no funciona
Cuenta o contraseña del servicio incorrectas.
Revisa Login en servicios; el motivo llega por *status.
En IRCnet no se identifica
IRCnet no tiene NickServ; xBNC no envía la contraseña a propósito.
Usa SASL de sasl.ircnet.com en la red, o nada.
El nick termina en «_»
El nick estaba ocupado al conectar.
Activa Recuperar nick o escribe /nick tu-nick.
xBNC cambia mi nick todo el rato
Recuperar nick vuelve al nick guardado.
/msg *status setnick nuevo, o desactiva el módulo.
Veo «�» en lugar de tildes
Raro: xBNC convierte Latin-1 a UTF-8.
Si quien no ve bien las tildes es la otra persona, añade su canal o nick en Codificación.
El certificado de cliente no identifica
La huella no está en tu cuenta, la red no usa TLS o no reconectaste.
Añade la huella (SHA-512 en Libera, SHA-1 en OFTC) y reconecta.
Módulos y plugins
Síntoma
Causa probable
Solución
/msg *plugin solo muestra «Uso: MSG <apodo> <mensaje>»
Es la ayuda de tu cliente: no escribiste nada después del nombre.
/msg *poll ayuda.
Un alias no hace nada
Tu cliente no envía comandos que no conoce.
/quote nombre argumentos.
No me da op el módulo autoop
No tienes op, la máscara no coincide o la otra persona no responde al desafío.
Comprueba con /whois el usuario@host real y la clave.
Un plugin con acceso web no responde
Sus sitios no están aprobados.
Un superadministrador los aprueba en Plugins («Sitios a los que puede conectarse»).
Un plugin se ha detenido en una red
Falló cinco veces seguidas.
Mira el registro de Plugins; desactívalo y actívalo de nuevo tras corregirlo.
No aparecen ficheros de registro
El módulo log está desactivado o no hay permisos en /var/lib/xbnc/logs.
Actívalo; el registro del servicio avisa si no puede escribir.
Las tareas programadas salen a otra hora
Tu zona horaria no está puesta.
Tu cuenta → Zona horaria.
Panel y servicio
Síntoma
Causa probable
Solución
El panel no carga (conexión rechazada)
Falta [web] o escucha en 127.0.0.1.
sudo xbncd info; para rehacer la configuración, sudo ./scripts/install.sh --reconfigure.
«La página estaba desactualizada»
Token CSRF antiguo tras volver a entrar.
Recarga la página.
«Petición rechazada por venir de otro sitio»
public_url no coincide con la dirección del navegador.
Corrige public_url o entra por esa dirección.
Tras entrar vuelves a la pantalla de login
Cookie Secure en HTTP sin TLS.
Usa HTTPS o localhost (o secure_cookies = false solo en pruebas).
Todos quedan bloqueados detrás de Nginx
Sin trust_proxy, todo viene de 127.0.0.1.
trust_proxy = true y la cabecera X-Forwarded-For en Nginx.
Envíalo con lo que hiciste, lo que esperabas, lo que pasó, la red, el cliente, la hora aproximada, la versión (/msg *status version) y los avisos del registro (sudo journalctl -u xbnc --since "1 hour ago" | grep -E "WARN|ERROR"; revísalo antes de enviarlo).
Si el proyecto está en GitHub, usa la plantilla de incidencia «Informe de prueba». Los fallos de seguridad no se publican: sigue SECURITY.md.
Límites conocidos
Undernet y QuakeNet no ofrecen TLS: la conexión con esas redes va sin cifrar.
FiSH es criptografía antigua (Blowfish, sin integridad): protege de los servidores de la red, no sustituye al cifrado de extremo a extremo. Los mensajes descifrados se guardan en tu historial como el resto.
Algunos clientes no envían comandos que no conocen: los alias necesitan /quote.
DCC entre un cliente solo IPv6 y otro solo IPv4 no se traduce.
La búsqueda por trozos de palabra solo se usa si no hay coincidencias por palabra completa.
Si el proceso se corta de golpe, se pueden repetir unos segundos de historial.
En un idioma añadido como fichero, las respuestas de los plugins oficiales salen en inglés (hablan inglés y español).
En esta beta la instalación es desde el código; no hay paquetes .deb ni .rpm publicados.
Pendiente de comprobar en redes reales: login con X, NickServ, Q y AuthServ; invitación y desbaneo; plugins con acceso web; FiSH con clientes reales; instalador con systemd real y Let's Encrypt; DCC entre redes; SASL EXTERNAL; importador con instalaciones reales.