diff --git a/infra/ansible/roles/veza_app/templates/stream.env.j2 b/infra/ansible/roles/veza_app/templates/stream.env.j2 new file mode 100644 index 000000000..b31d5dda9 --- /dev/null +++ b/infra/ansible/roles/veza_app/templates/stream.env.j2 @@ -0,0 +1,54 @@ +# Managed by Ansible — do not edit by hand. veza_app role, +# templates/stream.env.j2 ; rendered fresh on every deploy. +# Sourced by /etc/systemd/system/veza-stream.service via EnvironmentFile=. + +# --- Runtime --------------------------------------------------------- +APP_ENV={{ veza_env }} +RUST_LOG={{ veza_log_level | lower }} +PORT={{ veza_stream_port }} +HOST=0.0.0.0 +RELEASE_SHA={{ veza_release_sha }} +COLOR={{ veza_target_color }} + +# --- Required: stream server's symmetric secret (≥32 chars). --------- +# Reused for HMAC signing of HLS segment URLs + cache key salting. +# Distinct from JWT signing — stream verifies tokens with the +# backend's RS256 public key (path below) but signs its own +# short-lived stream URLs with this. +SECRET_KEY={{ vault_stream_internal_api_key }} + +# --- Backend ↔ stream shared secret ----------------------------------- +# Same value the backend stamps in X-Internal-API-Key for /api/v1/internal/*. +# Stream rejects internal calls without a matching header. +INTERNAL_API_KEY={{ vault_stream_internal_api_key }} +BACKEND_BASE_URL=http://{{ veza_container_prefix }}backend-{{ veza_target_color }}.{{ veza_incus_dns_suffix }}:{{ veza_backend_port }} + +# --- JWT verification (RS256 public key only — stream never signs) --- +JWT_PUBLIC_KEY_PATH={{ veza_config_root }}/secrets/jwt-public.pem +JWT_ALGORITHM=RS256 + +# --- Object storage (MinIO — pulls audio for transcode + HLS) ------- +S3_ENDPOINT=http://{{ veza_container_prefix }}minio-1.{{ veza_incus_dns_suffix }}:9000 +S3_REGION=us-east-1 +S3_ACCESS_KEY={{ vault_minio_access_key }} +S3_SECRET_KEY={{ vault_minio_secret_key }} +S3_BUCKET=veza-{{ veza_env }} + +# --- RabbitMQ (event bus — degraded mode tolerated, see lib.rs) ------ +RABBITMQ_URL=amqp://veza:{{ vault_rabbitmq_password }}@{{ veza_container_prefix }}rabbitmq.{{ veza_incus_dns_suffix }}:5672/veza + +# --- Observability --------------------------------------------------- +SENTRY_DSN={{ vault_sentry_dsn | default('') }} +OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector.{{ veza_incus_dns_suffix }}:4317 +OTEL_SERVICE_NAME=veza-stream +OTEL_TRACES_SAMPLER=parentbased_traceidratio +OTEL_TRACES_SAMPLER_ARG={{ veza_otel_sample_rate }} +OTEL_RESOURCE_ATTRIBUTES=deployment.environment={{ veza_env }},service.version={{ veza_release_sha[:12] }} + +# --- Streaming-specific ----------------------------------------------- +# HLS segment cache lives under {{ veza_state_root }}/hls — sized small +# (~500 MB) since MinIO is the source of truth and segments are +# regenerated on miss. +HLS_CACHE_DIR={{ veza_state_root }}/hls +HLS_CACHE_MAX_BYTES=536870912 +HLS_SEGMENT_DURATION_SECONDS=6 diff --git a/infra/ansible/roles/veza_app/templates/veza-stream.service.j2 b/infra/ansible/roles/veza_app/templates/veza-stream.service.j2 new file mode 100644 index 000000000..701b93408 --- /dev/null +++ b/infra/ansible/roles/veza_app/templates/veza-stream.service.j2 @@ -0,0 +1,35 @@ +# Managed by Ansible — do not edit by hand. +# veza_app role, templates/veza-stream.service.j2. +# Released SHA: {{ veza_release_sha }} ; color: {{ veza_target_color }} +[Unit] +Description=Veza stream server (Rust/Axum) — color {{ veza_target_color }}, sha {{ veza_release_sha[:12] }} +Documentation=https://veza.fr/docs +After=network-online.target +Wants=network-online.target +AssertPathExists={{ veza_app_current_link }}/{{ veza_app_binary_name }} + +[Service] +Type=simple +User={{ veza_app_user }} +Group={{ veza_app_group }} +EnvironmentFile=-{{ veza_app_env_file }} +WorkingDirectory={{ veza_app_current_link }} +ExecStart={{ veza_app_current_link }}/{{ veza_app_binary_name }} +Restart=on-failure +RestartSec=5s +# Stream server holds many WebSocket + HLS connections in flight ; +# the default LimitNOFILE=1024 chokes around 200 concurrent listeners. +LimitNOFILE=131072 + +# Hardening — same baseline as the backend. +NoNewPrivileges=true +ProtectSystem=strict +ProtectHome=true +ReadWritePaths={{ veza_app_install_dir }} {{ veza_log_root }} {{ veza_state_root }} +PrivateTmp=true +ProtectKernelTunables=true +ProtectKernelModules=true +ProtectControlGroups=true + +[Install] +WantedBy=multi-user.target