VeztaVezta
Architecture

Real-Time

Socket.IO architecture, channels, and Redis adapter

Vezta uses Socket.IO for real-time communication between the backend and connected clients. The WebSocket server runs at the /ws namespace and exposes channels covering market data, user events, crypto prices, sports state, and platform signals. Public channel prefixes (no auth): market:, monitor:, crypto:prices, sports:, weather:.

Server Configuration

MarketDataGateway is a NestJS WebSocket gateway:

@WebSocketGateway({ cors: { origin: '*' }, namespace: '/ws' })
export class MarketDataGateway
  implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect
{
  @WebSocketServer()
  server: Server;
}

Key details:

  • Namespace: /ws
  • CORS: Open at the gateway level; the actual CORS gate is enforced at Nginx (whitelist for vezta.io, dev.vezta.io, admin.vezta.io, localhost:3000)
  • Adapter: Redis adapter for horizontal scaling across worker processes

Channels

ChannelAuth RequiredDescription
market:prices:<id>NoReal-time YES/NO price updates for active markets
market:orderbook:<id>NoOrder book depth snapshots
market:trades:<id>NoLive trade feed from exchanges
market:holders:<id>NoTop-holders concentration (Polymarket only)
market:targetprices:<id>NoTarget-price / TP-SL threshold updates
market:sports-state:<id>NoLive sports market state, keyed by market id
market:sports-stats:<eventId>NoLive sports-stats deltas, keyed by Event id
sports:standings:<leagueSlug>NoLeague standings refresh (e.g. fifa.world)
crypto:pricesNoChainlink oracle prices for BTC/ETH/SOL/XRP via Polymarket RTDS
weather:<icao>NoWeather-station temperature readings for weather markets
user:ordersYesOrder status changes (fills, cancellations)
user:notificationsYesIn-app notification delivery
user:transactionsYesDeposit / withdrawal status updates
user:portfolioYesPortfolio value and position updates
user:copy-tradeYesCopy trade execution events
user:combo-ordersYesCombo (multi-leg) order status updates
monitor:signalsNoNews signals + insider alerts (event insider-alert for the latter)

Subscribe / Unsubscribe

Clients join channels by sending a subscribe event with the channel name. The gateway adds the client to a Socket.IO room matching the channel:

// Subscribe
socket.emit('subscribe', { channel: 'market:prices' });

// Server response
// { event: 'subscribed', data: { channel: 'market:prices' } }

// Unsubscribe
socket.emit('unsubscribe', { channel: 'market:prices' });

// Server response
// { event: 'unsubscribed', data: { channel: 'market:prices' } }

Under the hood, subscribe calls client.join(data.channel) and unsubscribe calls client.leave(data.channel).

Connection and Message Limits

The gateway enforces per-process limits (in-memory tracking, cleared on disconnect):

  • 5000 max total connections
  • 10 connections per userId
  • 20 connections per IP
  • 30 messages/min per client (subscribe + unsubscribe combined)

When a limit is hit, the gateway drops the offending socket.

Broadcast Pattern

Backend services broadcast data to channels using gateway.broadcastToChannel():

broadcastToChannel(channel: string, event: string, data: any) {
  this.server.to(channel).emit('data', { channel, data });
}

All broadcasts emit a consistent 'data' event with a { channel, data } payload. The client routes messages by inspecting message.channel.

For insider alerts, the backend uses gateway.broadcastToChannel('monitor:signals', 'insider-alert', ...) -- not raw server.emit, which would bypass rooms and the wrapper.

Authentication

The web client connects with JWT in the Socket.IO handshake:

import { io } from 'socket.io-client';

const socket = io('wss://backend.vezta.io/ws', {
  transports: ['websocket'],
  auth: {
    token: accessToken,
  },
});

Public channels (market:*, crypto:prices, monitor:signals) do not require authentication. User-specific channels (user:*) require a valid JWT and ownership verification at subscribe time.

Client Integration

Web (vezta-fe)

WsProvider (lib/ws/provider.tsx) wraps Socket.IO in Jotai atoms:

  • Connects when authenticated, disconnects on logout
  • WebSocket-only transport, exponential backoff reconnect (1s → 30s)
  • Global subscriptions: user:orders, user:notifications, monitor:signals
  • Per-market hook: useMarketSubscription(marketId) for prices, orderbook, trades
  • Connection status tracked in wsStatusAtom

Redis Adapter

The Socket.IO server uses the Redis adapter to synchronize events across multiple Node.js processes. When a service broadcasts to a channel, Redis pub/sub ensures the message reaches all connected clients regardless of which process they are connected to.

This is essential for the worker split, where the API container plus specialized workers (worker-traders, worker-market, worker-misc, worker-websocket, worker-rtds, worker-ai, worker-sports, worker-combos) all share the same Redis. The API container holds the WebSocket connections; workers publish to channels via Redis pub/sub; clients receive updates regardless of which worker produced them.

On this page