# nirc-rs a multi-protocol terminal chat client written in Rust. ![nirc-rs-screenshot](./nirc-rs.png) nirc-rs is a TUI (terminal user interface) chat client that places you in control of your data and communications. Inspired by [naim](https://github.com/naim-devel/naim), it consolidates eight chat protocols into a single terminal interface — no web browsers, no Electron, no JavaScript. **Version:** 0.9.0 **License:** GPL-3.0-or-later **Author:** Jeremy Anderson - dcos.net **Repository:** https://git.dcos.net/dcosnet/nirc-rs --- ## Features ### Protocols | Protocol | Status | Transport | Notes | |----------|--------|-----------|-------| | **IRC** | ✅ Tested & Working | TLS (6697) / plaintext | SASL PLAIN, CTCP, ISUPPORT, operator commands | | **ADC/DC++** | ✅ Tested & Working | TLS / plaintext | Hub search, file transfers, varnish guard pipeline | | **Matrix** | 🔧 Implemented, Untested | HTTPS (matrix-sdk 0.18) | Megolm E2EE, SQLite crypto store, room sync | | **Discord** | 🔧 Implemented, Untested | WebSocket (wss) | Gateway events, REST API | | **Stout** | 🔧 Implemented, Untested | WebSocket (wss) | Revolt-compatible fork | | **Spacebar** | 🔧 Implemented, Untested | WebSocket (wss) | Revolt fork | | **Nerimity** | 🔧 Implemented, Untested | WebSocket (wss) | Custom platform | | **BitChat** | 🔧 Implemented, Untested | libp2p (TCP) | P2P, mDNS discovery, gossipsub | ### UI - **Tab-based interface** — channels, queries, and server statuses each get their own tab - **Window list** (F4 toggle) — side panel showing all open windows with protocol badges and unread indicators - **F1 dropdown menu** — QBasic 4.5 / aptitude-style menu bar for discoverable access to all commands - **Transfer ticker** — footer bar showing active transfer progress (speed, ETA, percentage) - **HTML markup rendering** — messages containing HTML are rendered appropriately - **Theming** — four built-in themes (default, solarized, gruvbox, dracula) with per-color overrides - **Timestamps** — color-coded by protocol (IRC=yellow, Matrix=magenta, ADC=blue, BitChat=green) - **Status bar** — connection info, local IP, window count, unread count - **Bracket paste support** — pasted text is inserted at cursor position - **Scrollback** — per-tab message history (configurable, default 5000 lines) ### Security - **TLS everywhere** — rustls with webpki-roots (no system OpenSSL dependency) - **SASL authentication** — PLAIN mechanism for IRC (EXTERNAL with client certs on roadmap) - **Encrypted identity vault** — AES-256-GCM with Argon2id key derivation (64 MiB memory, 3 iterations), keys zeroed from RAM on lock - **ADC guard pipeline** — varnish-style security: rate limits, IP validation, SSRF prevention, path traversal blocking - **Zeroize** — sensitive key material uses the `zeroize` crate to securely clear memory ### File Transfers - **yamux-multiplexed streams** — multiple transfers over a single TCP connection - **SHA-256 verification** — computed in-flight during transfer, not post-hoc - **Resume support** — offset-based, writes to `.partial` then atomically renames on completion - **256 KiB I/O buffers** — minimizes syscalls, maximizes throughput - **2 GiB size cap** — prevents resource exhaustion - **Cancellation** — via `tokio::CancellationToken` - **Transfer ticker** — real-time speed and ETA in the footer ### Extensibility - **Plugin system** — dynamically loaded `.so` shared libraries via `libloading` - **Variables & aliases** — user-defined variables (`/set`), command aliases (`/alias`) - **Custom keybindings** — map any key to a slash-command in `config.toml` - **Per-channel logging** — naim-compatible format, 10 MiB rotation with 3 copies --- ## Installation ### From source (recommended) ```sh # Prerequisites: Rust 1.75+ (via rustup), a C compiler git clone https://git.dcos.net/dcosnet/nirc-rs.git cd nirc-rs cargo build --release cp target/release/nirc-rs ~/.local/bin/ ``` ### Via cargo install ```sh cargo install nirc-rs ``` ### System packages Arch Linux (AUR), Debian/Ubuntu `.deb`, RPM `.spec`, and Nix flake are available in the `packaging/` directory. See `packaging/` for details. --- ## Quick Start Run `nirc-rs` with no arguments. It creates `~/.nirc/config.toml` with sensible defaults and opens the TUI: ```sh nirc-rs ``` Connect to an IRC server: ``` /connect irc irc.libera.chat:6697 /join #rust ``` That's it — you're chatting. See [QUICKSTART.md](QUICKSTART.md) for a more detailed walkthrough. --- ## Key Bindings | Key | Action | |-----|--------| | `Enter` | Send message or command | | `Backspace` | Delete character before cursor (UTF-8 safe) | | `Delete` | Delete character after cursor | | `Left` / `Right` | Move cursor in input line | | `Home` / `End` | Previous / next window | | `Insert` | Scroll chat to bottom (unlock auto-scroll) | | `Ctrl-N` | Jump to next window with unread messages | | `Ctrl-B` | Jump back to previously active window | | `Ctrl-P` | Previous buffer | | `Ctrl-A` | Next active buffer | | `Ctrl-Z` | Cycle highlight (rotate through highlight words) | | `Ctrl-W` | Delete word before cursor | | `Ctrl-K` | Delete from cursor to end of line | | `Ctrl-A` / `Ctrl-E` | Cursor to start / end of line | | `Ctrl-U` | Clear entire input line | | `Ctrl-L` | Force terminal redraw | | `Ctrl-C` | Quit nirc-rs | | `Tab` | Tab-complete (nick/command), or cycle to next window if input is empty | | `F1` | Toggle dropdown menu bar | | `F4` | Cycle window list visibility (Auto → Visible → Hidden) | | `PgUp` / `PgDn` | Scroll chat history (PgUp locks view; new messages won't auto-scroll until you PgDn back) | | `Up` / `Down` | Navigate command history | --- ## Configuration Reference Configuration lives at `~/.nirc/config.toml` (auto-created on first run). A full example: ```toml [global] nickname = "yournick" realname = "Your Name" log_level = "info" # error | warn | info | debug | trace auto_connect = ["libera"] # server names to connect on startup # ─── Servers ──────────────────────────────────────────────────────────── [[servers]] name = "libera" protocol = "irc" # irc | matrix | adc | discord | stout | spacebar | nerimity | bitchat address = "irc.libera.chat:6697" tls = true auto_join = ["#rust", "#nirc"] auto_reconnect = true [servers.extra] sasl_mechanism = "plain" # plain | external (external not yet implemented) sasl_username = "your-account" sasl_password = "your-password" [[servers]] name = "adc-hub" protocol = "adc" address = "hub.example.com:2780" tls = false auto_join = [] # ─── Appearance ───────────────────────────────────────────────────────── [appearance] theme = "default" # default | solarized | gruvbox | dracula show_timestamps = true clock_24h = true max_scrollback = 5000 [appearance.custom_colors] # Override individual theme colors: # accent = "#FF79C6" # error_fg = "#FF5555" # bg = "#1E1E2E" # ─── Notifications ────────────────────────────────────────────────────── [notifications] desktop_enabled = true bell_enabled = true debounce_ms = 2000 extra_highlight_words = ["urgent", "ops"] # ─── File Transfers ───────────────────────────────────────────────────── [transfers] download_dir = "~/downloads" # defaults to system downloads dir buffer_size = 262144 # 256 KiB in bytes max_concurrent = 3 auto_accept_from = [] # nicks that auto-accept files from # ─── Custom Keybindings ───────────────────────────────────────────────── [keybindings] # "F5" = "/connect irc libera" # "Ctrl-G" = "/jump" ``` ### Matrix Server Configuration ```toml [[servers]] name = "matrix" protocol = "matrix" address = "https://matrix.org" auto_join = ["#nirc:matrix.org"] [servers.extra] user_id = "@alice:matrix.org" password = "hunter2" device_id = "NIRC-DEVICE-1" device_name = "nirc-rs" # access_token = "syt_abc..." # for session resume without password ``` ### BitChat P2P Configuration ```toml [[servers]] name = "bitchat" protocol = "bitchat" address = "/ip4/0.0.0.0/tcp/9394" [servers.extra] bootstrap = "/ip4/1.2.3.4/tcp/9394/p2p/QmSomePeerId" ``` --- ## Protocol Details ### IRC (Tested & Working) Full IRCv3 support with: - **TLS** via rustls (port 6697, auto-detected) - **SASL PLAIN** authentication - **CTCP** — auto-responds to `VERSION` requests - **ISUPPORT** — server capability negotiation - **Operator commands** — `/oper`, `/kill`, `/kline`, `/unkline`, `/wallops` - **Channel ops** — `/op`, `/deop`, `/kick`, `/invite`, `/mode` - **User commands** — `/whois`, `/who`, `/names`, `/topic`, `/away`, `/me`, `/notice` - **Raw IRC** — `/raw` or `/quote` to send arbitrary IRC lines - **UTF-8 safe** backspace and cursor movement ### ADC/DC++ (Tested & Working) - **Hub connection** — HSUP, HSID, INF handshake sequence - **Hub search** — SCH command for searching hub file listings - **File transfers** — yamux-multiplexed, SHA-256 verified, resumable - **Security pipeline** — rate limiting, IP validation (reject private/link-local IPs), SSRF prevention, path traversal blocking - **BINF** — user info broadcast with I4/U4 support for incoming C-C connections --- ## Other Protocols The following protocols are **fully implemented** in the codebase but have **not been tested against live servers yet**. They are on the TODO list for upcoming releases: - **Matrix** — E2EE via megolm, SQLite-backed crypto store, dedicated OS thread for non-Send crypto types, full room/member/event handling - **Discord** — Gateway WebSocket, REST API integration - **Stout** — Revolt-compatible fork via REST + WebSocket - **Spacebar** — Revolt fork via REST + WebSocket - **Nerimity** — Custom platform via REST + WebSocket - **BitChat** — P2P via libp2p (TCP, mDNS discovery, gossipsub, noise protocol, request-response) --- ## Plugin System nirc-rs supports dynamically loaded plugins via `.so` shared libraries (Linux/macOS). Plugins implement the `Plugin` trait and are loaded at runtime via `libloading`. ```rust // src/plugins/mod.rs defines the trait: pub trait Plugin { fn name(&self) -> &str; fn on_message(&self, msg: &ChatMessage) -> Option; fn on_command(&self, cmd: &str, args: &[&str]) -> Option; } ``` Place compiled `.so` files in `~/.nirc/plugins/` and they will be loaded automatically. --- ## File Transfers File transfers are yamux-multiplexed over existing connections: - **Send:** `/sendfile nick /path/to/file` or `/xfer [filepath]` - **Receive:** `/acceptfile ~/downloads/` - **Monitor:** `/transfers` to list active transfers; footer ticker shows real-time progress - **Resume:** interrupted transfers resume from the last byte written (offset-based, `.partial` files) - **Verify:** SHA-256 hash verified in-flight during transfer - **Size limit:** 2 GiB maximum per file Wire protocol: 4-byte magic (`NAIM`), 2-byte version, flags, 8-byte file size, 8-byte resume offset, filename, optional 64-byte SHA-256 — all little-endian. --- ## Contributing Contributions are welcome. The project uses Rust edition 2021 and requires Rust 1.75+. ```sh git clone https://git.dcos.net/dcosnet/nirc-rs.git cd nirc-rs cargo test # run all tests cargo build --release # production build ``` For bug reports, feature requests, or protocol testing, visit https://git.dcos.net/dcosnet/nirc-rs. --- ## License nirc-rs is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. See [LICENSE](LICENSE) for the full text. Copyright (C) 2026 dcos.net