Skip to content
rollecodePublic

About

A small, modern IRC client with self-hosted history next to irssi

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

Lobby

Lobby

Build Latest release Rust Tauri Linux macOS

A small, modern IRC client for people who keep irssi running.
One calm reading column, your whole history, and a window happy to live in the corner of your screen.

Lobby in a narrow window: backlog with photos, a video and link cards, a reply that gets reactions, marking read, switching channels and the slash command menu

Features

  • Your history, every time: lobby-server sits next to irssi and keeps every line, so Lobby opens with everything you missed
  • Grouped, readable chat: one photo and name per person, their lines stacked under it, link previews, pictures, videos and audio inline
  • Reactions and replies: sent as plain IRC (nick: 😂, nick: text) and shown as reactions and quotes in Lobby
  • People: a photo, a colour and any number of nicks per person, matched by pattern or ident and host
  • Who is here: the most active faces in the header open the channel's list, ops first, then by who spoke last; pick several to give op or voice in one go
  • Unread that stays put: a red New line, jump buttons, and nothing marked read until you say so
  • Attachments: paste, drop or pick a file, and its address goes into your message, through a file host of your own
  • Notifications: desktop notifications with the sender's photo for mentions and private messages, quiet for muted channels
  • irssi muscle memory: /wc, /whois, /names and the rest, with a / menu, Alt+1…9 and Alt+A
  • Same everywhere: people, channel pictures, read positions and settings live on your server and follow you between devices
  • Self-hosted, no bouncer: irssi stays in charge, Lobby reads through irssiproxy and talks to your own server over SSH

How it works

irssi already sees every message, but irssiproxy only hands over what happens after you connect. lobby-server fills that gap: it runs on the same machine as irssi, connects to irssiproxy like any client, and writes every line to SQLite. Lobby connects to lobby-server, loads the history, then follows the live chat.

flowchart TB
    nets[(IRC networks)] --- irssi
    subgraph host [Your shell host]
        irssi --- proxy[irssiproxy]
        proxy --> server[lobby-server<br/>SQLite history]
    end
    server -->|WebSocket over SSH| app[Lobby<br/>Linux and macOS]
Loading

Lobby can also connect straight to irssiproxy or any IRC server. That works without a server of your own, but only shows what arrived while Lobby was open.

Mode Lobby connects to History
Server lobby-server Everything recorded, also while Lobby was closed
Direct irssiproxy or an IRC server Only what arrived while Lobby was running

Installation

The app

Download the latest build from Releases:

System File
Debian, Ubuntu .deb, install with sudo apt install ./Lobby_*.deb
Other Linux .AppImage, make it executable and run it
macOS (Apple silicon) .dmg

The macOS build is not signed. After moving Lobby to Applications, run xattr -dr com.apple.quarantine /Applications/Lobby.app once, or macOS refuses to open it.

Building from source

You need Rust from rustup.rs, plus:

System Command
Arch sudo pacman -S --needed base-devel webkit2gtk-4.1
Debian, Ubuntu sudo apt install build-essential libwebkit2gtk-4.1-dev
Fedora sudo dnf install webkit2gtk4.1-devel gcc
macOS xcode-select --install
git clone https://github.com/rollecode/lobby
cd lobby
cargo build --release -p lobby
./target/release/lobby

To update: git pull and build again.

iPhone

There is no App Store build yet. On a Mac with Xcode and an Apple developer account, with the iPhone connected over USB:

rustup target add aarch64-apple-ios
npm install
export APPLE_DEVELOPMENT_TEAM=YOUR_TEAM_ID
npm run tauri ios init
sh tools/ios-icons.sh
npm run tauri ios build -- --export-method debugging
xcrun devicectl device install app --device YOUR_IPHONE_UDID src-tauri/gen/apple/build/arm64/Lobby.ipa

xcrun devicectl list devices shows the UDID. On first start Lobby opens its connection setup; use Through SSH and add the key line it shows on your shell host. Notifications and the badge work while Lobby is open or recently in the background; the iPhone has no background connection yet.

lobby-server

On the machine where irssi runs:

git clone https://github.com/rollecode/lobby
cd lobby
./server/install.sh

