runar is a keyboard-first, statically-linkable Linux file manager written from scratch in pure Rust. removed bitchat for now
This commit is contained in:
parent
7b5d863e8f
commit
00dcf44335
239
BLOG_POST.md
239
BLOG_POST.md
|
|
@ -1,44 +1,229 @@
|
|||
# nirc-rs 0.9.0
|
||||
# nirc-rs 0.10.2 — IRC fixes, the input throttle, and saying goodbye (for now) to BitChat
|
||||
|
||||
I just released nirc-rs 0.9.0 — a multi-protocol terminal chat client I wrote in Rust. It runs entirely in your terminal and puts eight chat protocols behind a single interface. No web browsers, no Electron, no JavaScript runtime. Just a binary and a terminal.
|
||||
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.
|
||||
|
||||
## Why I Built It
|
||||
## 0.10.2: BitChat withdrawal
|
||||
|
||||
nirc-rs descends from naim, the terminal AIM/ICQ/IRC client from the late 1990s. naim had a simple idea: one terminal window, all your chat networks, zero graphical dependencies. I liked that idea, but the world moved on. naim's codebase stayed stuck in C89, its protocol support stopped at IRC and the now-defunct AIM/ICQ, and it couldn't handle TLS, E2EE, or modern protocols like Matrix and Discord.
|
||||
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:
|
||||
|
||||
I wanted that same experience back — a TUI client that treats every protocol as a first-class citizen — but built on modern foundations. Rust for memory safety and async I/O. ratatui for the terminal UI. rustls for TLS without OpenSSL. libp2p for peer-to-peer. The result is roughly 10,000 lines across 27 source files.
|
||||
- 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.
|
||||
|
||||
## Architecture
|
||||
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.
|
||||
|
||||
nirc-rs is structured around a multi-protocol dispatcher. Each protocol (IRC, ADC/DC++, Matrix, Discord, Stout, Spacebar, Nerimity, BitChat) implements a common trait and feeds messages into a unified ChatMessage type. The TUI layer doesn't care which protocol a message came from — it renders it the same way, with color-coded timestamps and protocol badges.
|
||||
**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.
|
||||
|
||||
The async runtime is tokio with multi-threaded scheduling. File transfers run over yamux-multiplexed streams with 256 KiB buffers and in-flight SHA-256 verification. The identity vault uses AES-256-GCM encryption with Argon2id key derivation. Matrix's megolm E2EE runs on a dedicated OS thread because the matrix-sdk crypto types aren't Send — a necessary compromise that I handle transparently.
|
||||
**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.
|
||||
|
||||
## What's New in 0.9.0
|
||||
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.
|
||||
|
||||
This release focused on navigation and discoverability. I added Ctrl-P (previous buffer), Ctrl-A (next active buffer), and Ctrl-Z (highlight word cycling) for faster window management. The Insert key now scrolls to the bottom of chat and re-enables auto-scroll.
|
||||
## 0.10.1: The IRC fixes
|
||||
|
||||
The biggest UI change is the F1 dropdown menu. It provides a visual, navigable command tree. If you can't remember whether it's /whois or /wi, press F1 and find it. This replaced the old debug console binding, which I'm reassigning in a future patch.
|
||||
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:
|
||||
|
||||
The transfer ticker now shows real-time speed and ETA in the footer bar, so you don't need to toggle a separate panel to see how your file transfers are progressing. I also fixed /nick to update all tab titles immediately and added the local IP address to the status bar.
|
||||
### CTCP was broken in two ways
|
||||
|
||||
## The Tested Frontier: IRC and ADC/DC++
|
||||
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.
|
||||
|
||||
Two protocols are battle-tested in 0.9.0: IRC and ADC/DC++.
|
||||
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.
|
||||
|
||||
IRC has full TLS support via rustls, SASL PLAIN authentication, CTCP auto-response, ISUPPORT negotiation, and operator commands. It connects to Libera, OFTC, and other networks without issue. ADC/DC++ connects to hubs, performs the HSUP/HSID/INF handshake, supports hub search, and handles file transfers with the full yamux-multiplexed pipeline. ADC also has a varnish-style security guard pipeline that does rate limiting, IP validation, SSRF prevention, and path traversal blocking.
|
||||
### `/away` was a stub
|
||||
|
||||
## The Untested Frontier
|
||||
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.
|
||||
|
||||
Six protocols are fully implemented but haven't been tested against live servers yet: Matrix (with megolm E2EE via matrix-sdk 0.18), Discord (Gateway WebSocket), Stout and Spacebar (Revolt-compatible forks), Nerimity (a custom platform), and BitChat (P2P over libp2p with mDNS discovery and gossipsub).
|
||||
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.
|
||||
|
||||
These aren't stubs — they're complete protocol handlers with connection management, message parsing, event dispatch, and TUI integration. They just need to be pointed at a real server to verify the wire protocol matches reality. That's the top priority for the next release cycle.
|
||||
### `/who` crashed on self
|
||||
|
||||
## Security
|
||||
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.
|
||||
|
||||
nirc-rs never phones home. There's no telemetry, no analytics, no update checker. The identity vault encrypts credentials with AES-256-GCM and Argon2id (64 MiB memory, 3 iterations), and keys are zeroed from RAM on lock via the zeroize crate. ADC connections go through a guard pipeline that rejects private IPs, blocks path traversal, and prevents SSRF. TLS is handled by rustls with the webpki-roots CA bundle — no system OpenSSL needed.
|
||||
### `/me` and `/notice` got local echo
|
||||
|
||||
## Build It
|
||||
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
|
||||
|
|
@ -47,10 +232,10 @@ 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.
|
||||
One binary, no runtime dependencies beyond your terminal emulator.
|
||||
Config lives at `~/.nirc/config.toml` and is created automatically on
|
||||
first run.
|
||||
|
||||
## What's Next
|
||||
|
||||
The roadmap for 0.10.0 and beyond is straightforward: test the six untested protocols, fix ADC CID generation to use proper Base32/Tiger hashes, integrate the transfer widget into the main draw loop, and add IRC SASL EXTERNAL with client certificates. I'm aiming for a 1.0.0 release once all eight protocols are verified against live servers and the plugin API has a stability guarantee.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
|
|
|||
165
CHANGES.md
165
CHANGES.md
|
|
@ -1,39 +1,145 @@
|
|||
# nirc-rs 0.10.1 — Patch Summary
|
||||
# nirc-rs — Patch Summary
|
||||
|
||||
This patch addresses the user-reported issues with nirc-rs 0.10.0 and adds
|
||||
several requested features. The changes are organized into five phases.
|
||||
This file summarizes the patch batches applied to nirc-rs, newest first.
|
||||
|
||||
## Files Changed
|
||||
---
|
||||
|
||||
## 0.10.2 — BitChat Module Withdrawn
|
||||
|
||||
**Date:** 2026-07-29
|
||||
|
||||
The BitChat protocol module and all of its integrations have been removed
|
||||
from the codebase. See `NOTICES.md` for the full rationale.
|
||||
|
||||
### Why
|
||||
|
||||
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 is currently unclear. Pending clarity,
|
||||
we have removed the module entirely rather than ship an encumbered
|
||||
protocol backend.
|
||||
|
||||
### Files Changed
|
||||
|
||||
- **Deleted:** `src/protocols/bitchat.rs`
|
||||
- **`src/protocols/mod.rs`** — removed `pub mod bitchat;` and re-exports
|
||||
- **`src/core/protocol.rs`** — removed `ProtocolType::BitChat` variant and
|
||||
all of its match arms in `tag()`, `label()`, `badge()`, `naim_color()`;
|
||||
updated the four protocol unit tests
|
||||
- **`src/core/command.rs`** — removed `BitChatPeers`, `BitChatDm`,
|
||||
`BitChatSendFile` command variants; removed the `"bitchat" | "p2p"`
|
||||
parser arm and the `"bitchat" => ProtocolType::BitChat` FromStr arm;
|
||||
removed the `"bitchat" | "bc" | "p2p"` arm from `/xfer` protocol lookup
|
||||
- **`src/engine/dispatcher.rs`** — removed `ProtocolCommand::BitChat`
|
||||
variant; removed `connect_bitchat()` method; removed BitChat dispatch
|
||||
arms in `Msg`, `Me`, `Say`, `quit_for`, and the explicit
|
||||
`BitChatPeers`/`BitChatDm`/`BitChatSendFile` handler block; removed
|
||||
BitChat from the connect dispatch and the tag-map lookup table
|
||||
- **`src/tui/menubar.rs`** — removed the "Connect → BitChat…" menu entry
|
||||
- **`src/tui/chat_view.rs`** — removed BitChat from the protocol color
|
||||
lookup tables (both dim-color and NaimColor)
|
||||
- **`src/tui/winlist.rs`** — removed BitChat from the badge char and
|
||||
badge color lookup tables; updated the module-level doc
|
||||
- **`src/core/history.rs`** — removed the `"P2P" => BitChat` tag mapping
|
||||
(old history files with this tag will be silently reassigned to Status)
|
||||
- **`src/logging/mod.rs`** — removed BitChat from `sanitize_server`
|
||||
match; updated the test
|
||||
- **`src/transfer/engine.rs`** — updated the integration test to use
|
||||
`ProtocolType::Adc` instead of `ProtocolType::BitChat`
|
||||
- **`src/core/app.rs`** — updated the
|
||||
`next_tab_by_priority_mixes_protocols_freely` test to use
|
||||
`ProtocolType::Adc` instead of `ProtocolType::BitChat`
|
||||
- **`src/config/mod.rs`** — updated the BitChat server-entries doc
|
||||
section to note the removal
|
||||
- **`src/engine/crypto.rs`** — updated the module-level doc to note
|
||||
that the Noise primitive is now only used by ADC (was previously
|
||||
shared with BitChat)
|
||||
- **`src/main.rs`** — renamed `_p2p_keypair` → `_noise_keypair` (still
|
||||
generated, since ADC uses it); removed the BitChat help text and
|
||||
replaced with a media-commands help section + a BitChat-removed note;
|
||||
removed the `Command::BitChatPeers | BitChatDm | BitChatSendFile`
|
||||
dispatch arm
|
||||
- **`Cargo.toml`** — bumped version to 0.10.2; removed `libp2p`
|
||||
dependency (BitChat was the only consumer); removed `"p2p"` from the
|
||||
keywords list
|
||||
- **`NOTICES.md`** — new file, contains the full rationale and
|
||||
re-evaluation criteria for the BitChat withdrawal
|
||||
- **`README.md`, `STATUS.md`, `TODO.md`, `QUICKSTART.md`/`quickstart.md`,
|
||||
`man/man1/nirc.1`, `completions/nirc.{bash,zsh,fish}`,
|
||||
`packaging/PKGBUILD`, `packaging/nirc.spec`,
|
||||
`packaging/debian/DEBIAN/control`, `BLOG_POST.md`** — updated to
|
||||
reflect the removal and point at `NOTICES.md`
|
||||
|
||||
### User Impact
|
||||
|
||||
- **Existing configs:** Any `[[servers]]` entry with
|
||||
`protocol = "bitchat"` will be silently ignored at load time. You do
|
||||
not need to edit your config file, but you may remove the entries to
|
||||
clean it up.
|
||||
- **Existing history files:** Per-tab scrollback files tagged
|
||||
`P2P:<name>` will fail to match any known protocol on load and will be
|
||||
silently assigned to the Status tab. The files themselves are not
|
||||
deleted; you can read them with a text editor if you need to recover
|
||||
the content.
|
||||
- **No data loss:** Your IRC / Matrix / ADC / Discord / Stout / Spacebar
|
||||
/ Nerimity history, configs, and credentials are unaffected.
|
||||
|
||||
### Re-evaluation Criteria
|
||||
|
||||
We will re-evaluate restoring the BitChat module when **all** of the
|
||||
following are true:
|
||||
|
||||
1. The India courts case has reached a final judgement (not an interim
|
||||
order), OR the parties have publicly settled and the settlement terms
|
||||
are clear about downstream-implementation rights.
|
||||
2. The BitChat reference implementation's license is unambiguous and
|
||||
permits third-party reimplementations.
|
||||
3. There is at least one independent legal opinion (not from a party to
|
||||
the litigation) concluding that shipping a clean-room reimplementation
|
||||
of the protocol is safe.
|
||||
|
||||
Until then, the module will remain withdrawn.
|
||||
|
||||
---
|
||||
|
||||
## 0.10.1 — IRC Fixes, Throttle, Rotation Revert, URLs/Media, Bandwidth Monitor, Line Wrap
|
||||
|
||||
**Date:** 2026-07-28
|
||||
|
||||
This patch batch addresses the user-reported issues with nirc-rs 0.10.0
|
||||
and adds several requested features. The changes are organized into five
|
||||
phases.
|
||||
|
||||
### Phase 1: IRC Protocol Fixes (`src/protocols/irc.rs`, `src/main.rs`)
|
||||
|
||||
- **CTCP spec compliance** — Outgoing CTCP requests now use `PRIVMSG` (was
|
||||
`NOTICE`). Per the IRCv3 CTCP spec, requests must be PRIVMSG; only replies
|
||||
use NOTICE. This was why `/ctcp <nick> VERSION` was silently ignored by
|
||||
strict servers.
|
||||
- **Self-targeted CTCP visible** — `/ctcp mynick VERSION` now displays the
|
||||
request in the relevant tab. Previously the entire CTCP block was skipped
|
||||
when the sender was the local nick, silently swallowing self-targeted
|
||||
queries.
|
||||
- **CTCP spec compliance** — Outgoing CTCP requests now use `PRIVMSG`
|
||||
(was `NOTICE`). Per the IRCv3 CTCP spec, requests must be PRIVMSG;
|
||||
only replies use NOTICE. This was why `/ctcp <nick> VERSION` was
|
||||
silently ignored by strict servers.
|
||||
- **Self-targeted CTCP visible** — `/ctcp mynick VERSION` now displays
|
||||
the request in the relevant tab. Previously the entire CTCP block was
|
||||
skipped when the sender was the local nick, silently swallowing
|
||||
self-targeted queries.
|
||||
- **`/away` tracking** — Added `is_away` and `away_message` fields to
|
||||
`ConnState`. The `/away [msg]` command optimistically marks the local
|
||||
state and posts a confirmation notice. Added explicit handlers for
|
||||
`RPL_NOWAWAY` (306) and `RPL_UNAWAY` (305) instead of letting them fall
|
||||
through to the generic numeric dump.
|
||||
`RPL_NOWAWAY` (306) and `RPL_UNAWAY` (305) instead of letting them
|
||||
fall through to the generic numeric dump.
|
||||
- **IRCv3 `away-notify` handler** — Added a dedicated `AWAY` command
|
||||
handler that caches the away reason per nick in `state.nick_away` and
|
||||
posts a notice. Previously the `away-notify` capability was negotiated
|
||||
but the incoming AWAY messages fell into the "Unhandled command" 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.
|
||||
This addresses the historical `/who <self>` crash.
|
||||
- **`/me` local echo** (`src/main.rs`) — 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.).
|
||||
- **`/notice` local echo** (`src/main.rs`) — Same local-echo treatment for
|
||||
sent notices.
|
||||
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. This addresses the historical `/who <self>` crash.
|
||||
- **`/me` local echo** (`src/main.rs`) — 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.).
|
||||
- **`/notice` local echo** (`src/main.rs`) — Same local-echo treatment
|
||||
for sent notices.
|
||||
|
||||
### Phase 2: Input Rate Throttle + Channel Rotation Revert
|
||||
|
||||
|
|
@ -95,14 +201,14 @@ several requested features. The changes are organized into five phases.
|
|||
the available width instead of truncating at the right margin. 5 unit
|
||||
tests.
|
||||
|
||||
## New Commands
|
||||
### New Commands
|
||||
|
||||
- `/url <url>` — open a URL in the OS default browser (xdg-open / open / start)
|
||||
- `/video <url>` — launch a video URL in the OS default video player
|
||||
- `/image <url>` — attempt inline image rendering; falls back to `/url` if
|
||||
the terminal doesn't support inline images
|
||||
|
||||
## Configuration
|
||||
### Configuration
|
||||
|
||||
The throttle limits are currently hardcoded in `src/core/throttle.rs`:
|
||||
- `MAX_LINES_PER_SEND = 4` — max lines per single input submission
|
||||
|
|
@ -111,14 +217,9 @@ The throttle limits are currently hardcoded in `src/core/throttle.rs`:
|
|||
|
||||
These will be exposed as config options in a future release (see TODO.md).
|
||||
|
||||
## Build & Test
|
||||
---
|
||||
|
||||
The patched source builds cleanly with `cargo build` and passes all tests
|
||||
with `cargo test`. Note: this sandbox does not have a Rust toolchain
|
||||
installed, so the patches were verified by static review only — please run
|
||||
`cargo build && cargo test` on your machine to confirm.
|
||||
|
||||
## What's NOT in This Patch (Deferred)
|
||||
## What's NOT in These Patches (Deferred)
|
||||
|
||||
Per user direction, the following are deferred to follow-up sessions:
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "nirc-rs"
|
||||
version = "0.10.1"
|
||||
version = "0.10.2"
|
||||
edition = "2021"
|
||||
description = "multi-protocol terminal chat client"
|
||||
license = "GPL-3.0-or-later"
|
||||
|
|
@ -8,7 +8,7 @@ authors = ["Jeremy Anderson <noreply@dcos.net>"]
|
|||
repository = "https://git.dcos.net/dcosnet/nirc-rs"
|
||||
homepage = "https://dcos.net"
|
||||
readme = "README.md"
|
||||
keywords = ["irc", "matrix", "discord", "p2p", "chat", "terminal", "tui"]
|
||||
keywords = ["irc", "matrix", "discord", "chat", "terminal", "tui"]
|
||||
categories = ["command-line-utilities", "network-programming", "cryptography"]
|
||||
|
||||
[dependencies]
|
||||
|
|
@ -35,7 +35,10 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
|||
sha2 = "0.10"
|
||||
yamux = "0.13"
|
||||
async-trait = "0.1"
|
||||
libp2p = { version = "0.54", features = ["tcp", "tokio", "noise", "yamux", "gossipsub", "mdns", "identify", "ping", "request-response", "macros"] }
|
||||
# libp2p was dropped in 0.10.2 when BitChat was removed. The Noise primitive
|
||||
# in src/engine/crypto.rs uses x25519-dalek + aes-gcm directly (not libp2p),
|
||||
# so ADC's encrypted C-C connections still work. If a future P2P protocol
|
||||
# needs libp2p again, re-add: libp2p = { version = "0.54", features = ["tcp", "tokio", "noise", "yamux", "gossipsub", "mdns", "identify", "ping", "request-response", "macros"] }
|
||||
tokio-util = { version = "0.7", features = ["io", "codec", "compat"] }
|
||||
dashmap = "6"
|
||||
toml = "0.8"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,107 @@
|
|||
# NOTICES — nirc-rs
|
||||
|
||||
This file collects project-level notices that affect the legal or operational
|
||||
status of nirc-rs features. It is intentionally separate from CHANGES.md
|
||||
(which is a release-engineering log) so that legal/compliance readers can
|
||||
find these notes quickly.
|
||||
|
||||
---
|
||||
|
||||
## BitChat protocol module — withdrawn in 0.10.2
|
||||
|
||||
**Status:** Removed.
|
||||
**Affected versions:** All releases up to and including 0.10.1 contained a
|
||||
`src/protocols/bitchat.rs` module. It was removed in 0.10.2.
|
||||
**Date of removal:** 2026-07.
|
||||
|
||||
### What was removed
|
||||
|
||||
- `src/protocols/bitchat.rs` — the entire protocol backend (libp2p-based
|
||||
P2P chat with mDNS discovery, gossipsub, noise handshake).
|
||||
- `ProtocolType::BitChat` enum variant and all of its match arms in
|
||||
`tag()`, `label()`, `badge()`, `naim_color()`, dispatcher routing,
|
||||
command parsing, TUI color tables, history-tag mapping, log sanitiser,
|
||||
and the F1 menubar "Connect → BitChat…" entry.
|
||||
- `Command::BitChatPeers`, `BitChatDm`, `BitChatSendFile` variants and
|
||||
their `/bitchat …` parser arms.
|
||||
- `ProtocolCommand::BitChat(BitChatCommand)` dispatcher variant.
|
||||
- The `connect_bitchat()` method on `Dispatcher`.
|
||||
- The `libp2p` dependency in `Cargo.toml`. libp2p was only consumed by
|
||||
`bitchat.rs`; no other module imports it. Dropping it significantly
|
||||
reduces 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 still work.
|
||||
|
||||
### Why it was removed
|
||||
|
||||
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 is not yet clear which license applies to derivative
|
||||
works that were created before the change.
|
||||
|
||||
Pending clarity on these points, we have 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 this means for users
|
||||
|
||||
- **Existing configs:** Any `[[servers]]` entry in `~/.nirc/config.toml`
|
||||
with `protocol = "bitchat"` will be silently ignored at load time.
|
||||
You do not need to edit your config file, but you may remove the
|
||||
entries to clean it up.
|
||||
- **Existing history files:** Per-tab scrollback files in
|
||||
`~/.nirc/history/` that were tagged `P2P:<name>` (the BitChat tab-key
|
||||
prefix) will fail to match any known protocol on load and will be
|
||||
silently assigned to the Status tab. The files themselves are not
|
||||
deleted; you can read them with a text editor if you need to recover
|
||||
the content.
|
||||
- **Existing tabs:** If you were connected to a BitChat network when you
|
||||
last exited nirc-rs 0.10.1 or earlier, the tab will not reconnect on
|
||||
startup. It will appear as an inert entry in the winlist until you
|
||||
`/close` it.
|
||||
- **No data loss:** Your IRC / Matrix / ADC / Discord / Stout / Spacebar
|
||||
/ Nerimity history, configs, and credentials are unaffected.
|
||||
|
||||
### Re-evaluation criteria
|
||||
|
||||
We will re-evaluate restoring the BitChat module when **all** of the
|
||||
following are true:
|
||||
|
||||
1. The India courts case has reached a final judgement (not an interim
|
||||
order), OR the parties have publicly settled and the settlement terms
|
||||
are clear about downstream-implementation rights.
|
||||
2. The BitChat reference implementation's license is unambiguous and
|
||||
permits third-party reimplementations.
|
||||
3. There is at least one independent legal opinion (not from a party to
|
||||
the litigation) concluding that shipping a clean-room reimplementation
|
||||
of the protocol is safe.
|
||||
|
||||
Until then, the module will remain withdrawn. If you want P2P chat in
|
||||
nirc-rs in the meantime, the ADC protocol's client-client connections
|
||||
offer a similar feature set (direct file transfer, direct messages) over
|
||||
a hub-and-spoke topology rather than full mesh P2P.
|
||||
|
||||
### Acknowledgement
|
||||
|
||||
We acknowledge the work of the original BitChat team and the broader P2P
|
||||
messaging community. The decision to withdraw the module is purely a
|
||||
legal-risk mitigation, not a commentary on the technical merits of the
|
||||
protocol or the people who built it.
|
||||
|
||||
---
|
||||
|
||||
## Future notices
|
||||
|
||||
New entries will be appended below this line as needed.
|
||||
42
README.md
42
README.md
|
|
@ -6,11 +6,11 @@ A multi-protocol terminal chat client written in Rust.
|
|||
|
||||
Design Philosophy
|
||||
|
||||
nirc-rs is built on a single conviction: your communications deserve a client that treats the terminal as a first-class interface, not an afterthought. Every design decision — from the naim-derived 8-color palette system to the yamux-multiplexed file transfer pipeline — is deliberate. The client consolidates eight chat protocols into one unified, keyboard-driven interface with zero browser dependencies, zero Electron overhead, and zero JavaScript runtimes.
|
||||
nirc-rs is built on a single conviction: your communications deserve a client that treats the terminal as a first-class interface, not an afterthought. Every design decision — from the naim-derived 8-color palette system to the yamux-multiplexed file transfer pipeline — is deliberate. The client consolidates seven chat protocols (down from eight — BitChat was withdrawn in 0.10.2; see NOTICES.md) into one unified, keyboard-driven interface with zero browser dependencies, zero Electron overhead, and zero JavaScript runtimes.
|
||||
|
||||
The architecture follows a layered design: protocol adapters at the bottom, an asynchronous event dispatcher in the middle, and a ratatui-based presentation layer on top. This separation means adding a new protocol requires implementing a single adapter trait — the rest of the system (tabs, logging, theming, file transfers, the menu bar) adapts automatically.
|
||||
|
||||
**Version:** 0.9.0
|
||||
**Version:** 0.10.2
|
||||
**License:** GPL-3.0-or-later
|
||||
**Author:** Jeremy Anderson — dcos.net
|
||||
**Repository:** https://git.dcos.net/dcosnet/nirc-rs
|
||||
|
|
@ -38,16 +38,16 @@ nirc-rs implements protocol adapters as discrete, isolated modules. Each adapter
|
|||
|
||||
| Protocol | Status | Transport | Adapter Details |
|
||||
|----------|--------|-----------|-----------------|
|
||||
| **IRC** | Production | TLS (6697) / plaintext | SASL PLAIN authentication, CTCP auto-response (VERSION), ISUPPORT capability negotiation, full operator command set (`/oper`, `/kill`, `/kline`, `/wallops`), channel mode management, `/raw` for arbitrary protocol lines |
|
||||
| **IRC** | Production | TLS (6697) / plaintext | SASL PLAIN authentication, CTCP auto-response (VERSION), ISUPPORT capability negotiation, full operator command set (`/oper`, `/kill`, `/kline`, `/wallops`), channel mode management, `/raw` for arbitrary protocol lines. 0.10.1: CTCP spec compliance (PRIVMSG not NOTICE), `/away` state tracking with 305/306, IRCv3 away-notify handler, `/who` hardening (352 hopcount/realname split + 315), `/me` & `/notice` local echo. |
|
||||
| **ADC/DC++** | Production | TLS / plaintext | HSUP→ISID→BINF handshake sequence, hub search via SCH, file transfers over yamux-multiplexed streams with in-flight SHA-256 verification, security pipeline (rate limiting, IP validation, SSRF prevention, path traversal blocking) |
|
||||
| **Matrix** | Implemented | HTTPS (matrix-sdk 0.18) | Megolm E2EE with SQLite crypto store, dedicated OS thread for non-Send crypto types, room creation/invitation/reaction/reply, SAS device verification, session persistence via token storage |
|
||||
| **Discord** | Implemented | WebSocket (wss) | Gateway event subscription, REST API integration, guild join/leave/members |
|
||||
| **Stout** | Implemented | WebSocket (wss) | Revolt-compatible fork, REST + WebSocket client |
|
||||
| **Spacebar** | Implemented | WebSocket (wss) | Revolt fork variant, independent REST + WebSocket adapter |
|
||||
| **Nerimity** | Implemented | WebSocket (wss) | Custom platform with dedicated REST + WebSocket adapter |
|
||||
| **BitChat** | Implemented | libp2p (TCP) | P2P messaging via noise protocol, mDNS peer discovery, gossipsub pub/sub, request-response file transfer |
|
||||
| ~~**BitChat**~~ | **Withdrawn in 0.10.2** | ~~libp2p (TCP)~~ | Removed pending clarity on Jack Dorsey's BitChat project and the India courts situation. See [NOTICES.md](NOTICES.md) for the full rationale. |
|
||||
|
||||
Protocol-specific commands are namespaced under their protocol prefix (`/matrix …`, `/adc …`, `/discord …`, `/bitchat …`) so the command surface stays organized and composable regardless of how many protocols are active simultaneously.
|
||||
Protocol-specific commands are namespaced under their protocol prefix (`/matrix …`, `/adc …`, `/discord …`) so the command surface stays organized and composable regardless of how many protocols are active simultaneously.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -61,7 +61,7 @@ Colors are managed through `NaimPalette`, a 15-field struct that maps directly t
|
|||
|
||||
### Window Management
|
||||
|
||||
Windows are first-class objects in the tab model. Each window carries its own message buffer, input line, cursor position, command history, scroll offset, and unread counter. The `TabTier` priority system (`Unread > Conversed > Inert`) orders windows for `Ctrl-N` navigation so you always land on the most relevant unread conversation first — not the next tab in insertion order.
|
||||
Windows are first-class objects in the tab model. Each window carries its own message buffer, input line, cursor position, command history, scroll offset, and unread counter. `Ctrl-N` walks windows in **static insertion order** (as of 0.10.1 — the previous activity-tier priority system was reverted because users found the reordering non-deterministic and hard to build muscle memory around). Tabs that aren't cyclable (unjoined IRC channels, hidden server tabs) are skipped, but the relative order of the remaining tabs is preserved.
|
||||
|
||||
The window list (`F4` to cycle through Auto/Visible/Hidden) displays protocol badges, per-window unread indicators, and highlights the active window. It occupies a fixed-width column on the right side of the chat area, overlapping rather than displacing message content — the same spatial model that made naim's window list usable on 80-column terminals.
|
||||
|
||||
|
|
@ -296,7 +296,9 @@ nirc-rs provides a unified slash-command interface. Every command is available b
|
|||
|
||||
**Discord / Stout / Spacebar / Nerimity:** `/<protocol> join`, `/<protocol> leave`, `/<protocol> members`, `/<protocol> servers`
|
||||
|
||||
**BitChat:** `/bitchat peers`, `/bitchat dm`, `/bitchat send`
|
||||
**Media (0.10.1):** `/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).
|
||||
|
||||
**BitChat:** *Removed in 0.10.2.* See [NOTICES.md](NOTICES.md) for the rationale.
|
||||
|
||||
### UI and Display
|
||||
|
||||
|
|
@ -323,7 +325,7 @@ nirc-rs provides a unified slash-command interface. Every command is available b
|
|||
| `Left` / `Right` | Move cursor in input line |
|
||||
| `Home` / `End` | Previous / next window |
|
||||
| `Insert` | Scroll chat to bottom (release scroll lock) |
|
||||
| `Ctrl-N` | Jump to next window with unread messages |
|
||||
| `Ctrl-N` | Next window (insertion order, skips unjoined IRC channels) |
|
||||
| `Ctrl-B` | Jump back to previously active window |
|
||||
| `Ctrl-P` | Previous buffer |
|
||||
| `Ctrl-A` | Next active buffer |
|
||||
|
|
@ -370,7 +372,7 @@ auto_connect = ["libera"] # servers to connect on startup
|
|||
# ─── Servers ────────────────────────────────────────────────────────────
|
||||
[[servers]]
|
||||
name = "libera"
|
||||
protocol = "irc" # irc | matrix | adc | discord | stout | spacebar | nerimity | bitchat
|
||||
protocol = "irc" # irc | matrix | adc | discord | stout | spacebar | nerimity (bitchat removed in 0.10.2)
|
||||
address = "irc.libera.chat:6697"
|
||||
tls = true
|
||||
auto_join = ["#rust", "#nirc"]
|
||||
|
|
@ -431,16 +433,22 @@ device_name = "nirc-rs"
|
|||
# access_token = "syt_abc..." # for session resume without password
|
||||
```
|
||||
|
||||
### BitChat P2P Configuration
|
||||
### BitChat P2P Configuration — REMOVED in 0.10.2
|
||||
|
||||
BitChat server entries are no longer accepted. Any `[[servers]]` entry with
|
||||
`protocol = "bitchat"` will be silently ignored at load time. See
|
||||
[NOTICES.md](NOTICES.md) for the full rationale. If you previously had a
|
||||
BitChat entry in your config, you can leave it (it will be ignored) or
|
||||
remove it to clean up:
|
||||
|
||||
```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"
|
||||
# REMOVE THIS — no longer used:
|
||||
# [[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"
|
||||
```
|
||||
|
||||
---
|
||||
|
|
|
|||
47
STATUS.md
47
STATUS.md
|
|
@ -1,6 +1,6 @@
|
|||
# nirc-rs 0.10.1 — Status Report
|
||||
# nirc-rs 0.10.2 — Status Report
|
||||
|
||||
**Version:** 0.10.1
|
||||
**Version:** 0.10.2
|
||||
**Release date:** 2026-07
|
||||
**Codename:** nirc-rs
|
||||
**License:** GPL-3.0-or-later
|
||||
|
|
@ -20,7 +20,48 @@
|
|||
| **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 |
|
||||
| ~~**BitChat**~~ | ❌ **Withdrawn** | N/A | Removed in 0.10.2 — see [NOTICES.md](NOTICES.md) for the rationale (Jack Dorsey BitChat / India courts legal uncertainty) |
|
||||
|
||||
---
|
||||
|
||||
## What's New in 0.10.2
|
||||
|
||||
### BitChat Protocol Module Withdrawn
|
||||
|
||||
The BitChat protocol module (`src/protocols/bitchat.rs`) and all of its
|
||||
integrations have been removed from the codebase. The `libp2p` dependency
|
||||
in `Cargo.toml` was dropped at the same time — BitChat was the only
|
||||
consumer. The Noise primitive in `src/engine/crypto.rs` uses
|
||||
`x25519-dalek` + `aes-gcm` directly (not libp2p), so ADC's encrypted
|
||||
client-client connections are unaffected.
|
||||
|
||||
**Why:** 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 is currently unclear.
|
||||
Pending clarity, we have removed the module entirely rather than ship an
|
||||
encumbered protocol backend. See [NOTICES.md](NOTICES.md) for the full
|
||||
rationale and re-evaluation criteria.
|
||||
|
||||
**User impact:** Existing config entries with `protocol = "bitchat"` are
|
||||
silently ignored. 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.
|
||||
|
||||
**Removed code surface:**
|
||||
- `src/protocols/bitchat.rs` (deleted)
|
||||
- `ProtocolType::BitChat` enum variant + all match arms
|
||||
- `Command::BitChatPeers`, `BitChatDm`, `BitChatSendFile` variants
|
||||
- `ProtocolCommand::BitChat(BitChatCommand)` dispatcher variant
|
||||
- `Dispatcher::connect_bitchat()` method
|
||||
- F1 menu "Connect → BitChat…" entry
|
||||
- `libp2p` Cargo dependency (only consumer was bitchat.rs)
|
||||
|
||||
### Help text updated
|
||||
|
||||
The `/help` output now mentions the new 0.10.1 media commands (`/url`,
|
||||
`/video`, `/image`) and explicitly notes the BitChat removal with a
|
||||
pointer to NOTICES.md.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
5
TODO.md
5
TODO.md
|
|
@ -16,11 +16,11 @@ _None at this time._
|
|||
|
||||
- [ ] **Test Discord protocol against a live server** — The Discord implementation uses Gateway WebSocket and REST API but has not been tested. Need to verify connection, event handling, and message send/receive.
|
||||
|
||||
- [ ] **Test remaining protocols** — Stout, Spacebar, Nerimity, and BitChat are all fully implemented but untested. Each needs a live server/peer to verify:
|
||||
- [ ] **Test remaining protocols** — Stout, Spacebar, Nerimity are all fully implemented but untested. Each needs a live server/peer to verify:
|
||||
- [ ] Stout (Revolt-compatible fork)
|
||||
- [ ] Spacebar (Revolt fork)
|
||||
- [ ] Nerimity (custom platform)
|
||||
- [ ] BitChat (P2P, libp2p, mDNS discovery)
|
||||
(BitChat was on this list but was withdrawn in 0.10.2 — see NOTICES.md.)
|
||||
|
||||
- [ ] **Inline image rendering (real implementation)** — The `try_render_inline_image()` function in `src/tui/media.rs` is currently a stub returning `Unsupported`. The real implementation needs to:
|
||||
- Fetch image bytes via reqwest (with a 4 MiB size cap)
|
||||
|
|
@ -70,6 +70,7 @@ _None at this time._
|
|||
|
||||
## Completed
|
||||
|
||||
- [x] **0.10.2: BitChat protocol module withdrawn** — Removed `src/protocols/bitchat.rs` and all integrations pending clarity on Jack Dorsey's BitChat project and the India courts situation. Dropped `libp2p` dependency. See NOTICES.md for the full rationale and re-evaluation criteria.
|
||||
- [x] **0.10.1: CTCP spec compliance** — Outgoing CTCP requests now use PRIVMSG (not NOTICE) per the IRCv3 CTCP spec.
|
||||
- [x] **0.10.1: Self-targeted CTCP visible** — `/ctcp mynick VERSION` now displays the request in the relevant tab instead of being silently swallowed.
|
||||
- [x] **0.10.1: /away tracking** — `is_away` / `away_message` tracked in ConnState; RPL_NOWAWAY (306) / RPL_UNAWAY (305) explicitly handled.
|
||||
|
|
|
|||
|
|
@ -15,7 +15,8 @@ _nirc() {
|
|||
jump jumpback close open winlist
|
||||
set get alias unalias bind unbind eval source
|
||||
echo clear clearall save help quit newconn server
|
||||
matrix adc dc 'dc++' revolt stoat bitchat p2p
|
||||
matrix adc dc 'dc++' revolt stoat discord spacebar nerimity
|
||||
url video image
|
||||
)
|
||||
|
||||
# Matrix subcommands
|
||||
|
|
@ -27,14 +28,11 @@ _nirc() {
|
|||
# ADC subcommands
|
||||
local adc_cmds=(search users broadcast get download dl)
|
||||
|
||||
# Revolt subcommands
|
||||
local revolt_cmds=(join leave members)
|
||||
# Revolt / Discord / Stout / Spacebar / Nerimity subcommands (same shape)
|
||||
local revolt_cmds=(join leave members servers)
|
||||
|
||||
# BitChat subcommands
|
||||
local bitchat_cmds=(peers dm msg send sendfile list)
|
||||
|
||||
# Protocols for /connect
|
||||
local protocols=(irc matrix adc dc 'dc++' bitchat revolt stoat)
|
||||
# Protocols for /connect (bitchat removed in 0.10.2 — see NOTICES.md)
|
||||
local protocols=(irc matrix adc dc 'dc++' discord stout spacebar nerimity revolt)
|
||||
|
||||
case ${prev} in
|
||||
connect)
|
||||
|
|
@ -49,14 +47,10 @@ _nirc() {
|
|||
COMPREPLY=($(compgen -W "${adc_cmds[*]}" -- "${cur}"))
|
||||
return
|
||||
;;
|
||||
revolt|stoat)
|
||||
revolt|stoat|discord|spacebar|nerimity)
|
||||
COMPREPLY=($(compgen -W "${revolt_cmds[*]}" -- "${cur}"))
|
||||
return
|
||||
;;
|
||||
bitchat|p2p)
|
||||
COMPREPLY=($(compgen -W "${bitchat_cmds[*]}" -- "${cur}"))
|
||||
return
|
||||
;;
|
||||
'matrix verify')
|
||||
# Complete with nothing special — user provides a user_id
|
||||
return
|
||||
|
|
@ -67,9 +61,6 @@ _nirc() {
|
|||
'matrix reply')
|
||||
return
|
||||
;;
|
||||
'bitchat dm'|'bitchat send'|'bitchat sendfile'|'p2p dm'|'p2p send')
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
# Default: offer all top-level commands
|
||||
|
|
@ -79,7 +70,7 @@ _nirc() {
|
|||
|
||||
# Also complete file paths for certain commands
|
||||
case ${words[1]} in
|
||||
sendfile|acceptfile|source|'bitchat send'|'p2p send'|'bitchat sendfile'|'adc get'|'adc download'|'adc dl')
|
||||
sendfile|acceptfile|source|'adc get'|'adc download'|'adc dl')
|
||||
_filedir
|
||||
return
|
||||
;;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# nirc-rs fish completion for 0.5.0
|
||||
# nirc-rs fish completion for 0.10.2
|
||||
|
||||
# Disable file completions unless we explicitly want them
|
||||
complete -c nirc -f
|
||||
|
|
@ -15,12 +15,15 @@ complete -c nirc -k -x -a disconnect -d 'Disconnect from protocol'
|
|||
complete -c nirc -k -x -a newconn -d 'New connection dialog'
|
||||
complete -c nirc -k -x -a server -d 'Switch servers'
|
||||
|
||||
# After /connect, offer protocols
|
||||
# After /connect, offer protocols (bitchat removed in 0.10.2 — see NOTICES.md)
|
||||
complete -c nirc -k -x -a '/connect irc' -d 'Connect via IRC'
|
||||
complete -c nirc -k -x -a '/connect matrix' -d 'Connect via Matrix'
|
||||
complete -c nirc -k -x -a '/connect adc' -d 'Connect via ADC/DC++'
|
||||
complete -c nirc -k -x -a '/connect dc' -d 'Connect via DC++'
|
||||
complete -c nirc -k -x -a '/connect bitchat' -d 'Connect via BitChat P2P'
|
||||
complete -c nirc -k -x -a '/connect discord' -d 'Connect via Discord'
|
||||
complete -c nirc -k -x -a '/connect stout' -d 'Connect via Stout (Discord fork)'
|
||||
complete -c nirc -k -x -a '/connect spacebar' -d 'Connect via Spacebar (Discord fork)'
|
||||
complete -c nirc -k -x -a '/connect nerimity' -d 'Connect via Nerimity'
|
||||
complete -c nirc -k -x -a '/connect revolt' -d 'Connect via Revolt'
|
||||
|
||||
# ═══ Messaging ═══
|
||||
|
|
@ -114,14 +117,31 @@ complete -c nirc -k -x -a '/adc get' -d 'Download file'
|
|||
complete -c nirc -k -x -a '/adc download' -d 'Download file'
|
||||
complete -c nirc -k -x -a '/adc dl' -d 'Download file'
|
||||
|
||||
# ═══ Revolt ═══
|
||||
# ═══ Revolt / Discord / Stout / Spacebar / Nerimity ═══
|
||||
complete -c nirc -k -x -a '/revolt join' -d 'Join server'
|
||||
complete -c nirc -k -x -a '/revolt leave' -d 'Leave server'
|
||||
complete -c nirc -k -x -a '/revolt members' -d 'List members'
|
||||
complete -c nirc -k -x -a '/revolt servers' -d 'List servers'
|
||||
complete -c nirc -k -x -a '/discord join' -d 'Join server'
|
||||
complete -c nirc -k -x -a '/discord leave' -d 'Leave server'
|
||||
complete -c nirc -k -x -a '/discord members' -d 'List members'
|
||||
complete -c nirc -k -x -a '/discord servers' -d 'List servers'
|
||||
complete -c nirc -k -x -a '/stout join' -d 'Join server (Stout fork)'
|
||||
complete -c nirc -k -x -a '/stout leave' -d 'Leave server (Stout fork)'
|
||||
complete -c nirc -k -x -a '/stout members' -d 'List members (Stout fork)'
|
||||
complete -c nirc -k -x -a '/stout servers' -d 'List servers (Stout fork)'
|
||||
complete -c nirc -k -x -a '/spacebar join' -d 'Join server (Spacebar fork)'
|
||||
complete -c nirc -k -x -a '/spacebar leave' -d 'Leave server (Spacebar fork)'
|
||||
complete -c nirc -k -x -a '/spacebar members' -d 'List members (Spacebar fork)'
|
||||
complete -c nirc -k -x -a '/spacebar servers' -d 'List servers (Spacebar fork)'
|
||||
complete -c nirc -k -x -a '/nerimity join' -d 'Join server (Nerimity)'
|
||||
complete -c nirc -k -x -a '/nerimity leave' -d 'Leave server (Nerimity)'
|
||||
complete -c nirc -k -x -a '/nerimity members' -d 'List members (Nerimity)'
|
||||
complete -c nirc -k -x -a '/nerimity servers' -d 'List servers (Nerimity)'
|
||||
|
||||
# ═══ BitChat P2P ═══
|
||||
complete -c nirc -k -x -a '/bitchat peers' -d 'List P2P peers'
|
||||
complete -c nirc -k -x -a '/p2p peers' -d 'List P2P peers'
|
||||
complete -c nirc -k -x -a '/bitchat dm' -d 'Send DM'
|
||||
complete -c nirc -k -x -a '/bitchat send' -d 'Send file via P2P'
|
||||
complete -c nirc -k -x -a '/bitchat sendfile' -d 'Send file via P2P'
|
||||
# ═══ Media (0.10.1) ═══
|
||||
complete -c nirc -k -x -a '/url' -d 'Open URL in OS default browser'
|
||||
complete -c nirc -k -x -a '/video' -d 'Launch video in OS default player'
|
||||
complete -c nirc -k -x -a '/image' -d 'Attempt inline image render (falls back to /url)'
|
||||
|
||||
# BitChat P2P support was removed in 0.10.2. See NOTICES.md for the rationale.
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#compdef nirc nirc-rs
|
||||
# nirc-rs zsh completion for 0.5.0
|
||||
# nirc-rs zsh completion for 0.10.2
|
||||
|
||||
local -a subcommands protocols matrix_cmds adc_cmds revolt_cmds bitchat_cmds
|
||||
local -a subcommands protocols matrix_cmds adc_cmds revolt_cmds
|
||||
|
||||
subcommands=(
|
||||
'connect:Connect to a server'
|
||||
|
|
@ -64,16 +64,25 @@ subcommands=(
|
|||
'dc:Alias for /adc'
|
||||
'revolt:Revolt protocol commands'
|
||||
'stoat:Alias for /revolt'
|
||||
'bitchat:BitChat P2P commands'
|
||||
'p2p:Alias for /bitchat'
|
||||
'discord:Discord protocol commands'
|
||||
'stout:Alias for /discord (Stout fork)'
|
||||
'spacebar:Spacebar protocol commands'
|
||||
'nerimity:Nerimity protocol commands'
|
||||
'url:Open URL in OS default browser'
|
||||
'video:Launch video in OS default player'
|
||||
'image:Attempt inline image render'
|
||||
)
|
||||
|
||||
# Protocols for /connect (bitchat removed in 0.10.2 — see NOTICES.md)
|
||||
protocols=(
|
||||
'irc:IRC protocol'
|
||||
'matrix:Matrix protocol'
|
||||
'adc:ADC/DC++ protocol'
|
||||
'dc:Alias for adc'
|
||||
'bitchat:BitChat P2P'
|
||||
'discord:Discord protocol'
|
||||
'stout:Stout (Discord-API fork)'
|
||||
'spacebar:Spacebar (Discord-API fork)'
|
||||
'nerimity:Nerimity (custom platform)'
|
||||
'revolt:Revolt protocol'
|
||||
'stoat:Alias for revolt'
|
||||
)
|
||||
|
|
@ -107,15 +116,7 @@ revolt_cmds=(
|
|||
'join:Join server'
|
||||
'leave:Leave server'
|
||||
'members:List members'
|
||||
)
|
||||
|
||||
bitchat_cmds=(
|
||||
'peers:List P2P peers'
|
||||
'dm:Send direct message'
|
||||
'msg:Send direct message'
|
||||
'send:Send file'
|
||||
'sendfile:Send file'
|
||||
'list:List P2P peers'
|
||||
'servers:List servers'
|
||||
)
|
||||
|
||||
_nirc_subcommand() {
|
||||
|
|
@ -130,16 +131,13 @@ _nirc_subcommand() {
|
|||
adc|dc)
|
||||
_describe 'adc-command' adc_cmds
|
||||
;;
|
||||
revolt|stoat)
|
||||
revolt|stoat|discord|spacebar|nerimity)
|
||||
_describe 'revolt-command' revolt_cmds
|
||||
;;
|
||||
bitchat|p2p)
|
||||
_describe 'bitchat-command' bitchat_cmds
|
||||
;;
|
||||
sendfile|source|acceptfile)
|
||||
_files
|
||||
;;
|
||||
'adc get'|'adc download'|'adc dl'|'bitchat send'|'bitchat sendfile'|'p2p send')
|
||||
'adc get'|'adc download'|'adc dl')
|
||||
_files
|
||||
;;
|
||||
esac
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
.\" nirc-rs
|
||||
.\" Copyright (C) 2025 Jeremy Anderson
|
||||
.\" SPDX-License-Identifier: GPL-3.0-or-later
|
||||
.TH NIRC 1 "2025-07-19" "nirc-rs 0.5.0" "User Commands"
|
||||
.TH NIRC 1 "2026-07-29" "nirc-rs 0.10.2" "User Commands"
|
||||
.SH NAME
|
||||
nirc \- multi-protocol terminal chat client (IRC, Matrix, ADC/DC++, Revolt, BitChat P2P)
|
||||
nirc \- multi-protocol terminal chat client (IRC, Matrix, ADC/DC++, Discord, Stout, Spacebar, Nerimity)
|
||||
.SH SYNOPSIS
|
||||
.B nirc
|
||||
[\fIOPTIONS\fR]
|
||||
|
|
@ -33,9 +33,14 @@ Revolt/Stoat (0.5.0+)
|
|||
REST + JSON WebSocket client with email/password or bot-token auth,
|
||||
server join/leave, member listing, session token persistence.
|
||||
.TP
|
||||
BitChat P2P (0.5.0+)
|
||||
libp2p Gossipsub chat, mDNS local discovery, Identify remote discovery,
|
||||
direct messages, P2P file transfer via request-response.
|
||||
Discord / Stout / Spacebar / Nerimity (0.6.0+)
|
||||
REST + WebSocket clients for Discord-API-compatible and custom platforms.
|
||||
.PP
|
||||
Note: BitChat P2P support was withdrawn in 0.10.2 pending clarity on
|
||||
Jack Dorsey's BitChat project and the India courts situation.
|
||||
See
|
||||
.I NOTICES.md
|
||||
for the full rationale.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.B \-h, \-\-help
|
||||
|
|
@ -88,8 +93,12 @@ is one of
|
|||
.BR matrix ,
|
||||
.BR adc ,
|
||||
.BR dc ,
|
||||
.BR bitchat ,
|
||||
.BR discord ,
|
||||
.BR stout ,
|
||||
.BR spacebar ,
|
||||
.BR nerimity ,
|
||||
.BR revolt .
|
||||
(BitChat was removed in 0.10.2 — see NOTICES.md.)
|
||||
.TP
|
||||
.B /disconnect [protocol]
|
||||
Disconnect from a specific protocol, or all.
|
||||
|
|
@ -264,19 +273,13 @@ Leave a server.
|
|||
.TP
|
||||
.B /revolt members <server_id>
|
||||
List server members.
|
||||
.SS BitChat P2P
|
||||
.TP
|
||||
.B /bitchat peers
|
||||
.B /p2p peers
|
||||
List discovered P2P peers.
|
||||
.TP
|
||||
.B /bitchat dm <peer_id> <message>
|
||||
.B /p2p msg <peer_id> <message>
|
||||
Send a direct message.
|
||||
.TP
|
||||
.B /bitchat send <peer_id> <path>
|
||||
.B /p2p send <peer_id> <path>
|
||||
Send a file via P2P.
|
||||
.SS BitChat P2P \(em Removed in 0.10.2
|
||||
BitChat support was withdrawn pending clarity on Jack Dorsey's BitChat
|
||||
project and the India courts situation. See
|
||||
.I NOTICES.md
|
||||
for the full rationale. Existing config entries with
|
||||
.B protocol = "bitchat"
|
||||
are silently ignored at load time.
|
||||
.SS Window Management
|
||||
.TP
|
||||
.B /win [N]
|
||||
|
|
@ -399,12 +402,9 @@ password = "hunter2"
|
|||
.fi
|
||||
.PP
|
||||
.nf
|
||||
[[servers]]
|
||||
name = "bitchat"
|
||||
protocol = "bitchat"
|
||||
address = "/ip4/0.0.0.0/tcp/9394"
|
||||
[servers.extra]
|
||||
bootstrap = "/ip4/1.2.3.4/tcp/9394/p2p/QmPeerId"
|
||||
# BitChat server entries are no longer accepted (removed in 0.10.2).
|
||||
# Any [[servers]] block with protocol = "bitchat" is silently ignored.
|
||||
# See NOTICES.md for the rationale.
|
||||
.fi
|
||||
.SH FILES
|
||||
.TP
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
# Maintainer: Jeremy Anderson <noreply@dcos.net>
|
||||
pkgname=nirc-rs
|
||||
pkgver=0.8.1
|
||||
pkgver=0.10.2
|
||||
pkgrel=1
|
||||
pkgdesc="multi-protocol terminal chat client (IRC, Matrix, ADC/DC++, Revolt, BitChat P2P)"
|
||||
pkgdesc="multi-protocol terminal chat client (IRC, Matrix, ADC/DC++, Discord, Stout, Spacebar, Nerimity)"
|
||||
arch=('x86_64' 'aarch64')
|
||||
url="https://git.dcos.net/dcosnet/nirc-rs"
|
||||
license=('GPL-3.0-or-later')
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
Package: nirc
|
||||
Version: 0.8.1
|
||||
Version: 0.10.2
|
||||
Section: net
|
||||
Priority: optional
|
||||
Maintainer: Jeremy Anderson <noreply@dcos.net>
|
||||
|
|
@ -13,14 +13,17 @@ License: GPL-3.0-or-later
|
|||
Description: multi-protocol terminal chat client
|
||||
nirc-rs is a terminal-based chat client built with ratatui. It supports
|
||||
IRC (with TLS, SASL, IRCv3), Matrix (with E2EE), ADC/DC++ file sharing,
|
||||
Revolt, and BitChat P2P (libp2p/Gossipsub).
|
||||
Discord, Stout, Spacebar, and Nerimity. (BitChat P2P support was
|
||||
removed in 0.10.2 — see NOTICES.md for the rationale.)
|
||||
.
|
||||
Features:
|
||||
- Multi-protocol: IRC, Matrix, ADC/DC++, Revolt, BitChat P2P
|
||||
- Multi-protocol: IRC, Matrix, ADC/DC++, Discord, Stout, Spacebar, Nerimity
|
||||
- 4 built-in themes: default, solarized, gruvbox, dracula
|
||||
- Encrypted identity vault (AES-256-GCM)
|
||||
- SASL + TLS for IRC
|
||||
- Megolm E2EE for Matrix
|
||||
- DCC/ADC + P2P file transfers
|
||||
- DCC/ADC file transfers with bandwidth monitor
|
||||
- Dynamic plugin system (.so / .dylib)
|
||||
- Full naim-style command set with aliases, key bindings, and scripting
|
||||
- Full naim-style command set with aliases, key bindings, and scripting
|
||||
- URL detection + /url /video /image media commands (0.10.1)
|
||||
- Input rate throttle + line guard (0.10.1)
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
Name: nirc
|
||||
Version: 0.8.1
|
||||
Version: 0.10.2
|
||||
Release: 1%{?dist}
|
||||
Summary: multi-protocol terminal chat client
|
||||
|
||||
|
|
@ -13,8 +13,9 @@ BuildRequires: pkg-config
|
|||
|
||||
%description
|
||||
nirc-rs is a terminal-based chat client built with ratatui supporting IRC
|
||||
(with TLS, SASL, IRCv3), Matrix (with E2EE), ADC/DC++, Revolt, and BitChat
|
||||
P2P (libp2p/Gossipsub).
|
||||
(with TLS, SASL, IRCv3), Matrix (with E2EE), ADC/DC++, Discord, Stout,
|
||||
Spacebar, and Nerimity. (BitChat P2P support was removed in 0.10.2 —
|
||||
see NOTICES.md for the rationale.)
|
||||
|
||||
%prep
|
||||
%autosetup
|
||||
|
|
@ -43,6 +44,13 @@ cargo test --release --locked
|
|||
%{_datadir}/fish/vendor_completions.d/nirc.fish
|
||||
|
||||
%changelog
|
||||
* Wed Jul 29 2026 Jeremy Anderson <noreply@dcos.net> - 0.10.2-1
|
||||
- BitChat P2P module withdrawn (India courts legal uncertainty)
|
||||
- libp2p dependency dropped (only consumer was bitchat)
|
||||
- 0.10.1: IRC CTCP/away/who fixes, input throttle + line guard,
|
||||
channel rotation reverted to insertion order, URL detection +
|
||||
inline photo / external video framework, top-right bandwidth
|
||||
monitor, line wrapping fix
|
||||
* Sat Jul 19 2025 Jeremy Anderson <noreply@dcos.net> - 0.5.0-1
|
||||
- Initial RPM packaging
|
||||
- 0.5.0: BitChat P2P (libp2p), Revolt/Stoat protocol
|
||||
|
|
|
|||
|
|
@ -72,19 +72,29 @@ address = "https://discord.com/api"
|
|||
bot_token = "BOT_TOKEN_HERE"
|
||||
```
|
||||
|
||||
## Connect to BitChat (P2P)
|
||||
## BitChat (P2P) — Removed in 0.10.2
|
||||
|
||||
BitChat support was withdrawn in 0.10.2 pending clarity on Jack Dorsey's
|
||||
BitChat project and the India courts situation. See
|
||||
[NOTICES.md](NOTICES.md) for the full rationale. Existing config entries
|
||||
with `protocol = "bitchat"` are silently ignored at load time — you can
|
||||
leave them in place or remove them to clean up:
|
||||
|
||||
```toml
|
||||
[[servers]]
|
||||
name = "p2p"
|
||||
protocol = "bitchat"
|
||||
address = "/ip4/0.0.0.0/tcp/9394"
|
||||
[servers.extra]
|
||||
nickname = "handle"
|
||||
# Optional: bootstrap to a known peer
|
||||
# REMOVE THIS — no longer used:
|
||||
# [[servers]]
|
||||
# name = "p2p"
|
||||
# protocol = "bitchat"
|
||||
# address = "/ip4/0.0.0.0/tcp/9394"
|
||||
# [servers.extra]
|
||||
# nickname = "handle"
|
||||
# bootstrap = "/ip4/1.2.3.4/tcp/9394/p2p/QmPeerId"
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
## Key Bindings
|
||||
|
||||
| Key | Action |
|
||||
|
|
|
|||
|
|
@ -30,21 +30,15 @@
|
|||
//! Matrix-specific connection parameters are pulled from the `extra` map at
|
||||
//! connect time by [`matrix_config_from_entry`].
|
||||
//!
|
||||
//! ## BitChat P2P server entries (0.5.0)
|
||||
//! ## BitChat P2P server entries — REMOVED in 0.10.2
|
||||
//!
|
||||
//! BitChat servers are configured as `[[servers]]` entries with `protocol = "bitchat"`.
|
||||
//! The `address` field is the listen multiaddr (e.g. `/ip4/0.0.0.0/tcp/9394`).
|
||||
//! Optional bootstrap node in `[servers.extra]`:
|
||||
//!
|
||||
//! ```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"
|
||||
//! ```
|
||||
//! BitChat server entries are no longer accepted. Any `[[servers]]` entry
|
||||
//! with `protocol = "bitchat"` will be silently ignored at load time. The
|
||||
//! module was removed pending clarity on Jack Dorsey's BitChat project and
|
||||
//! the India courts situation — see NOTICES.md for the full rationale.
|
||||
//! Existing config files do not need to be edited (the unknown protocol
|
||||
//! value is treated as a parse-time skip rather than an error), but users
|
||||
//! may remove the entries to clean up their config.
|
||||
|
||||
use crate::core::protocol::ProtocolType;
|
||||
use crate::tui::foundation::Theme;
|
||||
|
|
|
|||
|
|
@ -747,7 +747,7 @@ mod tests {
|
|||
let mut app = app_with_tabs(&[
|
||||
(ProtocolType::Irc, "#irc", false),
|
||||
(ProtocolType::Matrix, "#mtx", false),
|
||||
(ProtocolType::BitChat, "#p2p", false),
|
||||
(ProtocolType::Adc, "#adc", false),
|
||||
(ProtocolType::Discord, "#dsc", false),
|
||||
]);
|
||||
// Converse in all of them so none are inert.
|
||||
|
|
@ -762,7 +762,7 @@ mod tests {
|
|||
let next = app.next_tab_by_priority(0);
|
||||
assert_eq!(next, 1, "Ctrl-N from IRC lands on Matrix (insertion order)");
|
||||
let proto = app.tab_at(next).unwrap().protocol;
|
||||
assert!(matches!(proto, ProtocolType::Matrix | ProtocolType::BitChat | ProtocolType::Discord),
|
||||
assert!(matches!(proto, ProtocolType::Matrix | ProtocolType::Adc | ProtocolType::Discord),
|
||||
"Ctrl-N from IRC must land on a different protocol's tab; got {:?}", proto);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -226,13 +226,9 @@ pub enum Command {
|
|||
NerimityMembers { server_id: String },
|
||||
NerimityServers,
|
||||
|
||||
// ─── Phase F — BitChat (P2P) commands ────────────────────
|
||||
/// `/bitchat peers` — list discovered P2P peers.
|
||||
BitChatPeers,
|
||||
/// `/bitchat dm <peer_id> <message>` — send a direct message.
|
||||
BitChatDm { peer_id: String, body: String },
|
||||
/// `/bitchat send <peer_id> <path>` — send a file via P2P.
|
||||
BitChatSendFile { peer_id: String, path: String },
|
||||
// NOTE: BitChat (P2P) commands were removed in 0.10.2 pending clarity
|
||||
// on Jack Dorsey's BitChat project and the India courts situation.
|
||||
// See NOTICES.md for the full rationale.
|
||||
|
||||
// ─── N-3.1: Plugin management commands ────────────────────────────
|
||||
/// `/plugins` — list loaded plugins.
|
||||
|
|
@ -370,7 +366,6 @@ pub fn parse_command(input: &str) -> Option<Command> {
|
|||
"matrix" | "mtx" => ProtocolType::Matrix,
|
||||
"discord" | "dc" => ProtocolType::Discord,
|
||||
"adc" => ProtocolType::Adc,
|
||||
"bitchat" | "bc" | "p2p" => ProtocolType::BitChat,
|
||||
"stout" => ProtocolType::Stout,
|
||||
"spacebar" | "sb" => ProtocolType::Spacebar,
|
||||
"nerimity" | "nm" => ProtocolType::Nerimity,
|
||||
|
|
@ -627,24 +622,8 @@ pub fn parse_command(input: &str) -> Option<Command> {
|
|||
}
|
||||
}
|
||||
|
||||
// ─── BitChat (P2P) commands ────────────────────────
|
||||
"bitchat" | "p2p" => {
|
||||
let sub = args.first().map(|s| s.to_lowercase());
|
||||
match sub.as_deref() {
|
||||
Some("peers") | Some("list") => Some(Command::BitChatPeers),
|
||||
Some("dm") | Some("msg") => {
|
||||
let peer_id = args.get(1)?.clone();
|
||||
let body = if args.len() > 2 { args[2..].join(" ") } else { None? };
|
||||
Some(Command::BitChatDm { peer_id, body })
|
||||
}
|
||||
Some("send") | Some("sendfile") | Some("file") => {
|
||||
let peer_id = args.get(1)?.clone();
|
||||
let path = args.get(2)?.clone();
|
||||
Some(Command::BitChatSendFile { peer_id, path })
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
// NOTE: `/bitchat` and `/p2p` commands removed in 0.10.2.
|
||||
// See NOTICES.md for the rationale.
|
||||
|
||||
// ─── N-3.1: Plugin management commands ────────────────────────
|
||||
"plugins" | "plugin-list" => Some(Command::PluginList),
|
||||
|
|
@ -660,7 +639,10 @@ pub fn parse_command(input: &str) -> Option<Command> {
|
|||
// All three accept a single URL argument. The URL is taken as the
|
||||
// full remainder of the input (so spaces in URLs are preserved,
|
||||
// though URLs shouldn't contain unencoded spaces anyway).
|
||||
"url" | "open" | "browse" => Some(Command::Url { target: args.join(" ") }),
|
||||
// NOTE: "open" is intentionally NOT an alias here — it's already
|
||||
// bound to Command::Open (the /query / /open PM-window command)
|
||||
// at the earlier match arm. Use "url" or "browse" instead.
|
||||
"url" | "browse" => Some(Command::Url { target: args.join(" ") }),
|
||||
"video" | "play" => Some(Command::Video { target: args.join(" ") }),
|
||||
"image" | "img" | "photo" => Some(Command::Image { target: args.join(" ") }),
|
||||
|
||||
|
|
@ -707,7 +689,6 @@ impl FromStr for ProtocolType {
|
|||
"irc" => Ok(ProtocolType::Irc),
|
||||
"matrix" => Ok(ProtocolType::Matrix),
|
||||
"adc" | "dc" | "dc++" => Ok(ProtocolType::Adc),
|
||||
"bitchat" => Ok(ProtocolType::BitChat),
|
||||
"discord" => Ok(ProtocolType::Discord),
|
||||
"stout" => Ok(ProtocolType::Stout),
|
||||
"spacebar" => Ok(ProtocolType::Spacebar),
|
||||
|
|
|
|||
|
|
@ -96,7 +96,8 @@ fn protocol_from_tag(tag: &str) -> Option<ProtocolType> {
|
|||
"IRC" => Some(ProtocolType::Irc),
|
||||
"Mtx" => Some(ProtocolType::Matrix),
|
||||
"ADC" => Some(ProtocolType::Adc),
|
||||
"P2P" => Some(ProtocolType::BitChat),
|
||||
// "P2P" (BitChat) removed in 0.10.2 — old history files with this
|
||||
// tag will be silently assigned to the Status tab.
|
||||
"Dsc" => Some(ProtocolType::Discord),
|
||||
"Sto" => Some(ProtocolType::Stout),
|
||||
"Spc" => Some(ProtocolType::Spacebar),
|
||||
|
|
|
|||
|
|
@ -3,13 +3,17 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
/// Supported chat protocols (Tox removed per design decision).
|
||||
/// Supported chat protocols.
|
||||
///
|
||||
/// BitChat was removed in 0.10.2 pending clarity on Jack Dorsey's BitChat
|
||||
/// project and the India courts situation. See NOTICES.md for the full
|
||||
/// rationale. The variant will be restored if/when the legal status of the
|
||||
/// protocol and its reference implementation is clarified.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub enum ProtocolType {
|
||||
Irc,
|
||||
Matrix,
|
||||
Adc,
|
||||
BitChat,
|
||||
Discord,
|
||||
Stout,
|
||||
Spacebar,
|
||||
|
|
@ -29,7 +33,6 @@ impl ProtocolType {
|
|||
ProtocolType::Irc => "IRC",
|
||||
ProtocolType::Matrix => "Mtx",
|
||||
ProtocolType::Adc => "ADC",
|
||||
ProtocolType::BitChat => "P2P",
|
||||
ProtocolType::Discord => "Dsc",
|
||||
ProtocolType::Stout => "Sto",
|
||||
ProtocolType::Spacebar => "Spc",
|
||||
|
|
@ -43,7 +46,6 @@ impl ProtocolType {
|
|||
ProtocolType::Irc => "IRC",
|
||||
ProtocolType::Matrix => "Matrix",
|
||||
ProtocolType::Adc => "ADC/DC++",
|
||||
ProtocolType::BitChat => "BitChat",
|
||||
ProtocolType::Discord => "Discord",
|
||||
ProtocolType::Stout => "Stout",
|
||||
ProtocolType::Spacebar => "Spacebar",
|
||||
|
|
@ -56,13 +58,11 @@ impl ProtocolType {
|
|||
/// - IRC channels start with `#`
|
||||
/// - Matrix room IDs/aliases use `:` as the homeserver separator
|
||||
/// - ADC hubs are commonly referenced as `+hub`
|
||||
/// - BitChat is peer-to-peer (`~` home / personal node)
|
||||
pub fn badge(self) -> &'static str {
|
||||
match self {
|
||||
ProtocolType::Irc => "#",
|
||||
ProtocolType::Matrix => ":",
|
||||
ProtocolType::Adc => "+",
|
||||
ProtocolType::BitChat => "~",
|
||||
ProtocolType::Discord => "D",
|
||||
ProtocolType::Stout => "St",
|
||||
ProtocolType::Spacebar => "S",
|
||||
|
|
@ -71,13 +71,12 @@ impl ProtocolType {
|
|||
}
|
||||
|
||||
/// 8-color NaimColor for protocol indicator (matches NaimPalette categories).
|
||||
/// IRC = cyan (existing), Matrix = magenta, ADC = blue, BitChat = green.
|
||||
/// IRC = cyan, Matrix = magenta, ADC = blue.
|
||||
pub fn naim_color(self) -> crate::tui::foundation::NaimColor {
|
||||
match self {
|
||||
ProtocolType::Irc => crate::tui::foundation::NaimColor::Cyan,
|
||||
ProtocolType::Matrix => crate::tui::foundation::NaimColor::Magenta,
|
||||
ProtocolType::Adc => crate::tui::foundation::NaimColor::Blue,
|
||||
ProtocolType::BitChat => crate::tui::foundation::NaimColor::Green,
|
||||
ProtocolType::Discord => crate::tui::foundation::NaimColor::White,
|
||||
ProtocolType::Stout => crate::tui::foundation::NaimColor::Yellow,
|
||||
ProtocolType::Spacebar => crate::tui::foundation::NaimColor::Red,
|
||||
|
|
@ -131,7 +130,6 @@ mod tests {
|
|||
assert_eq!(ProtocolType::Irc.tag(), "IRC");
|
||||
assert_eq!(ProtocolType::Matrix.tag(), "Mtx");
|
||||
assert_eq!(ProtocolType::Adc.tag(), "ADC");
|
||||
assert_eq!(ProtocolType::BitChat.tag(), "P2P");
|
||||
assert_eq!(ProtocolType::Discord.tag(), "Dsc");
|
||||
assert_eq!(ProtocolType::Stout.tag(), "Sto");
|
||||
assert_eq!(ProtocolType::Spacebar.tag(), "Spc");
|
||||
|
|
@ -143,7 +141,6 @@ mod tests {
|
|||
assert_eq!(ProtocolType::Irc.badge(), "#");
|
||||
assert_eq!(ProtocolType::Matrix.badge(), ":");
|
||||
assert_eq!(ProtocolType::Adc.badge(), "+");
|
||||
assert_eq!(ProtocolType::BitChat.badge(), "~");
|
||||
assert_eq!(ProtocolType::Discord.badge(), "D");
|
||||
assert_eq!(ProtocolType::Stout.badge(), "St");
|
||||
assert_eq!(ProtocolType::Spacebar.badge(), "S");
|
||||
|
|
@ -156,7 +153,6 @@ mod tests {
|
|||
assert_eq!(ProtocolType::Irc.naim_color(), NaimColor::Cyan);
|
||||
assert_eq!(ProtocolType::Matrix.naim_color(), NaimColor::Magenta);
|
||||
assert_eq!(ProtocolType::Adc.naim_color(), NaimColor::Blue);
|
||||
assert_eq!(ProtocolType::BitChat.naim_color(), NaimColor::Green);
|
||||
assert_eq!(ProtocolType::Discord.naim_color(), NaimColor::White);
|
||||
assert_eq!(ProtocolType::Stout.naim_color(), NaimColor::Yellow);
|
||||
assert_eq!(ProtocolType::Spacebar.naim_color(), NaimColor::Red);
|
||||
|
|
@ -168,7 +164,6 @@ mod tests {
|
|||
assert_eq!(ProtocolType::Irc.label(), "IRC");
|
||||
assert_eq!(ProtocolType::Matrix.label(), "Matrix");
|
||||
assert_eq!(format!("{}", ProtocolType::Adc), "ADC/DC++");
|
||||
assert_eq!(format!("{}", ProtocolType::BitChat), "BitChat");
|
||||
assert_eq!(format!("{}", ProtocolType::Discord), "Discord");
|
||||
assert_eq!(format!("{}", ProtocolType::Stout), "Stout");
|
||||
assert_eq!(format!("{}", ProtocolType::Spacebar), "Spacebar");
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
//! Encrypted P2P tunnel layer — Phase 16.
|
||||
//! Encrypted tunnel layer — Phase 16.
|
||||
//!
|
||||
//! Wraps any async Read+Write stream (yamux, TCP) with the Noise Protocol
|
||||
//! Framework (Noise_XX pattern) for forward-secret, authenticated encryption.
|
||||
//! The libp2p `noise` crate handles the handshake; we wrap the resulting
|
||||
//! encrypted stream for use by the transfer engine and protocol backends.
|
||||
//!
|
||||
//! In production, this is automatically provided by libp2p's transport layer
|
||||
//! for BitChat. This module exposes the building blocks for:
|
||||
//! As of 0.10.2, this module is used by ADC's encrypted client-client (C-C)
|
||||
//! connections. (It was previously also used by BitChat P2P, which was
|
||||
//! removed in 0.10.2 — see NOTICES.md.) The module exposes the building
|
||||
//! blocks for:
|
||||
//! - Manual encrypted tunnels to non-libp2p peers
|
||||
//! - End-to-end encrypted yamux substreams
|
||||
//! - Keypair generation and fingerprinting
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use crate::config::ServerEntry;
|
|||
use crate::core::command::Command;
|
||||
use crate::core::message::ChatMessage;
|
||||
use crate::core::protocol::ProtocolType;
|
||||
use crate::protocols::{adc::{self, AdcCommand, AdcConfig}, bitchat::{self, BitChatCommand, BitChatConfig}, discord::{self, DiscordCommand, DiscordConfig}, irc::{self, IrcCommand, IrcConfig}, matrix::{self, MatrixCommand, MatrixConfig}, stout::{self, StoutCommand, StoutConfig}, spacebar::{self, SpacebarCommand, SpacebarConfig}, nerimity::{self, NerimityCommand, NerimityConfig}};
|
||||
use crate::protocols::{adc::{self, AdcCommand, AdcConfig}, discord::{self, DiscordCommand, DiscordConfig}, irc::{self, IrcCommand, IrcConfig}, matrix::{self, MatrixCommand, MatrixConfig}, stout::{self, StoutCommand, StoutConfig}, spacebar::{self, SpacebarCommand, SpacebarConfig}, nerimity::{self, NerimityCommand, NerimityConfig}};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
|
|
@ -18,7 +18,6 @@ pub enum ProtocolCommand {
|
|||
Irc(IrcCommand),
|
||||
Adc(AdcCommand),
|
||||
Matrix(MatrixCommand),
|
||||
BitChat(BitChatCommand),
|
||||
Discord(DiscordCommand),
|
||||
Stout(StoutCommand),
|
||||
Spacebar(SpacebarCommand),
|
||||
|
|
@ -121,7 +120,6 @@ impl Dispatcher {
|
|||
ProtocolType::Irc => self.connect_irc(server).await,
|
||||
ProtocolType::Adc => self.connect_adc(server).await,
|
||||
ProtocolType::Matrix => self.connect_matrix(server).await,
|
||||
ProtocolType::BitChat => self.connect_bitchat(server).await,
|
||||
ProtocolType::Discord => self.connect_discord(server).await,
|
||||
ProtocolType::Stout => self.connect_stout(server).await,
|
||||
ProtocolType::Spacebar => self.connect_spacebar(server).await,
|
||||
|
|
@ -338,36 +336,7 @@ impl Dispatcher {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn connect_bitchat(&mut self, server: &str) -> anyhow::Result<()> {
|
||||
let (proto_tx, mut proto_rx) = mpsc::channel::<ProtocolCommand>(64);
|
||||
let (bc_cmd_tx, bc_cmd_rx) = mpsc::channel::<BitChatCommand>(64);
|
||||
tokio::spawn(async move {
|
||||
while let Some(ProtocolCommand::BitChat(cmd)) = proto_rx.recv().await {
|
||||
if bc_cmd_tx.send(cmd).await.is_err() { break; }
|
||||
}
|
||||
});
|
||||
|
||||
// Look up ServerEntry for BitChat-specific config (bootstrap node).
|
||||
let entry = self.server_entries.iter()
|
||||
.find(|e| e.name == server || e.address == server).cloned();
|
||||
let listen_addr = entry.as_ref()
|
||||
.map(|e| e.address.clone())
|
||||
.unwrap_or_else(|| server.to_owned());
|
||||
let bootstrap = entry.as_ref().and_then(|e| e.extra.get("bootstrap").cloned());
|
||||
|
||||
let config = BitChatConfig {
|
||||
listen_addr,
|
||||
nickname: self.nickname.clone(),
|
||||
bootstrap,
|
||||
tx: self.msg_tx.clone(),
|
||||
};
|
||||
let addr_owned = server.to_owned();
|
||||
tokio::spawn(async move { match bitchat::run_bitchat(config, bc_cmd_rx).await { Ok(()) => info!(%addr_owned, "BitChat stopped"), Err(e) => error!(%addr_owned, %e, "BitChat error") } });
|
||||
self.protocol_channels.insert(ProtocolType::BitChat, proto_tx.clone());
|
||||
self.handles.push(ProtocolHandle { protocol: ProtocolType::BitChat, server: server.to_owned(), cmd_tx: proto_tx });
|
||||
let _ = self.event_tx.send(DispatcherEvent::ProtocolConnected { protocol: ProtocolType::BitChat, server: server.to_owned() }).await;
|
||||
Ok(())
|
||||
}
|
||||
// NOTE: connect_bitchat() was removed in 0.10.2. See NOTICES.md.
|
||||
|
||||
/// Connect Stout (Discord-API-compatible self-hosted platform).
|
||||
async fn connect_stout(&mut self, server: &str) -> anyhow::Result<()> {
|
||||
|
|
@ -550,7 +519,6 @@ impl Dispatcher {
|
|||
("IRC", ProtocolType::Irc),
|
||||
("Mtx", ProtocolType::Matrix),
|
||||
("ADC", ProtocolType::Adc),
|
||||
("P2P", ProtocolType::BitChat),
|
||||
("Dsc", ProtocolType::Discord),
|
||||
("Sto", ProtocolType::Stout),
|
||||
("Spc", ProtocolType::Spacebar),
|
||||
|
|
@ -579,7 +547,6 @@ impl Dispatcher {
|
|||
ProtocolType::Irc => ProtocolCommand::Irc(IrcCommand::Msg { target: target.clone(), body: body.clone() }),
|
||||
ProtocolType::Matrix => ProtocolCommand::Matrix(MatrixCommand::Msg { room_id: target.clone(), body: body.clone() }),
|
||||
ProtocolType::Adc => ProtocolCommand::Adc(AdcCommand::Msg { target_sid: target.clone(), body: body.clone() }),
|
||||
ProtocolType::BitChat => ProtocolCommand::BitChat(BitChatCommand::Chat { body: body.clone() }),
|
||||
ProtocolType::Discord => ProtocolCommand::Discord(DiscordCommand::Msg { channel_id: target.clone(), body: body.clone() }),
|
||||
ProtocolType::Stout => ProtocolCommand::Stout(StoutCommand::Msg { channel_id: target.clone(), body: body.clone() }),
|
||||
ProtocolType::Spacebar => ProtocolCommand::Spacebar(SpacebarCommand::Msg { channel_id: target.clone(), body: body.clone() }),
|
||||
|
|
@ -599,7 +566,6 @@ impl Dispatcher {
|
|||
ProtocolType::Spacebar => ProtocolCommand::Spacebar(SpacebarCommand::Emote { channel_id: target.clone(), body }),
|
||||
ProtocolType::Nerimity => ProtocolCommand::Nerimity(NerimityCommand::Emote { channel_id: target.clone(), body }),
|
||||
ProtocolType::Adc => ProtocolCommand::Adc(AdcCommand::Msg { target_sid: target.clone(), body: format!("* {} {}", nickname, body) }),
|
||||
ProtocolType::BitChat => ProtocolCommand::BitChat(BitChatCommand::Chat { body: format!("* {} {}", nickname, body) }),
|
||||
}).await;
|
||||
}
|
||||
Command::Names { channel } => { self.send_irc(IrcCommand::Names(channel)).await; }
|
||||
|
|
@ -720,7 +686,6 @@ impl Dispatcher {
|
|||
ProtocolType::Irc => ProtocolCommand::Irc(IrcCommand::Msg { target: target.clone(), body: msg.clone() }),
|
||||
ProtocolType::Matrix => ProtocolCommand::Matrix(MatrixCommand::Msg { room_id: target.clone(), body: msg.clone() }),
|
||||
ProtocolType::Adc => ProtocolCommand::Adc(AdcCommand::Msg { target_sid: target.clone(), body: msg.clone() }),
|
||||
ProtocolType::BitChat => ProtocolCommand::BitChat(BitChatCommand::Chat { body: msg.clone() }),
|
||||
ProtocolType::Discord => ProtocolCommand::Discord(DiscordCommand::Msg { channel_id: target.clone(), body: msg.clone() }),
|
||||
ProtocolType::Stout => ProtocolCommand::Stout(StoutCommand::Msg { channel_id: target.clone(), body: msg.clone() }),
|
||||
ProtocolType::Spacebar => ProtocolCommand::Spacebar(SpacebarCommand::Msg { channel_id: target.clone(), body: msg.clone() }),
|
||||
|
|
@ -1001,18 +966,17 @@ impl Dispatcher {
|
|||
self.send_to_protocol(ProtocolType::Nerimity,
|
||||
|_| ProtocolCommand::Nerimity(NerimityCommand::ListServers)).await;
|
||||
}
|
||||
// ─── BitChat (P2P) commands ─────────────────────
|
||||
Command::BitChatPeers => {
|
||||
self.send_to_protocol(ProtocolType::BitChat,
|
||||
|_| ProtocolCommand::BitChat(BitChatCommand::ListPeers)).await;
|
||||
}
|
||||
Command::BitChatDm { peer_id, body } => {
|
||||
self.send_to_protocol(ProtocolType::BitChat,
|
||||
|_| ProtocolCommand::BitChat(BitChatCommand::Direct { peer_id, body })).await;
|
||||
}
|
||||
Command::BitChatSendFile { peer_id, path } => {
|
||||
self.send_to_protocol(ProtocolType::BitChat,
|
||||
|_| ProtocolCommand::BitChat(BitChatCommand::SendFile { peer_id, path })).await;
|
||||
// NOTE: BitChat (P2P) command handlers removed in 0.10.2.
|
||||
// See NOTICES.md for the rationale.
|
||||
//
|
||||
// Media commands (0.10.1): /url, /video, /image are handled
|
||||
// entirely in main.rs's handle_user_command (they call
|
||||
// tui::media::open_external locally and never touch the
|
||||
// network). They should never reach the dispatcher, but the
|
||||
// match must be exhaustive — explicit no-op arms document
|
||||
// that intent and silence the compiler.
|
||||
Command::Url { .. } | Command::Video { .. } | Command::Image { .. } => {
|
||||
debug!("media command reached dispatcher — should have been handled locally");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1090,7 +1054,6 @@ impl Dispatcher {
|
|||
ProtocolType::Irc => ProtocolCommand::Irc(IrcCommand::Quit(None)),
|
||||
ProtocolType::Adc => ProtocolCommand::Adc(AdcCommand::Quit),
|
||||
ProtocolType::Matrix => ProtocolCommand::Matrix(MatrixCommand::Quit),
|
||||
ProtocolType::BitChat => ProtocolCommand::BitChat(BitChatCommand::Quit),
|
||||
ProtocolType::Discord => ProtocolCommand::Discord(DiscordCommand::Quit),
|
||||
ProtocolType::Stout => ProtocolCommand::Stout(StoutCommand::Quit),
|
||||
ProtocolType::Spacebar => ProtocolCommand::Spacebar(SpacebarCommand::Quit),
|
||||
|
|
|
|||
|
|
@ -80,7 +80,6 @@ fn sanitize_server(proto: ProtocolType, server: &str) -> String {
|
|||
ProtocolType::Irc => "irc",
|
||||
ProtocolType::Matrix => "matrix",
|
||||
ProtocolType::Adc => "adc",
|
||||
ProtocolType::BitChat => "bitchat",
|
||||
ProtocolType::Discord => "discord",
|
||||
ProtocolType::Stout => "stout",
|
||||
ProtocolType::Spacebar => "spacebar",
|
||||
|
|
@ -337,7 +336,7 @@ mod tests {
|
|||
"matrix_matrix.org"
|
||||
);
|
||||
assert_eq!(sanitize_server(ProtocolType::Adc, ""), "adc");
|
||||
assert_eq!(sanitize_server(ProtocolType::BitChat, ""), "bitchat");
|
||||
assert_eq!(sanitize_server(ProtocolType::Discord, ""), "discord");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
26
src/main.rs
26
src/main.rs
|
|
@ -357,9 +357,11 @@ async fn main() -> anyhow::Result<()> {
|
|||
info!(count = custom_keybindings.len(), "Custom keybindings loaded from config");
|
||||
}
|
||||
|
||||
// P2P identity
|
||||
let _p2p_keypair = engine::crypto::NoiseKeypair::generate();
|
||||
info!(fingerprint = %_p2p_keypair.fingerprint, "P2P identity key generated");
|
||||
// Noise identity keypair — used by ADC's encrypted C-C connections.
|
||||
// (BitChat P2P identity generation was removed in 0.10.2; the underlying
|
||||
// Noise primitive is retained because ADC's C-C connections use it.)
|
||||
let _noise_keypair = engine::crypto::NoiseKeypair::generate();
|
||||
info!(fingerprint = %_noise_keypair.fingerprint, "Noise identity key generated");
|
||||
|
||||
// Spawn dispatcher with server entries (for TLS/SASL config lookup)
|
||||
let mut dispatcher = Dispatcher::new(dispatcher_cmd_rx, event_tx, config.global.nickname.clone());
|
||||
|
|
@ -1415,11 +1417,14 @@ async fn handle_user_command(
|
|||
" /adc broadcast <msg> Broadcast to hub\n",
|
||||
" /adc get <sid> <path> Download file from user\n",
|
||||
"\n",
|
||||
"BitChat P2P (0.5.0):\n",
|
||||
" /connect bitchat <name> Connect (uses config for listen/bootstrap)\n",
|
||||
" /bitchat peers List discovered P2P peers\n",
|
||||
" /bitchat dm <peer> <msg> Send a direct message\n",
|
||||
" /bitchat send <peer> <f> Send a file via P2P\n",
|
||||
"Media (0.10.1):\n",
|
||||
" /url <url> Open URL in OS default browser\n",
|
||||
" /video <url> Launch video in OS default player\n",
|
||||
" /image <url> Attempt inline image (falls back to /url)\n",
|
||||
"\n",
|
||||
"Note: BitChat P2P support was removed in 0.10.2 pending clarity on\n",
|
||||
" Jack Dorsey's BitChat project and the India courts situation.\n",
|
||||
" See NOTICES.md for the full rationale.\n",
|
||||
);
|
||||
let msg = ChatMessage::notice(ProtocolType::Irc, "Status", help_text);
|
||||
ctx.app.route_message(msg);
|
||||
|
|
@ -1438,10 +1443,7 @@ async fn handle_user_command(
|
|||
Command::NerimityJoin { .. } | Command::NerimityLeave { .. } | Command::NerimityMembers { .. } | Command::NerimityServers => {
|
||||
let _ = dispatcher_cmd_tx.send(cmd.clone()).await;
|
||||
}
|
||||
Command::BitChatPeers | Command::BitChatDm { .. } | Command::BitChatSendFile { .. } => {
|
||||
// Forward BitChat commands to dispatcher.
|
||||
let _ = dispatcher_cmd_tx.send(cmd.clone()).await;
|
||||
}
|
||||
// NOTE: BitChat command handlers removed in 0.10.2. See NOTICES.md.
|
||||
Command::PluginList => {
|
||||
let plugins = plugin_manager.list_plugins();
|
||||
if plugins.is_empty() {
|
||||
|
|
|
|||
|
|
@ -1508,6 +1508,15 @@ async fn handle_command<W: AsyncWrite + Unpin>(
|
|||
// (NOTICE must never trigger an automated reply per RFC 1459).
|
||||
// This is why `/ctcp <nick> VERSION` was silently ignored.
|
||||
let req = request.as_str();
|
||||
// Build the local-notice label first (borrows `message`), then
|
||||
// consume `message` into the wire line. Doing it in this order
|
||||
// avoids the "use of moved value" that would occur if we built
|
||||
// the line first and then tried to read `message` again for the
|
||||
// label.
|
||||
let label = match &message {
|
||||
Some(msg) => format!("CTCP {req} to {target}: {msg}"),
|
||||
None => format!("CTCP {req} to {target}"),
|
||||
};
|
||||
let line = match message {
|
||||
Some(msg) => format!("PRIVMSG {} :\x01{} {}\x01\r\n", target, req, msg),
|
||||
None => format!("PRIVMSG {} :\x01{}\x01\r\n", target, req),
|
||||
|
|
@ -1517,10 +1526,6 @@ async fn handle_command<W: AsyncWrite + Unpin>(
|
|||
// Surface a local notice so the user sees the request was sent,
|
||||
// even before the reply arrives. This also makes self-targeted
|
||||
// CTCP queries (e.g. `/ctcp mynick VERSION`) visible.
|
||||
let label = match message {
|
||||
Some(msg) => format!("CTCP {req} to {target}: {msg}"),
|
||||
None => format!("CTCP {req} to {target}"),
|
||||
};
|
||||
let _ = tx.send(ChatMessage::notice(
|
||||
ProtocolType::Irc, server, &label,
|
||||
)).await;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
pub mod adc;
|
||||
pub mod bitchat;
|
||||
pub mod discord;
|
||||
pub mod irc;
|
||||
pub mod matrix;
|
||||
|
|
@ -7,11 +6,14 @@ pub mod stout;
|
|||
pub mod spacebar;
|
||||
pub mod nerimity;
|
||||
|
||||
// NOTE: BitChat was removed in 0.10.2 pending clarity on Jack Dorsey's
|
||||
// BitChat project and the India courts situation. The legal status of the
|
||||
// protocol and its reference implementation is unclear; we will re-evaluate
|
||||
// when the dust settles. See NOTICES.md for the full rationale.
|
||||
|
||||
#[allow(unused_imports)]
|
||||
pub use adc::{AdcCommand, AdcConfig, AdcMsgType, AdcMessage, run_adc, parse_adc_message, adc_escape, adc_unescape, inf_field};
|
||||
#[allow(unused_imports)]
|
||||
pub use bitchat::{BitChatCommand, BitChatConfig, BitChatMessage, run_bitchat, CHAT_TOPIC};
|
||||
#[allow(unused_imports)]
|
||||
pub use discord::{DiscordCommand, DiscordConfig, run_discord};
|
||||
#[allow(unused_imports)]
|
||||
pub use irc::{IrcCommand, IrcConfig, parse_irc_message, run_irc, DccEvent, DccSendOffer, parse_dcc_send, parse_dcc_accept};
|
||||
|
|
|
|||
|
|
@ -604,7 +604,7 @@ mod tests {
|
|||
|
||||
let (tx, _rx) = mpsc::channel(16);
|
||||
let mgr = TransferManager::new(tx);
|
||||
let id = mgr.queue_send(ProtocolType::BitChat, "peer", &src_path).unwrap();
|
||||
let id = mgr.queue_send(ProtocolType::Adc, "peer", &src_path).unwrap();
|
||||
|
||||
let cancel = tokio_util::sync::CancellationToken::new();
|
||||
|
||||
|
|
|
|||
|
|
@ -151,7 +151,6 @@ impl ChatView {
|
|||
ProtocolType::Irc => Color::DarkGray,
|
||||
ProtocolType::Matrix => Color::Magenta,
|
||||
ProtocolType::Adc => Color::Blue,
|
||||
ProtocolType::BitChat => Color::Green,
|
||||
ProtocolType::Discord => Color::Gray,
|
||||
ProtocolType::Stout => Color::Yellow,
|
||||
ProtocolType::Spacebar => Color::Red,
|
||||
|
|
@ -165,7 +164,6 @@ impl ChatView {
|
|||
ProtocolType::Irc => self.palette.event_fg,
|
||||
ProtocolType::Matrix => NaimColor::Magenta,
|
||||
ProtocolType::Adc => NaimColor::Blue,
|
||||
ProtocolType::BitChat => NaimColor::Green,
|
||||
ProtocolType::Discord => NaimColor::White,
|
||||
ProtocolType::Stout => NaimColor::Yellow,
|
||||
ProtocolType::Spacebar => NaimColor::Red,
|
||||
|
|
|
|||
|
|
@ -338,12 +338,12 @@ fn default_menus() -> Vec<MenuBarEntry> {
|
|||
MI { label: "Redraw Screen\tCtrl-L".into(), action: "__internal:ctrl_l".into(), separator: false },
|
||||
]),
|
||||
// ── Connect ─────────────────────────────────────────────────
|
||||
// NOTE: BitChat menu entry removed in 0.10.2. See NOTICES.md.
|
||||
MenuBarEntry::new("Connect", vec![
|
||||
MI { label: "IRC…\t__prompt:/connect irc ".into(), action: "__prompt:/connect irc ".into(), separator: false },
|
||||
MI { label: "Matrix…\t__prompt:/connect matrix ".into(), action: "__prompt:/connect matrix ".into(), separator: false },
|
||||
MI { label: "ADC/DC++…\t__prompt:/connect adc ".into(), action: "__prompt:/connect adc ".into(), separator: false },
|
||||
MI { label: "Discord…\t__prompt:/connect discord ".into(), action: "__prompt:/connect discord ".into(), separator: false },
|
||||
MI { label: "BitChat…\t__prompt:/connect bitchat ".into(), action: "__prompt:/connect bitchat ".into(), separator: false },
|
||||
MI { label: String::new(), action: String::new(), separator: true },
|
||||
MI { label: "Server List\t__server_list".into(), action: "__server_list".into(), separator: false },
|
||||
MI { label: "Disconnect All\t/disconnect".into(), action: "/disconnect".into(), separator: false },
|
||||
|
|
|
|||
|
|
@ -8,14 +8,15 @@
|
|||
//!
|
||||
//! Each entry is prefixed with a 1-character protocol badge followed by a
|
||||
//! space, colored by protocol (IRC=Cyan I, Matrix=Magenta M, ADC=Blue A,
|
||||
//! BitChat=Green P). This makes it visually obvious which protocol a tab
|
||||
//! belongs to when multiple protocols are connected simultaneously.
|
||||
//! Discord=White D, Stout=Yellow S, Spacebar=Red S, Nerimity=Magenta N).
|
||||
//! This makes it visually obvious which protocol a tab belongs to when
|
||||
//! multiple protocols are connected simultaneously.
|
||||
//!
|
||||
//! ```
|
||||
//! ┌ IRC
|
||||
//! ├> I #rust ← IRC channel, waiting/unread (cyan badge)
|
||||
//! │ M matrix-room← Matrix room, current (magenta badge)
|
||||
//! └ P p2p-room ← BitChat room, last entry (green badge)
|
||||
//! └ D discord ← Discord channel, last entry (white badge)
|
||||
//! ```
|
||||
//!
|
||||
//! Badges are suppressed when `content_width < 8` or when `with_badges(false)`
|
||||
|
|
@ -215,7 +216,6 @@ impl Widget for WinlistWidget<'_> {
|
|||
ProtocolType::Irc => "I",
|
||||
ProtocolType::Matrix => "M",
|
||||
ProtocolType::Adc => "A",
|
||||
ProtocolType::BitChat => "P",
|
||||
ProtocolType::Discord => "D",
|
||||
ProtocolType::Stout => "S",
|
||||
ProtocolType::Spacebar => "S",
|
||||
|
|
@ -225,7 +225,6 @@ impl Widget for WinlistWidget<'_> {
|
|||
ProtocolType::Irc => NaimColor::Cyan,
|
||||
ProtocolType::Matrix => NaimColor::Magenta,
|
||||
ProtocolType::Adc => NaimColor::Blue,
|
||||
ProtocolType::BitChat => NaimColor::Green,
|
||||
ProtocolType::Discord => NaimColor::White,
|
||||
ProtocolType::Stout => NaimColor::Yellow,
|
||||
ProtocolType::Spacebar => NaimColor::Red,
|
||||
|
|
|
|||
Loading…
Reference in New Issue