This is a Node.js implementation of the WebSocket backend for the social group application, converted from Rust/Axum.
npm install
# or
pnpm install# Development with auto-reload
npm run dev
# Production
npm startAll configuration is done via environment variables:
BIND_ADDR(default:0.0.0.0:8080) - Server address and portWS_RATE_LIMIT(default:30) - Max WebSocket messages per windowWS_WINDOW_SECS(default:5) - WebSocket rate limit window in secondsHTTP_RATE_LIMIT(default:120) - Max HTTP requests per window per IPHTTP_WINDOW_SECS(default:60) - HTTP rate limit window in secondsMAX_WS_TEXT_LEN(default:1048576) - Max WebSocket message size in bytesMAX_ROOM_CONNECTIONS(default:200) - Max users per roomMAX_ROOM_ID_LEN(default:128) - Max room ID lengthRATE_LIMIT_REDIS_REST_URL+RATE_LIMIT_REDIS_REST_TOKEN(optional) - Enable distributed rate limiting across instancesWS_AUTH_SECRET(optional) - HMAC secret for signed websocket join tokensWS_AUTH_ENFORCE(0/1, default0) - Require valid ws token for/ws/*and/invite-ws/*
GET /healthz- Health check endpoint (returns "ok")GET /healthz?deep=1- Deep check endpoint (validates security config, returns JSON)
Chat room WebSocket endpoint. Supports:
- Per-connection sliding window rate limiting
- Message relaying between room members
- Automatic cleanup on disconnect
- Payload size validation
Messages must be JSON with a ciphertext field:
{
"ciphertext": "..."
}Relayed messages include:
{
"from": "connection-id",
"ciphertext": "..."
}Invite room WebSocket endpoint. Supports:
- Connection limits per room
- Anonymous connections
- Creator-defined room capacity
Query parameters:
limit(optional): Room capacity (clamped between 2-50, default 2)creator(optional): Set to "1", "true", or "yes" to mark as creator
Messages sent on successful join:
{
"type": "invite_accepted",
"by": "connection-id"
}Error message:
{
"type": "error",
"error": "invite_limit_reached"
}- WebSocket Rate Limiting: Per-connection sliding window using timestamps
- HTTP Rate Limiting: Per-IP sliding window middleware
- Configurable limits and windows via environment variables
- Automatic room creation on first join
- Automatic room cleanup when empty
- Connection limit enforcement
- Per-room invite capacity limits
- Room ID validation (alphanumeric, dashes, underscores, colons only)
- Message payload size validation
- JSON validation for incoming messages
- IP extraction from proxies (X-Forwarded-For, X-Real-IP headers)
The Node.js implementation maintains functional parity with the Rust version:
- Uses
wslibrary for WebSocket support - Uses JavaScript
Mapfor state storage instead ofDashMap - Rate limiting uses
Map<string, number[]>for sliding window timestamps - Uses Express.js for HTTP routing
- Message validation uses native JSON parsing
- Client IP extraction handles proxy headers identically
- All configuration values and defaults match the Rust version
server (HTTP + upgrade handling)
├── HTTP middleware (rate limiting)
├── /healthz (health check)
└── WebSocket upgrade handler
├── /ws/:room (chat rooms)
└── /invite-ws/:room (invite rooms)
invalid_room: Room ID fails validationroom_full: Room has reached max connectionsinvite_limit_reached: Invite room has reached capacitypayload_too_large: Message exceeds max sizerate_limited: Rate limit exceededtoo_many_requests: HTTP rate limit exceeded (429 status)