The installer builds lobby-server, writes a config and a random token, and sets up a systemd user service. server/README.md walks through turning on irssi's proxy, the config, starting the service, updating and removing it.

Important

lobby-server only listens on 127.0.0.1. Lobby reaches it through SSH with your usual key, so nothing on the server is opened to the internet. For an address of its own, put a reverse proxy with TLS in front, as shown in the server README.

Configuration

On first start Lobby opens its connection setup. You can come back to it any time from the gear, Set up the connection. The gear is in the channel list, which opens when you click the channel name in the header. The same three choices are on every device:

Choice What you fill in History
Through SSH you@your-shell-host, the SSH port if not 22, and the lobby-server token Everything lobby-server recorded
Server address wss://lobby.example.org and the token Everything lobby-server recorded
Straight to IRC Your nick and one row per IRC server or irssiproxy port Only while Lobby is open

The token is in ~/.config/lobby-server/token on the server.

With lobby-server, the setup also lists the server's networks. Adding or removing one there changes lobby-server's own config and takes effect right away, without restarting anything.

SSH. Lobby has SSH built in, so it also works where no ssh program exists, like the iPhone. It signs in with a key of its own, and on a computer also with your usual keys in ~/.ssh and your SSH agent. The setup shows Lobby's key as a line to add to ~/.ssh/authorized_keys on the shell host:

restrict,port-forwarding,permitopen="127.0.0.1:7667" ssh-ed25519 AAAA… lobby

That key can reach lobby-server and nothing else: no shell, no other ports. The server's host key is checked against ~/.ssh/known_hosts, or remembered on first connect.

The setup is saved in a plain file you can also edit by hand, and Lobby reconnects as soon as you save:

  • Linux: ~/.config/lobby/config.toml
  • macOS: ~/Library/Application Support/lobby/config.toml
[server]
ssh = "you@your-shell-host"
token_file = "~/.config/lobby/server.token"

Using Lobby

Keys What they do
Ctrl+K (Cmd+K on a Mac), or click the channel name Find and switch channels
Alt+1 … Alt+9 Jump to a channel
Alt+Left, Alt+Right Previous and next channel
Alt+A, or the unread counter Next channel with unread, mentions first
Alt+U, or the unread pill Jump to the first unread message in this channel
Ctrl+Enter (Cmd+Enter on a Mac) Mark this channel read
Tab Complete a nick
Up in an empty input Bring back the last message

Every shortcut can be changed under Settings, Keyboard shortcuts.

Reactions and replies. Point at a message for your favourite emojis, more emojis, reply and a menu. On a phone, hold a message instead: it lifts out of the chat with your favourite emojis, more emojis and reply above it. IRC has no reactions, so a reaction is sent as nick: 😂, and Lobby shows such lines from anyone as a reaction under that person's message. A reply goes out as nick: text and shows the message it answers.

Unread. A red New line marks where unread messages start. Nothing is marked read on its own: click the checkmark at the end of the New line, the Mark read button at the top, or press Ctrl+Enter (Cmd+Enter on a Mac).

People. Click a name or photo in the chat, or open the gear and People. Kaisla also covers Kaisla_ and Kaisla-, Kaisla* covers any ending, and kaisla@*.example.net matches by ident and host. The colour follows the photo unless you pick one. Click the channel icon in the header to give a channel its own picture or colour, or to mute it. A muted channel adds nothing to unread counts or jumps, except when someone mentions you. /mute and /unmute work too, and so does the bell beside each channel in the channel list.

Who is here. The faces at the right of the header are the people talking now, in the last hour. Click them for everyone in the channel: ops first, then who is talking now, earlier today and away, fading the longer they stay quiet. Voiced people have a small mint mark. The button at the top of the list keeps it beside the chat instead, and the faces then show or hide it. Click anywhere on a row to pick it, the photo turns into a checkbox. With people picked, Give voice and Give op go out as one MODE +vvv line, or a few when the server takes fewer per line. Right-click a row for the same and to message or edit the person; with one person picked, the bar under the list has Message and Edit too. Mode changes show in the chat like joins and quits.

Commands. Type / for the menu. /me, /msg, /query, /join, /part, /wc, /topic, /names, /whois, /nick, /away, /notice, /invite, /kick, /op, /deop, /voice, /devoice, /clear, /say and /quote work as in irssi. Anything else is sent as is. /quit is off, because through irssiproxy it would disconnect irssi from the network.

