From dc2641844ded336146fffd53e2865ffa17062445 Mon Sep 17 00:00:00 2001 From: Claude Code Date: Thu, 9 Apr 2026 20:10:04 -0700 Subject: [PATCH] =?UTF-8?q?chore(nginx):=20=F0=9F=94=A7=20Update=20routing?= =?UTF-8?q?=20and=20performance=20configs=20for=20companion.lilith.apricot?= =?UTF-8?q?.local=20and=20ai.quinn.apricot.local=20services?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Lilith Autocommit --- @deployments/nginx/README.md | 36 ++-- .../nginx/ai.quinn.apricot.local.conf | 166 +++++++++++++++++ .../nginx/companion.lilith.apricot.local.conf | 173 ------------------ 3 files changed, 186 insertions(+), 189 deletions(-) create mode 100644 @deployments/nginx/ai.quinn.apricot.local.conf delete mode 100644 @deployments/nginx/companion.lilith.apricot.local.conf diff --git a/@deployments/nginx/README.md b/@deployments/nginx/README.md index 17bf8e7..bc85b1d 100644 --- a/@deployments/nginx/README.md +++ b/@deployments/nginx/README.md @@ -3,36 +3,40 @@ ## Installation ```bash +# Generate wildcard cert (one-time): +mkcert -install +mkcert -cert-file /etc/nginx/certs/local/_wildcard.quinn.apricot.local+1.pem \ + -key-file /etc/nginx/certs/local/_wildcard.quinn.apricot.local+1-key.pem \ + "*.quinn.apricot.local" quinn.apricot.local + +# Add to /etc/hosts (if not already present): +echo "127.0.0.1 ai.quinn.apricot.local" | sudo tee -a /etc/hosts + # Symlink into nginx sites-enabled: -sudo ln -sf "$(pwd)/companion.lilith.apricot.local.conf" /etc/nginx/sites-enabled/companion.lilith.apricot.local.conf +sudo ln -sf "$(pwd)/ai.quinn.apricot.local.conf" /etc/nginx/sites-enabled/ai.quinn.apricot.local.conf + +# Remove old config if present: +sudo rm -f /etc/nginx/sites-enabled/companion.lilith.apricot.local.conf # Verify config and reload: sudo nginx -t && sudo systemctl reload nginx ``` -## Domains +## Domain | Domain | Upstream | Port | |--------|----------|------| -| `companion.lilith.apricot.local` | companion-api (NestJS) | 3850 | -| `companion-web.lilith.apricot.local` | companion-web (Vite) | 5850 | +| `ai.quinn.apricot.local` | companion-api (NestJS) + companion-web (Vite) | 3850 / 5850 | + +Single domain mirrors production (`ai.transquinnftw.com`). API paths (`/voice/`, `/chat`, `/session`, `/health`, `/api/`) route to companion-api on :3850. Everything else routes to the Vite dev server on :5850. ## SSL Certificates -Uses existing wildcard cert for `*.lilith.apricot.local`: +Uses wildcard cert for `*.quinn.apricot.local`: ``` -/etc/nginx/certs/local/_wildcard.lilith.apricot.local+1.pem -/etc/nginx/certs/local/_wildcard.lilith.apricot.local+1-key.pem -``` - -If the cert doesn't exist yet, generate with mkcert: - -```bash -mkcert -install -mkcert -cert-file /etc/nginx/certs/local/_wildcard.lilith.apricot.local+1.pem \ - -key-file /etc/nginx/certs/local/_wildcard.lilith.apricot.local+1-key.pem \ - "*.lilith.apricot.local" lilith.apricot.local +/etc/nginx/certs/local/_wildcard.quinn.apricot.local+1.pem +/etc/nginx/certs/local/_wildcard.quinn.apricot.local+1-key.pem ``` ## Voice WebSocket Notes diff --git a/@deployments/nginx/ai.quinn.apricot.local.conf b/@deployments/nginx/ai.quinn.apricot.local.conf new file mode 100644 index 0000000..687a02e --- /dev/null +++ b/@deployments/nginx/ai.quinn.apricot.local.conf @@ -0,0 +1,166 @@ +# ============================================================================= +# ai.quinn.apricot.local — Development nginx (companion-api + companion-web) +# ============================================================================= +# Single domain mirroring production (ai.transquinnftw.com). +# +# Routing: +# API paths → companion-api (NestJS) http://127.0.0.1:3850 +# Everything → companion-web (Vite) http://127.0.0.1:5850 +# +# Install: +# sudo ln -sf "$(pwd)/ai.quinn.apricot.local.conf" \ +# /etc/nginx/sites-enabled/ai.quinn.apricot.local.conf +# sudo nginx -t && sudo systemctl reload nginx +# ============================================================================= + +upstream quinn_dev_api { + server 127.0.0.1:3850; + keepalive 64; +} + +upstream quinn_dev_web { + server 127.0.0.1:5850; + keepalive 16; +} + +server { + listen 80; + listen [::]:80; + listen 443 ssl; + listen [::]:443 ssl; + server_name ai.quinn.apricot.local; + + ssl_certificate /etc/nginx/certs/local/_wildcard.quinn.apricot.local+1.pem; + ssl_certificate_key /etc/nginx/certs/local/_wildcard.quinn.apricot.local+1-key.pem; + + # ------------------------------------------------------------------------- + # API: Health check + # ------------------------------------------------------------------------- + location /health { + proxy_pass http://quinn_dev_api/health; + proxy_http_version 1.1; + proxy_set_header Host $host; + access_log off; + } + + # ------------------------------------------------------------------------- + # API: Voice WebSocket — binary PCM; buffering MUST be off + # ------------------------------------------------------------------------- + location /voice/ { + proxy_pass http://quinn_dev_api; + proxy_http_version 1.1; + + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + 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_buffering off; + proxy_request_buffering off; + + proxy_read_timeout 3600s; + proxy_send_timeout 3600s; + proxy_connect_timeout 10s; + } + + # ------------------------------------------------------------------------- + # API: Chat SSE stream — buffering off for streaming + # ------------------------------------------------------------------------- + location = /chat { + proxy_pass http://quinn_dev_api; + 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_buffering off; + proxy_cache off; + add_header X-Accel-Buffering "no" always; + proxy_read_timeout 300s; + proxy_send_timeout 300s; + proxy_connect_timeout 10s; + } + + # ------------------------------------------------------------------------- + # API: Session, push, and general API endpoints + # ------------------------------------------------------------------------- + location ~ ^/(session|api)(/|$) { + proxy_pass http://quinn_dev_api; + 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_connect_timeout 10s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + proxy_buffering on; + proxy_buffer_size 8k; + proxy_buffers 16 8k; + proxy_busy_buffers_size 16k; + } + + # ------------------------------------------------------------------------- + # Vite: HMR WebSocket + # ------------------------------------------------------------------------- + location /ws { + proxy_pass http://quinn_dev_web; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_set_header Host $host; + proxy_buffering off; + proxy_read_timeout 86400s; + } + + # ------------------------------------------------------------------------- + # Vite: Internal dev paths + # ------------------------------------------------------------------------- + location /@vite/ { + proxy_pass http://quinn_dev_web; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_set_header Host $host; + } + + location /@fs/ { + proxy_pass http://quinn_dev_web; + proxy_http_version 1.1; + proxy_set_header Host $host; + } + + location /node_modules/ { + proxy_pass http://quinn_dev_web; + proxy_http_version 1.1; + proxy_set_header Host $host; + } + + location /src/ { + proxy_pass http://quinn_dev_web; + proxy_http_version 1.1; + proxy_set_header Host $host; + } + + # ------------------------------------------------------------------------- + # Vite: SPA catch-all (everything not matched above) + # ------------------------------------------------------------------------- + location / { + proxy_pass http://quinn_dev_web; + 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 Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_connect_timeout 10s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + } + + access_log /var/log/nginx/ai.quinn.apricot.local.access.log; + error_log /var/log/nginx/ai.quinn.apricot.local.error.log; +} diff --git a/@deployments/nginx/companion.lilith.apricot.local.conf b/@deployments/nginx/companion.lilith.apricot.local.conf deleted file mode 100644 index b9df0a4..0000000 --- a/@deployments/nginx/companion.lilith.apricot.local.conf +++ /dev/null @@ -1,173 +0,0 @@ -# companion.lilith.apricot.local — companion-api (NestJS) -# companion-web.lilith.apricot.local — companion-web (Vite dev server) -# -# companion-api port: 3850 (from @deployments/ports.yaml) -# companion-web port: 5850 (Vite dev, assigned adjacent to api) -# -# Upstream declarations must be in the http context. -# Include this file from /etc/nginx/sites-enabled/ via: -# include /etc/nginx/sites-enabled/companion.lilith.apricot.local.conf; - -upstream companion_api { - server 127.0.0.1:3850; - keepalive 64; -} - -upstream companion_web { - server 127.0.0.1:5850; - keepalive 16; -} - -# companion-api: REST + WebSocket (voice pipeline) -server { - listen 80; - listen [::]:80; - listen 443 ssl; - listen [::]:443 ssl; - server_name companion.lilith.apricot.local; - - ssl_certificate /etc/nginx/certs/local/_wildcard.lilith.apricot.local+1.pem; - ssl_certificate_key /etc/nginx/certs/local/_wildcard.lilith.apricot.local+1-key.pem; - - # Health check (no buffering, no auth) - location /health { - proxy_pass http://companion_api/health; - proxy_http_version 1.1; - proxy_set_header Host $host; - access_log off; - } - - # Voice WebSocket endpoint — binary PCM; buffering MUST be off - location /voice/ { - proxy_pass http://companion_api; - proxy_http_version 1.1; - - # WebSocket upgrade headers - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $connection_upgrade; - 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; - - # Critical: disable buffering for binary PCM stream - proxy_buffering off; - proxy_request_buffering off; - - # Long timeouts for persistent voice sessions - proxy_read_timeout 3600s; - proxy_send_timeout 3600s; - proxy_connect_timeout 10s; - } - - # REST API - location / { - proxy_pass http://companion_api; - 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-Host $host; - proxy_set_header X-Forwarded-Port $server_port; - - # WebSocket support (for future WS endpoints) - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $connection_upgrade; - - # Buffering for REST responses - proxy_buffering on; - proxy_buffer_size 4k; - proxy_buffers 8 4k; - proxy_busy_buffers_size 8k; - - # SSE: disable buffering for /chat streaming - location /chat { - proxy_pass http://companion_api; - 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_buffering off; - proxy_read_timeout 300s; - proxy_send_timeout 300s; - } - - proxy_connect_timeout 10s; - proxy_send_timeout 60s; - proxy_read_timeout 60s; - } - - access_log /var/log/nginx/companion.lilith.apricot.local.access.log; - error_log /var/log/nginx/companion.lilith.apricot.local.error.log; -} - -# companion-web: Vite dev server (SPA + HMR) -server { - listen 80; - listen [::]:80; - listen 443 ssl; - listen [::]:443 ssl; - server_name companion-web.lilith.apricot.local; - - ssl_certificate /etc/nginx/certs/local/_wildcard.lilith.apricot.local+1.pem; - ssl_certificate_key /etc/nginx/certs/local/_wildcard.lilith.apricot.local+1-key.pem; - - # Vite HMR WebSocket - location /ws { - proxy_pass http://companion_web; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $connection_upgrade; - proxy_set_header Host $host; - proxy_buffering off; - proxy_read_timeout 86400s; - } - - # Vite internal paths - location /@vite/ { - proxy_pass http://companion_web; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $connection_upgrade; - proxy_set_header Host $host; - } - - location /@fs/ { - proxy_pass http://companion_web; - proxy_http_version 1.1; - proxy_set_header Host $host; - } - - location /node_modules/ { - proxy_pass http://companion_web; - proxy_http_version 1.1; - proxy_set_header Host $host; - } - - location /src/ { - proxy_pass http://companion_web; - proxy_http_version 1.1; - proxy_set_header Host $host; - } - - # SPA catch-all - location / { - proxy_pass http://companion_web; - 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 Upgrade $http_upgrade; - proxy_set_header Connection $connection_upgrade; - proxy_connect_timeout 10s; - proxy_send_timeout 60s; - proxy_read_timeout 60s; - } - - access_log /var/log/nginx/companion-web.lilith.apricot.local.access.log; - error_log /var/log/nginx/companion-web.lilith.apricot.local.error.log; -}