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 amateur ham radio relay.
This shows global morse signals.
/morse on to see, /morse-reply to send.
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
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] [-J json_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 yourname@chat.korokorok.comUsernames 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.
The server also exposes a JSON line protocol over TCP for automation and external integrations. It listens on port 34567 by default and can be disabled or reconfigured with -J:
# Disable the JSON API
./ssh-chatter -J off
# Bind JSON API on a custom port
./ssh-chatter -J 0.0.0.0:45678Each request is a single JSON object terminated by \n. Responses and chat events are JSON objects, also newline delimited. The API supports general chat and the /poll, /vote, /image, /video, /audio, /files, and /asciiart flows.
Event payloads (server → client)
{"type":"event","event":"message","payload":{"id":123,"username":"alice","message":"hello","created_at":1710000000,"system":false,"preserve_whitespace":false,"attachment":{"type":"none","target":"","caption":""}}}Request examples (client → server)
{"type":"chat","id":1,"username":"alice","message":"안녕하세요"}
{"type":"image","id":2,"username":"alice","url":"https://example.com/cat.png","caption":"cat"}
{"type":"asciiart","id":3,"username":"alice","message":" /\\_/\\\\n( o.o )\\\\n > ^ <"}
{"type":"poll","id":4,"username":"op","is_operator":true,"question":"Favorite color?","options":["red","blue","green"]}
{"type":"poll","id":5,"username":"bob","action":"vote","choice":2}
{"type":"vote","id":6,"username":"op","label":"weekend","question":"Plan?","options":["hike","rest"],"allow_multiple":true}
{"type":"vote","id":7,"username":"bob","label":"weekend","action":"vote","choice":1}Responses echo the id and include status, message, and optional result objects:
{"type":"response","id":4,"status":"ok","message":"poll started","result":{"poll":{"active":true,"allow_multiple":false,"id":10,"question":"Favorite color?","options":[{"index":1,"text":"red","votes":0},{"index":2,"text":"blue","votes":0}]}}}For a runnable example, see scripts/json_api_example.py:
python3 scripts/json_api_example.py --url tcp://127.0.0.1:34567 --save /tmp/json_api_output.txtA 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.