Emoji. :D, :), <3 and friends turn into emoji as you type. A colon at the start of a word opens the emoji picker, and :joy or a Finnish word like :nauru lists matching emoji, most used first. Both can be turned off in Settings.

Attachments. Paste an image into the message box, drop files on the window, or use the paperclip. The file goes to your file host and its address is put into your message, so everyone on IRC can open it. Set the host's address under Settings, File host for attachments.

Running a file host for attachments

Lobby uploads the way Halloy and soju-style hosts expect, so one host serves all of them:

  • POST the file as the raw request body to the host's address
  • Content-Type is the file's type, Content-Disposition carries its name (attachment; filename*=UTF-8''name.png)
  • The host answers 201 Created with the file's address in Location

tools/filehost/upload.php is a complete host in one PHP file. It keeps only images, PDFs and plain text, and needs a secret key in the address. Put it on any web server with PHP, set KEY, BASE_URL and STORE at the top, and allow uploads of 20 MB. With nginx:

server {
    server_name files.example.org;
    root /var/www/files;
    client_max_body_size 20m;

    location = /upload.php {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php-fpm.sock;
    }

    location ^~ /u/ {
        add_header X-Content-Type-Options nosniff;
    }
}

Then set https://files.example.org/upload.php?k=YOUR_KEY in Lobby's Settings. The address is stored on your lobby-server like other settings, so every device uses it.

Importing a folder of photos

If you already have photos named after nicks (kaisla.png, Kaisla_.png), import them all at once. Files with the same picture, or nicks that differ only by _, - or `, become one person. Nicks you have already set up are left alone. Round avatars are squared to fill the frame.

pip install Pillow websocket-client
ssh -N -L 17667:127.0.0.1:7667 you@your-shell-host &
python3 tools/import_photos.py ~/Pictures/irc ws://127.0.0.1:17667 ~/.config/lobby/server.token --dry-run

Drop --dry-run to import. Add --skip file.png … at the end to leave files out.

Development

flowchart TB
    core[core<br/>IRC, history, the hub]
    core --> server[server<br/>lobby-server]
    core --> app[src-tauri<br/>the app]
    ui[ui<br/>HTML, CSS, JS] --> app
Loading
  • core keeps IRC connections running, turns lines into events and stores them. The app in direct mode and lobby-server both use it.
  • server is lobby-server: the hub behind a WebSocket.
  • src-tauri is the desktop app. In server mode it talks to lobby-server, in direct mode it runs the hub itself.
  • ui is the window. It does not know which mode is in use.
npm ci
npm test         # JS and Rust unit tests
npm run check    # Biome, rustfmt and clippy, as CI runs them
npm run format   # Biome and rustfmt rewrite the code

Every push runs the same checks on GitHub Actions, plus the macOS app build, the iPhone lint and the core tests on the iPhone simulator.

Changes waiting for a release collect under [Unreleased] in CHANGELOG.md. Releases are automatic: when that heading becomes a version heading on main, GitHub Actions tags it, publishes the release notes and attaches the Linux and macOS builds.

The app and lobby-server protocol

JSON over a WebSocket. The app sends hello with the token first, the server answers welcome with the current state and then streams events. Requests carry an id that the reply repeats. The types live in core/src/protocol.rs.

app    {"type":"hello","token":"..."}
server {"type":"welcome","snapshot":{"networks":[...],"channels":[...],"people":[...],"settings":{...}}}
server {"type":"event","event":{"type":"line","net":"IRCnet","buffer":"#rust","nick":"kaisla","text":"hei",...}}
app    {"type":"history","id":1,"net":"IRCnet","buffer":"#rust","before":null}
server {"type":"reply","id":1,"data":[...],"error":null}
app    {"type":"send","id":2,"net":"IRCnet","buffer":"#rust","text":"moi"}
app    {"type":"save_person","id":3,"person":{"id":0,"name":"Kaisla","nicks":["kaisla"],...}}
app    {"type":"set_setting","id":4,"key":"read","value":"{...}"}

About

A small, modern IRC client with self-hosted history next to irssi

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages