open-standards/joule-code/joule-code-rs/crates/joule-moq-ingest/gateway.example.toml
Transaction Science d41fd22ea0
Some checks are pending
wai release binaries build on Windows / the seven release binaries · windows (push) Waiting to run
joule-moq-room room service / joule-moq-room · does this change touch it (push) Successful in 17s
WAI breaking changes and the conformance runner / WAI breaking · does this change touch WAI (push) Successful in 9s
WAI examples / WAI examples · does this change touch them (push) Successful in 13s
joule-code conformance gate / Build + round-trip C1-C4 vectors (push) Successful in 8m1s
joule-code conformance gate / joule-moq-relay, its own energy on a counter (macos) (push) Successful in 55s
WAI breaking changes and the conformance runner / WAI breaking · wire, record and API against main · linux (push) Successful in 40s
joule-code conformance gate / joule-moq-relay, every target (push) Successful in 9m9s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (push) Successful in 11m18s
joule-moq-room room service / joule-moq-room, every target (push) Successful in 9m53s
WAI examples / WAI examples · portable · linux (push) Successful in 12m11s
WAI breaking changes and the conformance runner / WAI breaking and conform · their tests, and the independent verifiers through wai conform · linux (push) Successful in 15m4s
joule-code conformance gate / joule-moq-ingest, every target (push) Successful in 22m9s
WAI examples / WAI examples · android-app · linux-arm64 (push) Successful in 10m55s
WAI examples / WAI examples · the archive, the readiness check, the browser and Apple · macos (push) Successful in 21m2s
joule-moq-ingest: the telephony bridge (feature telephony): calls from a trunk or PBX into a room, the room's verified mix back, held to real time
telephony::bridge: SIP over UDP, TCP and TLS with one configured peer, requests from any other
address dropped; RFC 3261's timers over UDP (a 2xx sent again until its ACK, BYE where none
comes); the bridge's own requests as client transactions answering a digest challenge;
REGISTER kept; calls placed (Bridge::place_call), a fork's 2xx acknowledged and ended.

A call joins a room under the grant its number names, or its PIN entered as DTMF (read from a
file, never logged or recorded). Its G.711 goes through the jitter buffer into one audio track
as verbatim FLAC, character for character, through the gateway's packager: objects of 20 ms,
groups of 100 ms, the first hop's receipts, each object stamped with its capture instant, the
catalog with its playout delay; a group ends when its caller falls silent, so no group's
receipts wait on more sound (the first loopback run found the last 100 ms never heard). Each
room's mix subscribes to the tracks its calls hear, verifies every object against its receipt
(the WHEP verifier, shared), decodes FLAC at 8 kHz, and every 20 ms sends each call the sum
less its own, in 32-bit integers, in its law. A signed record a call (protocol telephony): its
statement, every count by reason, what it heard, its origin claim naming the statement.

