Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

34 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Sync

A real-time TCP chat server and client written in Go.

Project Overview

Sync is a multi-client chat system that runs over raw TCP. It supports username-based identity, session tokens for reconnection, message history with WAL persistence, direct messages, user listings, per-user stats, and graceful shutdown.

Current Status

Aspect Status
Build Compiles (go build ./... passes, go vet clean)
Server Implemented — listens on :9000
Client Implemented — connects to :9000 (localhost only)
Persistence WAL + snapshot implemented
CLI cobra root command scaffolded but not wired up
Tests TestBroadcast exists but fails
Docker Not yet available

Features

  • Multi-client TCP chat — concurrent clients via goroutines
  • Username registration — with Guest<random> fallback
  • Direct messages/msg <user> <message>
  • User listing/users with idle status, message count, uptime
  • Message history/history [N] (last N messages, capped at 100)
  • Per-user stats/stats (messages sent/received, last active)
  • Session tokens — reconnect with reconnect:<user>:<token>, 1-hour TTL
  • WAL persistence — messages appended to messages.wal
  • Snapshot persistence — JSON snapshots every 5 min (when >100 messages) and on shutdown
  • Crash recovery — restores state from snapshot/WAL on startup
  • Graceful shutdown — SIGINT/SIGTERM handling
  • Inactive client cleanup — 30s tick, 5 min timeout
  • Read timeouts — 30s for username, 5 min for messages
  • Panic recovery — per-goroutine recover() guards
  • Slow-client simulation — ~10% of clients get artificial write delays

Getting Started

Prerequisites

  • Go 1.25 or later

Build

# Build both server and client
go build ./...

# Build individually
go build ./cmd/server
go build ./cmd/client

Run

# Start the server (listens on :9000)
./server

# Connect as a client (in another terminal)
./client

Usage

Server

Start the server with no arguments:

./server

The server listens on :9000 (all interfaces) and stores chat data in ./chatdata/.

Client

Connect to the server:

./client

Once connected, you'll be prompted for a username. The following commands are available in the chat:

Command Description
/msg <user> <message> Send a direct message
/users List connected users (with idle status, totals, uptime)
/history [N] Show last N messages
/stats Show messages sent/received and last-active time
/token Show your reconnect token
/quit Leave the chat
Any other text Broadcast to all users

Reconnecting

If you disconnect, you can reconnect with your previous identity:

reconnect:<username>:<token>

Your session token is displayed when you first connect, and can be shown again any time with /token.

Project Structure

├── cmd/
│   ├── root.go             # cobra root command (not yet wired to binaries)
│   ├── server/main.go      # Server entry point → room.StartServer()
│   └── client/main.go      # Client entry point → room.StartClient()
├── internal/
│   └── room/
│       ├── types.go        # Core types (Message, Client, Room, etc.)
│       ├── server.go       # Room init, Run() event loop, shutdown
│       ├── handler.go      # Broadcast, join/leave, history, user list, DMs
│       ├── io.go           # TCP I/O, command parsing, /commands
│       ├── client.go       # Interactive TCP client
│       ├── session.go      # Session creation, reconnection tokens, cleanup
│       ├── persist.go      # WAL + snapshot persistence
│       ├── startServer.go  # StartServer() wrapper
│       └── room_test.go    # Broadcast test (currently failing)
├── pkg/
│   └── token/
│       └── token.go        # Crypto-random hex token generator
├── chatdata/               # Runtime data (WAL + snapshots)
├── go.mod
└── README.md

Architecture

Event Loop

The Room.Run() method runs a central event loop that processes messages from connected clients and handles channel operations sequentially in a single goroutine, avoiding concurrency issues in message processing.

Persistence

Messages are written to a Write-Ahead Log (messages.wal) as JSON lines. Every 5 minutes (when more than 100 messages exist) or at shutdown, a snapshot is saved to snapshot.json. On startup, the server loads the most recent snapshot and replays any remaining WAL entries.

Sessions

Each client gets a cryptographically random 32-character hex token on connect. Sessions have a 1-hour TTL and are tracked with activity timestamps. Clients that are inactive for 5 minutes are removed.

Known Issues

  • TestBroadcast fails — the test itself has bugs: typo "Hellow" instead of "Hello!", wrong error messages (first assertion reports "Client1 didn't recieve" while reading from client1 correctly but the fallback says "Client2"), and typos (recieve, CLient2)
  • Client connects to :9000 onlyclient.go:12 hardcodes localhost, so the client cannot connect to a remote server (cross-device use not yet supported)
  • cobra root command not wiredcmd/root.go defines a sync command but neither binary uses it
  • Typos throughoutRecieved, persistance, actvity, recoverd, etc. (server output only, not user-facing)
  • chatdata/ and testdata/ not gitignored — runtime data shows up in git status

Roadmap

The project has a detailed future plan (see plan.md, which is gitignored). Key milestones: config/CI/Docker, WebSocket/SSE transports, channel-based pub/sub, client SDKs, resilience, horizontal scaling with Redis, observability, E2E encryption, CLI tool, and performance work.

Dependencies

  • spf13/cobra v1.10.2 — CLI scaffolding (root command not yet wired up)
  • Go standard library — everything else (net, bufio, sync, crypto/rand, encoding/json, os/signal, etc.)

License

This project is not yet licensed.

About

A single node TCP chat server with WAL persistance written in go from scratch

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages