Development Guide
Project Structure
arthas/├── arthas-client/ # Frontend (React + TypeScript)│ ├── index.html # HTML entry point│ ├── package.json # Dependency management│ ├── vite.config.ts # Vite build configuration│ ├── tsconfig.json # TypeScript configuration│ ├── tailwind.config.js # Tailwind CSS configuration│ ├── postcss.config.js # PostCSS configuration│ ├── .env.development # Development environment variables│ └── src/│ ├── main.tsx # React entry point│ ├── App.tsx # Root component + ErrorBoundary│ ├── crypto/ # E2EE encryption layer│ ├── network/ # WebSocket network layer│ ├── stores/ # Zustand state management│ ├── pages/ # Page components│ ├── components/ # UI components│ └── styles/ # Global styles├── arthas-server/ # Backend (Go)│ ├── go.mod # Go module definition│ ├── go.sum # Dependency lock file│ ├── Dockerfile # Docker build│ ├── cmd/server/main.go # Server entry point│ └── internal/│ ├── room/ # Room management│ └── network/ # Network layer├── docs/ # Project documentation└── official_doc/ # Official documentationDevelopment Environment Setup
Prerequisites
# Go 1.22+go version
# Node.js 18+node --version
# npm 9+npm --versionBackend Development
cd arthas-server
# Download dependenciesgo mod tidy
# Run with hot reload (requires air)# go install github.com/cosmtrek/air@latest# air
# Or run directlygo run cmd/server/main.go
# Run testsgo test ./...
# Buildgo build -o server ./cmd/server
# Lintgo vet ./...Frontend Development
cd arthas-client
# Install dependenciesnpm install
# Start development server (HMR)npm run dev
# Type checkingnpx tsc --noEmit
# Build for productionnpm run build
# Preview production buildnpm run previewProduction Build (Single Binary Deployment)
Arthas uses Go’s //go:embed to embed frontend build artifacts into the server binary, enabling single-file deployment. This means after modifying frontend code, you must rebuild the frontend and sync it to Go’s embed directory.
Why is manual sync needed?
In development, the frontend runs independently via Vite dev server (port 5173), and the backend uses -tags dev to skip embedding. But in production, users download a single Go binary that includes the frontend. Before release:
- Build frontend → generates
arthas-client/dist/ - Sync to embed directory → copy to
arthas-server/internal/static/dist/ - Compile Go →
go:embed distpackages files into the binary
Using the build script (recommended)
# Linux / macOS./scripts/build-server-all.sh
# Windows (PowerShell).\scripts\build-server-all.ps1Script options:
| Option | Description |
|---|---|
| (no args) | Full pipeline: npm build → sync → go build |
--sync-only / -SyncOnly | Skip npm build, only sync existing dist/ |
--no-go / -NoGo | Build frontend + sync, skip Go compilation |
Manual steps
# 1. Build frontendcd arthas-client && npm run build && cd ..
# 2. Sync to Go embed directoryrm -rf arthas-server/internal/static/distcp -r arthas-client/dist arthas-server/internal/static/dist
# 3. Compile (with embedded frontend)cd arthas-server && go build -ldflags "-s -w" -o server ./cmd/serverNote: If only backend code changed, no need to rebuild frontend. Just
go build(uses existing dist/).
Code Architecture Details
Backend Core Modules
Hub (internal/network/hub.go)
The connection management center, responsible for:
- Registering/unregistering WebSocket connections
- Message routing (dispatching to handlers based on message type)
- Disconnect handling (automatically leaving rooms)
type Hub struct { roomManager *room.RoomManager clients map[*Client]bool register chan *Client unregister chan *Client mu sync.RWMutex}Client (internal/network/client.go)
Abstraction for a single WebSocket connection:
readPump()— goroutine for reading messageswritePump()— goroutine for sending messages + heartbeatSend(data []byte)— non-blocking sendIsRateLimited()— sliding window rate limiting
RoomManager (internal/room/manager.go)
Room lifecycle management:
CreateRoom(roomId)— create a roomGetRoom(roomId)— find a roomRemoveRoom(roomId)— destroy a room
Room (internal/room/room.go)
Member management for a single room:
AddMember(member)— add a memberRemoveMember(id)— remove a memberBroadcast(senderId, data)— broadcast message (excluding sender)
Frontend Core Modules
Encryption Layer (src/crypto/)
| File | Responsibility |
|---|---|
keys.ts | Key generation, import, export |
encrypt.ts | AES-GCM encryption |
decrypt.ts | AES-GCM decryption |
shareKey.ts | Share code encoding/decoding |
utils.ts | base64url utility functions |
Network Layer (src/network/)
| File | Responsibility |
|---|---|
protocol.ts | Message type constants and TypeScript interfaces |
websocket.ts | WebSocket connection management, reconnection, MessagePack encoding/decoding |
State Management (src/stores/chatStore.ts)
Zustand store integrating the encryption and network layers:
- Connection state management
- Room operations (create/join/leave)
- Message sending (encryption) and receiving (decryption)
- Typing indicator management
- Rate limiting
Adding New Features
Adding a New Message Type
- Backend — Add constants and data structures in
internal/network/protocol.go - Backend — Add handler in
internal/network/hub.go - Frontend — Add constants and interfaces in
src/network/protocol.ts - Frontend — Add case in
handleServerMessageinsrc/stores/chatStore.ts
Adding a New UI Component
- Create
src/components/YourComponent.tsx - Import it in
ChatRoom.tsxorHome.tsx - Get required state from
chatStore
Testing
Backend Tests
cd arthas-server
# Run all testsgo test ./...
# With verbose outputgo test -v ./...
# Run tests for a specific packagego test -v ./internal/room/...
# Race condition detectiongo test -race ./...Frontend Type Checking
cd arthas-client
# TypeScript type checkingnpx tsc --noEmitManual Integration Testing
- Start the backend and frontend
- Open two browser windows
- Window A creates a room, copy the share code
- Window B joins using the share code
- Send messages in both directions, verify encryption/decryption works
- Check server logs for no plaintext
Code Style
Go
- Follow standard Go formatting (
gofmt) - Use
go vetfor linting - Comments in Chinese (consistent with the project)
- Error handling without panic
TypeScript
- Strict mode (
strict: true) - Use function components + Hooks
- State management via Zustand
- Styling with Tailwind CSS classes
Dependencies
Backend Dependencies
| Package | Version | Purpose |
|---|---|---|
gorilla/websocket | v1.5.3 | WebSocket server |
vmihailenco/msgpack/v5 | v5.4.1 | MessagePack serialization |
matoous/go-nanoid/v2 | v2.1.0 | Room ID generation |
google/uuid | v1.6.0 | Client ID generation |
Frontend Dependencies
| Package | Version | Purpose |
|---|---|---|
react | ^18.3.1 | UI framework |
react-dom | ^18.3.1 | DOM rendering |
zustand | ^5.0.3 | State management |
@msgpack/msgpack | ^3.0.0 | MessagePack encoding/decoding |
Production Deployment
Backend Deployment (HF Spaces)
The backend is deployed as a Docker container to Hugging Face Spaces.
Prerequisites
- Register a Hugging Face account
- Create an Access Token (Settings → Tokens, select Write permission)
- Create a Space: https://huggingface.co/new-space
- SDK: Docker
- Hardware: CPU Basic (free)
- Visibility: Public (Private URLs require authentication, frontend cannot connect)
One-Click Deploy Script
# Execute from project root.\official_doc\scripts\deploy-hf-space.ps1
# Customize username and Space name.\official_doc\scripts\deploy-hf-space.ps1 -HfUsername "your-username" -SpaceName "arthas-server"The script will prompt for your HF username and Token for authentication.
Manual Deployment Steps
# 1. Copy arthas-server to a temporary directory$tmp = "$env:TEMP\hf-deploy"Copy-Item -Recurse arthas-server $tmpcd $tmp
# 2. Clean build artifactsRemove-Item -Force *.exe, *.test -ErrorAction SilentlyContinue
# 3. Initialize git and commitgit initgit add -Agit commit -m "deploy: arthas production server"
# 4. Push to HF Spacegit remote add space https://huggingface.co/spaces/your-username/arthas-servergit push space master:main --force
# 5. Clean upcd ..Remove-Item -Recurse -Force $tmpImportant Files
arthas-server/Dockerfile— Multi-stage build, final image < 30MBarthas-server/README.md— Contains HF Space required YAML front matter (sdk: docker)arthas-server/.dockerignore— Excludes unnecessary files to speed up builds
Verification
After the build completes (approximately 2-3 minutes), visit:
https://your-username-arthas-server.hf.space/pingA pong response indicates successful deployment.
Environment Variable Configuration
Set in Space Settings → Repository secrets:
| Variable | Value | Description |
|---|---|---|
ALLOWED_ORIGINS | https://your-frontend.vercel.app | Allowed frontend domains (comma-separated for multiple) |
Restart the Space after setting for changes to take effect.
Frontend Deployment (Vercel)
The frontend is deployed to Vercel, selecting the arthas-client subdirectory from the GitHub monorepo.
Steps
- Push the entire arthas project to GitHub
- Visit vercel.com/new, import the GitHub repository
- Configure:
- Root Directory:
arthas-client - Framework Preset: Vite
- Environment Variables:
VITE_WS_URL=wss://your-username-arthas-server.hf.space/ws
- Root Directory:
- Click Deploy
Verification
After deployment, open the domain assigned by Vercel and check the browser Network panel to confirm the WebSocket connection uses wss://.
Keep-Alive Configuration (cron-job.org)
HF Spaces free tier goes to sleep when there is no traffic. Configure scheduled health checks to keep it active:
- Register at cron-job.org
- Create a job:
- URL:
https://your-username-arthas-server.hf.space/ping - Interval: Every 10 minutes
- Timeout: 30 seconds
- Failure retry: 1 time
- URL:
Post-Deployment Verification Checklist
| # | Verification Item | Method | Expected Result |
|---|---|---|---|
| 1 | Health check | Visit /ping | 200 + “pong” |
| 2 | WSS connection | Browser Network panel | WebSocket uses wss:// |
| 3 | Cross-network chat | Two devices create/join room | Messages encrypt/decrypt normally |
| 4 | No plaintext in logs | Check HF Space logs | No message content |
| 5 | CORS blocking | Connect from unauthorized domain | Returns 403 |
Next Steps
- Contributing Guide — How to submit code
- Architecture Document — In-depth design understanding