[package]
name = "oxpulse-sfu"
version = "0.1.0"
edition = "2021"
license = "AGPL-3.0-or-later"
description = "OxPulse SFU — str0m-based edge selective forwarding unit"

[lib]
name = "oxpulse_sfu"
path = "src/lib.rs"

[[bin]]
name = "oxpulse-sfu"
path = "src/main.rs"

[dependencies]
anyhow = "1"
axum = { version = "0.8", default-features = false, features = ["http1", "json", "tokio", "ws"] }
dominant-speaker = { package = "rust-dominant-speaker", version = "0.3" }
oxpulse-sfu-kit = { version = "0.11.7", features = [
    "active-speaker",
    "metrics-prometheus",
    "kalman-bwe",
    "pacer",
    "googcc-bwe",
    "vfm",
    "test-utils",
] }
prometheus = { version = "0.13", default-features = false }
str0m = "0.18"
tokio = { version = "1", features = ["rt-multi-thread", "net", "time", "sync", "signal", "macros"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
thiserror = "2"
jsonwebtoken = { version = "10", default-features = false, features = ["use_pem", "aws_lc_rs"] }
hmac = "0.12"
sha2 = "0.10"
tokio-tungstenite = { version = "0.26", default-features = false, features = ["rustls-tls-native-roots", "connect"] }
futures-util = { version = "0.3", features = ["sink"] }
reqwest = { version = "0.12", default-features = false, features = ["rustls-tls", "json"] }
aws-lc-rs = { version = "1", optional = true }

# OpenTelemetry / Jaeger pipeline. Distributed tracing for the SFU is opt-in
# via OTEL_EXPORTER_OTLP_ENDPOINT (typical: http://jaeger:4317). Empty/unset
# = no exporter spawned (zero overhead). Spans cover relay handshake,
# browser session lifecycle, and the udp_loop iteration cadence —
# anything per-packet stays out of the span budget.
opentelemetry = "0.31"
opentelemetry_sdk = { version = "0.31", features = ["rt-tokio"] }
opentelemetry-otlp = { version = "0.31", default-features = false, features = ["grpc-tonic", "trace"] }
opentelemetry-semantic-conventions = "0.31"
tracing-opentelemetry = "0.32"

[features]
test-utils = []
vfm = ["oxpulse-sfu-kit/vfm"]
fips = ["dep:aws-lc-rs"]

[dev-dependencies]
tokio = { version = "1", features = ["rt-multi-thread", "net", "time", "sync", "signal", "macros", "test-util"] }
ed25519-dalek = { version = "2", features = ["pkcs8", "pem", "rand_core"] }
pkcs8 = { version = "0.10", features = ["pem"] }
rand = "0.8"
serial_test = "3"

[[test]]
name = "multi_client"
required-features = ["test-utils"]

[[test]]
name = "simulcast_speaker"
required-features = ["test-utils"]

[[test]]
name = "metrics_integration"
required-features = ["test-utils"]

[[test]]
name = "bwe_metrics_integration"
required-features = ["test-utils"]

[[test]]
name = "active_rooms_metric"
# 2026-05-06 motherly1 outage post-mortem — pins active_rooms 0/1
# semantics. Uses `disconnect_client_for_tests` + `reap_dead_for_tests`
# seams from registry/test_seams.rs.
required-features = ["test-utils"]

[[test]]
name = "hard_red_migration"
required-features = ["test-utils"]

[[test]]
name = "ice_stun_destination_mismatch"
required-features = ["test-utils"]

[[test]]
name = "relay_api"
required-features = []

[[test]]
name = "fanout_relay"
required-features = ["test-utils"]

[[test]]
name = "relay_media_e2e"
required-features = ["test-utils"]

[[test]]
name = "client_ws_session"
required-features = []

[[test]]
name = "client_ws_fanout"
# M4.A3 fanout/simulcast tests use `test_seed::*` and the
# `Registry::*_for_tests` seams, both gated behind `test-utils`.
required-features = ["test-utils"]

[[test]]
name = "client_ws_active_speaker"
# M4.A4 — uses the cfg-gated `last_active_speaker_payload` capture seam
# on `Client`, parallel to `delivered_active_speaker: AtomicU64`.
required-features = ["test-utils"]

[[test]]
name = "relay_chat_e2e"
# Phase 2b — uses `client::test_seed::new_client` and the
# `fanout::fanout_for_tests` seam to drive ChatData / ChatCtrl through
# the per-peer relay path without a live DTLS pipeline.
required-features = ["test-utils"]

[[test]]
name = "voice_relay"
# Phase 8 T10 — uses `client::test_seed::new_client` and the
# `fanout::fanout_for_tests` seam to drive VoiceData through the
# per-peer voice relay path without a live DTLS pipeline.
required-features = ["test-utils"]

[[test]]
name = "m2_sdp_renegotiation"
required-features = ["test-utils"]

[[test]]
name = "solo_kick"
# Solo-peer auto-kick — uses `new_client`, `set_solo_since_for_tests`,
# `reap_dead_for_tests`, `disconnect_client_for_tests` seams from
# registry/test_seams.rs (all gated behind test-utils).
required-features = ["test-utils"]

[[test]]
name = "bwe_hint_test"
# Phase 2c review fix: scrub tests use `new_client`, `disconnect_client_for_tests`,
# `reap_dead_for_tests`, `evict_for_steal_for_tests` from registry/test_seams.rs.
# Counter + WS tests do not need test-utils but the feature gate is harmless
# (test-utils adds no overhead to integration tests).
required-features = ["test-utils"]

[[test]]
name = "sfu_events_dc"
# Phase 2c -- sfu-events DC (id:8) integration tests. Uses `new_client`,
# `fanout::fanout_for_tests`, `Registry::*_for_tests` seams gated behind test-utils.
required-features = ["test-utils"]
