GSM Application runs as five Docker containers connected over a private bridge network namedDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/ti-infinite/GSMApplication/llms.txt
Use this file to discover all available pages before exploring further.
gsm-network. No container is directly exposed to the public internet except the gateway (port 80) and the frontend (port 3000). All inter-service traffic travels over the internal network using Docker service names as hostnames, so gsmauth, gsmapplication, and gsmoperations are never reachable from outside the Docker host.
Container Topology
| Service | Docker name | Internal port | Public port |
|---|---|---|---|
| React SPA (Vite + React 19) | frontend | 80 | 3000 |
| YARP API Gateway | gateway | 80 | 80 |
| Authentication microservice | gsmauth | 8081 | — |
| Application metadata microservice | gsmapplication | 8082 | — |
| Operations microservice | gsmoperations | 8083 | — |
YARP Route Table
The gateway’sappsettings.json defines six YARP routes — one API route and one Swagger JSON proxy per downstream cluster. The URL path prefix determines which cluster receives the request.
| Route | Path match | Cluster | Auth policy | Path transform |
|---|---|---|---|---|
gsmAuthApi | /api/security/{**catch-all} | gsmAuthCluster | anonymous | Remove /api/security, prepend /api |
gsmApplicationApi | /api/application/{**catch-all} | gsmApplicationCluster | AuthenticatedUser | Remove /api/application, prepend /api |
gsmOperationsApi | /api/operations/{**catch-all} | gsmOperationsCluster | AuthenticatedUser | Remove /api/operations, prepend /api |
gsmAuthSwaggerJson | /swagger/security/{**catch-all} | gsmAuthCluster | anonymous | Remove /swagger/security, prepend /swagger |
gsmApplicationSwaggerJson | /swagger/application/{**catch-all} | gsmApplicationCluster | anonymous | Remove /swagger/application, prepend /swagger |
gsmOperationsSwaggerJson | /swagger/operations/{**catch-all} | gsmOperationsCluster | anonymous | Remove /swagger/operations, prepend /swagger |
The
anonymous auth policy on the /api/security/** routes is intentional — the login endpoint must be reachable before a token exists. All other API routes require a valid JWT (AuthenticatedUser policy).http://gsmauth:8081/, etc.) via the ReverseProxy__Clusters__* environment variables.
JWT and Tenant Propagation Flow
Authentication and tenant resolution happen in a strict sequence. Understanding this flow is essential for debugging multi-tenant issues or adding new microservices.- The gateway is the only JWT validator. Microservices trust the
X-Company-Idheader because only the gateway can set it — no request reaches them without passing through the gateway’s middleware first. - Client-supplied
X-Company-Idheaders are stripped. A malicious client cannot impersonate another tenant by manually setting this header; the gateway removes any incoming value and replaces it with the claim extracted from the signed JWT. - The login route is unauthenticated at the gateway.
gsmauthitself validates credentials; the gateway merely forwards to it without a JWT check.
Tenant Registry Pattern
Every microservice that needs to connect to a tenant database follows the same resolution pattern: read theX-Company-Id header, look up the corresponding row in TenantRegistryDb.Tenants, and build a connection string dynamically.
The gateway project deliberately has no
DataAccess layer. The gateway does not connect to any database — it only reads JWT claims. Adding a database layer would violate YAGNI and introduce unnecessary coupling. All tenant resolution happens inside the downstream microservices.Internal Project Structure
Each backend service follows the same layered project structure:GSMGateway — API Gateway
GSMGateway — API Gateway
- GSMGateway.Api — ASP.NET Core host, Swagger aggregation, YARP configuration
- GSMGateway.Business — resolves tenant identity from JWT claims
- GSMGateway.Abstractions — shared interfaces and contracts
- GSMGateway.Entities — constants, options, lightweight models
- GSMGateway.Tenant — tenant context middleware
- GSMGateway.Infrastructure — JWT claim reading utilities
- (no DataAccess — by design)
GSMAuth — Authentication Microservice
GSMAuth — Authentication Microservice
- GSMAuth.Api — HTTP/Swagger exposure, login endpoint (
POST /api/v1/auth/login) - GSMAuth.Business — login orchestration logic
- GSMAuth.Abstractions — dependency inversion contracts
- GSMAuth.Entities — DTOs and domain entities
- GSMAuth.Tenant — cross-cutting tenant context
- GSMAuth.Infrastructure — PBKDF2 hashing, JWT issuance, tenant resolver
- GSMAuth.DataAccess — EF Core DbContext for registry + dynamic tenant context
GSMApplication — Application Metadata Microservice
GSMApplication — Application Metadata Microservice
- GSMApplication.Api — HTTP exposure, menu endpoint (
GET /api/v1/application/getMenu) - GSMApplication.Business — menu and profile resolution
- GSMApplication.Abstractions — interfaces
- GSMApplication.Entities — DTOs
- GSMApplication.DataAccess — EF Core,
RegistryDbContextfor tenant lookup - GSMApplication.Infrastructure — supporting utilities
- GSMApplication.Tenant —
TenantContextpopulated fromX-Company-Idheader
Health Checks
All three backend services and the gateway expose health endpoints. Docker Compose uses these fordepends_on ordering — the gateway will not start until all three microservices are healthy.
| Service | Health endpoint | Check interval |
|---|---|---|
gateway | http://localhost:80/api/health | 30 s |
gsmauth | http://localhost:8081/health | 30 s |
gsmapplication | http://localhost:8082/health | 30 s |
gsmoperations | http://localhost:8083/health | 30 s |