openvibers/openvibe.ai Commits
Add the ai.openvibe.services reference nginx vhost (same allow-list as ai.openvibe.network) (#19)
Changes
@@@ -386,10 +386,15 @@ Deployed: `/opt/openvibe.ai`, env `/etc/openvibe/ai.env` (0600), unit `deploy/sy(`StateDirectory=openvibe-ai`; database `ov_ai` on the host's data role), principal `ai`. The nginx vhost`deploy/nginx/ai.openvibe.network.conf` (health/ready, `/`, `/robots.txt`, the operator console`/console` and its sign-in `/auth/` public; the API is host-local) is not installed:--`ai.openvibe.network` still serves the Sites placeholder. The console also needs++`ai.openvibe.network` still serves the Sites placeholder. `deploy/nginx/ai.openvibe.services.conf`++mirrors that vhost for `ai.openvibe.services` (the same proxied locations to the same upstream,++`/metrics` and the API still blocked, on the `*.openvibe.services` certificate the host already holds) and++replaces the Sites placeholder for `ai.openvibe.services` when installed; it is not installed or live yet. The console also needs`AI_CONSOLE_SESSION_SECRET` (32+ random characters) and `OV_OAUTH_CLIENT_SECRET` in the env file(without them it answers 503 in production), and the Network must list--`https://ai.openvibe.network/auth/callback` among client `ai`'s redirect URIs.++`https://ai.openvibe.network/auth/callback` among client `ai`'s redirect URIs (and++`https://ai.openvibe.services/auth/callback` once that vhost serves the console, since the callback is built from++the base URL).## Not done yetnnew file mode 100644@@@ -0,0 +1,102 @@++## ═══════════════════════════════════════════════════════════════++## ai.openvibe.services — Nginx Configuration (REFERENCE)++##++## Deployed to /etc/nginx/sites-available/ai.openvibe.services.conf — keep them in sync, and reload++## nginx after changing it. It uses the *.openvibe.services certificate the host already holds++## (/etc/letsencrypt/live/openvibe.services/); installed in place of the OpenVibe.Sites placeholder++## vhost of the same name, it replaces that placeholder.++##++## The Node service (port 4700, systemd unit openvibe-ai) serves:++## /api/health, /api/ready++## /metrics Prometheus text, answered only to direct loopback callers (never proxied)++## /api/v1/* service-to-service API (Network service tokens, audience openvibe.ai). Services on++## this host call 127.0.0.1:4700 directly, so the public vhost refuses it; open the++## location below only when a service on another host needs it (it still needs a token).++## /console the operator console (server-rendered, OpenVibe.Network staff only) and /auth/* its++## Network sign-in (redirect URI https://ai.openvibe.services/auth/callback, OAuth client++## `ai`); both noindex. /robots.txt keeps crawlers out of them.++## /stats public price and latency per provider and model (server-rendered, no sign-in)++## ═══════════════════════════════════════════════════════════════++++server {++ listen 80;++ listen [::]:80;++ server_name ai.openvibe.services;++++ location /.well-known/acme-challenge/ { root /var/www/certbot; }++ location / { return 301 https://ai.openvibe.services$request_uri; }++}++++server {++ listen 443 ssl;++ listen [::]:443 ssl;++ http2 on;++ server_name ai.openvibe.services;++++ ssl_certificate /etc/letsencrypt/live/openvibe.services/fullchain.pem;++ ssl_certificate_key /etc/letsencrypt/live/openvibe.services/privkey.pem;++ ssl_protocols TLSv1.2 TLSv1.3;++++ access_log /var/log/nginx/ai.openvibe.services.access.log;++ error_log /var/log/nginx/ai.openvibe.services.error.log;++++ # Inline images arrive as data URLs inside run inputs.++ client_max_body_size 12m;++++ add_header X-Content-Type-Options nosniff always;++ add_header Referrer-Policy strict-origin-when-cross-origin always;++ add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;++++ location = /api/health { proxy_pass http://127.0.0.1:4700; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header CF-Connecting-IP $remote_addr; }++ location = /api/ready { proxy_pass http://127.0.0.1:4700; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header CF-Connecting-IP $remote_addr; }++++ # Metrics are for host-local scrapers only (http://127.0.0.1:4700/metrics).++ location = /metrics { return 404; }++++ # ── Service API: host-local only (see header) ───────────++ location /api/ {++ return 403;++ # To expose it (token-guarded) to off-host services instead:++ # proxy_pass http://127.0.0.1:4700;++ # proxy_http_version 1.1;++ # proxy_set_header Host $host;++ # proxy_set_header X-Real-IP $remote_addr; # client address from $remote_addr only (realip),++ # proxy_set_header X-Forwarded-For $remote_addr; # never appended to or passed through++ # proxy_set_header CF-Connecting-IP $remote_addr;++ # proxy_set_header X-Forwarded-Proto $scheme;++ # proxy_read_timeout 90s; # ?wait= holds a request up to 60 s++ }++++ # ── Operator console and its Network sign-in (staff only; the app refuses everyone else) ──++ location /console {++ proxy_pass http://127.0.0.1:4700;++ proxy_set_header Host $host;++ proxy_set_header X-Real-IP $remote_addr;++ proxy_set_header X-Forwarded-For $remote_addr;++ proxy_set_header CF-Connecting-IP $remote_addr;++ proxy_set_header X-Forwarded-Proto $scheme;++ }++ location /auth/ {++ proxy_pass http://127.0.0.1:4700;++ proxy_set_header Host $host;++ proxy_set_header X-Real-IP $remote_addr;++ proxy_set_header X-Forwarded-For $remote_addr;++ proxy_set_header CF-Connecting-IP $remote_addr;++ proxy_set_header X-Forwarded-Proto $scheme;++ }++ location = /robots.txt { proxy_pass http://127.0.0.1:4700; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header CF-Connecting-IP $remote_addr; }++++ location = /stats { proxy_pass http://127.0.0.1:4700; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header CF-Connecting-IP $remote_addr; }++++ location = / {++ proxy_pass http://127.0.0.1:4700;++ proxy_set_header Host $host;++ proxy_set_header X-Real-IP $remote_addr;++ proxy_set_header X-Forwarded-For $remote_addr;++ proxy_set_header CF-Connecting-IP $remote_addr;++ }++++ location / {++ return 404;++ }++}
@@ -386,10 +386,15 @@ Deployed: `/opt/openvibe.ai`, env `/etc/openvibe/ai.env` (0600), unit `deploy/sy
@@ -386,10 +386,15 @@ Deployed: `/opt/openvibe.ai`, env `/etc/openvibe/ai.env` (0600), unit `deploy/sy
(`StateDirectory=openvibe-ai`; database `ov_ai` on the host's data role), principal `ai`. The nginx vhost
(`StateDirectory=openvibe-ai`; database `ov_ai` on the host's data role), principal `ai`. The nginx vhost
`deploy/nginx/ai.openvibe.network.conf` (health/ready, `/`, `/robots.txt`, the operator console
`deploy/nginx/ai.openvibe.network.conf` (health/ready, `/`, `/robots.txt`, the operator console
`/console` and its sign-in `/auth/` public; the API is host-local) is not installed:
`/console` and its sign-in `/auth/` public; the API is host-local) is not installed:
-`ai.openvibe.network` still serves the Sites placeholder. The console also needs
+`ai.openvibe.network` still serves the Sites placeholder. `deploy/nginx/ai.openvibe.services.conf`
+mirrors that vhost for `ai.openvibe.services` (the same proxied locations to the same upstream,
+`/metrics` and the API still blocked, on the `*.openvibe.services` certificate the host already holds) and
+replaces the Sites placeholder for `ai.openvibe.services` when installed; it is not installed or live yet. The console also needs
`AI_CONSOLE_SESSION_SECRET` (32+ random characters) and `OV_OAUTH_CLIENT_SECRET` in the env file
`AI_CONSOLE_SESSION_SECRET` (32+ random characters) and `OV_OAUTH_CLIENT_SECRET` in the env file
(without them it answers 503 in production), and the Network must list
(without them it answers 503 in production), and the Network must list
-`https://ai.openvibe.network/auth/callback` among client `ai`'s redirect URIs.
+`https://ai.openvibe.network/auth/callback` among client `ai`'s redirect URIs (and
+`https://ai.openvibe.services/auth/callback` once that vhost serves the console, since the callback is built from
+the base URL).
## Not done yet
## Not done yet
new file mode 100644
new file mode 100644
@@ -0,0 +1,102 @@
@@ -0,0 +1,102 @@
+## ═══════════════════════════════════════════════════════════════
+## ai.openvibe.services — Nginx Configuration (REFERENCE)
+##
+## Deployed to /etc/nginx/sites-available/ai.openvibe.services.conf — keep them in sync, and reload
+## nginx after changing it. It uses the *.openvibe.services certificate the host already holds
+## (/etc/letsencrypt/live/openvibe.services/); installed in place of the OpenVibe.Sites placeholder
+## vhost of the same name, it replaces that placeholder.
+##
+## The Node service (port 4700, systemd unit openvibe-ai) serves:
+## /api/health, /api/ready
+## /metrics Prometheus text, answered only to direct loopback callers (never proxied)
+## /api/v1/* service-to-service API (Network service tokens, audience openvibe.ai). Services on
+## this host call 127.0.0.1:4700 directly, so the public vhost refuses it; open the
+## location below only when a service on another host needs it (it still needs a token).
+## /console the operator console (server-rendered, OpenVibe.Network staff only) and /auth/* its
+## Network sign-in (redirect URI https://ai.openvibe.services/auth/callback, OAuth client
+## `ai`); both noindex. /robots.txt keeps crawlers out of them.
+## /stats public price and latency per provider and model (server-rendered, no sign-in)
+## ═══════════════════════════════════════════════════════════════
+
+server {
+ listen 80;
+ listen [::]:80;
+ server_name ai.openvibe.services;
+
+ location /.well-known/acme-challenge/ { root /var/www/certbot; }
+ location / { return 301 https://ai.openvibe.services$request_uri; }
+}
+
+server {
+ listen 443 ssl;
+ listen [::]:443 ssl;
+ http2 on;
+ server_name ai.openvibe.services;
+
+ ssl_certificate /etc/letsencrypt/live/openvibe.services/fullchain.pem;
+ ssl_certificate_key /etc/letsencrypt/live/openvibe.services/privkey.pem;
+ ssl_protocols TLSv1.2 TLSv1.3;
+
+ access_log /var/log/nginx/ai.openvibe.services.access.log;
+ error_log /var/log/nginx/ai.openvibe.services.error.log;
+
+ # Inline images arrive as data URLs inside run inputs.
+ client_max_body_size 12m;
+
+ add_header X-Content-Type-Options nosniff always;
+ add_header Referrer-Policy strict-origin-when-cross-origin always;
+ add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
+
+ location = /api/health { proxy_pass http://127.0.0.1:4700; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header CF-Connecting-IP $remote_addr; }
+ location = /api/ready { proxy_pass http://127.0.0.1:4700; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header CF-Connecting-IP $remote_addr; }
+
+ # Metrics are for host-local scrapers only (http://127.0.0.1:4700/metrics).
+ location = /metrics { return 404; }
+
+ # ── Service API: host-local only (see header) ───────────
+ location /api/ {
+ return 403;
+ # To expose it (token-guarded) to off-host services instead:
+ # proxy_pass http://127.0.0.1:4700;
+ # proxy_http_version 1.1;
+ # proxy_set_header Host $host;
+ # proxy_set_header X-Real-IP $remote_addr; # client address from $remote_addr only (realip),
+ # proxy_set_header X-Forwarded-For $remote_addr; # never appended to or passed through
+ # proxy_set_header CF-Connecting-IP $remote_addr;
+ # proxy_set_header X-Forwarded-Proto $scheme;
+ # proxy_read_timeout 90s; # ?wait= holds a request up to 60 s
+ }
+
+ # ── Operator console and its Network sign-in (staff only; the app refuses everyone else) ──
+ location /console {
+ proxy_pass http://127.0.0.1:4700;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $remote_addr;
+ proxy_set_header CF-Connecting-IP $remote_addr;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ }
+ location /auth/ {
+ proxy_pass http://127.0.0.1:4700;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $remote_addr;
+ proxy_set_header CF-Connecting-IP $remote_addr;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ }
+ location = /robots.txt { proxy_pass http://127.0.0.1:4700; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header CF-Connecting-IP $remote_addr; }
+
+ location = /stats { proxy_pass http://127.0.0.1:4700; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header CF-Connecting-IP $remote_addr; }
+
+ location = / {
+ proxy_pass http://127.0.0.1:4700;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $remote_addr;
+ proxy_set_header CF-Connecting-IP $remote_addr;
+ }
+
+ location / {
+ return 404;
+ }
+}