diff --git a/codebase/@features/broadcast/infra/mediamtx/mediamtx.yml b/codebase/@features/broadcast/infra/mediamtx/mediamtx.yml index 0f591664..d3010ad4 100644 --- a/codebase/@features/broadcast/infra/mediamtx/mediamtx.yml +++ b/codebase/@features/broadcast/infra/mediamtx/mediamtx.yml @@ -26,6 +26,13 @@ rtspAddress: :8554 webrtc: yes webrtcAddress: :8889 +# HLS for easy preview (used by live admin /admin and /shows/live player). +# URL example: http://:8888/hls/live/produced/index.m3u8 +hls: yes +hlsAddress: :8888 +hlsAllowOrigin: '*' +hlsTrustedProxies: [] + # Control API (used by healthchecks and optionally controller for stats). api: yes apiAddress: :9997 diff --git a/codebase/@features/broadcast/infra/scripts/bootstrap.sh b/codebase/@features/broadcast/infra/scripts/bootstrap.sh index 8d83ae21..496bf705 100755 --- a/codebase/@features/broadcast/infra/scripts/bootstrap.sh +++ b/codebase/@features/broadcast/infra/scripts/bootstrap.sh @@ -66,6 +66,7 @@ ufw default allow outgoing || true ufw allow 22/tcp || true ufw allow 8080/tcp || true # LLM controller UI (gate with passphrase; put real TLS/domain in front soon) ufw allow 8890/udp || true # SRT ingest (public by design; hotel push targets this) +ufw allow 8888/tcp || true # HLS preview (for live.transquinnftw.com/admin + shows; operator or proxied) ufw allow 1935/tcp || true # RTMP (local produced from OBS; also optional public ingest) ufw allow 4455/tcp || true # obs-websocket (internal to compose; do not expose publicly) ufw allow 8554/tcp || true # rtsp (optional) diff --git a/deployments/@domains/live.transquinnftw.com/deploy.sh b/deployments/@domains/live.transquinnftw.com/deploy.sh new file mode 100644 index 00000000..687e8f33 --- /dev/null +++ b/deployments/@domains/live.transquinnftw.com/deploy.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +# Deploy for live.transquinnftw.com (VIP live shows player + /admin preview). +# Light static surface. Run via ./run deploy:live or directly with --from-local. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" + +REMOTE="${REMOTE:-quinn-vps}" +WWW_ROOT="/var/www/live.transquinnftw.com/public" +NGINX_SITE="live.transquinnftw.com" + +echo "==> Deploying live.transquinnftw.com static to ${REMOTE}" + +# Sync the static assets (player + admin preview) +ssh "$REMOTE" "mkdir -p ${WWW_ROOT} ${WWW_ROOT}/admin ${WWW_ROOT}/shows" +rsync -a --delete \ + --exclude '.git' \ + "${SCRIPT_DIR}/public/" \ + "${REMOTE}:${WWW_ROOT}/" + +# Deploy nginx vhost (if present) +if [[ -f "${SCRIPT_DIR}/nginx/prod.conf" ]]; then + echo "==> Syncing nginx vhost" + ssh "$REMOTE" "mkdir -p /etc/nginx/sites-available" + scp "${SCRIPT_DIR}/nginx/prod.conf" "${REMOTE}:/etc/nginx/sites-available/${NGINX_SITE}" + ssh "$REMOTE" "ln -sf /etc/nginx/sites-available/${NGINX_SITE} /etc/nginx/sites-enabled/${NGINX_SITE}" + ssh "$REMOTE" "nginx -t && systemctl reload nginx || true" +fi + +echo "==> Done. Test: curl -I https://live.transquinnftw.com/ and https://live.transquinnftw.com/admin (SSO)" +echo "==> Player demo: https://live.transquinnftw.com/shows/live (add ?src=... for manual HLS test)" diff --git a/deployments/@domains/live.transquinnftw.com/nginx/prod.conf b/deployments/@domains/live.transquinnftw.com/nginx/prod.conf new file mode 100644 index 00000000..c62c9328 --- /dev/null +++ b/deployments/@domains/live.transquinnftw.com/nginx/prod.conf @@ -0,0 +1,108 @@ +# live.transquinnftw.com - VIP live shows player + operator admin +# +# This domain powers the VIP live streaming feature (vip.transquinnftw.com/shows/live,list). +# Public: /shows/live (player for authorized VIP clients), /shows (list?). +# Operator: /admin (light admin + preview of the OBS produced stream from the active relay). +# +# The high-bitrate feed is produced on a quinn.cast DO relay droplet (hotel SRT thin -> DO OBS encode) +# and fanned out (including to rtmp://live.transquinnftw.com/app/) so the platform ingest +# can serve it here. +# +# SSO: reuse the quinn-www / quinn-vip pattern. +# - /admin is edge-gated with auth_request to quinn.sso (operator only). +# - Public player uses client-side VIP token (like the vip portal) or show-specific access. +# - The admin preview can pull HLS from the relay's mediamtx (e.g. http://:8888/hls/live/produced/index.m3u8) +# or from the platform ingest HLS once wired. +# +# CSP strict, private where needed, noindex for admin. + +server { + listen 80; + listen [::]:80; + server_name live.transquinnftw.com; + + return 301 https://live.transquinnftw.com$request_uri; +} + +server { + listen 443 ssl http2; + listen [::]:443 ssl http2; + server_name live.transquinnftw.com; + + # SSL — shared SAN cert with transquinnftw.com + ssl_certificate /etc/letsencrypt/live/transquinnftw.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/transquinnftw.com/privkey.pem; + ssl_trusted_certificate /etc/letsencrypt/live/transquinnftw.com/chain.pem; + + # Mozilla Intermediate + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; + ssl_prefer_server_ciphers off; + ssl_session_timeout 1d; + ssl_session_cache shared:SSLLIVE:10m; + ssl_session_tickets off; + ssl_stapling on; + ssl_stapling_verify on; + + root /var/www/live.transquinnftw.com/public; + index index.html; + + client_body_timeout 10s; + client_header_timeout 10s; + + # Security headers + add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always; + add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:; connect-src 'self' https://my.transquinnftw.com; media-src 'self' blob: http: https:; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'; upgrade-insecure-requests" always; + add_header X-Frame-Options "DENY" always; + add_header X-Content-Type-Options "nosniff" always; + add_header Referrer-Policy "no-referrer" always; + add_header X-Robots-Tag "noindex, nofollow" always; + add_header Cross-Origin-Resource-Policy "same-origin" always; + + # Simple static for player (can be enhanced to SPA later) + location /shows { + add_header Cache-Control "no-cache, no-store, must-revalidate"; + try_files $uri $uri/ /shows/index.html; + } + + location = /shows/live { + add_header Cache-Control "no-cache, no-store, must-revalidate"; + try_files /shows/live/index.html /shows/index.html =404; + } + + # SSO gate for /admin (exact reuse of quinn-www / quinn-vip pattern) + location = /_sso_verify { + internal; + proxy_pass http://127.0.0.1:3025/auth/validate; + proxy_pass_request_body off; + proxy_set_header Content-Length ""; + proxy_set_header Cookie $http_cookie; + proxy_set_header X-Original-URI $request_uri; + proxy_set_header X-Forwarded-Host $host; + } + location @sso_redirect { + return 302 https://sso.transquinnftw.com/login?redirect=https://live.transquinnftw.com$request_uri; + } + + # /admin : light admin + OBS stream preview (SSO required) + # The page (static for now, or future SPA shell) can: + # - Input cast IP or relay ID (from active VIP show) + # - Preview the OBS output via HLS from the relay mediamtx (http://:8888/hls/live/produced/index.m3u8) + # or platform ingest HLS. + # - Light controls/status (future: call relay controller API for scenes, start/stop if exposed). + location ^~ /admin { + auth_request /_sso_verify; + error_page 401 = @sso_redirect; + add_header Cache-Control "no-cache, no-store, must-revalidate"; + try_files /admin/index.html /index.html =404; + } + + # SPA / static fallback for other paths (player etc.) + location / { + add_header Cache-Control "no-cache, no-store, must-revalidate"; + try_files $uri $uri/ /index.html; + } + + access_log /var/log/nginx/live.transquinnftw.com.access.log; + error_log /var/log/nginx/live.transquinnftw.com.error.log; +} diff --git a/deployments/@domains/live.transquinnftw.com/public/admin/index.html b/deployments/@domains/live.transquinnftw.com/public/admin/index.html new file mode 100644 index 00000000..5b2019d7 --- /dev/null +++ b/deployments/@domains/live.transquinnftw.com/public/admin/index.html @@ -0,0 +1,134 @@ + + + + + + live / admin — VIP shows (SSO) + + + + +
+
live.transquinnftw.com / admin
+
SSO protected • light admin + OBS preview (VIP shows)
+
+ +
+
+ +
+

