|
|
||
|---|---|---|
| completions | ||
| man/man1 | ||
| packaging | ||
| src | ||
| BLOG_POST.md | ||
| Cargo.lock | ||
| Cargo.toml | ||
| LICENSE | ||
| QUICKSTART.md | ||
| README.md | ||
| STATUS.md | ||
| TODO.md | ||
| build.sh | ||
README.md
nirc-rs
a multi-protocol terminal chat client written in Rust.
nirc-rs is a TUI (terminal user interface) chat client that places you in control of your data and communications. Inspired by 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
zeroizecrate 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
.partialthen 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
.soshared libraries vialibloading - 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)
# 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
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:
nirc-rs
Connect to an IRC server:
/connect irc irc.libera.chat:6697
/join #rust
That's it — you're chatting. See 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:
[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
[[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
[[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
VERSIONrequests - 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 —
/rawor/quoteto 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.
// src/plugins/mod.rs defines the trait:
pub trait Plugin {
fn name(&self) -> &str;
fn on_message(&self, msg: &ChatMessage) -> Option<ChatMessage>;
fn on_command(&self, cmd: &str, args: &[&str]) -> Option<String>;
}
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/fileor/xfer <protocol> <nick> [filepath] - Receive:
/acceptfile <transfer-id> ~/downloads/ - Monitor:
/transfersto list active transfers; footer ticker shows real-time progress - Resume: interrupted transfers resume from the last byte written (offset-based,
.partialfiles) - 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+.
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 for the full text.
Copyright (C) 2026 dcos.net