242 lines
11 KiB
Markdown
Executable File
242 lines
11 KiB
Markdown
Executable File
# nirc-rs 0.10.2 — IRC fixes, the input throttle, and saying goodbye (for now) to BitChat
|
|
|
|
Two patch batches landed back-to-back: 0.10.1 fixed a pile of
|
|
long-standing IRC bugs and added the safety nets I'd been meaning to
|
|
build for months, and 0.10.2 removes the BitChat protocol module
|
|
entirely. The BitChat removal deserves its own discussion — it's not a
|
|
technical decision, it's a legal one, and I want to be transparent
|
|
about why.
|
|
|
|
## 0.10.2: BitChat withdrawal
|
|
|
|
The BitChat project — Jack Dorsey's recently-announced P2P messaging
|
|
protocol — is the subject of active litigation in the Indian courts.
|
|
The legal status of the protocol specification, the reference
|
|
implementation, and downstream reimplementations such as the one
|
|
previously shipped in nirc-rs is currently unclear. Specifically:
|
|
|
|
- Court filings have not yet established whether the BitChat protocol
|
|
itself is encumbered by intellectual-property claims, or whether only
|
|
the reference client is affected.
|
|
- There is live ambiguity about whether shipping a third-party
|
|
implementation of the protocol exposes downstream distributors to
|
|
secondary liability.
|
|
- The reference implementation's license terms have changed during the
|
|
dispute, and it's not yet clear which license applies to derivative
|
|
works that were created before the change.
|
|
|
|
Pending clarity on these points, I've removed the BitChat module from
|
|
nirc-rs entirely. The risk of shipping an encumbered protocol backend
|
|
outweighs the benefit of offering P2P chat that users can obtain from
|
|
other sources in the meantime.
|
|
|
|
**What was removed:** `src/protocols/bitchat.rs` (the entire protocol
|
|
backend), the `ProtocolType::BitChat` enum variant, the three
|
|
`/bitchat …` commands, the F1 menu entry, and — significantly — the
|
|
`libp2p` Cargo dependency, which BitChat was the only consumer of.
|
|
Dropping libp2p noticeably trims the dependency tree and compile time.
|
|
The Noise primitive in `src/engine/crypto.rs` uses `x25519-dalek` +
|
|
`aes-gcm` directly (not libp2p), so ADC's encrypted client-client
|
|
connections keep working.
|
|
|
|
**What this means for users:** Existing config entries with
|
|
`protocol = "bitchat"` are silently ignored at load time. Old history
|
|
files tagged `P2P:<name>` are silently reassigned to the Status tab.
|
|
No data is lost; IRC / Matrix / ADC / Discord / Stout / Spacebar /
|
|
Nerimity configs and history are unaffected. The full rationale and
|
|
re-evaluation criteria are in `NOTICES.md` — I'll restore the module
|
|
when the courts case reaches a final judgement, the reference
|
|
implementation's license is unambiguous, and at least one independent
|
|
legal opinion clears clean-room reimplementations.
|
|
|
|
If you want P2P-style direct messaging in the meantime, ADC's
|
|
client-client connections offer direct file transfer and direct
|
|
messages over a hub-and-spoke topology rather than full mesh P2P.
|
|
|
|
## 0.10.1: The IRC fixes
|
|
|
|
A user audit turned up a stack of real bugs in the IRC backend, plus a
|
|
couple of "this should exist" features I'd been deferring. The fixes:
|
|
|
|
### CTCP was broken in two ways
|
|
|
|
Outgoing CTCP requests (`/ctcp <nick> VERSION` and friends) were being
|
|
sent as `NOTICE` instead of `PRIVMSG`. Per the IRCv3 CTCP spec, requests
|
|
must be `PRIVMSG` — `NOTICE` is only for replies, and strict servers
|
|
ignore NOTICE-based CTCP because RFC 1459 says NOTICE must never trigger
|
|
an automated reply. This was why `/ctcp` queries were silently ignored
|
|
by some networks.
|
|
|
|
The second bug was worse: if you `/ctcp`'d yourself (e.g.
|
|
`/ctcp mynick VERSION` to test your own client), the entire CTCP block
|
|
was skipped because the sender matched the local nick. The echo from
|
|
the server was silently swallowed. Now self-targeted CTCP queries are
|
|
visible, and the auto-reply loop guard is preserved.
|
|
|
|
### `/away` was a stub
|
|
|
|
The `away-notify` IRCv3 capability was in the requested-caps list, but
|
|
the incoming `AWAY` command from other users had no handler — it fell
|
|
into the "Unhandled command" notice path. Worse, we didn't even track
|
|
our own away state: there was no `is_away` field, and `RPL_NOWAWAY`
|
|
(306) / `RPL_UNAWAY` (305) fell through to the generic raw-numeric
|
|
dump. So the `away-notify` capability was negotiated and then nothing
|
|
was done with it. Textbook stub.
|
|
|
|
Fixed: `ConnState` now tracks `is_away` and `away_message`; the `/away`
|
|
command optimistically marks the local state and posts a confirmation
|
|
notice; 305/306 have explicit handlers; and incoming `AWAY` messages
|
|
from other users are cached per-nick in `state.nick_away` and surfaced
|
|
as notices.
|
|
|
|
### `/who` crashed on self
|
|
|
|
I never managed to reproduce this from static reading alone, but the
|
|
`RPL_WHOREPLY` (352) handler was mis-splitting the trailing field per
|
|
RFC 1459 (the trailing is `<hopcount> <real name>`, not just the real
|
|
name), and there was no explicit `RPL_ENDOFWHO` (315) terminator. Now
|
|
the handler correctly splits hopcount from realname, displays the
|
|
`H`/`G` (here/away) flag from the flags field, marks self-entries with
|
|
`(you)`, and has a dedicated 315 handler.
|
|
|
|
### `/me` and `/notice` got local echo
|
|
|
|
Many servers — especially bouncers, mock servers, and servers with
|
|
`echo-message` disabled — don't echo your own `PRIVMSG`s back to you.
|
|
So you'd type `/me dances` and see nothing. Now `/me` and `/notice`
|
|
echo locally in the active tab immediately. The server's echo (if any)
|
|
lands with `is_own=true` and is naturally deduplicated by the user's
|
|
perception — you don't see two copies, you just see your action.
|
|
|
|
## 0.10.1: The input throttle
|
|
|
|
The trigger for this was a user reporting they'd accidentally spammed a
|
|
room with 20+ lines. The fix is two-layered:
|
|
|
|
1. **Per-send line cap.** A single input submission is capped at 4
|
|
lines (`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.
|
|
|
|
2. **Sliding-window rate limit.** At most 8 outgoing lines per 3
|
|
seconds (`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).
|
|
|
|
Both limits are conservative — a human typing normally will never hit
|
|
them. They exist purely as a safety net for accidents. The
|
|
`InputThrottle` struct is in `src/core/throttle.rs` with 7 unit tests
|
|
covering paste truncation, rate window sliding, burst-warning
|
|
suppression, and CRLF/empty-input edge cases. Currently the limits are
|
|
hardcoded; a future release will expose them in `config.toml` under a
|
|
`[throttle]` section.
|
|
|
|
## 0.10.1: Channel rotation reverted
|
|
|
|
This is a revert of a previous "smart" feature. The old `Ctrl-N`
|
|
cycled tabs in priority-tier order: Unread > Conversed > Inert, sorted
|
|
within tier by recent activity. It matched naim's semantics on paper.
|
|
In practice, 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.
|
|
Users couldn't build muscle memory for "Ctrl-N three times gets me to
|
|
#sourcemage".
|
|
|
|
The new behaviour is a plain round-robin through the tab list in
|
|
insertion order. Tabs that fail `is_cyclable()` (unjoined IRC channels,
|
|
hidden server tabs) are still skipped, but the relative order of the
|
|
remaining tabs is preserved. Boring, predictable, and your fingers
|
|
will thank you.
|
|
|
|
## 0.10.1: URL detection + media framework
|
|
|
|
There was previously zero URL detection in the TUI. Now message bodies
|
|
are scanned for URLs (`http://`, `https://`, `ftp://`, `www.` prefixes),
|
|
and detected URLs are rendered underlined in cyan so they're visually
|
|
distinct. Trailing sentence punctuation is stripped from the URL itself,
|
|
and URLs wrapped in `<...>` or `(...)` are extracted cleanly without
|
|
the surrounding punctuation.
|
|
|
|
On top of that, three new commands:
|
|
|
|
- **`/url <url>`** — open a URL in the OS default browser (xdg-open /
|
|
open / start). Refuses non-http/https/ftp schemes for safety.
|
|
- **`/video <url>`** — launch a video URL in the OS default player.
|
|
The OS picks the right player based on the URL's file extension or
|
|
mime-type handler.
|
|
- **`/image <url>`** — attempt inline image rendering. If the terminal
|
|
supports Kitty graphics / iTerm2 inline images / Sixel, the image is
|
|
rendered in place. If not, falls back to `/url` behaviour.
|
|
|
|
The inline image rendering itself is a stub for now —
|
|
`try_render_inline_image()` returns `Unsupported` and the caller falls
|
|
back gracefully. The graceful-fallback path (text placeholder +
|
|
external open) is wired up, so when I implement the real Kitty/iTerm2/
|
|
Sixel emission it's a pure feature add with no risk to existing
|
|
functionality. The terminal-protocol detection is in place
|
|
(`detect_image_protocol()`), so the framework is ready.
|
|
|
|
## 0.10.1: Top-right bandwidth monitor
|
|
|
|
The top-right corner of the TUI used to say `nirc`. Static, useless.
|
|
Now, when transfers are active, it's replaced with a live bandwidth
|
|
monitor:
|
|
|
|
```
|
|
↓1.2MiB/s ↑0.5MiB/s file.zip 45%
|
|
```
|
|
|
|
Aggregate download/upload rates, plus the most-active file's progress
|
|
percentage. Falls back to `nirc` when no transfers are active. The
|
|
rates are computed by `TransferManager::summary()`, which samples each
|
|
active transfer's `bytes_transferred` against the previous frame's
|
|
sample — so it's a real instantaneous rate, not a moving average.
|
|
|
|
## 0.10.1: Line wrapping
|
|
|
|
The bug report was "extremely long lines from IRC doesn't wrap lines
|
|
and text is lost if resolution is small". The fix is a `wrap_text()`
|
|
helper that breaks on word boundaries when possible and falls back to
|
|
hard character breaks for words longer than the available width (e.g.
|
|
long URLs). The `ChatView::render` method now wraps each body line to
|
|
fit the available width instead of truncating at the right margin. Long
|
|
messages are now fully readable even on 80-column terminals.
|
|
|
|
## What's next
|
|
|
|
Per user direction, Matrix production hardening and the Stout/Spacebar/
|
|
Nerimity full builds are deferred to dedicated follow-up sessions.
|
|
Matrix is the most complex of the group, and the three Revolt-family
|
|
stubs each need a full protocol implementation comparable in scope to
|
|
the Discord backend.
|
|
|
|
The two natural follow-ups from 0.10.1/0.10.2:
|
|
|
|
1. **Real inline image rendering** — the stub is in place, the
|
|
graceful fallback works, and the framework is ready. Just need the
|
|
Kitty/iTerm2/Sixel emission code.
|
|
2. **Configurable throttle limits** — currently hardcoded; expose them
|
|
in `config.toml` under a `[throttle]` section.
|
|
|
|
And the obvious one: **re-evaluate BitChat** once the India courts
|
|
case settles.
|
|
|
|
## Build it
|
|
|
|
```sh
|
|
git clone https://git.dcos.net/dcosnet/nirc-rs.git
|
|
cd nirc-rs
|
|
cargo build --release
|
|
./target/release/nirc-rs
|
|
```
|
|
|
|
One binary, no runtime dependencies beyond your terminal emulator.
|
|
Config lives at `~/.nirc/config.toml` and is created automatically on
|
|
first run.
|
|
|
|
If you want to help test a protocol, write a plugin, or contribute a
|
|
patch, the repository is at git.dcos.net/dcosnet/nirc-rs. It's
|
|
GPL-3.0-or-later, and contributions are welcome.
|