nirc-rs/STATUS.md

257 lines
14 KiB
Markdown
Executable File

# nirc-rs 0.10.1 — Status Report
**Version:** 0.10.1
**Release date:** 2026-07
**Codename:** nirc-rs
**License:** GPL-3.0-or-later
**Rust edition:** 2021
**Source:** https://git.dcos.net/dcosnet/nirc-rs
---
## Protocol Status
| Protocol | Implementation | Testing | Notes |
|----------|---------------|---------|-------|
| **IRC** | ✅ Complete | ✅ Tested & Working | TLS, SASL PLAIN, CTCP, ISUPPORT, oper commands, IRCv3 away-notify |
| **ADC/DC++** | ✅ Complete | ✅ Tested & Working | Hub connect, search, file transfers, guard pipeline |
| **Matrix** | ✅ Complete | ❌ Untested | Megolm E2EE, SQLite store, dedicated OS thread |
| **Discord** | ✅ Complete | ❌ Untested | Gateway WebSocket, REST API |
| **Stout** | ✅ Complete | ❌ Untested | REST + WebSocket (Revolt fork) |
| **Spacebar** | ✅ Complete | ❌ Untested | REST + WebSocket (Revolt fork) |
| **Nerimity** | ✅ Complete | ❌ Untested | REST + WebSocket (custom platform) |
| **BitChat** | ✅ Complete | ❌ Untested | libp2p P2P, mDNS, gossipsub, noise |
---
## What's New in 0.10.1
### IRC Protocol Fixes
- **CTCP spec compliance** — Outgoing CTCP requests (`/ctcp`, `/ctcp version`,
etc.) are now sent as `PRIVMSG` per the IRCv3 CTCP spec. The previous code
used `NOTICE`, which strict servers ignore (NOTICE must never trigger an
automated reply per RFC 1459). This was the root cause of `/ctcp <nick>
VERSION` being silently ignored by some networks.
- **Self-targeted CTCP visible** — Running `/ctcp mynick VERSION` (targeting
yourself) now displays the CTCP request in the relevant tab. The previous
code skipped the entire CTCP block when the sender was the local nick,
silently swallowing self-targeted queries.
- **`/away` properly tracked** — The connection state now tracks `is_away`
and `away_message`. The `/away [msg]` command optimistically marks the
local state and posts a confirmation notice; the server confirms via
`RPL_NOWAWAY` (306) / `RPL_UNAWAY` (305), both of which now have explicit
handlers instead of falling through to the generic numeric dump.
- **IRCv3 `away-notify` handler** — When the `away-notify` capability is
active, the server forwards other users' AWAY commands as `:nick AWAY
:msg`. These now have a dedicated handler that caches the away reason
per nick in `state.nick_away` and posts a notice. Previously the
capability was negotiated but the messages fell into the "Unhandled
command" notice path.
- **`/who` hardening** — `RPL_WHOREPLY` (352) now correctly splits the
trailing field into hopcount and realname (per RFC 1459), displays the
here/away flag (`H`/`G`) from the flags field, marks self-entries with
`(you)`, and has an explicit `RPL_ENDOFWHO` (315) terminator handler
instead of dumping as a raw numeric. This addresses the historical
`/who <self>` crash.
- **`/me` local echo** — `/me` actions are now echoed locally in the
active tab immediately, so the user sees their action even on servers
that don't echo own PRIVMSGs (bouncers, mock servers, etc.). The
server's echo (if any) lands with `is_own=true` and is naturally
deduplicated by the user's perception.
- **`/notice` local echo** — Same local-echo treatment for `/notice`, so
sent notices are visible in the target tab immediately.
### Input Rate Throttle + Line Guard
- **Per-send line cap** — A single input submission is now capped at 4
lines (configurable via `MAX_LINES_PER_SEND`). Pasting a 50-line file
no longer dumps 50 lines into the channel — only the first 4 are sent
and a notice explains the truncation.
- **Sliding-window rate limit** — At most 8 outgoing lines per 3 seconds
(configurable via `MAX_LINES_PER_WINDOW` / `WINDOW_SECS`). A stuck
Enter key or rapid-fire paste that would otherwise flood the channel
is rejected after the cap, with a single warning notice per burst
(subsequent rejections in the same burst are silent to avoid flooding
the user's own tab with throttle notices).
- **New module:** `src/core/throttle.rs``InputThrottle` struct with
`check()` returning `Allow { lines_sent }` or `Reject { lines_sent,
dropped, reason }`. 7 unit tests covering paste truncation, rate
window sliding, burst-warning suppression, and CRLF/empty-input edge
cases.
### Channel Rotation Revert
- **Static insertion-order cycling** — `next_tab_by_priority` and
`prev_tab_by_priority` in `core/app.rs` now walk tabs in the order
they were created, instead of ranking them into Unread/Conversed/Inert
tiers sorted by recent activity. The activity-based reordering made
Ctrl-N feel non-deterministic: the same keypress could land on a
different tab each time depending on which channel received a message
most recently. The new plain round-robin preserves muscle memory —
"Ctrl-N three times gets me to #sourcemage" works every time. Tabs
that fail `is_cyclable()` (unjoined IRC channels, hidden server tabs)
are still skipped.
- **Tests updated** — The four tests that asserted priority-tier
behavior now document the new insertion-order behavior.
### URL Detection + Inline Photo + External Video
- **URL detection in chat view** — Message bodies are now scanned for
URLs (`http://`, `https://`, `ftp://`, `www.` prefixes). Detected URLs
are rendered underlined and in cyan so they're visually distinct.
Trailing sentence punctuation (`.`, `,`, `;`, `!`, `?`) is stripped
from the URL itself. URLs wrapped in `<...>` or `(...)` are extracted
cleanly without the surrounding punctuation.
- **New module:** `src/tui/media.rs``detect_urls()`,
`classify_url()` (image / video / other by file extension),
`open_external()` (xdg-open / open / start with http-scheme safety
check), `detect_image_protocol()` (Kitty / iTerm2 / Sixel / None),
`try_render_inline_image()` (stub returning `Unsupported` for now —
graceful fallback to text placeholder), `image_placeholder_text()`.
20+ unit tests covering URL extraction edge cases, media
classification, and external-launch safety.
- **New commands:** `/url <url>`, `/video <url>`, `/image <url>`
open URLs externally, launch video in the OS default player, or
attempt inline image rendering (falls back to `/url` if the terminal
doesn't support inline images).
- **Inline photo support is graceful** — If the terminal doesn't support
an inline-image protocol, the user sees a text placeholder
`[image: <url>]` and can still open the image externally via `/url`.
No garbage escape sequences are dumped on unsupported terminals.
### Top-Right Bandwidth Monitor
- **Live transfer stats in the top status bar** — The static `nirc`
label in the top-right corner is now replaced with a live bandwidth
monitor when transfers are active. Format: `↓1.2MiB/s ↑0.5MiB/s
file.zip 45%`. Shows aggregate download/upload rates and the
most-active file's progress percentage. Falls back to `nirc` when
no transfers are active.
- **`TransferManager::summary()`** — New method that samples each
active transfer's `bytes_transferred` against the previous frame's
sample to compute instantaneous bytes/sec. Tracks the top transfer
by bandwidth (most active file). Rate-sampling state is cleaned up
when transfers complete.
- **New struct:** `TransferSummary` — Compact bandwidth + active-file
summary with `is_empty()`, `fmt_dl_rate()`, `fmt_ul_rate()` helpers.
### Line Wrapping Fix
- **Word-wrap for long lines** — Extremely long IRC lines (and any
message body) now wrap to the next display line instead of being
truncated at the right margin. The previous code dropped all text
past the visible width; the new `wrap_text()` helper breaks on word
boundaries when possible and falls back to hard character breaks for
words longer than the available width (e.g. long URLs). 5 unit tests
covering word-boundary wrapping, long-URL hard-breaking, and
empty-input edge cases.
- **Fixes:** "extremely long lines from IRC doesn't wrap lines and
text is lost if resolution is small" — long messages are now fully
readable even on 80-column terminals.
---
## What's New in 0.10.0
### IRC Hardening
- **SASL EXTERNAL with client certificates** — Full TLS client certificate support via the identity vault. Loads combined PEM files (cert+key) or separate cert/key files. Configurable per-server via `sasl_client_cert` in the server entry's `extra` map.
- **MONITOR (watch list) support** — IRCv3 MONITOR capability for tracking online/offline status of specific users. New `/watch + <nick>`, `/watch - <nick>`, `/watch l`, `/watch c`, `/watch s` commands. Handles RPL_MONONLINE (730), RPL_MONOFFLINE (731), RPL_MONLIST (732), RPL_ENDOFMONLIST (733), RPL_MONLISTFULL (734).
- **User mode tracking** — Local tracking of user modes (+i, +w, etc.) via MODE handler and RPL_UMODEIS (221). Modes displayed in status bar.
- **DCC SEND/ACCEPT framework** — Parsed incoming DCC SEND CTCP messages with IP/port/size extraction. Outbound DCC SEND with listening socket and local IP discovery. CTCP DCC ACCEPT handling for resume support. 8 new unit tests for DCC parsing.
### Security & Configuration
- **Config file hot-reload** — Background task polls `~/.nirc/config.toml` mtime every 5 seconds. On change, reloads config, updates palette/theme, and syncs nickname changes without dropping active connections.
- **Custom keybindings from config** — Users can remap keys in `config.toml` via `[keybindings]` section. Supports compound modifiers (`ctrl-alt-x`), F-keys, and all crossterm key names. Custom bindings checked before hardcoded defaults.
- **Plugin management commands** — `/plugins`, `/plugin-load <name>`, `/plugin-unload <name>`, `/plugin-enable <name>`, `/plugin-disable <name>` now wired up in the command dispatcher.
- **Scrollback persistence to disk** — Per-tab message history saved as JSONL files in `~/.nirc/history/`. Loaded on startup, saved every 30 seconds and on clean exit. Respects `max_scrollback` limit.
- **Terminal title (XTITLE)** — OSC 0 escape sequences set the terminal window title to `nirc - <protocol> <channel> (N unread)`. Updated on tab switch. Reset to "nirc" on exit.
### ADC Protocol
- **Proper CID generation** — Replaced `NIRC{SID}` placeholder with SHA-256 (first 24 bytes) + RFC 4648 Base32 encoding. Produces spec-compliant 39-character CIDs.
- **I4/U4 BINF fields** — ADC client-client connections now include proper I4 (IPv4) and U4 (UDP4 port) in BINF messages for inbound peer connections.
### Code Quality & Hardening
- **`#![deny(unsafe_code)]`** at crate root. `#[allow(unsafe_code)]` scoped to only the plugin loader (`libloading`) and yamux integration that require it.
- **Atomic config save** — `save_config()` now uses hard-link + rename strategy for atomic file replacement on POSIX.
- **Vault key zeroization** — Verified `derive_key()` in the vault wipes the stack copy of derived keys via `zeroize`.
- **Lightgray color fix** — `lightgray`/`lightgrey` now correctly maps to `Color::Indexed(252)` (75% brightness) instead of `Color::Gray` (40%).
- **Removed unused dependencies** — Dropped `irc`, `nucleo-matcher`, `nom`, and `bytes` crates from Cargo.toml.
---
## What's New in 0.9.0
### Keybindings
- **`Ctrl-P`** — jump to previous buffer in the window list
- **`Ctrl-A`** — jump to next active (connected) buffer
- **`Ctrl-Z`** — cycle through highlight words for notification filtering
- **`Insert`** — scroll chat view to the bottom and re-enable auto-scroll
- **`Delete`** — now deletes the character after the cursor only (no longer cycles connections; use `Home`/`End` for window switching)
### F1 Dropdown Menu
The F1 key now opens a **QBasic 4.5 / aptitude-style dropdown menu bar** at the top of the screen. Navigate with arrow keys, select with Enter, dismiss with Esc or F1. All slash-commands are accessible through the menu for discoverability.
### Transfer Ticker
A **transfer progress ticker** now appears in the footer bar, showing real-time transfer speed and ETA for all active file transfers — no need to toggle the transfer panel.
### UI Fixes
- **`/nick` UI update** — changing your nickname now immediately updates all tab titles and status bar displays
- **Local IP footer** — the status bar now shows your local network IP address
- **Window list badges** — protocol badges and unread indicators in the winlist sidebar
### Under the Hood
- Ratatui 0.29 migration
- Crossterm 0.28
- libp2p 0.54 (with `macros` feature, not the removed `swarm-derive`)
- matrix-sdk 0.18
- tokio-rustls 0.26 with webpki-roots
---
## Known Issues
1. **Matrix crypto types are not `Send`** — the matrix-sdk crypto types require running on a dedicated OS thread with a single-threaded tokio runtime. This is handled correctly but adds architectural complexity.
2. **4 protocol stubs** — Discord, Stout, Spacebar, and Nerimity have full type definitions but their `run_*()` functions log "not yet implemented" and return. These contribute dead-code warnings.
3. **DCC transfers need async I/O integration** — DCC SEND/ACCEPT parsing and socket setup is implemented, but the actual file data transfer loop needs to be wired into the transfer engine's async I/O pipeline.
---
## Build & Test Status
- **Build:** `cargo build --release` succeeds
- **Tests:** All 313 tests pass
- **Binary size (release):** ~834 MB debug, optimized release binary significantly smaller
- **Rust version required:** 1.75+
---
## Roadmap / Next Steps
### 0.10.x (Stabilization)
- [ ] Test Matrix protocol against matrix.org
- [ ] Test Discord protocol
- [ ] Test remaining protocols (Stout, Spacebar, Nerimity, BitChat)
- [ ] Integrate transfer widget into main draw loop as split view
### 1.0.0 (Release)
- [ ] All 8 protocols tested and working
- [ ] Full plugin API stability guarantee
- [ ] Man page and completion scripts finalized
- [ ] Packaging for major distributions
- [ ] Scrollback persistence to disk
- [ ] SASL SCRAM-SHA-256 support
- [ ] Matrix SSO login