You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
synctv/synctv.example.yaml

643 lines
26 KiB
YAML

# SyncTV Configuration Example
#
# Copy this file to synctv.yaml and modify as needed
#
# Configuration priority: defaults < config file < environment variables
#
# Secret-like settings also support sibling *_file keys in this config file.
# Relative *_file paths are resolved relative to the config file location.
#
# Environment variables use SYNCTV_ prefix with single underscore separator:
# SYNCTV_SERVER_HOST=0.0.0.0
# SYNCTV_DATABASE_URL=postgresql://...
# SYNCTV_DATABASE_URL_FILE=/run/secrets/database_url
# SYNCTV_OAUTH2_REDIRECT_SCHEME=https
#
# Secret-like settings support two file-based forms:
# 1. Config file sibling keys: jwt.secret_file, management.auth_token_file, ...
# 2. Env file variables: SYNCTV_JWT_SECRET_FILE, SYNCTV_MANAGEMENT_AUTH_TOKEN_FILE, ...
server:
host: "0.0.0.0"
port: 8080
enable_reflection: false
# Maximum gRPC message size in bytes (both incoming and outgoing).
# Prevents OOM attacks from oversized messages. Default: 16MB (16777216 bytes).
# Minimum: 1MB (1048576 bytes), Maximum: 1GB (1073741824 bytes).
# Environment variable: SYNCTV_SERVER_GRPC_MAX_MESSAGE_SIZE_BYTES
# grpc_max_message_size_bytes: 16777216
# Trusted proxy IP addresses/CIDRs for X-Forwarded-For validation.
# If empty, X-Forwarded-For headers are NOT trusted (socket address is used).
# Example: ["10.0.0.0/8", "192.168.0.0/16"]
trusted_proxies: []
# CORS allowed origins for cross-origin requests from web frontends.
#
# You MUST set this to your frontend URL(s) or cross-origin
# requests (fetch, WebSocket upgrades from browsers) will be rejected.
#
# Examples:
# cors_allowed_origins: ["https://app.example.com"]
# cors_allowed_origins: ["https://app.example.com", "https://admin.example.com"]
#
# Environment variable:
# SYNCTV_SERVER_CORS_ALLOWED_ORIGINS='["https://app.example.com"]'
#
# WARNING: An empty list in production means ALL cross-origin requests are denied.
cors_allowed_origins: []
# Shared secret for authenticating cluster gRPC calls between nodes.
# REQUIRED when cluster.enabled is true — startup fails if this is empty in cluster mode.
# This prevents unauthorized inter-node gRPC calls in multi-replica deployments.
# Not required in standalone mode even when Redis is configured.
# Generate a secret with: openssl rand -hex 32
# Environment variable: SYNCTV_SERVER_CLUSTER_SECRET
# Environment file variable: SYNCTV_SERVER_CLUSTER_SECRET_FILE
# File-based secret: server.cluster_secret_file: "/run/secrets/cluster_secret"
cluster_secret: ""
# Advertise host for cluster node registration (address other nodes use to reach this instance).
# Falls back to POD_IP env var, then system hostname if empty.
# In Kubernetes, set via SYNCTV_SERVER_ADVERTISE_HOST or downward API (status.podIP).
advertise_host: ""
# Maximum time in seconds to wait for active connections to drain during shutdown.
# Defaults to 30 seconds. Increase for deployments with many long-lived connections.
shutdown_drain_timeout_seconds: 30
time:
# Default IANA timezone used for human-readable time output and local datetime parsing.
# Resolution priority:
# 1. time.timezone
# 2. SYNCTV_TIME_TIMEZONE
# 3. TZ
# 4. system timezone
# 5. UTC
#
# Examples: "Asia/Shanghai", "UTC", "America/New_York"
# Leave empty to use env/system detection.
timezone: ""
# Public IDs are prefixed decimal IDs by default, for example usr_1 and room_1.
# Set sqids to enable prefixed sqids instead, for example usr_<sqid>.
# Environment variables:
# SYNCTV_PUBLIC_IDS_SQIDS_ALPHABET
# SYNCTV_PUBLIC_IDS_SQIDS_MIN_LENGTH
public_ids: {}
# public_ids:
# sqids:
# alphabet: null
# min_length: 12
security:
# AES-256-GCM key used to encrypt sensitive provider credentials.
# Prefer file-based secrets so keys are not stored inline in config files.
# Generate with: openssl rand -hex 32
# Environment variables:
# SYNCTV_SECURITY_CREDENTIAL_ENCRYPTION_KEY
# SYNCTV_SECURITY_CREDENTIAL_ENCRYPTION_KEY_FILE
# credential_encryption_key_file: "/run/secrets/credential_encryption_key"
credential_encryption_key: ""
metrics:
# Dedicated metrics listener. When enabled, /metrics is not served on the API port.
enabled: false
host: "0.0.0.0"
port: 9090
# tls:
# enabled: true
# # Static input files: relative cert/key paths stay relative to this config file.
# # They are NOT rebased through data_dir.
# cert_path: "./tls/metrics.crt"
# key_path: "./tls/metrics.key"
auth:
# Authentication mode for /metrics: bearer_token, basic, kubernetes
mode: "bearer_token"
bearer_token: ""
# Environment variable: SYNCTV_METRICS_AUTH_BEARER_TOKEN_FILE
# bearer_token_file: "/run/secrets/metrics_bearer_token"
# basic_username: "metrics"
# basic_password: ""
# Environment variable: SYNCTV_METRICS_AUTH_BASIC_PASSWORD_FILE
# basic_password_file: "/run/secrets/metrics_basic_password"
management:
# Local-only management daemon transport used by the `synctv` CLI for remote lifecycle commands.
# Unix-like platforms default to a Unix domain socket for daemon-style local access.
# Switch transport to "tcp" when the CLI must connect over a TCP loopback endpoint.
# TCP transport REQUIRES auth_token; startup fails if it is empty.
# Unix transport also supports auth_token, but defaults to empty and may rely on
# owner-only socket permissions instead.
# Runtime-owned local paths use the top-level `data_dir` root shown below.
# Leave unix_socket_path unset to use the platform default under data_dir/run/.
# Environment variables:
# SYNCTV_DATA_DIR=/var/lib/synctv
# SYNCTV_MANAGEMENT_ENABLED=true
# SYNCTV_MANAGEMENT_TRANSPORT=unix
# SYNCTV_MANAGEMENT_UNIX_SOCKET_PATH=/path/to/synctv.sock
# SYNCTV_MANAGEMENT_PORT=50052
# SYNCTV_MANAGEMENT_AUTH_TOKEN=replace-with-a-random-secret
# SYNCTV_MANAGEMENT_AUTH_TOKEN_FILE=/run/secrets/management_auth_token
enabled: true
transport: "unix"
port: 50052
# Relative unix_socket_path values are resolved from data_dir.
# unix_socket_path: "/absolute/path/to/synctv.sock"
# auth_token: "replace-with-a-random-secret"
# auth_token_file is a static config input. Relative paths stay relative to this config file.
# auth_token_file: "/run/secrets/management_auth_token"
# Reflection can be configured independently from the public API gRPC listener.
enable_reflection: false
# `data_dir` is the shared root for runtime-owned local files.
# It affects relative:
# - management.unix_socket_path
# - logging.file_path
# - livestream.hls_storage_path
# - cache.proxy_slice_file_cache_dir
# It does NOT affect:
# - *_file secret paths (they stay relative to this config file)
# - metrics.tls.cert_path / metrics.tls.key_path
# CLI `--data-dir` and env `SYNCTV_DATA_DIR` override this value.
# data_dir: "/var/lib/synctv"
database:
# Environment variables:
# SYNCTV_DATABASE_URL
# SYNCTV_DATABASE_URL_FILE
# Split configuration is also supported:
# SYNCTV_DATABASE_HOST / PORT / USERNAME / PASSWORD / PASSWORD_FILE / NAME
# `user` is accepted as a config-file alias for `username`;
# SYNCTV_DATABASE_USER is accepted as an env alias for SYNCTV_DATABASE_USERNAME.
url: "postgresql://synctv:synctv@localhost:5432/synctv"
# url_file: "/run/secrets/database_url"
# host: "localhost"
# port: 5432
# username: "synctv"
# password_file: "/run/secrets/database_password"
# name: "synctv"
max_connections: 20
min_connections: 5
connect_timeout_seconds: 10
idle_timeout_seconds: 600
# Maximum lifetime of a connection in seconds before it is closed and replaced.
# Defaults to 1800 (30 minutes).
max_lifetime_seconds: 1800
# ============================================================================
# Redis Configuration
# ============================================================================
#
# Redis is OPTIONAL in standalone mode (cluster.enabled=false) and MANDATORY
# in cluster mode (cluster.enabled=true).
#
# When Redis is not configured (url is empty), all features still work using
# in-memory fallbacks:
# - Token blacklist → in-memory cache (lost on restart)
# - Rate limiting → in-memory counters (lost on restart)
# - Brute-force protection → in-memory tracking (lost on restart)
# - Username/user/room caches → in-memory only
# - OAuth2 state → in-memory store
# - Livestream registry → in-memory publisher tracking
# - Migration locking → PostgreSQL advisory locks
# - Leader election → single node is always the leader
#
# When Redis IS configured in standalone mode, it enhances features with
# persistence across restarts and shared state.
#
# Redis provides critical infrastructure for:
# - Token blacklist (JWT revocation across replicas)
# - Rate limiting
# - Username cache
# - Cache invalidation (Redis Streams with per-node consumer groups)
# - Cluster coordination (pub/sub for room events, kicks, etc.)
#
# Deployment Modes:
# - standalone: Single Redis instance (default, OK for development)
# - sentinel: Redis Sentinel for high availability (recommended for production)
# - cluster: Redis Cluster (not yet fully supported - requires refactoring)
#
# IMPORTANT: Standalone Redis is a single point of failure (SPOF). For production
# deployments, use sentinel mode with 3+ sentinel nodes monitoring a master-replica setup.
redis:
# Leave empty to run without Redis in standalone mode.
# Environment variables:
# SYNCTV_REDIS_URL
# SYNCTV_REDIS_URL_FILE
# Split configuration also supports:
# SYNCTV_REDIS_HOST / PORT / USERNAME / PASSWORD / PASSWORD_FILE / DATABASE
# `user` is accepted as a config-file alias for `username`;
# SYNCTV_REDIS_USER is accepted as an env alias for SYNCTV_REDIS_USERNAME.
url: "redis://localhost:6379"
# url_file: "/run/secrets/redis_url"
# host: "localhost"
# port: 6379
# username: ""
# password_file: "/run/secrets/redis_password"
# database: 0
connect_timeout_seconds: 5
key_prefix: "synctv:"
# Deployment mode: standalone, sentinel, or cluster
deployment_mode: "standalone"
# --- Sentinel Mode Configuration (for high availability) ---
# When deployment_mode is "sentinel", these fields are required:
# sentinel_master_name: "mymaster"
# sentinel_addresses:
# - "redis://sentinel1:26379"
# - "redis://sentinel2:26379"
# - "redis://sentinel3:26379"
# --- Cluster Mode Configuration (experimental) ---
# Cluster mode is not yet fully supported and will fail at startup.
# Future implementation will require switching from ConnectionManager to ClusterConnection.
# cluster_nodes:
# - "redis://node1:6379"
# - "redis://node2:6379"
# - "redis://node3:6379"
jwt:
# REQUIRED in production. Generate with: openssl rand -base64 32
# Minimum 32 characters with high entropy (at least 16 unique characters).
# Environment variables:
# SYNCTV_JWT_SECRET
# SYNCTV_JWT_SECRET_FILE
secret: ""
# secret_file is a static config input. Relative paths stay relative to this config file,
# not data_dir.
# secret_file: "/run/secrets/jwt_secret"
access_token_duration_hours: 1
refresh_token_duration_days: 30
# Duration for guest (anonymous) access tokens
guest_token_duration_hours: 4
# Clock skew leeway for token validation (seconds)
clock_skew_leeway_secs: 60
logging:
level: "info"
format: "pretty"
# Relative file_path values are treated as runtime-owned output and resolved from data_dir.
# file_path: "/var/log/synctv/app.log"
cache:
# Enable persisted proxy slice files on local/shared storage.
proxy_slice_file_backend_enabled: false
# Relative proxy_slice_file_cache_dir values are resolved from data_dir.
# Leave empty to use the built-in default under data_dir/cache/proxy-slice when enabled.
# proxy_slice_file_cache_dir: "cache/proxy-slice"
livestream:
rtmp_port: 1935
gop_cache_size: 2
stream_timeout_seconds: 300
# How often to check for idle streams (seconds)
cleanup_check_interval_seconds: 60
# Max retries for pull stream connections
pull_max_retries: 10
# Initial backoff for pull retries (milliseconds)
pull_initial_backoff_ms: 1000
# Max backoff for pull retries (milliseconds)
pull_max_backoff_ms: 30000
# Max FLV tag size to accept in bytes (prevents OOM). Default: 10MB
max_flv_tag_size_bytes: 10485760
# Maximum memory (in megabytes) for the GOP cache across all GOPs per stream.
# When exceeded, the oldest GOP is evicted even if gop_cache_size hasn't
# been reached. Default: 100 MB. Set to 0 to use the built-in default (50 MB).
gop_cache_max_memory_mb: 100
# Relative hls_storage_path values are resolved from data_dir.
# This is runtime-owned storage, unlike *_file secrets or metrics TLS files.
# hls_storage_path: "livestream/hls"
# ============================================================================
# Email Configuration (SMTP)
# ============================================================================
#
# Configure SMTP to enable email features (verification, notifications).
# Leave smtp_host empty to disable email functionality.
email:
smtp_host: ""
smtp_port: 587
smtp_username: ""
# Environment variables:
# SYNCTV_EMAIL_SMTP_PASSWORD
# SYNCTV_EMAIL_SMTP_PASSWORD_FILE
smtp_password: ""
# smtp_password_file is a static config input. Relative paths stay relative to this config file.
# smtp_password_file: "/run/secrets/smtp_password"
from_email: ""
from_name: "SyncTV"
use_tls: true
# ============================================================================
# Password Complexity Requirements
# ============================================================================
#
# Controls password strength validation for user account passwords.
# These settings do NOT apply to room passwords (which have simpler requirements).
password_complexity:
# Minimum password length
min_length: 8
# Require at least one uppercase letter
require_uppercase: true
# Require at least one lowercase letter
require_lowercase: true
# Require at least one digit
require_digit: true
# Require at least one special character (e.g., !@#$%^&*)
require_special: false
# Maximum consecutive repeated characters allowed (prevents "aaaaaa").
# Set to 0 to disable this check.
max_repeated_chars: 3
# ============================================================================
# Connection Limits
# ============================================================================
#
# Controls concurrent WebSocket/streaming connection limits.
connection_limits:
# Maximum concurrent connections per user
max_per_user: 5
# Maximum concurrent connections per room
max_per_room: 200
# Maximum total concurrent connections
max_total: 10000
# Idle timeout in seconds (disconnect if no activity)
idle_timeout_seconds: 300
# Maximum connection duration in seconds (24 hours)
max_duration_seconds: 86400
# Global per-connection WebSocket message rate limit (messages per second).
# Prevents abuse from flooding the server with rapid messages.
ws_message_rate_limit_per_second: 50
# ============================================================================
# Messaging Rate Limits
# ============================================================================
#
# Domain-level chat and danmaku limits enforced by the shared messaging logic.
messaging_rate_limits:
# Maximum chat messages per sliding window.
chat_per_second: 10
# Maximum danmaku messages per sliding window.
danmaku_per_second: 3
# Sliding-window size in seconds.
window_seconds: 1
# ============================================================================
# Bootstrap Configuration
# ============================================================================
#
# Initial setup options for first startup.
# IMPORTANT: Change root_password in production!
bootstrap:
# Whether to create root user on first startup
create_root_user: true
root_username: "root"
# REQUIRED for production: set a strong password (>= 12 chars, must contain
# uppercase, lowercase, and digits). Do NOT use any default value in production.
# Set via SYNCTV_BOOTSTRAP_ROOT_PASSWORD env var.
# Or file-based env: SYNCTV_BOOTSTRAP_ROOT_PASSWORD_FILE
# root_password: ""
# root_password_file: "/run/secrets/bootstrap_root_password"
# ============================================================================
# Cluster Configuration
# ============================================================================
#
# Controls multi-node cluster mode and internal channel buffer sizes.
#
# When enabled=true, Redis is MANDATORY and cluster_secret must be set.
# When enabled=false (default), this is a standalone single-node deployment
# and Redis is optional.
cluster:
# Enable cluster mode for multi-node deployments.
# When true, Redis is required and cluster_secret must be set.
# When false (default), runs as a standalone single-node instance.
# Environment variable: SYNCTV_CLUSTER_ENABLED
enabled: false
# Capacity for high-priority critical event channel (KickPublisher, KickUser, etc.)
# Critical events are never dropped; senders block when full.
critical_channel_capacity: 1000
# Capacity for normal-priority Redis publish channel.
# Normal events are dropped with a warning when full.
publish_channel_capacity: 10000
# Discovery mode: "redis" (default) or "k8s_dns" (requires HEADLESS_SERVICE_NAME
# and POD_NAMESPACE env vars).
#
# IMPORTANT: "k8s_dns" mode still requires Redis for health monitoring, load
# balancing, and cluster pub/sub. DNS only supplements peer discovery by detecting
# new pods faster. Without Redis, k8s_dns provides DNS resolution only -- no
# NodeRegistry, HealthMonitor, or LoadBalancer will be created.
discovery_mode: "redis"
# --- Static Discovery Configuration ---
# When discovery_mode is "static", list peers explicitly.
# Each peer is a single shared API address. The same port serves gRPC over h2
# and REST over h1.
# If a peer omits its port, SyncTV probes it on server.port.
#
# peers:
# - "node2.example.com:8080"
# - "node3.example.com"
# Leader election mode for singleton operations.
# - "redis": Use Redis-based distributed locks (default, works everywhere)
# - "k8s_lease": Use Kubernetes coordination.k8s.io/v1 Lease resource
# (requires POD_NAME and POD_NAMESPACE env vars, RBAC permissions)
leader_election_mode: "redis"
# How far back (in seconds) to replay Redis Stream events when a new node
# first connects to the cluster. Replaying recent history prevents events
# published just before this node subscribed from being silently missed.
# Increase in clusters with high event rates; decrease to shorten startup
# replay time. Default: 300 (5 minutes).
catchup_window_secs: 300
# Maximum number of entries per Redis Stream (approximate, uses MAXLEN ~).
# Controls how many events are retained in each per-room stream for catch-up
# after reconnection. In high-throughput scenarios, increase this to avoid
# trimming events that disconnected nodes still need to catch up on.
# Default: 10000
# Environment variable: SYNCTV_CLUSTER_STREAM_MAX_LENGTH
stream_max_length: 10000
# ============================================================================
# Media Providers Configuration
# ============================================================================
#
# Stores media provider configurations (Alist, Emby, Jellyfin, Bilibili, etc.)
media_providers:
# Provider configurations as a dynamic JSON/YAML value.
# Secret-like provider fields also support sibling *_file keys, for example
# token_file / api_key_file / password_file / access_token_file.
# Example:
# providers:
# alist:
# base_url: "https://alist.example.com"
# token: "your_token"
# # token_file: "/run/secrets/alist_token"
# emby:
# base_url: "https://emby.example.com"
# api_key: "your_api_key"
# # api_key_file: "/run/secrets/emby_api_key"
providers: {}
# ============================================================================
# OAuth2/OIDC Configuration
# ============================================================================
#
# Providers are configured as a YAML string that is parsed separately
# to support dynamic provider-specific fields.
#
# Supported Providers:
# - github: GitHub OAuth2
# - google: Google OAuth2
# - oidc: Generic OIDC provider with .well-known discovery
# Supports issuer, auth_url, token_url, userinfo_url fields
# - qq, gitee, feishu, wechat: Chinese OAuth2 Providers (not yet implemented)
# - Any OIDC-compliant provider (Casdoor, Keycloak, Authelia, etc.)
# requires 'type: oidc' field
#
# Common fields (all providers):
# client_id: OAuth2 client ID
# client_secret: OAuth2 client secret
# client_secret_file: Path to a file containing the client secret
# redirect_url: (required) frontend/client callback URI
#
# Provider-specific fields:
# logto:
# endpoint: Logto server URL (e.g., "https://logto.example.com")
# oidc:
# issuer: Issuer URL for .well-known discovery
# auth_url: (optional) Custom authorization endpoint
# token_url: (optional) Custom token endpoint
# userinfo_url: (optional) Custom userinfo endpoint
#
# Multiple Provider Instances:
# Use different instance names (e.g., logto1, logto2) and add 'type' field
# to specify the provider type.
#
# Callback URLs must point to your frontend or native client, because SyncTV
# uses a frontend-driven OAuth2 flow:
# 1. Provider redirects to your frontend/client callback URI with code+state
# 2. Frontend/client calls POST /api/oauth2/<instance_name>/exchange
# Examples:
# https://app.example.com/oauth2/callback
# synctv://oauth2/callback
#
# Security Notes:
# - OAuth2 tokens are only used temporarily during login and then discarded
# - Only provider-user mappings are stored (for future logins)
# - Always use HTTPS in production for OAuth2 callbacks
oauth2:
# URL scheme for OAuth2 redirect URLs ("http" or "https").
# When behind a reverse proxy terminating TLS, set to "https".
redirect_scheme: "http"
# Example configuration (uncomment and modify):
# providers: |
# # Standard OAuth2 Providers
# github:
# client_id: "your_github_client_id"
# client_secret: "your_github_client_secret"
# # client_secret_file: "/run/secrets/github_client_secret"
# redirect_url: "https://app.example.com/oauth2/callback"
#
# google:
# client_id: "your_google_client_id"
# client_secret: "your_google_client_secret"
# # client_secret_file: "/run/secrets/google_client_secret"
# redirect_url: "https://app.example.com/oauth2/callback"
#
# # Multiple instances of same provider (requires 'type' field)
# logto1:
# type: logto
# client_id: "logto1_client_id"
# client_secret: "logto1_client_secret"
# # client_secret_file: "/run/secrets/logto1_client_secret"
# endpoint: "https://logto1.example.com"
# redirect_url: "https://app.example.com/oauth2/callback"
#
# logto2:
# type: logto
# client_id: "logto2_client_id"
# client_secret: "logto2_client_secret"
# # client_secret_file: "/run/secrets/logto2_client_secret"
# endpoint: "https://logto2.example.com"
# redirect_url: "https://app.example.com/oauth2/callback"
#
# # Generic OIDC provider
# custom_oidc:
# type: oidc
# client_id: "custom_client_id"
# redirect_url: "https://app.example.com/oauth2/callback"
# client_secret: "custom_client_secret"
# # client_secret_file: "/run/secrets/custom_oidc_client_secret"
# issuer: "https://custom.oidc.provider.com"
providers: ""
# Environment Variables for OAuth2:
# Format: SYNCTV_OAUTH2_<FIELD>
# Examples:
# SYNCTV_OAUTH2_REDIRECT_SCHEME=https
# ============================================================================
# WebRTC Configuration
# ============================================================================
#
# WebRTC supports 2 operation modes:
# - signaling_only: Pure signaling relay without ICE bootstrap
# - peer_to_peer: P2P with built-in STUN plus optional external ICE servers
webrtc:
mode: "peer_to_peer"
# --- Built-in STUN Server ---
enable_builtin_stun: true
stun_port: 3478
stun_host: "0.0.0.0"
# External address for STUN reflexive candidates. In K8s/NAT, set to
# the pod IP or service IP (e.g., via SYNCTV_WEBRTC_STUN_EXTERNAL_ADDR).
# If empty, falls back to advertise_host:stun_port.
# stun_external_addr: ""
# --- External ICE Servers ---
# Configured dynamically via the settings API (no restart required):
# "webrtc.external_ice_servers" - JSON array of ICE server objects, e.g.:
# [{"urls":["stun:stun.l.google.com:19302"]},{"urls":["turn:turn.example.com:3478"],"username":"user","credential":"pass"}]
# ============================================================================
# Dynamic ICE Servers Configuration
# ============================================================================
#
# External ICE servers are managed via the settings API at runtime.
#
# Setting key: "webrtc.external_ice_servers"
# Value: JSON array of ICE server objects
# Default:
# [{"urls":["stun:stun.l.google.com:19302"]},{"urls":["stun:stun1.l.google.com:19302"]}]
#
# Changes take effect immediately without restarting the server.
# ============================================================================
# Buffer Size Tuning
# ============================================================================
#
# Controls internal channel buffer sizes. Larger values provide more resilience
# during traffic spikes but use more memory. Defaults match the current
# built-in runtime values.
#
# Additional buffer sizes are configured in their respective sections:
# - cluster.critical_channel_capacity (default: 1000)
# - cluster.publish_channel_capacity (default: 10000)
# - livestream.stream_hub_event_channel_capacity (default: 4096)
buffer_sizes:
# Per-connection WebSocket outbound message queue.
# When full, new messages are dropped (backpressure for slow clients).
websocket_outbound: 256
# Audit log event buffer. Events are batched and flushed to the database.
# When full, new audit events are dropped with a warning.
audit_buffer: 10000