# v0.2.15 | EchoHub Documentation

# v0.2.15

A big one. Server owners can gate registration behind invite codes, users can export or delete everything the server holds about them, and chat gets three classics — `/me` actions that interop natively with IRC, `/banner` ASCII text, and message replies with quote rendering and jump-to-original. Images become properly shareable: every image gets an [open] action that views it in your browser (or, in end-to-end encrypted rooms, decrypts and opens it locally). The IRC gateway stops painting ANSI art nobody asked for and posts plain image links any IRC client can open or auto-preview. And a long-standing dual-session annoyance is fixed: messages you send from the TUI now reach your own connected IRC client instantly.

## New Features

- Invite-gated registration — set `Server:Registration` to `"invite"` (or `"closed"`) in `appsettings.json` and new accounts require a valid invite code; the default `"open"` keeps today's behavior. Admins mint codes with `/invite [uses] [hours]` (single-use, never-expiring by default), list them with `/invite list`, and revoke with `/invite revoke <code>`. Codes are unguessable (`K7QM-3XPF` style), stored only in your server's own database, and consumed atomically so a code's last use can't be taken twice. The IRC gateway respects the gate too: unknown nicknames connecting to an invite-only server get a clear error instead of a silent auto-registered account. The very first account (server owner bootstrap) is always allowed.
- Data export — `/export` downloads everything the server stores about you (profile, your messages, attachment metadata) as JSON into your download folder. Content from end-to-end encrypted rooms appears as ciphertext, because the server never had the plaintext — "you own the data", demonstrably.
- Account deletion — `/deleteaccount` permanently removes your account after a typed confirmation and password re-check: profile, sessions, and every file you uploaded are deleted. Your messages remain (deleting them would gut other people's conversations) but are re-attributed to a reserved `deleted-user` name. The last remaining Owner can't self-delete.
- `/me` actions — `/me waves` renders as `* nick waves`. On the wire it's a real CTCP ACTION, so irssi/WeeChat users see a native action line, and their `/me` renders properly in the TUI. Works in encrypted rooms (the marker encrypts with the text).
- `/banner <text>` — renders short text (up to 20 characters) as a 5-row block-letter banner. Rendered locally with a built-in font — no network, no dependencies — and sent as plain message content, so it works over IRC and in encrypted rooms.
- Message replies — right-click a message (or F6-select) and pick Reply: the input shows a "Replying to" strip (Esc cancels), and the sent message renders with a dim `┌ nick: snippet` quote line. Activating the quote line jumps to the original message. Being replied to lights up the channel like an @mention. IRC clients see the familiar `> nick: snippet | text` convention. In encrypted rooms the quoted snippet is decrypted client-side with the room key — the server still never reads room content.
- `[open]` images without saving them — every image attachment now shows `[open] [↓ save original]` beneath its preview. Open views the image in your default browser straight from the server; in end-to-end encrypted rooms (where a browser would only see ciphertext) the client downloads, decrypts with the room key, and opens the image in your OS viewer from a temp file instead. Both actions are individually clickable, Enter on the line opens, and the right-click menu carries both.
- Attachment links work in a browser — `GET /api/files/{id}` is now a capability URL: the unguessable GUID in the link is the access token (Discord-CDN style), so attachment links can be opened directly in a browser or shared to IRC without a login token. Images and audio are served inline so the browser displays them instead of forcing a download. Blobs from encrypted rooms remain ciphertext, so their links reveal nothing.
- IRC gets image links instead of terminal art — the gateway no longer floods IRC clients with truecolor-ANSI ASCII art for images. Each attachment is now a single line — `[Image: photo.png] https://your-server/api/files/…` — the convention every IRC client understands, and ones like TheLounge or IRCCloud auto-preview. Set the new `Irc:PublicBaseUrl` option (e.g. `"https://chat.example.com"`) so those links come out absolute; unset, they fall back to relative paths as before.
- Spam protection — per-user message flood and duplicate-message limits, join and channel-creation throttles, and automatic escalation: enough rejected sends in a few minutes earns a timed auto-mute through the normal mute system (moderators see it, and it expires on its own). One guard covers every ingress — TUI and IRC clients hit the same limits. Mods and above are exempt, everything is configurable under the new `Spam` section (`Spam:Enabled` master switch, lenient defaults a fast typist won't trip), and the guard only ever sees stored content — encrypted-room messages stay ciphertext.

## Improvements

- Strict `/status` — `/status garbage` is now an error instead of silently becoming your status message (and resetting you to Online). Status messages moved to `/status msg <text>`, which preserves your current status; `/status msg` with no text clears it. The server also rejects out-of-range status values sent by misbehaving clients.
- Registration dialog — gained Invite Code support, and the Display Name field you could already type into is now actually sent with registration.

## Bug Fixes

- Messages sent from the TUI now reach your own IRC session. With the same account online via both the TUI and an IRC client, messages sent from the TUI never appeared in the IRC client until it reconnected (which replayed history). The gateway suppressed the sender's echo by nickname, which swallowed the message for every IRC connection on that account — it now excludes only the exact connection a message originated from, so all your other sessions (a second IRC client included) receive it immediately.
- Channel deleted event — when a channel is deleted, all connected clients are now immediately notified via the new `ChannelDeleted` SignalR event. The channel is removed from the channel list. Previously only the deleting client was able to delete the channel. Other clients would see the channel linger until the next manual refresh.
- Deleted accounts don't break history — channel history now left-joins users, so messages from deleted accounts render (as `deleted-user`) instead of silently disappearing.

## Notes for server operators

- New config key: `Server:Registration` — `"open"` (default), `"invite"`, or `"closed"`.
- New config section: `Spam` — flood/duplicate/join/create thresholds and auto-mute duration; see `appsettings.example.json`. On by default with lenient limits; `Spam:Enabled: false` turns it all off. Note `Spam:MaxJoinsPerWindow` counts only first-time channel joins — keep it above your public channel count so a new member's first connect isn't throttled.
- New REST endpoints: `POST/GET/DELETE /api/invites` (Admin+), `GET /api/users/me/export`, `DELETE /api/users/me`.
- One new database migration (`AddInvitesAndReplies`) applies automatically on startup.