# CloudCLI UI Nginx subpath deployment template. # # Purpose: # Serve CloudCLI UI from a path prefix such as: # http://localhost/ai/ # https://example.com/ai/ # # CloudCLI itself still runs at the root of its own HTTP server, for example: # http://127.0.0.1:3001/ # # Nginx receives public requests under /ai, strips that prefix, and forwards the # remaining path to CloudCLI. For example: # /ai/ -> / # /ai/session/abc -> /session/abc # /ai/assets/index.js -> /assets/index.js # # Important Nginx limitation: # Nginx does not allow variables in `location` matchers or `rewrite` regexes. # The configurable variables below are still useful for proxy/filter values, # but if you change /ai to a different subpath, also update every line marked: # [SUBPATH LITERAL] # # To use a different subpath, replace these literal matchers: # location = /ai # location ^~ /ai/ # rewrite ^/ai(?/.*)$ ... # # Recommended deployment shape: # CloudCLI is the only app using /ai, while root paths /api, /ws, and /shell # are also proxied because the current frontend still calls those endpoints # with root-relative URLs. worker_processes 1; events { # Maximum simultaneous connections handled by each worker process. # The default is enough for local testing and small self-hosted deployments. worker_connections 1024; } http { # WebSocket requests include an Upgrade header. Normal HTTP requests do not. # This map gives us the right Connection header for both cases: # Upgrade present -> "upgrade" # Upgrade absent -> "close" map $http_upgrade $connection_upgrade { default upgrade; '' close; } server { # For HTTPS deployments, replace this with `listen 443 ssl http2;` and # add ssl_certificate / ssl_certificate_key lines. listen 80 default_server; # Use your real hostname in production, for example: # server_name cloudcli.example.com; server_name localhost 127.0.0.1; # ---- User settings ------------------------------------------------- # # Public path prefix where users access CloudCLI. # Do not add a trailing slash. # # This variable can be used in redirects and response rewrites. It # cannot be used in `location` matchers, so update the [SUBPATH LITERAL] # lines too if you change it. set $cloudcli_subpath /ai; # Private upstream URL where the CloudCLI server is listening. # For a default local server this is usually http://127.0.0.1:3001. set $cloudcli_upstream http://127.0.0.1:3001; # Allow larger file uploads through the code editor/project file APIs. client_max_body_size 100m; # Redirect /ai to /ai/ so relative browser URL resolution is stable. # [SUBPATH LITERAL] Change `/ai` if you change $cloudcli_subpath. location = /ai { return 301 $cloudcli_subpath/; } # Main prefixed CloudCLI UI route. # # [SUBPATH LITERAL] Change `/ai/` and the `^/ai` rewrite if you change # $cloudcli_subpath. location ^~ /ai/ { # Strip the public subpath before proxying. CloudCLI expects to see # root paths such as /, /session/:id, /assets/..., /manifest.json. rewrite ^/ai(?/.*)$ $cloudcli_path break; # Forward the rewritten request to the private CloudCLI server. proxy_pass $cloudcli_upstream; # Use HTTP/1.1 so WebSocket upgrade requests can pass through if a # browser reaches a socket endpoint under the subpath. proxy_http_version 1.1; # Preserve useful request metadata for logs and future app support. 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 X-Forwarded-Prefix $cloudcli_subpath; # WebSocket upgrade headers. Harmless for normal HTTP requests. proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; # Long-running agent and terminal sessions can stay open for a long # time, so avoid closing idle proxied connections too aggressively. proxy_read_timeout 3600s; proxy_send_timeout 3600s; # Disable gzip from the upstream response so sub_filter can inspect # and rewrite HTML/JSON/JS response bodies. proxy_set_header Accept-Encoding ""; # Rewrite browser-visible root-relative URLs so the runtime can # discover that the app is mounted under the subpath. # # Examples: # href="/manifest.json" -> href="/ai/manifest.json" # src="/assets/app.js" -> src="/ai/assets/app.js" # # These rewrites are important for React Router basename detection. sub_filter_once off; sub_filter_types application/json application/manifest+json application/javascript text/javascript; sub_filter 'href="/' 'href="$cloudcli_subpath/'; sub_filter 'src="/' 'src="$cloudcli_subpath/'; # The production HTML and JS register the service worker at /sw.js. # Rewrite that registration so the worker is served from /ai/sw.js. sub_filter "register('/sw.js')" "register('$cloudcli_subpath/sw.js')"; sub_filter 'register("/sw.js")' 'register("$cloudcli_subpath/sw.js")'; # The manifest and service worker contain root-relative paths too. # Rewriting them keeps PWA metadata and cached manifest requests # under the same public subpath. sub_filter '"start_url": "/"' '"start_url": "$cloudcli_subpath/"'; sub_filter '"scope": "/"' '"scope": "$cloudcli_subpath/"'; sub_filter '"src": "/' '"src": "$cloudcli_subpath/'; sub_filter "'/manifest.json'" "'$cloudcli_subpath/manifest.json'"; sub_filter '"/manifest.json"' '"$cloudcli_subpath/manifest.json"'; } # Root API proxy. # # The current CloudCLI frontend calls APIs with root-relative URLs such # as /api/auth/login. Keep this location unless the frontend becomes # fully prefix-aware for API requests. location ^~ /api/ { proxy_pass $cloudcli_upstream; 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; proxy_set_header X-Forwarded-Prefix $cloudcli_subpath; proxy_read_timeout 3600s; proxy_send_timeout 3600s; } # Main app WebSocket proxy. # # The frontend opens /ws for realtime chat/session/task updates. location /ws { proxy_pass $cloudcli_upstream; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Prefix $cloudcli_subpath; proxy_read_timeout 3600s; proxy_send_timeout 3600s; } # Shell WebSocket proxy. # # The browser terminal uses /shell. It requires the same WebSocket # upgrade handling as /ws. location /shell { proxy_pass $cloudcli_upstream; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Prefix $cloudcli_subpath; proxy_read_timeout 3600s; proxy_send_timeout 3600s; } # Optional health endpoint proxy used by the frontend version checker. location = /health { proxy_pass $cloudcli_upstream; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Prefix $cloudcli_subpath; } } }