General BBS Software
SSH-Chatter has started from a C reimplementation of the Go ssh-chat server. It mirrors/extends the original behaviour while using modern C patterns and a small, testable core. The server listens for SSH/TELNET connections and places every authenticated user into a shared chat room that exposes the same command surface as the Go reference implementation.
Do you know why it takes so long to understand C? Because it is an instinct.
- Terminal-friendly RSS reader accessible with
/rss list,/rss read <tag>, plus/rss add <url> <tag>and/rss del <tag>(operators only) so the room can browse headlines together. - Background BBS watchdog thread that uses the Gemini/Ollama moderation backends to remove posts that advertise crimes or harmful material, plus
/delete-msgfor targeted chat history cleanup. /bbscommand unlocking a retro bulletin board system with tags, comments, bumping, and a multi-line composer that ends on a locale-aware terminator (defaulting to>/__BBS_END>)./asciiartlive composer with a 640-line limit, a ten-minute per-IP cooldown, multi-line output, and keyboard shortcuts for cancelling with Ctrl+A and submitting with Ctrl+S or the locale-aware>/__ARTWORK_END>default./birthdayto register birthdays,/grant <ip>so LAN operators can delegate privileges by address, and/revoke <ip>so top LAN admins can reclaim them.- Chat UI refresh with a clean divider between history and input, instant input clearing after send, and a friendly "Wait for a moment..." banner
- Friendly multilingual captcha featuring easy comparisons and language-based name counts.
- Expanded nickname support for non-Latin characters plus
/banupgrades that accept raw IP addresses alongside usernames. /weather <region> <city>for quick global forecasts.- Simplified experience with polls, status messages, and the Eliza moderator removed.
The codebase is intentionally compact so new contributors can navigate it quickly:
| Path | Description |
|---|---|
src/main.c |
Command-line parsing and process bootstrap (bind address, port, MOTD, host key directory). |
src/host.c, include/ssh_chatter/host.h |
Chat host implementation – session lifecycle, MOTD handling, and hooks for future message broadcast logic. |
src/host_parts |
Modular host subsystems that compile into a single translation unit through src/host.c. |
include/ssh_chatter |
Shared headers for the daemon, stress tools, and the translation backend. |
include/ssh_chatter/contexts |
Definitions for session_ctx_t and related structures that encapsulate per-connection state. |
data/banner/banner |
Sample welcome banner that can be pointed to with CHATTER_WELCOME_BANNER. |
scripts/install_chatter_service.sh |
Convenience installer that builds the binary, installs it under /usr/local/bin, and wires up a systemd unit (chatter.service). |
scripts/install_dependencies.sh |
Minimal package installer for build prerequisites on Debian/Ubuntu systems. |
The work branch regularly diverges from upstream development so larger features can
incubate without interrupting production traffic. When it is time to synchronize with
main, pull the latest tree and merge it locally before opening a pull request:
git fetch origin main
git checkout work
git merge --no-ff origin/mainResolve any conflicts in place (the src/host.c helper routines already mirror the
layout used on main, so merges are typically straightforward) and run make to
confirm the build still succeeds before pushing the result.
host_snapshot_last_captchaexposes the most recently generated captcha prompt and answer along with a timestamp so external clients can pass challenges on behalf of unattended automation.
scripts/safe_permission.shtightens the ownership and mode on runtime data files (BBS state, vote state, cooldown snapshots, and general chatter state). Run it after deployment to confine the data directory tossh-chatterand to ensure each file is set to0600. Override the targets by passing explicit paths or by exportingSTATE_ROOTor the correspondingCHATTER_*_FILEenvironment variables before execution.- A background BBS watchdog periodically feeds posts and comments through the AI moderation pipeline (Gemini primary with Ollama fallback). Flagged posts are removed automatically and a notice is broadcast to the room.
- Chat messages, ASCII art, and BBS posts/comments flow through a layered security filter. ClamAV scans the payload first (defaulting to
clamscan --no-summary --stdout -, overridable withCHATTER_CLAMAV_COMMANDor disabled withCHATTER_CLAMAV=off). AI-based moderation is now opt-in—enable it withCHATTER_SECURITY_AI=onto query Gemini (GEMINI_API_KEY) with an automatic Ollama fallback (http://127.0.0.1:11434by default). Disable the entire feature withCHATTER_SECURITY_FILTER=off. If every provider fails, the filter automatically disables itself to avoid blocking conversations while misconfigured. - SSH transport is pinned to modern key exchanges, ciphers, and MACs, and every bridge payload is wrapped in a triple AES-256-GCM onion so relays only see ciphertext.
- Suspicious submissions that trip the layered filter are now tracked per-IP; repeated hits trigger an automatic kick and ban when enabled, while the rapid reconnect detector allows longer recovery windows so unstable network sessions can rejoin without being penalized. Automatic ban entries are off by default; set
CHATTER_AUTO_BAN=on(ortrue/1) to enable them, or leave the variable unset to keep warnings and throttling without writing automatic ban entries. - Operators can mark trusted ingress points (VPN exits, reverse proxies, localhost) with
CHATTER_PROTECTED_IPS(comma-separated, defaults to127.0.0.1,::1,192.168.0.1) so emergency bans never lock the daemon out of its own control plane.
SSH-Chatter supports connecting to IRC servers, allowing integration with external IRC relay networks such as magviz.ca. The IRC relay automatically forwards messages between the SSH chat room and the configured relay server.
Set these environment variables to enable the IRC relay:
CHATTER_IRC_SERVER– hostname or IP address of the IRC server (e.g.,irc.magviz.ca)CHATTER_IRC_PORT– port number (defaults to6667if not specified)CHATTER_IRC_CHANNEL– IRC-style channel to join (e.g.,#chat)CHATTER_IRC_NICKNAME– nickname for the bot (defaults tossh-chatter)CHATTER_IRC_USERNAME– username for IRC USER command (defaults to nickname if not specified)CHATTER_IRC_REALNAME– real name for IRC USER command (defaults toSSH-Chatter Botif not specified)
Example configuration in chatter.env:
CHATTER_IRC_SERVER=irc.magviz.ca
CHATTER_IRC_PORT=6667
CHATTER_IRC_CHANNEL=#general
CHATTER_IRC_NICKNAME=chatbot
CHATTER_IRC_USERNAME=myuser
CHATTER_IRC_REALNAME=SSH-Chatter BotOperators can manage the IRC relay connection using the /ircserver command:
/ircserver status # Check connection status
/ircserver reconnect # Reconnect to the IRC server
/ircserver disconnect # Disconnect from the IRC serverMessages received from the IRC relay appear in the chat with a [IRC] prefix and the sender's nickname. The relay automatically handles reconnections if the connection drops, with a 30-second delay between attempts.
SSH-Chatter now supports FidoNet integration via the Binkp protocol, allowing message synchronization with FidoNet networks such as magviz.ca. The FidoNet relay uses the standard Binkp protocol on port 24554 to exchange messages between the SSH chat room and FidoNet nodes.
Set these environment variables to enable the FidoNet relay:
CHATTER_FIDONET_SERVER– hostname or IP address of the FidoNet/Binkp server (e.g.,magviz.ca)CHATTER_FIDONET_PORT– port number (defaults to24554if not specified)CHATTER_FIDONET_ADDRESS– your FidoNet node address (e.g.,2:5030/1997)CHATTER_FIDONET_PASSWORD– optional session password for Binkp authentication
Example configuration in chatter.env:
CHATTER_FIDONET_SERVER=magviz.ca
CHATTER_FIDONET_PORT=24554
CHATTER_FIDONET_ADDRESS=2:5030/1997
CHATTER_FIDONET_PASSWORD=your_password_hereOperators can manage the FidoNet relay connection using the /fidonet command:
/fidonet status # Check connection status
/fidonet reconnect # Reconnect to the FidoNet server
/fidonet disconnect # Disconnect from the FidoNet serverMessages received from the FidoNet relay appear in the chat with a [FidoNet] prefix and the sender's username. The relay implements the standard Binkp handshake protocol and uses a custom CHAT command for message exchange. The relay automatically handles reconnections if the connection drops, with a 30-second delay between attempts.
The implementation follows the Binkp protocol specification:
- Standard Binkp frame structure with 2-byte headers
- Session password authentication (CMD_PWD/CMD_OK)
- Keepalive mechanism (CMD_NUL) every 60 seconds
- Custom CHAT command (CMD_CHAT, extension) for message synchronization
- Support for multiple FidoNet address formats
Set these environment variables (either inside chatter.env or the systemd unit) to synchronise the room with Matrix while preserving onion-style secrecy:
CHATTER_MATRIX_HOMESERVER– base URL of your homeserver (for examplehttps://matrix.example.com).CHATTER_MATRIX_ACCESS_TOKEN– bot access token with permission to post to the target room.CHATTER_MATRIX_ROOM_ID– canonical room identifier (such as!room:example.com).CHATTER_MATRIX_DEVICE_NAME– optional device label shown to the homeserver; defaults tossh-chatter.
Outbound messages are serialised into TorOnion/v1 envelopes produced by three layers of AES-256-GCM encryption; inbound Matrix events must decrypt with the same key schedule before they are replayed back into the terminal room.
Once the bridge variables are configured and the daemon is running, you can access the shared room with any Matrix client:
- Sign in to the same homeserver that the bridge uses. For self-hosted servers this means the URL defined in
CHATTER_MATRIX_HOMESERVER. - Join the room by its canonical ID (
CHATTER_MATRIX_ROOM_ID) or any published alias that points to that ID. - Start chatting—messages will be mirrored between SSH-Chatter and Matrix as soon as the bot is online.
For terminal users, clients such as gomuks or matrix-commander work well: authenticate against your homeserver, run the room join command, and the bridge bot will relay messages automatically. GUI users can follow the same steps with Element, FluffyChat, or another desktop/mobile client—search for the room ID or alias, join it, and the bridge keeps both sides in sync.
If the room is invite-only, ensure the bridge bot has been invited first so it can forward events. Client-side encryption (E2EE) is not supported through the bridge, so disable it for the bridged room to avoid missing messages.
Server operators who want to expose their SSH-Chatter room to Matrix can prepare the bridge with the following workflow:
- Create a Matrix service account. Either register a dedicated bot user on your homeserver or create a new user on a managed homeserver that you control. Give it a strong password and avoid reusing an existing personal account.
- Create or pick the target room. From the Matrix client of your choice, create a new room (or reuse an existing community room) and note its canonical room ID (the form
!room:example.com). Invite the bot user if the room is restricted. - Generate an access token. Log in to the bot account with a client that exposes the developer tools (Element: Settings → Help & About → Advanced → Access Token). Copy the token and store it safely; the bridge uses it instead of the password.
- Configure SSH-Chatter. Set
CHATTER_MATRIX_HOMESERVER,CHATTER_MATRIX_ACCESS_TOKEN,CHATTER_MATRIX_ROOM_ID, and optionallyCHATTER_MATRIX_DEVICE_NAMEin the environment. For systemd installations, add the variables to/etc/ssh-chatter/chatter.envor the[Service]section of the unit file and runsystemctl daemon-reload. - Restart the service. Bounce the daemon (
systemctl restart chatter.serviceor restart the container) so the new settings take effect. Watch the logs for lines beginning withmatrix_bridgeto confirm that the bot joined successfully.
If you ever rotate the access token or move the room, repeat steps 3–5. When decommissioning the bridge, clear the CHATTER_MATRIX_* variables and restart to return to a standalone SSH/TELNET deployment.
Building the project requires a POSIX environment with:
- A C23 compatible compiler (e.g.
gccorclang) makelibsshdevelopment headers and library (libssh-devon Debian/Ubuntu)libcurldevelopment headers and library (libcurl4-openssl-devon Debian/Ubuntu)- POSIX threads (usually supplied by the system
libpthread) python3-pygments(provides thepygmentizehighlighter for the Tetris camouflage screen)
On Debian/Ubuntu the dependencies can be installed with:
sudo apt-get update
sudo apt-get install build-essential libssh-dev libcurl4-openssl-devClone the repository and use the provided Makefile:
makeThis produces an ssh-chatter binary in the repository root and a libssh_chatter_backend.so shared object that exposes the
translation helpers for reuse in other applications. Clean intermediate artifacts with make clean.
The shared object reuses the server's C translation pipeline (including ANSI placeholder preservation) so other processes can
obtain translations without spawning the full SSH host. Link against libssh_chatter_backend.so and include
include/ssh_chatter/ssh_chatter_backend.h:
#include "ssh_chatter/ssh_chatter_backend.h"
int main(void) {
char translated[4096];
char detected[64];
if (ssh_chatter_backend_translate_line("Hello, world!", "ko", translated, sizeof(translated), detected, sizeof(detected))) {
printf("Detected %s -> %s\n", detected, translated);
}
}Set GEMINI_API_KEY (and optionally GEMINI_API_BASE or GEMINI_MODEL) in the environment so the helper can reach the Google Generative Language API, mirroring the runtime requirements of the main daemon. You can run ./scripts/test_gemini_connection.sh before launching the chat server to verify that the credentials allow outbound calls; the script prints the raw Gemini response so you can see whether the request succeeded.
The server defaults to listening on 0.0.0.0:2222. You can adjust runtime parameters with the available flags:
Usage: ./ssh-chatter [-a address] [-p port] [-m motd_file] [-k host_key_dir] [-T telnet_port|off]
./ssh-chatter [-h]
./ssh-chatter [-V]
When provided, -m reads the message of the day from the specified file path.
Common examples:
# Start the chat server on port 2022, loading host keys from /etc/ssh
./ssh-chatter -p 2022 -k /etc/ssh
# Enable telnet access on 0.0.0.0:4242 alongside SSH
./ssh-chatter -T 0.0.0.0:4242
# Serve a custom MOTD from a file and bind to localhost
./ssh-chatter -a 127.0.0.1 -m /etc/ssh-chatter/motdThe host key directory must contain an ssh_host_rsa_key file (and optional .pub). Generate one with ssh-keygen -t rsa -b 4096 -f /path/to/dir/ssh_host_rsa_key if you do not want to reuse your system SSH host keys. Additional host keys named ssh_host_ed25519_key and ssh_host_ecdsa_key are loaded automatically when present so the server can offer modern algorithms during key exchange.
Once running, connect with any SSH client:
ssh -p 2222 user@server-addressThe public server is available at chat.korokorok.com on the default SSH port:
ssh -p 2222 [email protected]Usernames provided at the SSH prompt are used as your chat nickname.
Telnet clients can join with the same feature set. Telnet listening is enabled by default on port 2323 and can be adjusted or disabled with the -T flag. Provide -T address:port to override the bind address (it inherits the SSH bind when omitted; use an empty host like -T :4242 to listen on all interfaces). For example, to join over telnet from a retro terminal:
telnet server-address 2323Pass -T off (or -T disable) to turn the telnet listener off entirely.
A helper script is provided to automate installation on systems that use systemd:
sudo ./scripts/install_chatter_service.shWhat the script does:
- Compiles the project (
make). - Installs the resulting binary to
/usr/local/bin/ssh-chatter. - Creates a dedicated
ssh-chattersystem user and group (if they do not already exist). - Creates
/var/lib/ssh-chatterfor runtime state (including the SSH host key) and/etc/ssh-chatterfor configuration files. - Generates a default RSA host key under
/var/lib/ssh-chatter/ssh_host_rsa_keywhen missing. - Creates a default MOTD at
/etc/ssh-chatter/motdand an override file/etc/ssh-chatter/chatter.envfor environment-based tuning. - Writes
/etc/systemd/system/chatter.service, reloadssystemd, enables the service, and starts it immediately.
The resulting chatter.service unit starts the server with sensible defaults and grants the CAP_NET_BIND_SERVICE capability so the non-root service account can bind to privileged ports if required.
You can adjust defaults by editing /etc/ssh-chatter/chatter.env and restarting the service:
sudo systemctl edit chatter.service # or edit the environment file directly
sudo systemctl restart chatter.serviceSupported environment variables include:
CHATTER_BIND_ADDRESS– IP address to bind (default0.0.0.0).CHATTER_PORT– TCP port exposed to clients (default2222).CHATTER_MOTD_FILE– Path to the message-of-the-day file (default/etc/ssh-chatter/motd).CHATTER_HOST_KEY_DIR– Directory containingssh_host_rsa_key(default/var/lib/ssh-chatter).CHATTER_EXTRA_ARGS– Additional arguments appended to thessh-chatterinvocation.CHATTER_VOTE_FILE– Path to the vote state file (defaultvote_state.dat).CHATTER_GEMINI_COOLDOWN_FILE– Path to the Gemini cooldown state file (defaultgemini_cooldown.dat).CHATTER_SECURITY_FILTER– Set tooff/false/0to disable the layered security filter (enabled by default).CHATTER_SECURITY_AI– Set toon/true/1to enable AI moderation (disabled by default).CHATTER_CLAMAV– Set tooff/false/0to disable ClamAV scanning (enabled by default whenclamscanis available).CHATTER_CLAMAV_COMMAND– Override the command used to feed payloads into ClamAV (defaultclamscan --no-summary --stdout -).
Camouflage Code Snippets:
For the Tetris camouflage feature, you need to manually create code snippet files in /var/lib/ssh-chatter/.
Create files named c.txt, cpp.txt, java.txt, go.txt, js.txt, ts.txt, and rust.txt in this directory.
Each file should contain the code you wish to display when the camouflage screen is active.
Translation support now relies on the Google Gemini API. Set the following in chatter.env (or the environment) to enable it:
GEMINI_API_KEY– Secret API key used to authenticate translation requests.GEMINI_API_BASE– Optional override for the API base URL (defaults tohttps://generativelanguage.googleapis.com/v1beta).GEMINI_MODEL– Optional override for the Gemini model name (defaults togemini-2.5-flash).
When translation is active the chat delivers each message immediately in its original language and follows up with an indented caption that contains the translated text once the Gemini response arrives. Reaction summaries use the same caption styling so updates appear directly beneath the message they reference.
If the inline caption inserts feel jarring you can reserve a small buffer of blank lines ahead of time with /chat-spacing <0-5>.
The setting only affects live chat threads—bulletin board content continues to translate without reservation—so you can tune the
spacing for your own session without impacting long-form posts.
Your translation toggle and language choices are saved in chatter_state.dat, so future sessions automatically restore the same
configuration once you reconnect.
If you prefer to install without immediately starting the service, run the script with SKIP_START=1.
Service management commands:
sudo systemctl status chatter.service
sudo systemctl restart chatter.service
sudo systemctl disable --now chatter.service- SSH listener that negotiates connections and spawns a thread per session.
- Login Captcha
- MOTD delivery through the
-mflag or service-managed configuration file. /helpcommand for connected clients.- Server-side logging of joins, parts, and administrative command attempts (
/ban,/poke). - Broadcasting chat messages to all connected participants.
- Color Palettes
- BBS-style personal message
- Media Tag
- Ban User
- Clock
- Nickname Changer
- Chat Scroll
- Checking user list
- Language Translation
- OpenWeather
/weather - Named polls with label-based voting, supporting multiple-choice
/votepolls and single-choice/vote-singlealternatives, including/elect <label> <choice>as a text-friendly voting shortcut. - Retro bulletin board system accessible through
/bbswith tagging, comments, bumping, and an interactive composer that ends with a locale-aware terminator (default>/__BBS_END>). /asciiarteditor with 640-line drafts, a ten-minute per-IP posting cooldown, multi-line delivery, and Ctrl+A/Ctrl+S shortcuts./gamehub featuring built-intetris(transcoded from the original Soviet-era C implementation) andliargame, both suspendable via/suspend!or Ctrl+Z.
- Enforcing moderation commands beyond logging.
Issues and pull requests are welcome. Please include reproduction steps for bugs and ensure make succeeds before submitting changes.



