Halloy is an open-source IRC client written in Rust, with the iced GUI library. It aims to provide a simple and fast client for Mac, Windows, and Linux platforms.


Halloy is free and open source. You can find the source code as well as report issues and feature requests on GitHub.

Installing Halloy

💡 To get the latest nightly version of Halloy, you can build from source.

Pre-built binaries

Download pre-built binaries from GitHub page.

Packaging status

Packaging status


The following third party repositories are available for macOS


brew install --cask halloy 


sudo port install halloy


The following third party repositories are available for Linux







winget install squidowl.halloy

Build from source

Clone the Halloy GitHub repository into a directory of your choice and build with cargo.


# Clone the repository
git clone https://github.com/squidowl/halloy.git

cd halloy

# Build and run
cargo build --release
cargo run --release

Get in touch

Join #halloy on libera.chat if you have questions, looking for help or just want to say hello. For feature requests or reporting issues, please open a ticket on GitHub.



To edit configuration parameters, create a config.toml file located in your configuration directory:

  • Windows: %AppData%\halloy
  • Mac: ~/Library/Application Support/halloy or $HOME/.config/halloy
  • Linux: $XDG_CONFIG_HOME or $HOME/.config

💡 You can easily open the config file directory from command bar in Halloy

Example config for connecting to Libera:

nickname = "halloy-user"
server = "irc.libera.chat"
channels = ["#halloy"]

enabled = true


[buffer] Section

[buffer.nickname] Section

color = "unique" | "solid"
brackets = { left = "<string>", right = "<string>" }
colorNickname colors. Can be "unique" or "solid"."unique"
bracketsBrackets for nicknames.{ left = "", right = "" }

[buffer.timestamp] Section

format = "<string>"
brackets = { left = "<string>", right = "<string>" }
formatFormat expected is strftime. To disable, simply pass empty string ""."%R"
bracketsBrackets for nicknames{ left = "", right = "" }

[buffer.text_input] Section

visibility = "always" | "focused"
visibilityText input visibility. Can be "always" or "focused"."always"

[buffer.channel] Section

[buffer.channel.nicklist] Section

enabled = true | false
position = "left" | "right"
color = "unique" | "solid"
enabledControl if nicklist should be shown or nottrue
positionNicklist position. Can be "left" or "right"."right"
colorNickname colors. Can be "unique" or "solid"."unique"

[buffer.channel.topic] Section

enabled = true | false
max_lines = <integer>
enabledControl if topic banner should be shown or notfalse
max_linesAmount of visible lines before you have to scroll in topic banner.2

[buffer.server_messages] Section

