lmrc-cli 0.3.16

CLI tool for scaffolding LMRC Stack infrastructure projects
Documentation
# 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`

| Method | Path | Target | Auth Required |
|--------|------|--------|---------------|
| 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)

| Pattern | Target | 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