Skip to main content

System Overview

Status

Active

Date

2026-04-28

Owners

  • Platform Backend

Last Verified Commit

Use git log -- <this file> for current last-touch history; this field is intentionally not pinned to a static hash so it does not become stale after unrelated commits.

Goal

Describe the current RGB backend topology, the servers_v2 target runtime, and the migration posture between legacy servers/ and servers_v2/.

Repo Runtime Layers

The repo currently contains four important runtime layers:

  • web/
    • player-facing web clients
  • bo/
    • back-office and agent-facing frontends
  • servers/
    • legacy Python backend services
  • servers_v2/
    • target backend runtime built around service ownership, contracts, workers, and tests

Target Runtime Shape

servers_v2 is the target backend for the next-stage platform runtime.

Current primary services:

  • gateway
    • player-facing HTTP edge
  • player_service
    • player auth, profile, messages, and public/common data
  • wallet_service
    • target owner for player-wallet balances, deposits, withdrawals, wallet buckets, and legacy player-balance compatibility writes
  • game_service
    • local versioned Aggregator catalog reads, live launch/events, plus Provider-protocol and wallet semantics for signed relay callbacks
  • rolling_service
    • rolling lifecycle and wallet-event consumption
  • promotion_service
    • coupon, rebate, cashback, and lossback orchestration
  • agent_service
    • agent-facing portal APIs
  • admin_service
    • back-office edge, top-info websocket, and scheduler-backed operational APIs
  • recon_service
    • SMS automation and reconciliation domain extracted from middle_server

Infrastructure Model

The current local and target runtime model is:

  • shared PostgreSQL
  • shared Redis
  • shared contracts package: servers_v2/shared/contracts
  • shared DB runtime package: servers_v2/shared/rgb_db
  • one Docker Compose stack for local full-system verification

Several services are intentionally split into API and worker runtimes:

  • wallet_service + wallet_worker

  • rolling_service + rolling_worker

  • promotion_service + promotion_worker

  • agent_service + agent_worker

  • admin_service + admin_worker

  • recon_service + recon_worker

  • game_service + game_catalog_worker

  • player_service + player_worker

API processes do not own worker loops in compose. Worker-owned flags are disabled on API containers and enabled on the matching worker containers, so API horizontal scaling does not duplicate outbox, saga, sweeper, scheduler, or stream-consumer work.

Entry Surface Summary

External entry surfaces are intentionally separated by audience:

  • player traffic
    • enters through gateway
  • agent traffic
    • enters through agent_service
  • back-office traffic
    • enters through admin_service
  • provider callbacks
    • enter through gateway, then proxy to loopback-only game_service

Internal service-to-service traffic uses narrowly scoped /internal/* endpoints.

Key Operating Rules

  • wallet_service is the only writer for player-wallet topology buckets and the legacy player-balance compatibility boundary.
  • gateway is an edge and compatibility layer, not a domain owner.
  • admin_service is the only back-office HTTP edge for bo/admin.
  • recon_service owns reconciliation state but must not mutate wallet balances directly.
  • worker health must reflect forward progress, not just process liveness.

Current exception: agent-domain money is still in migration. Some agent.balance / agent.coupon writes still happen in admin_service and agent_service while emitting audit/outbox/legacy-write metrics. Treat those as active ownership debt, not proof that the Single Money Writer target is fully closed.

Multi-Brand Posture

Per ADR-009, servers_v2 hosts multiple brands on the same runtime:

  • one PostgreSQL, one Redis, one Docker stack
  • brand identity is resolved from the request domain by gateway and forwarded as X-Brand-Id
  • after login, JWT carries brand_id; gateway rejects mismatch between JWT brand and domain brand
  • player identity, wallet topology, wallet state, rolling, promotion, and per-brand configuration are scoped per brand
  • agent is a global entity; the agent_brand join controls which brands an agent can serve
  • native game-provider credentials and provider authentication live only in Aggregator; RGB has one Aggregator Brand API credential and one relay credential per enabled RGB brand/team mapping
  • Aggregator signs the resolved RGB external player and brand into callback context; RGB verifies that identity through player_service instead of reverse-parsing a provider account
  • game_catalog_worker periodically closes and validates a complete Aggregator provider/game version, then atomically publishes a per-brand PostgreSQL snapshot; player list requests never depend on a live Aggregator catalog call
  • back-office staff identity is intentionally absent in this iteration

Current Migration Posture

The current target is not "replace every legacy service at once". The posture is staged:

  • player-facing web paths are being absorbed by gateway + domain services
  • agent-facing paths are being absorbed by agent_service
  • provider callbacks are being absorbed by game_service
  • SMS reconciliation is being absorbed by admin_service + recon_service

The main remaining blocker to a full legacy replacement is the residual middle_server scope that is still not fully represented in servers_v2. The currently tracked blocking prefixes are:

  • /api/admin/role/*
  • /api/admin/menu/*
  • /api/admin/config/*
  • /api/admin/i18n/*
  • /api/admin/bi/*
  • /coin/*
  • legacy POST /{path:path} catch-all forwarding behavior

Legacy web CMS routes removed by ADR-009 are not a v2 replacement surface; any remaining caller must migrate or retire rather than expecting a broad middle_server proxy. See migration-readiness.md for the current cutover decision.