# Gateway - Unified API Gateway
**Ready-to-use application** bundled with LMRC CLI. This is NOT a template - it's a complete, working gateway that handles all traffic routing and authentication.
## Purpose
Single entry point for ALL traffic:
- **Infrastructure subdomain** (`infra.*`) - Authenticated access to infrastructure management
- **User service subdomains** (`app.*`) - Routes to user API services with auth integration
## Architecture
```
Internet
↓
gateway (THIS APP)
┌──────────────┼──────────────┐
↓ ↓
infra.domain app.domain
↓ ↓
┌──────────┐ ┌─────────┐
│ Infra │ │ User │
│ Auth │ │ Service │
│ (infra_ │ │ Proxy │
│ sessions│ │ │
│ from │ │ (app │
│ infra_ │ │ handles│
│ db) │ │ own │
└────┬─────┘ │ auth) │
│ └────┬────┘
┌───┴────┐ ↓
↓ ↓ User Backend
infra- infra- (user-api)
api front
```
## Features
### Infrastructure Routes (`infra.*` subdomain)
**Authentication**: Uses `infra_sessions` table from `infra_db`
| POST | `/auth/login` | gateway (local) | No |
| POST | `/auth/logout` | gateway (local) | Yes |
| GET | `/auth/me` | gateway (local) | Yes |
| GET/POST | `/api/*` | → infra-api:8080 | Yes |
| GET | `/*` | → infra-front:3000 | No (static) |
### User Service Routes (`app.*` subdomain)
**Authentication**: Forwarded to user backends (they handle their own auth)
| `/*` | → Configured backend | Backend handles |
Example:
- `api.example.com` → `user-api-service:8081`
- `admin.example.com` → `admin-service:8082`
## Configuration
### Environment Variables
```env
# Server
GATEWAY_HOST=0.0.0.0
GATEWAY_PORT=8080
# Infrastructure Database (for infra auth)
INFRA_DATABASE_URL=postgres://infra_user:pass@postgres:5432/infra_db
# Session Settings
SESSION_EXPIRATION_HOURS=168 # 7 days
# Service Endpoints
INFRA_API_URL=http://infra-api:8080
INFRA_FRONT_URL=http://infra-front:3000
# User Service Routes (JSON configuration)
USER_ROUTES='[
{"subdomain": "api", "backend": "http://user-api:8081"},
{"subdomain": "admin", "backend": "http://admin-api:8082"}
]'
# CORS
CORS_ORIGINS=http://localhost:3000,https://infra.example.com
# Logging
RUST_LOG=gateway=debug,tower_http=debug
```
## Subdomain Routing Logic
### Extract Subdomain
```rust
// Extract from Host header
// "infra.example.com" → "infra"
// "api.example.com" → "api"
```
### Route Decision
```rust
match subdomain {
"infra" => {
// Check infra_sessions in infra_db
if authenticated {
match path {
"/api/*" => proxy_to("infra-api:8080"),
"/auth/*" => handle_locally(),
"/*" => proxy_to("infra-front:3000"),
}
} else {
redirect_to_login()
}
}
other => {
// Look up user service route
if let Some(backend) = user_routes.get(other) {
proxy_to(backend) // Forward as-is
} else {
404_not_found()
}
}
}
```
## Authentication Flow
### Infrastructure Login
```bash
# User visits infra.example.com
curl -X POST https://infra.example.com/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"admin@example.com","password":"password"}'
# Response: Sets HTTP-only cookie with session token
# Cookie: infra_session=<uuid>; HttpOnly; Secure; SameSite=Strict
```
### Session Validation
```rust
// On each request to infra.*:
1. Extract cookie: infra_session
2. Query infra_db.infra_sessions WHERE token = <uuid>
3. Check expires_at > now()
4. If valid: Attach user_id to request
5. Proxy to backend with X-User-Id header
```
### User Service Auth
```bash
# User visits api.example.com
# Gateway forwards request AS-IS to user-api:8081
# user-api handles its own authentication
# Gateway just routes traffic
```
## Integration with User Services
### For User API Services
Your API service receives requests from the gateway with all original headers preserved.
**If you need auth**, implement it in your service:
```rust
// In your user-api service
#[derive(Deserialize)]
struct LoginRequest {
email: String,
password: String,
}
async fn login(Json(req): Json<LoginRequest>) -> Result<impl IntoResponse> {
// Check user_db.users table (YOUR database)
// Create session in user_db.sessions
// Return session cookie
}
```
**Gateway doesn't interfere** - it just routes traffic.
## Reverse Proxy Implementation
### Using hyper
```rust
async fn proxy_request(
uri: Uri,
method: Method,
headers: HeaderMap,
body: Body,
backend_url: &str,
) -> Result<Response> {
let client = reqwest::Client::new();
// Build target URL
let target = format!("{}{}", backend_url, uri.path());
// Forward request
let response = client
.request(method, target)
.headers(headers)
.body(body)
.send()
.await?;
// Return response
Ok(response)
}
```
## Deployment
### As Part of LMRC Stack
This gateway is **automatically included** when you run `lmrc new`:
```
project/
├── apps/ # Your services
├── infra/
│ ├── gateway/ # THIS APP (copied, ready to use)
│ ├── infra-api/
│ ├── infra-front/
│ └── infra-migrator/
```
### Kubernetes Deployment
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: gateway
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: gateway
template:
metadata:
labels:
app: gateway
spec:
containers:
- name: gateway
image: your-registry/gateway:latest
ports:
- containerPort: 8080
env:
- name: INFRA_DATABASE_URL
valueFrom:
secretKeyRef:
name: infra-db-secret
key: database-url
- name: INFRA_API_URL
value: "http://infra-api:8080"
---
apiVersion: v1
kind: Service
metadata:
name: gateway
spec:
selector:
app: gateway
ports:
- port: 80
targetPort: 8080
type: LoadBalancer # Or use Ingress
```
### Ingress Configuration
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: gateway-ingress
annotations:
cert-manager.io/cluster-issuer: letsencrypt
spec:
rules:
- host: infra.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: gateway
port:
number: 80
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: gateway
port:
number: 80
tls:
- hosts:
- "*.example.com"
secretName: wildcard-tls
```
## Security
- **Infrastructure Sessions**: Stored in infra_db, HTTP-only cookies, auto-expire
- **HTTPS Only**: Secure flag on cookies in production
- **CORS**: Configurable per environment
- **Rate Limiting**: Applied at gateway level
- **Request Validation**: Size limits, timeout enforcement
## Development
```bash
# Run locally
cargo run -p gateway
# With environment file
cp .env.example .env
# Edit .env with your settings
cargo run -p gateway
# Build release
cargo build --release -p gateway
# Docker build
docker build -f apps/gateway/Dockerfile -t gateway:latest .
```
## Key Differences from Template
**This is NOT a template:**
- ❌ No `{{placeholders}}`
- ❌ No customization expected
- ✅ Works out-of-the-box
- ✅ Configured via environment variables
- ✅ Same for all projects
**User projects use this gateway as-is**, they don't modify it.
## License
See workspace LICENSE