shazow/ssh-chat
Chat over SSH.
Healthy across all four use cases
weakest axisPermissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 4mo ago
- ✓15 active contributors
- ✓MIT licensed
Show all 7 evidence items →Show less
- ✓CI configured
- ⚠Slowing — last commit 4mo ago
- ⚠Concentrated ownership — top contributor handles 73% of recent commits
- ⚠No test directory detected
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/shazow/ssh-chat)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/shazow/ssh-chat on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: shazow/ssh-chat
Generated by RepoPilot · 2026-05-09 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/shazow/ssh-chat shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across all four use cases
- Last commit 4mo ago
- 15 active contributors
- MIT licensed
- CI configured
- ⚠ Slowing — last commit 4mo ago
- ⚠ Concentrated ownership — top contributor handles 73% of recent commits
- ⚠ No test directory detected
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live shazow/ssh-chat
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/shazow/ssh-chat.
What it runs against: a local clone of shazow/ssh-chat — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in shazow/ssh-chat | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 148 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of shazow/ssh-chat. If you don't
# have one yet, run these first:
#
# git clone https://github.com/shazow/ssh-chat.git
# cd ssh-chat
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of shazow/ssh-chat and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "shazow/ssh-chat(\\.git)?\\b" \\
&& ok "origin remote is shazow/ssh-chat" \\
|| miss "origin remote is not shazow/ssh-chat (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
&& ok "license is MIT" \\
|| miss "license drift — was MIT at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "cmd/ssh-chat/cmd.go" \\
&& ok "cmd/ssh-chat/cmd.go" \\
|| miss "missing critical file: cmd/ssh-chat/cmd.go"
test -f "chat/room.go" \\
&& ok "chat/room.go" \\
|| miss "missing critical file: chat/room.go"
test -f "sshd/client.go" \\
&& ok "sshd/client.go" \\
|| miss "missing critical file: sshd/client.go"
test -f "chat/message/message.go" \\
&& ok "chat/message/message.go" \\
|| miss "missing critical file: chat/message/message.go"
test -f "host.go" \\
&& ok "host.go" \\
|| miss "missing critical file: host.go"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 148 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~118d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/shazow/ssh-chat"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
ssh-chat is a custom SSH server written in Go that replaces the shell with an interactive chat interface, allowing users to connect via standard SSH clients (e.g., ssh ssh.chat) and participate in a multiplayer chat room. It solves the problem of creating accessible, zero-setup group chat by leveraging existing SSH infrastructure and authentication mechanisms. Monolithic structure: root-level auth.go, host.go, identity.go handle SSH server setup and key management; chat/ package encapsulates room state, messaging, commands, and history (e.g., chat/room.go, chat/message/message.go); cmd/ssh-chat/cmd.go is the CLI entry point; internal/ has utilities like humantime and sanitize for presentation and security.
👥Who it's for
System administrators and developers who want to quickly spin up a shared chat server, users who prefer SSH-based tools, and anyone curious about implementing custom SSH protocol handlers. Contributors are likely interested in Go networking, SSH protocol internals, or building collaborative tools.
🌱Maturity & risk
This is an actively maintained, production-ready project with a public demo at ssh.chat, comprehensive test coverage (test files present for core modules), CI/CD via GitHub Actions, and a stable API. The codebase shows professional polish with Dockerfile support, release binaries for multiple architectures (darwin/amd64, Linux 386/amd64/ARM6), and clear documentation.
The dependency surface is small and well-vetted (golang.org/x/crypto, golang.org/x/term, go-flags), reducing supply-chain risk. However, this is a single-maintainer project (shazow), and the modest GitHub activity suggests it's mature but not rapidly evolving—critical security fixes in SSH or Go dependencies may have delayed turnaround. No visible breaking-change history suggests good backward compatibility.
Active areas of work
The project is in maintenance mode—no active feature development visible in the file list. The presence of go.mod (not Gopkg.lock as primary) and recent security dependency updates (golang.org/x/crypto v0.17.0, golang.org/x/sys v0.15.0) indicate active dependency maintenance. GitHub Actions workflow (.github/workflows/go.yml) suggests CI is operational.
🚀Get running
git clone https://github.com/shazow/ssh-chat.git
cd ssh-chat
make build
# or for development with auto-reload:
make run
# for profiling:
make debug
Daily commands:
# Development: auto-rebuilds on changes
make run
# Production: build binary then run
make build
./ssh-chat --bind=0.0.0.0:2022 --identity=~/.ssh/id_rsa
# With profiling enabled
make debug
# then open http://localhost:6060/debug/pprof/
# Docker
docker-compose up
🗺️Map of the codebase
cmd/ssh-chat/cmd.go— Entry point for the SSH chat server; defines CLI flags, host initialization, and server startup logic.chat/room.go— Core chat room abstraction managing user connections, message broadcasting, and room state.sshd/client.go— SSH client handler; bridges SSH protocol to chat I/O, manages user sessions and terminal interaction.chat/message/message.go— Message data structure and broadcasting logic; fundamental to all chat operations.host.go— SSH host configuration and key management; handles server authentication and connection acceptance.auth.go— Authentication logic for SSH connections; enforces security policies and user identity verification.sshd/pty.go— Pseudo-terminal handling for SSH sessions; manages terminal size, input/output streams, and terminal control.
🛠️How to make changes
Add a new chat command
- Define command handler function in chat/command.go following the pattern
handleCommand<Name>()that receives a User and args. (chat/command.go) - Register the command in the command switch statement in chat/command.go with a name and permission level check. (
chat/command.go) - Add help text entry in chat/help.go describing the command syntax and behavior. (
chat/help.go) - Write unit tests in chat/command_test.go testing both valid and invalid command inputs. (
chat/command_test.go)
Add authentication backend
- Implement AuthHandler interface in auth.go with methods for key and password auth. (
auth.go) - Integrate into sshd/auth.go callback functions (PublicKeyCallback, PasswordCallback) to route to your implementation. (
sshd/auth.go) - Add CLI flag in cmd/ssh-chat/cmd.go to enable/configure the new auth backend. (
cmd/ssh-chat/cmd.go) - Test with auth_test.go and sshd/client_test.go to ensure proper session flow. (
auth_test.go)
Customize terminal display and formatting
- Modify color/style mappings in chat/message/theme.go (e.g., SystemColor, UserColor, PrivateColor). (
chat/message/theme.go) - Update message rendering in chat/message/message.go to use theme colors in String() method. (
chat/message/message.go) - Test theme application in chat/message/theme_test.go with various message types. (
chat/message/theme_test.go)
Extend rate limiting or security policies
- Define new rate limit rules in sshd/ratelimit.go (connection, message, or command limits). (
sshd/ratelimit.go) - Integrate checks into sshd/client.go to enforce limits before processing user input or commands. (
sshd/client.go) - Add configuration flags in cmd/ssh-chat/cmd.go to make limits configurable. (
cmd/ssh-chat/cmd.go)
🔧Why these technologies
- Go + golang.org/x/crypto — Enables fast, concurrent SSH server with minimal dependencies; crypto package provides robust SSH and terminal handling.
- SSH protocol (golang.org/x/crypto/ssh) — Allows secure, authenticated connections without custom auth infrastructure; leverages existing SSH key infrastructure.
- Pseudo-terminals (PTY) — Provides full terminal emulation so clients see a native SSH-like terminal rather than raw text.
- In-memory room + message history — Low-latency chat suitable for real-time interaction; sufficient for ephemeral chat use case without persistence requirements.
- Rate limiting (sshd/ratelimit.go) — Prevents spam and DoS attacks on shared public server without external rate-limit infrastructure.
⚖️Trade-offs already made
-
In-memory message history only
- Why: Simplicity and fast access for real-time chat
- Consequence: Message history lost on server restart; no durable storage; scales memory with chat duration.
-
No database or persistence layer
- Why: Reduces operational complexity for a transient chat service
- Consequence: Cannot implement user account persistence, message archives, or chat replay; each connection is isolated.
-
Public key authentication primary mode
- Why: Leverages existing SSH ecosystem and user key management
- Consequence: Users must have SSH keys or resort to password auth (less secure); higher friction for casual users.
-
Single-room broadcast architecture
- Why: Simplicity and immediate visibility of all messages
- Consequence: Does not scale to large numbers of users; all clients receive all messages regardless of interest.
-
Terminal-based UI with in-band commands
- Why: Works seamlessly over SSH without UI framework
- Consequence: Limited interactivity; text-only interface; command-driven rather than menu-driven.
🚫Non-goals (don't propose these)
- Persistent message storage or chat history after server restart
- Multi-room or channel support
- User account persistence across sessions
- Direct peer-to-peer messaging without server relay
- TLS encryption (relies on SSH transport encryption)
- Web client or non-SSH access methods
- Administrative user roles or moderation tools beyond basic owner model
🪤Traps & gotchas
No required env vars, but -i/--identity flag must point to a valid SSH private key (defaults to ~/.ssh/id_rsa); if missing or malformed, the server won't start. The --admin and --allowlist flags expect file paths with one public key per line (OpenSSH format); malformed keys silently skip rather than error loudly. --bind defaults to 0.0.0.0:2022; ports <1024 require root. Message history is unbounded by default in some older versions—monitor memory on long-running servers with many users.
🏗️Architecture
💡Concepts to learn
- SSH protocol (RFC 4254 channel subsystem) — This project implements a custom SSH subsystem instead of a shell; understanding SSH channel multiplexing and subsystem negotiation is key to how the server accepts connections.
- Public key authentication (OpenSSH format) — The auth system uses SSH public keys from authorized_keys files; knowing ed25519/RSA key formats and OpenSSH serialization is needed to add or debug authentication features.
- Terminal I/O and ANSI escape codes — The chat UI uses ANSI colors and terminal control codes (managed via golang.org/x/term); essential for understanding how messages are rendered with themes.
- Goroutines and channels for concurrency — The room broadcasts messages to many concurrent user goroutines using Go channels; misunderstanding channel semantics leads to race conditions or deadlocks.
- Message history as a bounded ring buffer — The
chat/message/history.gomodule implements a fixed-size circular log of past messages; understanding this pattern prevents memory leaks in long-running servers. - Input sanitization for CLI/user-generated content — The
internal/sanitize/module prevents injection attacks via usernames and messages; knowing HTML escaping and terminal control character stripping is critical for security.
🔗Related repos
golang/crypto— Upstream SSH implementation (golang.org/x/crypto/ssh); this project depends on it directly.charm/bubbletea— Alternative Go library for building interactive CLI/TUI chat; represents a different architectural approach.gravitational/teleport— Production SSH bastion host with advanced auth; similar SSH server customization but focused on access control rather than chat.urfave/cli— Go CLI framework alternative to go-flags; if you wanted to refactor CLI parsing, this is a common choice in Go ecosystem.shazow/rateio— Rate-limited I/O library by the same author; used as a dependency in this project.
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add integration tests for sshd/client.go SSH connection lifecycle
The sshd/client.go file handles SSH client connections and terminal handling, but client_test.go exists with minimal coverage. The sshd package lacks integration tests covering the full SSH handshake, authentication flow, and message routing through the chat room. This is critical since sshd is the core networking layer.
- [ ] Extend sshd/client_test.go with tests for SSH session establishment and teardown
- [ ] Add tests in sshd/net_test.go for multi-client connection scenarios and concurrent message handling
- [ ] Create integration test covering auth flow via sshd/auth.go → client connection → chat/room.go message broadcast
Add GitHub Actions workflow for building Docker images and cross-platform binaries
The repo has a Dockerfile and build_release script but relies on deprecated Travis CI (.travis.yml). No GitHub Actions workflow exists to build and push Docker images to a registry, or to validate multi-platform (linux/amd64, linux/arm64) binary builds on PR/release. This is essential for maintainability.
- [ ] Create .github/workflows/docker.yml to build and optionally push Docker images on releases
- [ ] Create .github/workflows/build.yml to cross-compile binaries (via build_release script) for linux/{amd64,arm64,arm}, darwin, windows on each push
- [ ] Update README.md to reflect the new CI/CD pipeline and remove Travis CI badge
Add unit tests for chat/command.go command parsing and execution
The chat/command.go file implements the command system (/nick, /users, /help, etc.) but command_test.go appears minimal. Commands are user-facing features and bugs here directly impact UX. Tests should cover command parsing, validation, edge cases, and help text generation.
- [ ] Add tests in chat/command_test.go for command.Parse() with valid/invalid syntax and special characters
- [ ] Add tests for each command handler (e.g., nick command with name conflicts, users command output format)
- [ ] Add tests for command help generation (chat/help.go) to ensure all commands are properly documented
🌿Good first issues
- Add test coverage for
chat/logger.goandchat/help.go—both are currently untested (no*_test.gofiles) and are simple, focused modules. - Implement a
/motdcommand to display the Message of the Day in-chat (currently motd.txt is only shown on connection); requires modifyingchat/command.goandchat/room.goto expose the MOTD. - Add
internal/sanitize/sanitize_test.go—the sanitize module exists but has no tests; write tests for HTML/escape edge cases to prevent XSS-like injection in usernames.
⭐Top contributors
Click to expand
Top contributors
📝Recent commits
Click to expand
Recent commits
844e5d5— Merge pull request #445 from shinobi9/master (shazow)d73cf70— Adding tzdata lets the application follow the TZ environment variable and optimizes image builds. (shinobi9)2dc8758— Merge pull request #439 from jagtalon/jag/readme (shazow)6dd5f27— README: Change whitelist to allowlist (Jag Talon)6356738— Update BUG.yml (shazow)e16725f— Update BUG.yml (shazow)89b7218— Update and rename bug_report.md to BUG.yml (shazow)daf4677— Merge pull request #427 from bsiegert/crypto (shazow)bdd716e— Bump golang.org/x/crypto to 0.17.0 (security) (bsiegert)1fc7f7b— Merge pull request #421 from DejavuMoe/build/linux-arm64 (shazow)
🔒Security observations
- High · Outdated Go Cryptography Dependencies —
go.mod - golang.org/x/crypto dependency. The project uses golang.org/x/crypto v0.17.0, which is significantly outdated. Modern versions (v0.21+) contain important security patches for SSH key handling, cryptographic algorithms, and potential side-channel vulnerabilities. Regular security updates to cryptographic libraries are critical. Fix: Update golang.org/x/crypto to the latest stable version (0.24.0 or higher). Run 'go get -u golang.org/x/crypto' and test thoroughly. - High · Outdated System Terminal Dependencies —
go.mod - golang.org/x/term dependency. golang.org/x/term v0.15.0 is outdated and may contain security or stability issues. Since this is an SSH chat application that directly handles terminal I/O, outdated terminal handling libraries could introduce vulnerabilities. Fix: Update golang.org/x/term to the latest stable version (0.24.0 or higher). - High · Outdated System Package Dependencies —
go.mod - golang.org/x/sys dependency. golang.org/x/sys v0.15.0 is outdated. This package handles low-level system calls critical for security operations. Outdated versions may contain OS-level security vulnerabilities. Fix: Update golang.org/x/sys to the latest stable version (0.24.0 or higher). - Medium · Outdated Minimum Go Version —
go.mod - go version declaration. The project specifies 'go 1.13' which reached end-of-life in August 2020. Using obsolete Go versions means missing security patches, performance improvements, and language features that address vulnerabilities. Fix: Update minimum Go version to 1.21 or later. Modern versions include significant security hardening for cryptographic operations and memory safety. - Medium · SSH Key Auto-Generation in Docker Container —
Dockerfile - RUN ssh-keygen command. The Dockerfile auto-generates SSH host keys at build time using 'ssh-keygen -t rsa'. This means all containers built from this image share the same host keys, creating a severe security vulnerability. Additionally, RSA keys are generated without specifying key length (likely 2048-bit, which is weak for modern standards). Fix: Generate SSH keys at container runtime (not build time) and use stronger algorithms. Use 'ssh-keygen -t ed25519' or '-t rsa -b 4096'. Consider using volume mounts for persisted host keys: 'RUN mkdir -p /root/.ssh' without key generation in Dockerfile. - Medium · Docker Image Uses Alpine Without Explicit Pinning —
Dockerfile - FROM statements. Both Docker stages use 'FROM alpine' and 'FROM golang:alpine' without version pinning. This creates non-deterministic builds and potential security vulnerabilities as the base image can change between builds. Fix: Pin Alpine version explicitly: 'FROM alpine:3.18' or 'FROM golang:1.21-alpine3.18' to ensure reproducible, secure builds. - Medium · Insecure SSH Host Key Type in README —
README.md and Dockerfile mismatch. The README displays the public host key for ssh.chat using ed25519, but the Dockerfile generates RSA keys by default. There's an inconsistency that suggests either the documentation is outdated or the Dockerfile implementation is incorrect. Fix: Ensure consistency: either update Dockerfile to generate ed25519 keys ('ssh-keygen -t ed25519'), or update documentation to match the actual key type being used. - Medium · Docker Compose Mounts Host SSH Directory —
docker-compose.yml - volumes section. docker-compose.yml mounts ~/.ssh directory (containing private keys) into the container with read-only access. While read-only mitigates some risk, exposing private keys to a containerized service increases attack surface. If the container is compromised, sensitive keys are accessible. Fix: Only mount the directory if absolutely necessary. Consider using SSH agent forwarding instead. Alternatively, copy only the public key (~/.ssh/authorized_keys or specific public keys) rather than the entire ~/.ssh directory.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.