tests/telephony_bridge.rs (loopback, the tests' own SIP peer and RTP callers): two calls hear
each other through the room character for character and the first's track verifies at a relay
subscriber sample for sample; credentials and a PIN; every planted fault over the wire; TCP
and TLS; a placed call and a registration. CI runs it. tests/telephony_rt.rs, run on a 32-
thread x86-64 host on 2026-11-11 under a load of 22 to 24 (no quiet host found; stated):
one call both ways for 30 s, 1500 of 1500 packets each way in order, no underrun, delay 203.4
ms at most against a bound of 400; 50 calls for 20 s, 50 000 of 50 000 packets published,
none lost, the mix to every call with no gap.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-11 15:30:43 -05:00

187 lines
10 KiB
TOML

# joule-moq-ingest: every field, with its default where it has one.
# No secret lives here: the relay token and the signing key are files the operator
# provides, and a room is its signed descriptor, which is public.
[http]
listen = "127.0.0.1:8088" # the WHIP and WHEP resources; put a TLS front before them
origin = "" # this gateway's origin as grants name it, e.g. "https://ingest.example"
cors_allow_origin = "*"
max_body_bytes = 65536
request_timeout_ms = 10000
challenge_ttl_s = 300 # how long a proof-of-possession challenge stays good
max_challenges = 65536 # challenges held at once, issued and consumed each
[media]
bind_ip = "127.0.0.1" # each session's UDP socket, and its host candidate
ice_lite = true
connect_timeout_ms = 15000
idle_timeout_ms = 20000
[relay]
addr = "127.0.0.1:4443" # the relay's native QUIC endpoint
draft = 22 # the MOQT draft offered in the ALPN: 23, 22, 18 or 16
server_name = "localhost"
cert_path = "relay-cert.pem" # the relay's certificate, trusted as a root
token_path = "relay-token" # the token value the relay's grant names by SHA-256
[signing]
key_path = "gateway.key" # 32 bytes, the Ed25519 seed; absent: made at start
signer_id = "" # empty: the key's did:key
origin_label = "wai-ingest"
[[rooms]]
descriptor_path = "room.wrd1" # the room's signed descriptor (WRD1)
revoked = [] # grant digests and viewer tags the room's key log revokes
[limits]
max_whip_sessions = 64 # one more: 503 with Retry-After
max_whep_sessions = 256
max_tracks_per_session = 2 # media sections in one offer; more: 422
max_bitrate_kbps = 8000 # per WHIP session, all tracks; stated per track as b=AS
bitrate_window_ms = 2000
max_frame_bytes = 4194304
max_queue_bytes = 33554432 # a session's queue to the relay
retry_after_s = 5
[groups]
audio_group_ms = 200 # an audio group's duration
max_video_group_ms = 2000 # a longer video group asks the publisher for a keyframe
keyframe_request_interval_ms = 1000
[timeline]
max_skew_ms = 250 # |mapped time - arrival| bound; past it, re-anchor
[whep]
trust = [] # first-hop keys whose receipts WHEP accepts; empty: this gateway's
hold_ms = 5000 # how long a group's objects wait for its receipts
catalog_wait_ms = 2000
retry_after_s = 2 # what a 409 asks a player to wait
[summary]
# out_path = "sessions.jsonl" # session records, one JSON line each; absent: kept in memory only
# link_out_path = "links.hex" # contribution link records (WLK1), one hexadecimal line each
[rtmp]
# Enhanced RTMP ingest (live-ingest §12); off unless a listener is set.
listen = "127.0.0.1:1935" # RTMP over TCP
# tls_listen = "127.0.0.1:1936" # RTMPS, with the two files below
# cert_path = "rtmps-chain.pem"
# key_path = "rtmps-key.pem" # PKCS #8
url = "" # what a stream key's response names; empty: rtmp://<listen>/live
stream_key_ttl_s = 60 # a stream key from POST /rtmp is good this long for its first use
max_sessions = 64
max_message_bytes = 8388608 # the largest message the chunk stream takes
max_bitrate_kbps = 40000 # per publisher, every track, over limits.bitrate_window_ms
connect_timeout_ms = 15000 # a connection that has not published by then is closed
idle_timeout_ms = 20000
chunk_size = 4096 # the chunk size the gateway sends with
reconnect_url = "" # the tcUrl a reconnect request names; empty: none
reconnect_grace_ms = 5000 # how long a session stays after a reconnect request
[injector]
# SCTE 104 automation into signed cues (cue-injection); off unless listen is set.
# listen = "127.0.0.1:5167"
room = "" # the room id, 64 hexadecimal digits, one of [[rooms]]
# key_path = "injector.key" # 32 bytes, the injector principal's Ed25519 seed
# grant_path = "injector.wrg1" # the principal's room grant, permitting cue and meta
leap_seconds = 18 # GPS - UTC, for a UTC timestamp()
fps_num = 30000 # the frame rate duration_extension_frames counts in
fps_den = 1001
dpi_pid_index = [] # the DPI_PID_index values answered; empty: any
max_pending = 256 # cues held for splices still to come
# Uncompressed essence over RTP (WAI extensions/essence-contribution.md): a SMPTE ST 2110
# contribution's video, PCM and ancillary data into the room's tracks. Served by a gateway
# built with the st2110 feature. A publisher POSTs /st2110 with its room grant and proof and
# the description (SDP) of its streams, and gets the ports to send to.
[st2110]
enabled = false
bind_ip = "0.0.0.0" # where a unicast stream's socket binds; the port is the gateway's
multicast = false # whether a stream sent to a multicast group is joined
interface = "0.0.0.0" # the interface an IPv4 group is joined on
encoder = "auto" # auto (the first that passes its probe: va, svt-av1, rav1e), or one by name
svt_av1_library = "" # the SVT-AV1 library svt-av1 loads: a path, or "" for libSvtAv1Enc.so.4
svt_av1_preset = 0 # its slowest preset a session may use, 7 to 13 (the stage moves up to 13);
# 0: chosen for the stream's size and frame rate
va_device = "/dev/dri/renderD128" # the render node va encodes through
encoder_fallback = false # a named encoder that is not here: false refuses to start; true takes the
# next and says so at start and in every session's answer and record
encoder_threads = 0 # 0: as many as the host has
bitrate_kbps = 0 # the target bit rate, at most 20000; 0: a tenth of a bit a luma sample
calibrate_ms = 400 # how long the encoder is timed on computed pictures at start; 0: not timed
key_interval_ms = 2000 # a keyframe, and so a group, at least this often
quantizer = 100 # rav1e's base quantizer index, 0 to 255 (it runs at a constant quantizer)
encode_queue = 0 # frames that may wait for the encoder; the next is not encoded, and counted.
# 0: the frames of two fifteenths of a second, at least 4
audio_object_ms = 20 # the sound one audio object holds
playout_delay_ms = 0 # the playout delay the tracks' catalog states
leap_seconds = 37 # the seconds the PTP timescale is ahead of UTC
max_capture_skew_ms = 1000 # past it, an RTP timestamp is not stated as a capture instant
queue_packets = 65536 # datagrams that may wait for a session's readers
receive_buffer_octets = 16777216 # asked for on each stream's socket; the host's limit bounds what is given
# The telephony bridge (WAI extensions/telephony-bridge.md): callers from one SIP trunk or PBX
# into a room as audio participants, and the room's mix back to them. Served by a gateway built
# with the telephony feature. No secret lives here: passwords and PINs are files.
[telephony]
enabled = false
sip_udp = "0.0.0.0:5060" # port 0: the system's, logged
# sip_tcp = "0.0.0.0:5060"
# sip_tls = "0.0.0.0:5061" # with tls_cert_path and tls_key_path (PEM)
peer = "192.0.2.10:5060" # the one trunk or PBX; requests from elsewhere are dropped
peer_transport = "udp" # how the bridge sends its own requests: udp or tcp
media_ip = "0.0.0.0" # where a call's RTP socket binds
advertise_ip = "192.0.2.1" # the address a description states
jitter_ms = 60 # the longest a packet waits for the one before it
audio_group_ms = 100 # a call's group: its receipts follow its end
playout_ms = 160 # a room track's sound held before the mix takes from it
# source = "digitalCapture" # the digital source type the trunk carries: each call's origin claim
hear_callers = true # each call hears the room's other calls
hear_owners = [] # and these owners' audio tracks
caller_in_record = false # whether a call's record holds the caller's From URI
# [telephony.auth] # credentials asked of the peer
# realm = "bridge.example"
# user = "trunk"
# password_path = "/etc/wai/trunk.password"
# [telephony.outbound] # credentials the bridge answers the peer's challenges with
# user = "bridge"
# password_path = "/etc/wai/bridge.password"
# register = false
# [[telephony.number]]
# number = "+15550100" # the Request-URI's user part, as the peer writes it
# grants = ["<WRG1 in base64url>"]
# [[telephony.number]]
# number = "+15550200"
# pin = true # the caller enters a PIN, which names its grants
# [[telephony.pin]]
# pin_path = "/etc/wai/pin-1"
# grants = ["<WRG1 in base64url>"]
# Contribution over SRT and RIST (WAI extensions/live-contribution.md). A publisher POSTs
# /srt or /rist with its room grant and proof, and gets a one-time session.
[contrib]
host = "" # the host name stated in a session's answer
session_ttl_s = 60 # a session not connected by then is dropped
max_sessions = 16
max_bitrate_kbps = 80000 # per session, all streams, over limits.bitrate_window_ms
record_interval_ms = 10000 # how often a session signs a part of its link record
idle_ms = 5000 # a link that receives nothing this long is ended
[contrib.srt]
# listen = "127.0.0.1:9000" # the SRT listener; absent: /srt is 404
latency_ms = 120 # the connection's is the larger of the two ends'
pbkeylen = 16 # AES key bytes: 16, 24 or 32
call_allow = [] # publishers' listeners the gateway may call: "ip:port" or "ip"
[contrib.rist]
enabled = false # needs a build with the feature `rist`
bind_ip = "127.0.0.1"
port_min = 0 # the ports sessions are given; 0 and 0: any free port
port_max = 0
buffer_ms = 1000
aes = 128 # the Main Profile's PSK key bits: 128 or 256