enabled = true | false
smart = <integer>
username_format = "full" | "short"
enabled = true | false
smart = <integer>
username_format = "full" | "short"
enabled = true | false
smart = <integer>
username_format = "full" | "short"
enabled = true | false
enabledControl if the server message should appear in buffers or nottrue
smartOnly show server message if the user has sent a message in the given time interval (seconds) prior to the server message.not set
username_formatAdjust how the username should look. Can be "full" (shows the longest username available (nickname, username and hostname) or "short" (only shows nickname)."full"

[buffer.internal_messages] Section

enabled = true | false
smart = <integer>
enabled = true | false
smart = <integer>
enabledControl if the internal message should appear in buffers or nottrue
smartOnly show internal message if received within the given time duration (seconds).not set

File Transfer

[file_transfer] Section

save_directory = "<string>"
passive = true | false
timeout = <integer>
save_directoryDirectory opened when prompted to save a file$HOME/Downloads
passiveIf true, act as the "client" for the transfer. Requires the remote user act as the servertrue
timeoutTime (in seconds) to wait before timing out a transfer waiting to be accepted300

[file_transfer.server] Section

This section is required if passive = false. One side of the file transfer must operate as the "server", who the other user connects with to establish a connection.

public_address = "<string>"
bind_address = "<string>"
bind_port_first = <integer>
bind_port_last = <integer>
public_addressAddress advertised to the remote user to connect to""
bind_addressAddress to bind to when accepting connections""
bind_port_firstFirst port in port range to bind to""
bind_port_lastLast port in port range to bind to""


[font] Section

family = "<string>"
size = <integer>
familyMonospaced font family to use""1
sizeFont size.13

Iosevka Term is provided by the application, and used by default.


[keyboard] Section

move_up = "<string>"
move_down = "<string>"
move_left = "<string>"
move_right = "<string>"
close_buffer = "<string>"
maximize_buffer = "<string>"
restore_buffer = "<string>"
cycle_next_buffer = "<string>"
cycle_previous_buffer = "<string>"
toggle_nick_list = "<string>"
command_bar = "<string>"
refresh_configuration = "<string>"
KeyDescriptionDefault MacOSDefault Other
move_upMoves focus up + alt +
move_downMoves focus down + alt +
move_leftMoves focus left + alt +
move_rightMoves focus right + alt +
close_bufferClose focused buffer + wctrl + w
maximize_bufferMaximize focused buffer + ctrl +
restore_bufferRestore focused buffer + ctrl +
cycle_next_bufferCycle to next bufferctrl + tabctrl + tab
cycle_previous_bufferCycle to previous bufferctrl + shift + tabctrl + shift + tab
toggle_nick_listToggle nick list + + mctrl + alt + m
command_barToggle command bar + kctrl + k
reload_configurationRefresh configuration file + rctrl + r

Example for vim like movement

move_up = "alt+k"
move_down = "alt+j"
move_left = "alt+h"
move_right = "alt+l"


[notifications] Section

enabled = true | false
sound = "<string>"
mute = true | false

enabled = true | false
sound = "<string>"
mute = true | false

enabled = true | false
sound = "<string>"
mute = true | false

enabled = true | false
sound = "<string>"
mute = true | false

enabled = true | false
sound = "<string>"
mute = true | false

enabledControl if notification should be enabled or not.false
muteControl if the notification should have sound or not.false
soundThe sound which plays when the notification is fired."Submarine" (macOS1), "Mail" (Windows2), "message-new-instant" (Linux3)

The following sounds are available for macOS:

  • "Basso"
  • "Blow"
  • "Bottle"
  • "Frog"
  • "Funk"
  • "Glass"
  • "Hero"
  • "Morse"
  • "Ping"
  • "Pop"
  • "Purr"
  • "Sosumi"
  • "Submarine"
  • "Tink"

The following sounds are avaiable for Windows:

  • "Default"
  • "IM"
  • "Mail"
  • "Reminder"
  • "SMS"

The following sounds are avaiable for Linux:

  • "message-new-instant"


[proxy] Section


type = "socks5"
host = "<string>"
port = <integer>
typeProxy type. http and socks5 are currently supported.""
hostProxy host to connect to""
portProxy port to connect on""
usernameProxy username, optional""
passwordProxy password, optional""

Scale factor

scale_factor = <float>
scale_factor1Control the scale factor of the entire application.1.02

scale_factor is a root key, so it must be placed before any section.


Limited between 0.1 and 3.0.


[servers] Section


nickname = "halloy-user"
server = "irc.libera.chat"
channels = ["#halloy"]
nicknameThe client's nickname.""
nick_passwordThe client's NICKSERV password.""
nick_password_fileAlternatively read nick_password from the file at the given path.""
nick_identify_syntaxThe server's NICKSERV IDENTIFY syntax. Can be "nick-password" or "password-nick".""
alt_nicksAlternative nicknames for the client, if the default is taken.[""]
usernameThe client's username.""
realnameThe client's real name.""
serverThe server to connect to.""
portThe port to connect on.6697
passwordThe password to connect to the server.""
password_fileAlternatively read password from the file at the given path.""
channelsA list of channels to join on connection.[""]
channel_keysA mapping of channel names to keys for join-on-connect.{}
ping_timeThe amount of inactivity in seconds before the client will ping the server.180
ping_timeoutThe amount of time in seconds for a client to reconnect due to no ping response.20
reconnect_delayThe amount of time in seconds before attempting to reconnect to the server when disconnected.10
should_ghostWhether the client should use NickServ GHOST to reclaim its primary nickname if it is in use.false
ghost_sequenceThe command(s) that should be sent to NickServ to recover a nickname.["GHOST"]
umodesUser modestring to set on connect. Example: "+RB-x".""
use_tlsWhether or not to use TLS. Clients will automatically panic if this is enabled without TLS support.true
dangerously_accept_invalid_certsOn true, all certificate validations are skipped. Defaults to false.false
root_cert_pathThe path to the root TLS certificate for this server in PEM format.""
on_connectCommands which are executed once connected. Example. ["/msg NickServ IDENTIFY foo bar"].[]
who_poll_intervalWHO poll interval (in seconds) for servers without away-notify.1801
who_retry_intervalWHO retry interval (in seconds) for servers without away-notify.101

Limited between 5 and 3600 seconds.

[servers.sasl] Section


username = "<string>"
password = "<string>"
usernameThe account name used for authentication.""
passwordThe password associated with the account used for authentication.""
password_fileAlternatively read password from the file at the given path.""


cert = "<string>"
key = "<string>"

💡 External SASL auth uses a PEM encoded X509 certificate. Reference.

certThe path to PEM encoded X509 user certificate for external auth""
keyThe path to PEM encoded PKCS#8 private key for external auth (optional)""


default_action = "new-pane" | "replace-pane"
width = <integer>
default_actionAction when selecting buffers in the sidebar. Can be "new-pane" or "replace-pane"."new-pane"
widthSpecify sidebar width in pixels.120

[sidebar.buttons] Section

file_transfer = true | false
command_bar = true | false
file_transferFile transfer button in sidebar.true
command_barCommand bar button in sidebar.true


theme = "<string>"
theme1Name of the theme to use2""3

theme is a root key, so it must be placed before any section.


Name of theme file inside themes folder.


Using Ferra by default.

Custom themes

To create a custom theme for Halloy, simply place a theme file (with a .toml extension) inside the themes folder within the configuration directory.

💡 The configuration direction can be found here.

A custom theme is structured as follows.

name = "<string>"

background = "<string>"
text = "<string>"
action = "<string>"
accent = "<string>"
alert = "<string>"
error = "<string>"
info = "<string>"
success = "<string>"
nameName of the theme to use
paletteColors expect a hex color string. Eg: "#2b292d"


This is a list of community created themes for Halloy.

💡 Have a great theme you want to share? Open a pull-request here.

Zenburn - Save

Material - Save

Oceanic Next - Save

Tomorrow Night - Save

Monokai Pro - Save

One Dark Pro - Save

Nord - Save

Solarized (Dark) - Save

Harmony (Dark) - Save

Dracula - Save

Kanagawa - Save

Boo berry - Save

Catpuccin Frappe - Save

Catpuccin Macchiato - Save

Catpuccin Mocha - Save


tooltips = true | false
tooltips1Control if tooltips are displayed or not.true

tooltips is a root key, so it must be placed before any section.

Getting started

To get started with Halloy, you need to connect to at least one IRC server. The template config file has been set up with the Libera server. However, there are many other servers available: OFTC, Undernet, EFnet, QuakeNet and many more. Halloy can connect to multiple servers at the same time.

Once connected to a server, you can join channels. This can be done automatically from the config file or manually using the join command: /join #channel1. To find channels, you can either use the list command: /list, or browse for channels online.

💡 Configuration in Halloy happens through a config.toml file. See Configuration.

Here are a few useful IRC commands for a new user2

/join/join #halloyJoin a new channel
/part/part #halloyPart a channel
/nick/nick halloyisgreatChange your nickname
/whois nickname/whois halloyisgreatDisplays information of nickname requested
/list *keyword*/list *linux*List channels. Keyword is optional

Channel names always start with a # symbol and do not contain spaces.


Find more commands here.

Migrating from YAML

Halloy recently switched configuration file format from YAML to TOML (PR-278) This page will help you migrate your old config.yaml to a new config.toml file.

The basic structure of a TOML file consists of key-value pairs, where keys are strings. There are no nested indentations like YAML, which makes it easier to read and write. Consider the following old YAML config with of two servers in Halloy:

    nickname: foobar
    server: irc.libera.chat
    nickname: barbaz
    server: underworld2.no.quakenet.org
    port: 6667
    use_tls: true

This now looks the following in TOML

nickname = "foobar"
server = "irc.libera.chat"

nickname = "barbaz"
server = "underworld2.no.quakenet.org"
port = 6667
use_tls = true

💡 You can convert YAML to TOML using a converter tool like this one. Just note that a few keys and values have be renamed during the conversion process.

To migrate, and ensure everything is working, make sure to read through the Configuration section of this book. Here, every configuration option is documented using TOML.

If you still have trouble or find any issues, make sure to get in touch.

Connect with Soju

To connect with a soju1 bouncer, the configuration below can be used as a template. Simply change so it fits your credentials.

nickname = "casperstorm"
username = "<your-username>/irc.libera.chat@desktop"
server = "irc.squidowl.org"
password = "<your-password>"

Connect with ZNC

To connect with a ZNC1 bouncer, the configuration below can be used as a template. Simply change so it fits your credentials.

nickname = "<znc-user>/<znc-network>"
server = "znc.example.com"
password = "<your-password>"

Portable mode

To enable portable mode for Halloy, simply place the config.toml file in the same directory as the running executable.

├── Halloy.app
└── config.toml

Multiple servers

Creating multiple [servers] sections lets you connect to multiple servers.
All configuration options can be found here.

nickname = "halloy-user"
server = "irc.libera.chat"
channels = ["#halloy"]

nickname = "halloy-user"
server = "irc.oftc.net"
channels = ["#asahi-dev"]

Storing passwords in a File

If you need to commit your configuration file to a public repository, you can keep your passwords in a separate file for security. Below is an example of using a file for nickname password for NICKSERV.

💡 Avoid adding extra lines in the password file, as they will be treated as part of the password.

nickname = "foobar"
nick_password_file = "~./config/halloy/password"
server = "irc.libera.chat"
channels = ["#halloy"]