Relay Preview

+
+
+ + +
+ +
+ +
+
Enter cast IP and load preview. HLS from relay mediamtx (produced feed).
+
+ +
+ + +
HLS URL: http://<ip>:8888/hls/live/produced/index.m3u8 (enable hls in cast mediamtx if needed)
+
+ +
+ Light admin: use the relay controller UI on the cast (http://<ip>:8080) or the quinn.cast LLM chat for full control (scenes, start/stop, destinations including vip-live). + Status can be pulled from the relay controller /api/status when exposed. +
+
+ + +
+

Light Admin

+

This is the operator view for the active VIP live show relay.

+
    +
  • Preview the clean OBS output (scenes + overlays + mix) before/while fanning to the VIP ingest.
  • +
  • Active relay details (cast IP, status) come from the VIP show booking or relay controller.
  • +
  • For full control (LLM commands, add "vip-live" destination with show key): use the relay controller on the cast or the broadcast API.
  • +
  • The fanout from this relay powers the player at /shows/live for authorized VIP clients.
  • +
+
SSO via quinn.sso (same as vip.transquinnftw.com/admin and transquinnftw.com protected routes).
+
+
+
+ + + + diff --git a/deployments/@domains/live.transquinnftw.com/public/index.html b/deployments/@domains/live.transquinnftw.com/public/index.html new file mode 100644 index 00000000..b1457a00 --- /dev/null +++ b/deployments/@domains/live.transquinnftw.com/public/index.html @@ -0,0 +1,7 @@ + +live.transquinnftw.com + +

live.transquinnftw.com

+

VIP live shows: /shows/live

+

Admin (SSO): /admin

+ diff --git a/deployments/@domains/live.transquinnftw.com/public/shows/index.html b/deployments/@domains/live.transquinnftw.com/public/shows/index.html new file mode 100644 index 00000000..73c3324d --- /dev/null +++ b/deployments/@domains/live.transquinnftw.com/public/shows/index.html @@ -0,0 +1,14 @@ + + + + + VIP Shows + + + +

VIP Shows

+

Live: /shows/live (player for active authorized show).

+

List and admin preview at /admin (SSO operator only).

+

The live feed is produced via the quinn.cast relay (hotel thin SRT → DO OBS → fanout to platform ingest at live.transquinnftw.com and/or external RTMPs).

+ + diff --git a/deployments/@domains/live.transquinnftw.com/public/shows/live/index.html b/deployments/@domains/live.transquinnftw.com/public/shows/live/index.html new file mode 100644 index 00000000..5cfe90f1 --- /dev/null +++ b/deployments/@domains/live.transquinnftw.com/public/shows/live/index.html @@ -0,0 +1,63 @@ + + + + + + VIP Show — Live + + + + +
+

VIP Show — Live

+
Authorized clients only. Stream from the active relay (high-bitrate from DO, thin contribution from performer location).
+ +
HLS source will be set by the show system or query param (?src=...).
+
+ + + + diff --git a/deployments/@domains/quinn.cast/data/docker-compose.yml b/deployments/@domains/quinn.cast/data/docker-compose.yml index ede8f6f5..cbfd2e0f 100644 --- a/deployments/@domains/quinn.cast/data/docker-compose.yml +++ b/deployments/@domains/quinn.cast/data/docker-compose.yml @@ -27,6 +27,7 @@ services: - "8890:8890/udp" # SRT ingest from hotel (main path) - "1935:1935" # RTMP (for local produced feed + optional ingest) - "8554:8554" # RTSP + - "8888:8888" # HLS (for /admin preview and /shows/live player on live.transquinnftw.com) - "8889:8889" # WebRTC (future) - "9997:9997" # API (controller can poll if needed) volumes: diff --git a/deployments/@domains/quinn.cast/scripts/bootstrap-cast-droplet.sh b/deployments/@domains/quinn.cast/scripts/bootstrap-cast-droplet.sh index bdaf47fb..29cb3442 100755 --- a/deployments/@domains/quinn.cast/scripts/bootstrap-cast-droplet.sh +++ b/deployments/@domains/quinn.cast/scripts/bootstrap-cast-droplet.sh @@ -183,4 +183,4 @@ echo " 3. From laptop: ./run deploy:cast --from-local (will install nginx sit echo " 4. After deploy: ssh root@... journalctl -u quinn-cast -f" echo " 5. Open https://cast.transquinnftw.com/?p= from anywhere (or via mesh)." echo "" -echo "Security: firewall only 22/tcp + 80/tcp + 443/tcp + 8890/udp . Use ufw or doctl." +echo "Security: firewall only 22/tcp + 80/tcp + 443/tcp + 8890/udp + 8888/tcp (HLS preview) . Use ufw or doctl." diff --git a/run b/run index ee4fd617..bcebb201 100755 --- a/run +++ b/run @@ -34,6 +34,7 @@ # ./run deploy:ai Deploy quinn.ai dashboard to VPS (--direct only) # ./run deploy:ai-worker Deploy ai inference worker to black (--direct only) # ./run deploy:cast Deploy quinn.cast (broadcast relay) to dedicated droplet via provision-stream (--from-local only) +# ./run deploy:live Deploy live.transquinnftw.com (VIP shows player + SSO /admin with OBS preview) (--from-local only) # ./run deploy:att Deploy adulttherapytour.com + SEO bait to vps-0 (--from-local only) # ./run deploy:cocotte Deploy cocotte.maison (+ defensive cocottehouse.com via defensive-coms) to vps-0 (--from-local only) # ./run deploy:sansonnet Deploy sansonnet.maison (+ defensive maisonsansonnet.com via defensive-coms) to vps-0 (--from-local only) diff --git a/scripts/run/deploy.sh b/scripts/run/deploy.sh index a841b268..76b1efa6 100755 --- a/scripts/run/deploy.sh +++ b/scripts/run/deploy.sh @@ -216,6 +216,17 @@ case "$COMMAND" in fi ;; + deploy:live) + if [[ "$FROM_LOCAL" == "true" ]]; then + echo "[direct] Deploying live.transquinnftw.com (VIP shows player + /admin)..." + bash "$ROOT_DIR/deployments/@domains/live.transquinnftw.com/deploy.sh" "$@" + else + echo "ERROR: No CI for live yet. Use --from-local." >&2 + echo " ./run deploy:live --from-local" >&2 + exit 1 + fi + ;; + deploy:api) if [[ "$FROM_LOCAL" == "true" ]]; then echo "[direct] Deploying quinn.api to production..." @@ -294,6 +305,7 @@ case "$COMMAND" in echo " ./run deploy:ai Deploy quinn.ai dashboard to VPS (--from-local only)" echo " ./run deploy:ai-worker Deploy ai inference worker to black (--from-local only)" echo " ./run deploy:cast Deploy quinn.cast (broadcast relay) to dedicated droplet via provision-stream (--from-local only)" + echo " ./run deploy:live Deploy live.transquinnftw.com (VIP shows + /admin SSO preview) (--from-local only)" echo " ./run deploy:api Deploy quinn.api data API to VPS (--from-local only)" echo " ./run deploy:att Deploy adulttherapytour.com + SEO bait to vps-0 (--from-local only)" echo " ./run deploy:cocotte Deploy cocotte.maison to vps-0 (--from-local only)"