WebSocket to IRC gateway for Simple IRC Client.
pnpm install
pnpm startGateway runs on ws://localhost:8667/irc
pnpm test # Run tests once
pnpm run test:watch # Run tests in watch mode
pnpm run lint # TypeScript + ESLint checksdocker build -t simple-irc-gateway .
docker run -d \
--name sic-gateway \
--restart unless-stopped \
-p 8667:8667 \
-p 113:8113 \
-e PORT=8667 \
-e HOST=0.0.0.0 \
-e IDENTD_ENABLED=true \
-e IDENTD_PORT=8113 \
simple-irc-gatewayThe container runs as the non-root node user. Identd binds to a high port (8113) inside the container, mapped to host port 113 via -p 113:8113 β no CAP_NET_BIND_SERVICE needed.
sudo apt install caddy/etc/caddy/Caddyfile:
irc.yourdomain.com {
reverse_proxy /irc localhost:8667
}
sudo systemctl reload caddyserver {
listen 443 ssl http2;
server_name irc.yourdomain.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location /irc {
proxy_pass http://127.0.0.1:8667;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 86400;
}
}CI deploys via a non-privileged deploy user with scoped sudo β it never logs in as
root. Provision the server once as root:
# from a checkout of this repo on the server (or after scp-ing the deploy/ dir)
sudo DEPLOY_PUBKEY="$(cat deploy_key.pub)" bash deploy/bootstrap.shbootstrap.sh creates the deploy user, installs its SSH key, installs podman + caddy,
creates /opt/sic-gateway (deploy-owned), and writes a scoped /etc/sudoers.d/sic-deploy
granting only the binaries deploys need (systemctl, podman, nft, apt-get, tee,
the firewall script). The private half of DEPLOY_PUBKEY is the GitHub Actions SSH_KEY
secret. Re-run bootstrap.sh (as root) after changing the package set or sudoers scope.
| Variable | Default | Description |
|---|---|---|
PORT |
8667 | Server port |
HOST |
0.0.0.0 | Bind address |
PATH_PREFIX |
/irc | WebSocket path |
WEBIRC_PASSWORD |
- | WEBIRC password (optional) |
WEBIRC_GATEWAY |
gateway | WEBIRC gateway name |
ALLOWED_SERVERS |
- | Comma-separated list (e.g., irc.libera.chat:6697) |
IDENTD_ENABLED |
false | Enable identd (RFC 1413) server |
IDENTD_PORT |
113 | Identd listen port |
IDENTD_TIMEOUT |
30 | Identd connection timeout (seconds) |
IRC servers query port 113 on connecting clients to verify identity. Without identd, users appear with an unverified ident (prefixed with ~). Enabling the built-in identd server lets the gateway respond with the correct username.
Set IDENTD_ENABLED=true and configure the port. When running in Docker/Podman, use a high port inside the container (e.g. IDENTD_PORT=8113) and map it to host port 113 with -p 113:8113. This avoids needing any special capabilities.
WebSocket clients can pass a custom ident username via the ident query parameter:
ws://gateway:8667/webirc?host=irc.example.com&port=6697&tls=true&ident=myuser
If not provided, the gateway derives a username from the client's IP address.
To forward real client IPs to IRC servers:
- Request WEBIRC access from the IRC network
- Set
WEBIRC_PASSWORDenvironment variable - Gateway sends client IP to IRC server
Update your frontend to connect to the gateway:
const WS_URL = 'wss://irc.yourdomain.com/irc';