- Disable file logging in init_observability — use console-only (stdout). Containerd captures stdout, Promtail ships to Loki. Eliminates "Failed to create log directory: Permission denied" errors on 3 services. - Remove LOG_DIR env vars and /app/logs emptyDir volumes from 5 deployments. - Add OTEL_EXPORTER_OTLP_ENDPOINT=tempo to 5 services that were missing it (api-gateway, web-gateway, data-acquisition, trading-agent, broker-gateway). - Make OTLP tracing init non-fatal — services degrade gracefully if Tempo is unavailable instead of failing entire observability stack. - Stop web-gateway gRPC stream retry on Unimplemented status. When trading-service doesn't implement streaming endpoints, log once and stop instead of retrying every 60s indefinitely. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
web-gateway
REST and WebSocket gateway for the Foxhunt web dashboard. Built with Axum 0.7.9, it proxies HTTP requests to backend gRPC services and bridges gRPC streams to WebSocket clients.
Configuration
All settings are read from environment variables.
| Variable | Default | Description |
|---|---|---|
GATEWAY_LISTEN_ADDR |
0.0.0.0:3000 |
HTTP listen address |
JWT_SECRET |
(required, min 32 chars) | HMAC key for JWT validation |
CORS_ORIGINS |
http://localhost:5173 |
Comma-separated allowed origins |
TRADING_SERVICE_URL |
https://localhost:50051 |
Trading gRPC upstream |
BACKTESTING_SERVICE_URL |
https://localhost:50052 |
Backtesting gRPC upstream |
ML_TRAINING_SERVICE_URL |
https://localhost:50053 |
ML training gRPC upstream |
Routes
All API routes are under /api. Authentication is required unless noted.
| Tier | Rate Limit | Endpoints |
|---|---|---|
| Public | 10 req/min | POST /api/auth/login |
| Trading | 200 req/min | /api/trading/*, /api/risk/*, /api/ml/*, /api/performance/*, /api/config/* |
| Compute | 30 req/min | /api/training/*, /api/backtest/*, /api/tune/* |
| WebSocket | 50 msg/min | GET /api/ws (JWT via token query param) |
| Health | none | GET /health, GET /ready |
Security
- JWT authentication middleware (32-character minimum secret enforced at startup)
- Per-IP rate limiting across three tiers
- 1 MB request body size limit
- CORS with explicit methods (
GET,POST,PUT,DELETE) and headers (Authorization,Content-Type) - Response headers: HSTS,
x-frame-options: DENY,x-content-type-options: nosniff,referrer-policy: strict-origin-when-cross-origin - WebSocket topic whitelist (8 valid topics), per-connection rate limit, max 20 subscriptions
X-Request-Idpropagation (generated if absent, echoed on response)
Modules
| Module | Purpose |
|---|---|
auth |
JWT validation and login handler |
config |
GatewayConfig loaded from environment |
error |
Unified error types and HTTP error responses |
grpc |
Tonic clients and gRPC stream bridges |
rate_limit |
Per-IP token-bucket rate limiter (3 tiers) |
routes |
Axum router construction and all HTTP handlers |
state |
AppState (gRPC channels, WS broadcast, config) |
ws |
WebSocket upgrade, topic subscriptions, keepalive |
Running
export JWT_SECRET="your-secret-at-least-32-characters-long"
cargo run -p web-gateway