events { worker_connections 1024; } pid logs/nginx.pid; http { # Basic settings sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 2048; # Logging access_log logs/nginx-access.log; error_log logs/nginx-error.log; # Upstream servers (using 127.0.0.1 for local development) upstream gateway { server 127.0.0.1:8001; } upstream frontend { server 127.0.0.1:3000; } server { listen 2026; listen [::]:2026; server_name _; # Hide CORS headers from upstream to prevent duplicates proxy_hide_header 'Access-Control-Allow-Origin'; proxy_hide_header 'Access-Control-Allow-Methods'; proxy_hide_header 'Access-Control-Allow-Headers'; proxy_hide_header 'Access-Control-Allow-Credentials'; # CORS headers for all responses (nginx handles CORS centrally) add_header 'Access-Control-Allow-Origin' '*' always; add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PATCH, OPTIONS' always; add_header 'Access-Control-Allow-Headers' '*' always; # Handle OPTIONS requests (CORS preflight) if ($request_method = 'OPTIONS') { return 204; } # LangGraph-compatible API routes served by Gateway. # Rewrites /api/langgraph/* to /api/* before proxying to Gateway. location /api/langgraph/ { rewrite ^/api/langgraph/(.*) /api/$1 break; proxy_pass http://gateway; proxy_http_version 1.1; # Headers proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Connection ''; # SSE/Streaming support proxy_buffering off; proxy_cache off; proxy_set_header X-Accel-Buffering no; # Timeouts for long-running requests proxy_connect_timeout 600s; proxy_send_timeout 600s; proxy_read_timeout 600s; # Chunked transfer encoding chunked_transfer_encoding on; } # Custom API: Models endpoint location /api/models { proxy_pass http://gateway; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # Custom API: Memory endpoint location /api/memory { proxy_pass http://gateway; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # Custom API: MCP configuration endpoint location /api/mcp { proxy_pass http://gateway; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # Custom API: Skills configuration endpoint location /api/skills { proxy_pass http://gateway; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # Custom API: Agents endpoint location /api/agents { proxy_pass http://gateway; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # Custom API: Uploads endpoint location ~ ^/api/threads/[^/]+/uploads { proxy_pass http://gateway; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # Large file upload support client_max_body_size 100M; proxy_request_buffering off; } # Custom API: Other endpoints under /api/threads location ~ ^/api/threads { proxy_pass http://gateway; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # API Documentation: Swagger UI location /api/docs { proxy_pass http://gateway/docs ; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # API Documentation: ReDoc location /api/redoc { proxy_pass http://gateway/redoc; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # API Documentation: OpenAPI Schema location /openapi.json { proxy_pass http://gateway; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # Health check endpoint (gateway) location /health { proxy_pass http://gateway; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # Catch-all for any /api/* prefix not matched by a more specific block above. # Covers the auth module (/api/v1/auth/login, /me, /change-password, ...), # plus feedback / runs / token-usage routes that 2.0-rc added without # updating this nginx config. Longest-prefix matching ensures the explicit # blocks above (/api/models, /api/threads regex, /api/langgraph/, ...) still # win for their paths — only truly unmatched /api/* requests land here. location /api/ { proxy_pass http://gateway; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # Auth endpoints set HttpOnly cookies — make sure nginx doesn't # strip the Set-Cookie header from upstream responses. proxy_pass_header Set-Cookie; } # All other requests go to frontend location / { proxy_pass http://frontend; proxy_http_version 1.1; # Headers proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_cache_bypass $http_upgrade; # Disable response buffering for the frontend. Without this, # nginx tries to spool large upstream responses (e.g. Next.js # static chunks) into ``proxy_temp_path``, which defaults to # the system-owned ``/var/lib/nginx/proxy`` and fails with # ``[crit] open() ... failed (13: Permission denied)`` when # nginx is launched as a non-root user (every dev machine # except production root containers). The symptom on the # client side is ``ERR_INCOMPLETE_CHUNKED_ENCODING`` and # ``ChunkLoadError`` partway through page hydration. # # Streaming the response straight through avoids the # temp-file path entirely. The frontend already sets its # own cache headers, so we don't lose anything from # disabling nginx-side buffering. proxy_buffering off; proxy_request_buffering off; # Timeouts proxy_connect_timeout 600s; proxy_send_timeout 600s; proxy_read_timeout 600s; } } }