Skip to content

System Architecture

Vidivo is built as a set of independent microservices, each responsible for a distinct domain. Services communicate over HTTP/JSON internally (gRPC is planned for a later phase). The system is deployed on Docker Swarm with Traefik as the reverse proxy.

┌─────────────────────┐
│ Clients │
│ Web · iOS · Android │
└──────────┬──────────┘
│ HTTPS
┌──────────▼──────────┐
│ Traefik 3 │
│ Reverse Proxy / │
│ SSL Termination │
└──┬───────────────┬──┘
│ │
┌────────────────▼──┐ ┌───────▼────────────┐
│ api.vidivo.app │ │ signal.vidivo.app │
│ API Gateway │ │ Signaling Service │
│ (Echo v5) │ │ (WebSocket / Echo) │
└──┬──────────┬────┘ └────────────────────-┘
│ │ │
┌────────────┘ ┌─────┘ ┌────┘
│ │ │
▼ ▼ ▼
┌──────┐ ┌────────────┐ ┌──────────────┐ ┌──────────────┐
│ Auth │ │ User │ │ Call │ │ Billing │
│ Svc │ │ Service │ │ Service │ │ Service │
└──┬───┘ └─────┬──────┘ └──────┬───────┘ └──────┬───────┘
│ │ │ │
▼ ▼ ▼ ▼
┌──────────────────────────────────────────────────────────┐
│ PostgreSQL 16 │
│ (Primary + Read Replica planned) │
└──────────────────────────────────────────────────────────┘
│ │ │ │
▼ ▼ ▼ ▼
┌──────────────────────────────────────────────────────────┐
│ Redis 7 │
│ Sessions · Call State · Signaling · Rate Limits │
└──────────────────────────────────────────────────────────┘
┌────────────────────┐ ┌──────────────┐ ┌────────────┐
│ Notification Svc │ │ Admin Svc │ │ Verif. │
│ (Email/Push/SMS) │ │ (internal) │ │ Service │
└────────────────────┘ └──────────────┘ └────────────┘
┌──────────────────────┐
│ turn.vidivo.app │
│ TURN Server │
│ (pion/turn, VPS) │
│ UDP 3478 / TCP 5349 │
└──────────────────────┘
ServicePathResponsibility
api-gatewayapi.vidivo.appRequest routing, auth middleware, rate limiting
auth/v1/auth/*Registration, login, JWT issuance, token refresh
user/v1/users/*Profile management, role promotion, preferences
call/v1/calls/*Call session lifecycle, signaling coordination
signalingsignal.vidivo.appWebSocket relay for WebRTC SDP/ICE exchange
billing/v1/payments/*Stripe hold/capture, window billing, payouts
notificationInternalEmail, push notifications, SMS (via Resend + FCM/APNs)
verification/v1/verification/*Age verification via Xident integration
adminadmin.vidivo.appInternal dashboard for platform operations
TechnologyVersionRole
Go1.26All microservice runtimes
Echov5HTTP framework (router, middleware, binding)
GORMlatestORM for PostgreSQL
golang-migratelatestDatabase schema migrations
pion/webrtcv4WebRTC (signaling, TURN)
pion/turnv3TURN server binary
zaplatestStructured JSON logging
PrometheuslatestMetrics collection
StoreVersionRole
PostgreSQL16Primary relational database
Redis7Sessions, call state, signaling, rate limits
TechnologyVersionRole
React19Web app and admin panel UI
Vite7Build tool and dev server
React Routerv7Client-side routing
ZustandlatestGlobal state management
TanStack Queryv5Server state, caching, mutations
Radix UIlatestAccessible component primitives
Tailwind CSS4Utility-first styling
Stripe ElementslatestSecure card input (PCI compliance)
ComponentTechnologyNotes
Container orchestrationDocker SwarmNot Kubernetes (simpler for single-region V1)
Reverse proxyTraefik 3Auto-discovery, Let’s Encrypt TLS
CDNCloudflareWeb app, DDoS protection
Object storageCloudflare R2Profile images, future media assets
EmailResendTransactional emails
ObservabilityPrometheus + Grafana + Loki + JaegerMetrics, logs, traces
Guest Browser
│ 1. GET /c/{short_code} (Cloudflare CDN → Web App)
Web App (React)
│ 2. GET /v1/links/{short_code} — fetch link metadata
│ POST /v1/auth/guest — obtain guest token
│ POST /v1/calls/initiate — place Stripe hold
API Gateway → Billing Service → Stripe API
│ 3. Signaling token issued
WebSocket → signal.vidivo.app
│ 4. SDP offer/answer exchanged
P2P WebRTC (direct or via TURN relay)
Call starts
│ PaymentIntent (hold) created via Stripe API
│ → payment_transactions record (status: hold)
Window timer runs (Redis: call:{id}:timer)
│ At window_size - 60s: next hold placed
│ At window_size: current window captured
│ → Stripe captures PaymentIntent
│ → payment_transactions updated (status: captured)
Call ends (POST /calls/{id}/end)
│ Partial window captured
│ Remaining holds released
│ call_sessions record finalized
│ Host transfer initiated (Stripe Connect)
Payout sent to host bank (T+2 business days)

The platform uses approximately 12 core tables. Key tables:

TableDescription
usersAll user accounts (multi-role, soft deletes)
call_linksShareable URLs with configuration
call_sessionsActive and completed call records with JSONB quality metrics
payment_transactionsPer-window Stripe PaymentIntent records
guest_sessionsAnonymous caller sessions with card reference
verification_recordsAge verification results from Xident
stripe_accountsHost Stripe Connected Account references
notificationsDelivery log for all notifications

All primary keys use ULID format. All timestamps use TIMESTAMPTZ (UTC). Soft deletes are implemented on all user-facing tables for GDPR compliance.

HostnameBackendNotes
vidivo.appCloudflare CDNWeb app (React PWA)
api.vidivo.appTraefik → API GatewayREST API
signal.vidivo.appTraefik → SignalingWebSocket (sticky sessions required)
turn.vidivo.appDirect to TURN VPSUDP 3478 / TCP 5349 — no proxy
admin.vidivo.appTraefik → AdminAdmin panel
docs.vidivo.appCloudflare CDNThis documentation site
Client → API Gateway
│ Validate JWT (RS256, public key from JWKS)
│ Check token expiry, role claims
Upstream Service

Access tokens are signed with a private RSA key held only by the auth service. All other services validate tokens using the public key available at /.well-known/jwks.json. This means no service-to-service token sharing is needed.

  • Passwords: bcrypt with cost factor 12
  • Media: DTLS-SRTP (mandatory, browser-enforced)
  • Transport: TLS 1.2+ enforced by Traefik
  • Card data: Never touches Vidivo servers (Stripe Elements)
  • TURN credentials: HMAC-SHA1 time-limited (24h expiry, RFC 5389)

All services emit:

  • Structured JSON logs via zap, collected by Loki
  • Prometheus metrics scraped every 15s, visualized in Grafana
  • Distributed traces via OpenTelemetry → Jaeger

Key dashboards (planned):

  • Active calls and concurrent sessions
  • Billing success/failure rate
  • TURN usage percentage
  • API latency percentiles (p50, p95, p99)
  • WebRTC connection failure rate