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
- SMS automation and reconciliation domain extracted from
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
- enters through
- agent traffic
- enters through
agent_service
- enters through
- back-office traffic
- enters through
admin_service
- enters through
- provider callbacks
- enter through
gateway, then proxy to loopback-onlygame_service
- enter through
Internal service-to-service traffic uses narrowly scoped /internal/* endpoints.
Key Operating Rules
wallet_serviceis the only writer for player-wallet topology buckets and the legacy player-balance compatibility boundary.gatewayis an edge and compatibility layer, not a domain owner.admin_serviceis the only back-office HTTP edge forbo/admin.recon_serviceowns 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
gatewayand forwarded asX-Brand-Id - after login, JWT carries
brand_id;gatewayrejects mismatch between JWT brand and domain brand - player identity, wallet topology, wallet state, rolling, promotion, and per-brand configuration are scoped per brand
agentis a global entity; theagent_brandjoin 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_serviceinstead of reverse-parsing a provider account game_catalog_workerperiodically 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.