Freeq Feature List
This document catalogs every feature implemented in Freeq, organized by category. Features unique to Freeq (not present in classic IRC) are marked with 🆕. Features that extend or modify standard IRC behavior are marked with 🔧. Standard IRC features are unmarked.
1. IRC Protocol — Core
Connection & Registration
| Feature |
Status |
Notes |
| NICK / USER registration |
✅ |
Standard IRC registration flow |
| NICK change after registration |
✅ |
Broadcasts :old NICK :new to user + shared channels + S2S |
| PING / PONG keepalive |
✅ |
Both client→server and server→client |
| QUIT with reason broadcast |
✅ |
Broadcasts to all shared channels |
| Connection timeout detection |
✅ |
90s ping interval, 180s timeout |
| Rate limiting (token bucket) |
✅ |
10 cmd/sec; exempt during registration |
| ERR_UNKNOWNCOMMAND (421) |
✅ |
For unrecognized commands |
Channels
| Feature |
Status |
Notes |
| JOIN (single and multi-channel) |
✅ |
JOIN #a,#b with per-channel keys |
| PART (single and multi-channel) |
✅ |
|
| PRIVMSG to channels |
✅ |
|
| PRIVMSG to users (PM) |
✅ |
|
| NOTICE to channels and users |
✅ |
|
CTCP ACTION (/me) |
✅ |
Via \x01ACTION ...\x01 |
| TOPIC query and set |
✅ |
RPL_TOPIC (332), RPL_TOPICWHOTIME (333), RPL_NOTOPIC (331) |
| NAMES (353/366) |
✅ |
With @ and + prefixes for ops/voiced |
| LIST (322/323) |
✅ |
Channel list with member counts and topics |
| WHO (352/315) |
✅ |
Per-channel and global, shows DID/handle for authenticated users |
| AWAY (301/305/306) |
✅ |
Sets/clears away, RPL_AWAY on PM |
| MOTD (375/372/376) |
✅ |
On registration + standalone command |
| KICK |
✅ |
With reason, proper numeric errors |
| INVITE |
✅ |
RPL_INVITING (341), notifies target |
Channel Modes
| Mode |
Status |
Notes |
+o / -o (channel operator) |
✅ |
|
+v / -v (voice) |
✅ |
|
+b / -b (ban) |
✅ |
Hostmask + DID wildcard matching |
+i / -i (invite-only) |
✅ |
|
+t / -t (topic lock) |
✅ |
Only ops can set topic when enabled |
+k / -k (channel key) |
✅ |
Password required to join |
+n / -n (no external messages) |
✅ |
Non-members can't send to channel |
+m / -m (moderated) |
✅ |
Only ops/voiced can speak |
| MODE query (324) |
✅ |
Lists current channel modes |
Ban list query (+b no arg) |
✅ |
RPL_BANLIST (367), RPL_ENDOFBANLIST (368) |
User Modes
| Feature |
Status |
Notes |
| User mode query (221) |
✅ |
Returns + (no user modes implemented) |
WHOIS
| Feature |
Status |
Notes |
| RPL_WHOISUSER (311) |
✅ |
|
| RPL_WHOISSERVER (312) |
✅ |
|
| RPL_ENDOFWHOIS (318) |
✅ |
|
| RPL_WHOISACCOUNT (330) |
🆕 |
Shows authenticated DID |
| Custom 671: AT Protocol handle |
🆕 |
Shows resolved Bluesky handle |
| Custom 672: iroh endpoint |
🆕 |
Shows P2P iroh endpoint ID |
| RPL_WHOISCHANNELS (319) |
✅ |
For remote S2S users |
| Feature |
Status |
Notes |
| VERSION (351) |
✅ |
Server version and feature summary |
| TIME (391) |
✅ |
Server UTC time |
| LUSERS (251-255) |
✅ |
User/channel/server counts, local + remote |
| ADMIN (256-259) |
✅ |
Server admin info |
| INFO (371/374) |
✅ |
Server description and links |
| USERHOST (302) |
✅ |
Up to 5 nicks, with op status |
| ISON (303) |
✅ |
Online presence check |
Missing Standard IRC Commands
| Feature |
Status |
Notes |
| OPER (server operator) |
❌ |
Not implemented |
| WALLOPS |
❌ |
Not implemented |
| LINKS |
❌ |
Not implemented |
| STATS |
❌ |
Not implemented |
Channel modes: +s / +p (secret/private) |
❌ |
Not implemented |
Channel modes: +l (user limit) |
❌ |
Not implemented |
| Hostname cloaking |
❌ |
|
| Reverse DNS lookup |
❌ |
|
| K-line / G-line (server bans) |
❌ |
|
2. IRCv3 Capabilities
| Feature |
Status |
Notes |
| CAP LS / REQ / ACK / NAK / END |
✅ |
IRCv3 capability negotiation |
sasl capability |
✅ |
With ATPROTO-CHALLENGE mechanism |
message-tags capability |
✅ |
Tag-aware routing per client |
server-time capability |
✅ |
Timestamps on history replay |
batch capability |
✅ |
History wrapped in chathistory batch |
multi-prefix capability |
✅ |
Shows all prefix chars in NAMES |
echo-message capability |
✅ |
Echoes own messages to negotiated clients |
| TAGMSG (tags-only messages) |
✅ |
With fallback for plain clients |
iroh=<id> CAP advertisement |
🆕 |
Transport discovery via CAP LS |
SASL AUTHENTICATE * abort |
✅ |
Cleanly aborts SASL negotiation |
| account-notify capability | ✅ | Broadcasts ACCOUNT on auth to shared channels |
| extended-join capability | ✅ | JOIN includes account + realname |
| draft/chathistory capability | ✅ | On-demand CHATHISTORY LATEST/BEFORE/AFTER |
Missing IRCv3 Extensions
| Feature |
Status |
Notes |
away-notify |
❌ |
|
account-tag |
❌ |
|
labeled-response |
❌ |
|
invite-notify |
❌ |
|
chghost |
❌ |
|
cap-notify |
❌ |
|
setname |
❌ |
|
standard-replies |
❌ |
|
msgid (message IDs) |
❌ |
|
3. Authentication — SASL ATPROTO-CHALLENGE 🆕
| Feature |
Status |
Notes |
| Challenge-response SASL flow |
✅ |
Custom ATPROTO-CHALLENGE mechanism |
| Cryptographically random nonce (32 bytes) |
✅ |
Per challenge |
| Challenge single-use enforcement |
✅ |
Consumed on take, replay blocked |
| Configurable challenge timeout |
✅ |
Default 60s, --challenge-timeout-secs |
| JSON-encoded challenges |
✅ |
Deviation from binary: for debuggability |
| RPL_LOGGEDIN (900) |
✅ |
|
| RPL_SASLSUCCESS (903) |
✅ |
|
| ERR_SASLFAIL (904) |
✅ |
|
| Guest fallback (no SASL) |
✅ |
Standard IRC clients work unmodified |
Verification Methods
| Method |
Status |
Notes |
crypto (DID document key signature) |
✅ |
Signs raw challenge bytes |
pds-session (app password Bearer JWT) |
✅ |
Verifies via PDS getSession |
pds-oauth (DPoP-bound access token) |
✅ |
DPoP proof forwarded to PDS |
Key Types
| Key Type |
Status |
Notes |
| secp256k1 |
✅ |
MUST per spec — compressed SEC1 encoding |
| ed25519 |
✅ |
SHOULD per spec |
| Multibase/multicodec parsing |
✅ |
z prefix (base58btc), proper varint codecs |
DID Resolution
| Feature |
Status |
Notes |
did:plc resolution (plc.directory) |
✅ |
|
did:web resolution |
✅ |
Including path-based DIDs |
Handle resolution (.well-known/atproto-did) |
✅ |
|
| PDS endpoint extraction from DID doc |
✅ |
AtprotoPersonalDataServer service type |
| PDS URL verification (claimed vs doc) |
✅ |
Prevents spoofing |
| Authentication key extraction |
✅ |
From authentication + assertionMethod |
| Static resolver (testing) |
✅ |
In-memory DID document map |
4. DID-Aware IRC Features 🆕
| Feature |
Status |
Notes |
DID-based bans (MODE +b did:plc:xyz) |
✅ |
Identity-based, survives nick changes |
| DID-based invites |
✅ |
Stored by DID, survive reconnect |
| Nick ownership (DID binding) |
✅ |
Persisted across restarts |
| Nick enforcement at registration |
✅ |
Non-owners renamed to GuestXXXX |
| Persistent DID-based channel ops |
✅ |
Auto-op on rejoin by DID, persisted in DB |
| Channel founder (first authenticated user) |
✅ |
Can't be de-opped, persisted in DB |
| DID in WHOIS output |
✅ |
Numeric 330 |
| AT handle in WHOIS output |
✅ |
Resolved asynchronously from DID doc |
| Auto-op on empty channel rejoin |
✅ |
First user joining empty+zero-ops channel gets ops |
5. Transport Stack
TCP / TLS (Standard)
| Feature |
Status |
Notes |
| Plain TCP (port 6667) |
✅ |
|
| TLS (port 6697) |
✅ |
rustls with configurable cert/key |
| Auto-detect TLS by port (client) |
✅ |
Port 6697 → TLS |
| Self-signed cert support (client) |
✅ |
--tls-insecure flag |
WebSocket 🆕
| Feature |
Status |
Notes |
WebSocket IRC transport (/irc) |
✅ |
IRC-over-WS, not a new protocol |
| Text frame ↔ IRC line bridging |
✅ |
One line per frame, \r\n handling |
--web-addr opt-in |
✅ |
Zero-cost when disabled |
| HTML test client |
✅ |
test-client.html |
Iroh QUIC Transport 🆕
| Feature |
Status |
Notes |
| Iroh endpoint for IRC connections |
✅ |
ALPN: freeq/iroh/1 |
Persistent secret key (iroh-key.secret) |
✅ |
Stable endpoint ID across restarts |
| Iroh endpoint stored in SharedState |
✅ |
Proper lifetime (no mem::forget) |
| NAT hole-punching + relay fallback |
✅ |
Via iroh's infrastructure |
| Transport-agnostic handler |
✅ |
All transports → handle_generic() |
| Iroh ID in CAP LS for auto-discovery |
✅ |
iroh=<endpoint-id> |
| Client auto-upgrade to iroh |
✅ |
Probes CAP LS, reconnects via iroh |
| Configurable iroh UDP port |
✅ |
--iroh-port |
| Connection held alive for session |
✅ |
Explicit close with CONNECTION_CLOSE frame |
| Bridge task abort on disconnect |
✅ |
Clean cleanup |
6. End-to-End Encryption (E2EE) 🆕
| Feature |
Status |
Notes |
| AES-256-GCM channel encryption |
✅ |
Per-channel passphrase |
| HKDF-SHA256 key derivation |
✅ |
Channel-name-salted |
Wire format: ENC1:<nonce>:<ciphertext> |
✅ |
Version-tagged, base64 encoded |
| Server-transparent relay |
✅ |
Server sees ciphertext only |
/encrypt and /decrypt commands |
✅ |
TUI commands |
| Unicode passphrase support |
✅ |
|
| Tamper detection (GCM auth tag) |
✅ |
|
DID-Based E2EE (ENC2) 🆕
| Feature |
Status |
Notes |
| Identity-bound group encryption |
✅ |
Key derived from sorted member DIDs |
Wire format: ENC2:<epoch>:<nonce>:<ct> |
✅ |
Epoch tracks membership changes |
| Group key rotation on member change |
✅ |
New epoch = new key |
| ECDH DM encryption (secp256k1) |
✅ |
Pairwise key from DID document keys |
Wire format: ENC2:dm:<nonce>:<ct> |
✅ |
DM variant |
| DID-sorted deterministic derivation |
✅ |
Same members = same key regardless of order |
7. Peer-to-Peer Encrypted DMs 🆕
| Feature |
Status |
Notes |
| Client-side iroh endpoint for DMs |
✅ |
ALPN: freeq/p2p-dm/1 |
| Direct encrypted QUIC connections |
✅ |
Server-free |
/p2p start/id/connect/msg commands |
✅ |
TUI commands |
| Newline-delimited JSON wire format |
✅ |
Not IRC protocol |
Dedicated p2p:<id> TUI buffers |
✅ |
|
| Iroh endpoint ID in WHOIS (672) |
✅ |
For peer discovery |
8. Server-to-Server Federation (S2S) 🆕
| Feature |
Status |
Notes |
| Iroh QUIC-based S2S links |
✅ |
ALPN: freeq/s2s/1 |
--s2s-peers CLI option |
✅ |
Connect to peers on startup |
| Incoming S2S acceptance (when iroh enabled) |
✅ |
|
| ALPN-based routing (client vs S2S) |
✅ |
|
| Origin tracking (loop prevention) |
✅ |
origin field in S2S messages |
| Newline-delimited JSON S2S protocol |
✅ |
|
| Auto-reconnection with exponential backoff |
✅ |
1s→60s cap, connect_peer_with_retry() |
| Diagnostic logging (byte/message counts) |
✅ |
Which side ended link, close reasons |
What Syncs
| Feature |
Status |
Notes |
| PRIVMSG relay |
✅ |
Channel messages, enforces +n/+m |
| JOIN / PART / QUIT propagation |
✅ |
Membership tracking per origin server |
| NICK change propagation |
✅ |
Updates remote_members map in all channels |
| TOPIC sync |
✅ |
Enforces +t on incoming S2S topics |
| MODE sync (real-time) |
✅ |
+t/+i/+n/+m/+k broadcast via S2S Mode message |
| MODE sync (SyncResponse) |
✅ |
Full state replacement (not additive) |
| Remote member tracking |
✅ |
remote_members with DID, handle, is_op |
| SyncRequest / SyncResponse |
✅ |
Initial state exchange with rich nick_info |
| NAMES includes remote members |
✅ |
With op status from home server + DID-based |
| WHOIS for remote users |
✅ |
Shows DID, handle, origin |
| DID-based ops sync |
✅ |
Union merge |
| Founder sync (first-write-wins) |
✅ |
No timestamp dependency |
| ChannelCreated propagation |
✅ |
Founder + DID ops + created_at |
CRDT State Layer (Automerge)
| Feature |
Status |
Notes |
| Flat-key Automerge document |
✅ |
Avoids nested-map conflicts |
| Channel membership CRDT |
✅ |
member:{channel}:{nick} |
| Topic CRDT (LWW) |
✅ |
|
| Ban CRDT (add/remove) |
✅ |
|
| Nick ownership CRDT |
✅ |
|
| Founder CRDT (first-write-wins) |
✅ |
Conditional put, deterministic convergence |
| DID ops CRDT (grant/revoke) |
✅ |
|
| Sync message generation/receipt |
✅ |
Automerge sync protocol |
| Save/load from bytes |
✅ |
|
| 🆕 Live CRDT sync via S2S |
✅ |
CrdtSync message type; mutations written to CRDT alongside in-memory state; Automerge sync messages exchanged on link establishment and after each remote sync |
S2S Limitations (see also docs/s2s-audit.md)
| Limitation |
Notes |
| Bans not propagated cross-server |
Only local bans enforced |
| S2S Join doesn't check bans or +i |
Remote server should enforce |
| ChannelCreated race in narrow window |
Both servers may create simultaneously |
Rogue server can add did_ops |
Authorization-on-write not implemented |
9. Persistence (SQLite)
| Feature |
Status |
Notes |
--db-path opt-in |
✅ |
In-memory by default |
| WAL mode |
✅ |
Good concurrent read performance |
| Message history storage |
✅ |
All channel messages |
| Channel state persistence |
✅ |
Topics, modes (+t/+i/+k/+n/+m), keys |
| Ban persistence |
✅ |
Hostmask and DID bans |
| DID-nick identity bindings |
✅ |
Survive restarts |
| DID-based ops persistence |
✅ |
did_ops_json column |
| Founder persistence |
✅ |
founder_did column |
| History replay on JOIN |
✅ |
Last 100 messages with server-time + batch |
| Message pruning |
✅ |
--max-messages-per-channel config |
| Idempotent DB migration |
✅ |
ALTER TABLE ADD COLUMN on startup |
| Graceful persistence failures |
✅ |
Logged, don't crash server |
| Load persisted state on startup |
✅ |
Channels, bans, messages, identities |
Persistence Gaps
| Gap |
Notes |
No --message-retention-days |
Only count-based pruning |
| No full-text search |
SQLite FTS5 not wired up |
10. REST API 🆕
| Endpoint |
Status |
Notes |
GET /api/v1/health |
✅ |
Server stats |
GET /api/v1/channels |
✅ |
List all channels |
GET /api/v1/channels/{name}/history |
✅ |
Paginated, ?limit=N&before=T |
GET /api/v1/channels/{name}/topic |
✅ |
|
GET /api/v1/users/{nick} |
✅ |
Online status, DID, handle |
GET /api/v1/users/{nick}/whois |
✅ |
+ channels |
| CORS support |
✅ |
Permissive by default |
| Read-only (no write endpoints) |
✅ |
By design |
| Feature |
Status |
Notes |
| Media attachment tags |
✅ |
content-type, media-url, media-alt, etc. |
| Multipart/alternative semantics |
✅ |
Tags for rich clients, body for plain clients |
| Link preview tags |
✅ |
text/x-link-preview content type |
Reaction tags (+react) |
✅ |
With TAGMSG, fallback ACTION for plain clients |
| Media upload to AT Protocol PDS |
✅ |
Blob upload + record pinning |
blue.irc.media custom lexicon |
✅ |
Prevents blob GC, doesn't pollute feed |
| Optional cross-post to Bluesky feed |
✅ |
|
| OpenGraph link preview fetching |
✅ |
HTML parsing, 64KB limit |
| CDN URL generation (bsky.app) |
✅ |
|
| DPoP nonce retry for PDS uploads |
✅ |
Up to 3 attempts |
| Tag escaping/unescaping (IRCv3 spec) |
✅ |
\:, \s, \\, \r, \n |
12. OAuth 2.0 (AT Protocol) 🆕
| Feature |
Status |
Notes |
| Browser-based OAuth login |
✅ |
Opens system browser |
| Authorization server discovery |
✅ |
Protected resource metadata → AS metadata |
| Pushed Authorization Request (PAR) |
✅ |
Required by Bluesky |
| PKCE (S256) |
✅ |
|
| DPoP key generation (P-256 / ES256) |
✅ |
|
| DPoP proof creation (RFC 9449) |
✅ |
With ath claim |
| DPoP nonce discovery and retry |
✅ |
|
| Token exchange |
✅ |
|
| Token refresh |
✅ |
PdsSessionSigner with RwLock interior mutability |
| Session caching to disk |
✅ |
~/.config/freeq-tui/<handle>.session.json |
| Cached session validation |
✅ |
Probes PDS on reuse |
| Restrictive file permissions (0600) |
✅ |
|
| Handle → DID → PDS resolution |
✅ |
|
13. TUI Client
Buffers & Navigation
| Feature |
Status |
Notes |
| Multi-buffer UI (status + channels + PMs) |
✅ |
|
| Buffer switching (Ctrl-N/P, Alt-N/P, Shift-Tab) |
✅ |
|
| Auto-buffer creation on JOIN/PM |
✅ |
|
| P2P DM dedicated buffers |
✅ |
p2p:<short-id> |
| Unread indicator (●) |
✅ |
|
| PageUp/PageDown scroll |
✅ |
|
| Channel member list in buffer |
✅ |
|
| Feature |
Status |
Notes |
| Emacs keybindings (default) |
✅ |
Full readline-style |
Vi mode (--vi) |
✅ |
Normal + Insert modes |
| Kill ring (Ctrl-K/U/W/Y) |
✅ |
|
| Word motion (Alt-F/B/D) |
✅ |
|
| Case transforms (Alt-U/L/C) |
✅ |
|
| Transpose (Ctrl-T) |
✅ |
|
| Tab nick completion |
✅ |
|
| Input history (Up/Down) |
✅ |
|
Display
| Feature |
Status |
Notes |
| Status bar (transport, nick, auth, uptime) |
✅ |
|
| Transport badge (color-coded) |
✅ |
Red=TCP, Green=TLS, Cyan=WS, Magenta=Iroh |
Network info popup (/net) |
✅ |
|
Debug mode (/debug) |
✅ |
Raw IRC lines |
| Rich media display (🖼 badge) |
✅ |
Image/video/audio formatting |
| E2EE status display |
✅ |
🔒 prefix on encrypted channels |
Commands (45+ total)
/join, /part, /msg, /me, /topic, /mode, /op, /deop, /voice, /kick, /ban, /unban, /invite, /whois, /names, /who, /list, /away, /motd, /nick, /raw, /encrypt, /decrypt, /p2p start, /p2p id, /p2p connect, /p2p msg, /net, /debug, /quit, /help, /commands, plus MODE variants (+o/-o, +v/-v, +b/-b, +i/-i, +t/-t, +k/-k, +n/-n, +m/-m).
14. SDK
| Feature |
Status |
Notes |
(ClientHandle, Receiver<Event>) pattern |
✅ |
Any UI/bot can consume |
Pluggable ChallengeSigner trait |
✅ |
KeySigner, PdsSessionSigner, StubSigner |
PdsSessionSigner with token refresh |
✅ |
RwLock interior mutability, new_with_refresh() |
establish_connection() pre-TUI |
✅ |
Connection errors before UI starts |
Iroh auto-discovery (discover_iroh_id) |
✅ |
Probe CAP LS for iroh upgrade |
| Tagged message sending |
✅ |
send_tagged, send_media, send_reaction |
| P2P DM subsystem |
✅ |
Full lifecycle management |
| E2EE encrypt/decrypt |
✅ |
Library functions |
| DID resolution |
✅ |
HTTP and static resolvers |
| Crypto key generation and signing |
✅ |
secp256k1 + ed25519 |
| PDS client (create session, verify) |
✅ |
|
| Bluesky profile fetching |
✅ |
Public API, no auth needed |
| Media upload to PDS |
✅ |
With DPoP retry |
| Link preview fetching |
✅ |
OpenGraph parsing |
| 🆕 Bot framework |
✅ |
Command routing, permission levels (Anyone/Auth/Admin), auto-help |
| 🆕 DID-based E2EE (ENC2) |
✅ |
Group key + ECDH DM encryption |
| Echo bot example |
✅ |
examples/echo_bot.rs |
| Framework bot example |
✅ |
examples/framework_bot.rs — commands with permissions |
| IRC message parser with tag support |
✅ |
|
15. Testing
| Category |
Count |
Notes |
| SDK unit tests |
35 |
IRC parsing, crypto, DID, media, auth |
| Server unit tests |
33 |
Parsing, SASL, channel state, DB, CRDT |
| Integration tests |
27 |
End-to-end auth flows, channel ops, persistence |
| S2S acceptance tests |
39 |
16 single-server + 14 S2S + 9 netsplit/reconnect |
| Total |
134 |
|
16. Configuration
| Option |
Default |
Notes |
--listen-addr |
127.0.0.1:6667 |
Plain TCP |
--tls-listen-addr |
127.0.0.1:6697 |
TLS |
--tls-cert / --tls-key |
None |
Enables TLS |
--server-name |
freeq |
|
--challenge-timeout-secs |
60 |
|
--db-path |
None (in-memory) |
|
--web-addr |
None |
Enables HTTP/WS |
--iroh |
false |
Enables iroh |
--iroh-port |
random |
|
--s2s-peers |
empty |
Comma-separated endpoint IDs |
--max-messages-per-channel |
None |
Message pruning |
--plugin |
None |
Load a plugin by name (repeatable) |
--plugin-dir |
None |
Directory of *.toml plugin configs |
17. Plugin System 🆕
| Feature |
Status |
Notes |
Plugin trait with event hooks |
✅ |
Extensible server behavior |
PluginManager in SharedState |
✅ |
Dispatches events to all loaded plugins |
CLI activation (--plugin name:k=v) |
✅ |
Inline config via key=value pairs |
Directory loading (--plugin-dir) |
✅ |
Each *.toml file = one plugin |
| TOML config format |
✅ |
Supports multi-rule plugins |
on_auth hook |
✅ |
Override DID/handle after SASL auth |
identity-override built-in plugin |
✅ |
Match by handle or DID, replace display ID |
Example: examples/plugins/kurt.toml |
✅ |
TimeSync.bsky.social → 3|337 |