Sign in

    rtc

    Experimental Sans-I/O WebRTC stack for MoonBit

    webrtc
    sans-io
    networking
    media
    Download zip
    Version
    0.0.1
    License
    MIT OR Apache-2.0
    Last updated
    16 hours ago
    Downloads
    2

    Dependencies

    #rtc.mbt

    rtc.mbt is an experimental, native MoonBit implementation of a Sans-I/O WebRTC stack. Its compatibility target is the fixed webrtc-rs/rtc snapshot recorded in UPSTREAM.md.

    The first supported platform is Linux x86_64 with the native backend and OpenSSL 3.x libcrypto.so.3. CI additionally verifies the exact OpenSSL 3.0.2-0ubuntu1.25 Ubuntu build in a digest- and archive-snapshot-pinned environment documented in docs/ci-crypto-environment.md.

    #What is implemented

    The stack includes:

    • SDP/JSEP offer, answer, provisional answer, rollback, glare handling, perfect negotiation, BUNDLE, trickle ICE, ICE restart, RTP media sections, RTX, RID, simulcast, and SSRC groups;
    • STUN, mDNS query/gather, host/srflx/prflx/relay ICE, TURN over UDP/TCP/TLS, and active/passive ICE TCP;
    • DTLS 1.2 with ECDSA, RSA, and PSK authentication across the eleven pinned cipher suites, EMS, replay protection, certificate fingerprints, and use_srtp;
    • SRTP/SRTCP for AES-CM and AEAD profiles;
    • SCTP association setup, fragmentation, SACK/retransmission, partial reliability, unordered delivery, stream reset, flow control, and DCEP DataChannels;
    • RTP/RTCP codecs, packetizers/depacketizers, NACK, TWCC, RTX, reports, interceptors, encoded transforms, tracks/transceivers, simulcast controls, media helpers, and detailed statistics.

    The fixed Rust webrtc 0.14.0 peer and Playwright Chromium 149.0.7827.55 exchange reliable and partial/unordered DataChannels plus Opus audio and VP8 video RTP over SRTP with MoonBit in both offerer and answerer roles. A Linux x86_64 coturn 4.14.0-r0 image is pinned by digest and carries a relay-only MoonBit DataChannel in CI.

    #Sans-I/O boundary

    The core never opens sockets or reads system time. Applications inject IoEvent, InboundDatagram, RtcMessage, and explicit monotonic timestamps, then drain:

    • poll_io_action for UDP and TCP/TLS connect/listen/read/write/close work;
    • poll_message and poll_event for application output;
    • poll_timeout for the next protocol deadline.

    The clock mapping is explicit and deterministic:

    ///|
    test "inject a deterministic clock sample" {
    let monotonic = @rtc.Instant::from_milliseconds(42L)
    let wall = @rtc.WallTime::from_unix_nanoseconds(1700000000000000000L)
    let sample = @rtc.ClockSample::new(monotonic~, wall~)
    assert_eq(sample.monotonic().as_milliseconds(), 42L)
    assert_eq(sample.wall().as_unix_nanoseconds(), 1700000000000000000L)
    }

    runtime/async is the official bounded-queue adapter. It manages UDP sockets, TCP listeners and streams, TURN TLS legs, timers, cancellation, and output backpressure while preserving the same core contract. Its bounded priority lane keeps protocol control traffic, timers, flushing, and cancellation live when application output is temporarily full.

    Run the complete in-memory example:

    moon run --target native examples/sans_io_datachannel

    The schema-free WebSocket signaling relay is in examples/websocket_signaling. Signaling, media capture, codecs, rendering, and device integration intentionally remain application concerns.

    #Development and acceptance

    The upstream audit expects --upstream to point to a webrtc-rs/rtc checkout at the commit recorded in UPSTREAM.md.

    moon fmt --check moon check --target native --warn-list +73 moon test --target native scripts/test-rust-interop scripts/test-chromium-interop scripts/test-coturn-interop scripts/audit-upstream.mjs \ --upstream /path/to/webrtc-rs-rtc \ --write-ledger docs/upstream-coverage.json moon bench --release --target native --no-parallelize moon info

    The ASan entry point is scripts/run-asan.mjs. The performance gate is scripts/bench-gate.mjs. Its committed benchmarks/baseline.json binds the fixed workloads and 20 raw samples to clean commit 29f4ff26dcb507715a0bfdacd58a0a3b48f3c776. See benchmarks/README.md.

    The generated per-item upstream inventory is docs/upstream-coverage.json. Public interfaces are reviewed through every pkg.generated.mbti; DTLS record ciphers, PRF/key schedule helpers, crypto providers, parsers, and replay state remain internal.

    The project is dual-licensed under MIT or Apache-2.0. It remains pre-1.0 while the reviewed public API is intentionally experimental.

    CandidateType

    EncodedFrameContext

    EncodedTransformPipeline

    IceConnectionState

    IceGatheringState

    IceTransportPolicy

    RtpEncodingParameters

    SessionDescription

    SignalingState

    TcpCandidateType

    TransceiverDirection

    TransformDirection

    ActiveRtcListener

    type ActiveRtcListener

    ActiveRtcStream

    type ActiveRtcStream

    CandidatePairStats

    pub(all) struct CandidatePairStats {
    id : String
    local_candidate :
    IceCandidate

    remote_candidate :
    IceCandidate

    priority : UInt64
    state :
    CandidatePairState

    nominated : Bool
    selected : Bool
    } derive(Eq,
    Debug
    )

    CodecStats

    pub(all) struct CodecStats {
    id : String
    payload_type : Byte
    mime_type : String
    clock_rate : UInt
    channels : UInt16
    fmtp : String
    } derive(Eq,
    Debug
    )

    Configuration

    pub struct Configuration {
    ice_servers : Array[IceServer]
    ice_transport_policy :
    IceTransportPolicy

    mdns_mode :
    Mode

    host_candidates : Array[
    IceCandidate
    ]
    sctp_port : UInt16
    max_message_size : UInt64
    command_capacity : Int
    event_capacity : Int
    message_capacity : Int
    settings : Settings
    interceptor_pipeline :
    Pipeline

    encoded_transform_pipeline :
    EncodedTransformPipeline

    }

    Configuration::command_capacity

    fn Configuration::command_capacity(self : Configuration) -> Int

    Configuration::encoded_transform_pipeline

    Configuration::event_capacity

    fn Configuration::event_capacity(self : Configuration) -> Int

    Configuration::host_candidates

    Configuration::ice_servers

    fn Configuration::ice_servers(self : Configuration) -> Array[IceServer]

    Configuration::ice_transport_policy

    Configuration::interceptor_pipeline

    Configuration::max_message_size

    fn Configuration::max_message_size(self : Configuration) -> UInt64

    Configuration::mdns_mode

    Configuration::message_capacity

    fn Configuration::message_capacity(self : Configuration) -> Int

    Configuration::new

    fn Configuration::new(ice_servers? : Array[IceServer], ice_transport_policy? :
    IceTransportPolicy
    , mdns_mode? :
    Mode
    , host_candidates? : Array[
    IceCandidate
    ], sctp_port? : UInt16, max_message_size? : UInt64, command_capacity? : Int, event_capacity? : Int, message_capacity? : Int, settings? : Settings, interceptor_pipeline? :
    Pipeline
    , encoded_transform_pipeline? :
    EncodedTransformPipeline
    ) -> Configuration raise RtcError

    Configuration::sctp_port

    fn Configuration::sctp_port(self : Configuration) -> UInt16

    Configuration::settings

    fn Configuration::settings(self : Configuration) -> Settings

    DataChannelStats

    pub(all) struct DataChannelStats {
    id : String
    stream_id :
    StreamId

    label : String
    protocol : String
    state :
    DataChannelState

    buffered_amount : UInt64
    } derive(Eq,
    Debug
    )

    DetailedStatsReport

    pub(all) struct DetailedStatsReport {
    timestamp :
    WallTime

    codecs : Array[CodecStats]
    outbound_rtp : Array[RtpStreamStats]
    inbound_rtp : Array[RtpStreamStats]
    candidate_pairs : Array[CandidatePairStats]
    data_channels : Array[DataChannelStats]
    transport : TransportStats
    } derive(Eq,
    Debug
    )

    IceServer

    pub(all) struct IceServer {
    urls : Array[String]
    username : String?
    credential : String?
    } derive(Eq,
    Debug
    )

    IceServer::credential

    fn IceServer::credential(self : IceServer) -> String?

    IceServer::new

    fn IceServer::new(urls~ : Array[String], username? : String, credential? : String) -> IceServer raise RtcError

    IceServer::urls

    fn IceServer::urls(self : IceServer) -> Array[String]

    IceServer::username

    fn IceServer::username(self : IceServer) -> String?

    LocalMediaSection

    type LocalMediaSection

    OfferCollisionPolicy

    pub(all) enum OfferCollisionPolicy {
    Polite
    Impolite
    } derive(Eq,
    Debug
    )

    PeerConnection

    pub struct PeerConnection {
    configuration : Configuration
    clock_sample :
    ClockSample

    signaling :
    SignalingStateMachine

    identity :
    CertificateIdentity

    local_credentials :
    IceCredentials

    session_id : UInt64
    session_version : UInt64
    tie_breaker : UInt64
    events :
    Queue
    [PeerEvent]
    messages :
    Queue
    [RtcMessage]
    outbound :
    Queue
    [
    OutboundDatagram
    ]
    io_actions :
    Queue
    [
    IoAction
    ]
    pending_streams : Array[PendingRtcStream]
    active_streams : Array[ActiveRtcStream]
    active_listeners : Array[ActiveRtcListener]
    next_connection_id : UInt64
    next_listener_id : UInt64
    local_transport_candidates : Array[
    IceCandidate
    ]
    local_advertised_candidates : Array[
    IceCandidate
    ]
    srflx_gatherers : Array[
    SrflxGatherer
    ]
    turn_allocations : Array[
    Allocation
    ]
    pending_relay_datagrams : Array[PendingRelayDatagram]
    remote_candidates : Array[
    IceCandidate
    ]
    mdns_resolver :
    Resolver

    pending_mdns_candidates : Map[
    QueryId
    ,
    IceCandidate
    ]
    state : PeerConnectionState
    gathering_state :
    IceGatheringState

    data_manager :
    Manager
    ?
    ice_agent :
    IceAgent
    ?
    dtls_endpoint :
    Endpoint
    ?
    data_transport :
    Transport
    ?
    local_media_sections : Array[LocalMediaSection]
    remote_media_sections : Array[RemoteMediaSection]
    outbound_srtp :
    Context
    ?
    inbound_srtp :
    Context
    ?
    media_started : Bool
    rtcp_sender_ssrc : UInt
    next_rtcp_report :
    Instant
    ?
    next_transport_sequence : UInt16
    remote_twcc_recorder :
    TwccRecorder
    ?
    data_channel_buffered_amount_low_thresholds : Map[
    StreamId
    , UInt64]
    transport_started : Bool
    ice_restart_pending : Bool
    dtls_started : Bool
    data_started : Bool
    closing : Bool
    bytes_sent : UInt64
    bytes_received : UInt64
    packets_sent : UInt64
    packets_received : UInt64
    data_channels_opened : UInt
    data_channels_closed : UInt
    }

    PeerConnection::add_ice_candidate

    PeerConnection::close

    PeerConnection::close_data_channel

    PeerConnection::connection_state

    fn PeerConnection::connection_state(self : PeerConnection) -> PeerConnectionState

    PeerConnection::data_channel_buffered_amount

    fn PeerConnection::data_channel_buffered_amount(self : PeerConnection, channel_id :
    StreamId
    ) -> UInt64

    PeerConnection::data_channel_buffered_amount_low_threshold

    fn PeerConnection::data_channel_buffered_amount_low_threshold(self : PeerConnection, channel_id :
    StreamId
    ) -> UInt64

    PeerConnection::detailed_stats

    PeerConnection::driver_command_capacity

    fn PeerConnection::driver_command_capacity(self : PeerConnection) -> Int

    PeerConnection::driver_event_capacity

    fn PeerConnection::driver_event_capacity(self : PeerConnection) -> Int

    PeerConnection::driver_message_capacity

    fn PeerConnection::driver_message_capacity(self : PeerConnection) -> Int

    PeerConnection::driver_receive_mtu

    fn PeerConnection::driver_receive_mtu(self : PeerConnection) -> Int

    PeerConnection::handle_datagram

    PeerConnection::handle_io_event

    PeerConnection::handle_mdns_datagram

    PeerConnection::handle_message

    fn PeerConnection::handle_message(self : PeerConnection, message : RtcMessage, now :
    Instant
    ) -> Unit raise RtcError

    PeerConnection::handle_timeout

    PeerConnection::local_description

    PeerConnection::local_host_candidates

    PeerConnection::local_track_encodings

    PeerConnection::local_track_payload_type

    fn PeerConnection::local_track_payload_type(self : PeerConnection, track_id : String) -> Byte?

    PeerConnection::local_track_ssrc

    fn PeerConnection::local_track_ssrc(self : PeerConnection, track_id : String) -> UInt?

    PeerConnection::new

    PeerConnection::poll_event

    fn PeerConnection::poll_event(self : PeerConnection) -> PeerEvent?

    PeerConnection::poll_io_action

    PeerConnection::poll_message

    fn PeerConnection::poll_message(self : PeerConnection) -> RtcMessage?

    PeerConnection::poll_timeout

    PeerConnection::remote_description

    PeerConnection::remove_track

    fn PeerConnection::remove_track(self : PeerConnection, track_id : String, now :
    Instant
    ) -> Unit raise RtcError

    PeerConnection::restart_ice

    PeerConnection::rollback

    PeerConnection::set_data_channel_buffered_amount_low_threshold

    fn PeerConnection::set_data_channel_buffered_amount_low_threshold(self : PeerConnection, channel_id :
    StreamId
    , threshold : UInt64) -> Unit raise RtcError

    PeerConnection::set_local_description

    PeerConnection::set_remote_description

    PeerConnection::set_remote_description_perfect

    PeerConnection::set_sender_encodings

    PeerConnection::signaling_state

    PeerConnectionState

    pub(all) enum PeerConnectionState {
    New
    Connecting
    Connected
    Disconnected
    Failed
    Closed
    } derive(Eq,
    Debug
    )

    PendingRelayDatagram

    type PendingRelayDatagram

    PendingRtcStream

    type PendingRtcStream

    RemoteMediaSection

    type RemoteMediaSection

    RtcStreamOwner

    type RtcStreamOwner derive(Eq,
    Debug
    )

    RtpStreamStats

    pub(all) struct RtpStreamStats {
    id : String
    track_id : String
    kind :
    MediaKind

    outbound : Bool
    ssrc : UInt
    rid : String?
    codec_id : String
    packets : UInt64
    bytes : UInt64
    packets_lost : UInt64
    jitter : UInt64
    retransmitted_packets : UInt64
    retransmitted_bytes : UInt64
    active : Bool
    } derive(Eq,
    Debug
    )

    Settings

    pub struct Settings {
    ice_credentials :
    IceCredentials
    ?
    ice_check_interval :
    Duration

    ice_initial_rto :
    Duration

    ice_reliable_timeout :
    Duration

    ice_max_retransmissions : Int
    ice_disconnected_timeout :
    Duration

    ice_failed_timeout :
    Duration

    ice_keepalive_interval :
    Duration

    mdns_retry_interval :
    Duration

    mdns_query_timeout :
    Duration

    mdns_local_name : String?
    answering_dtls_role :
    Role
    ?
    verify_peer_fingerprint : Bool
    dtls_certificate_key_type :
    CertificateKeyType

    dtls_cipher_suites : Array[
    CipherSuite
    ]
    dtls_psk : Bytes?
    dtls_psk_identity : Bytes?
    dtls_srtp_profiles : Array[
    SrtpProtectionProfile
    ]
    dtls_flight_interval :
    Duration

    dtls_mtu : Int
    dtls_replay_window : Int
    srtp_replay_window : Int
    srtcp_replay_window : Int
    sctp_max_payload_size : Int
    sctp_send_buffer_size : UInt64
    sctp_receive_buffer_size : UInt
    sctp_initial_rto :
    Duration

    receive_mtu : Int
    ignore_rid_pause_for_recv : Bool
    write_ssrc_attributes_for_simulcast : Bool
    }

    Settings::answering_dtls_role

    fn Settings::answering_dtls_role(self : Settings) ->
    Role
    ?

    Settings::dtls_certificate_key_type

    Settings::dtls_cipher_suites

    Settings::dtls_flight_interval

    Settings::dtls_mtu

    fn Settings::dtls_mtu(self : Settings) -> Int

    Settings::dtls_psk

    fn Settings::dtls_psk(self : Settings) -> Bytes?

    Settings::dtls_psk_identity

    fn Settings::dtls_psk_identity(self : Settings) -> Bytes?

    Settings::dtls_replay_window

    fn Settings::dtls_replay_window(self : Settings) -> Int

    Settings::dtls_srtp_profiles

    Settings::ice_check_interval

    Settings::ice_credentials

    Settings::ice_disconnected_timeout

    Settings::ice_failed_timeout

    Settings::ice_initial_rto

    Settings::ice_keepalive_interval

    Settings::ice_max_retransmissions

    fn Settings::ice_max_retransmissions(self : Settings) -> Int

    Settings::ice_reliable_timeout

    Settings::ignore_rid_pause_for_recv

    fn Settings::ignore_rid_pause_for_recv(self : Settings) -> Bool

    Settings::mdns_local_name

    fn Settings::mdns_local_name(self : Settings) -> String?

    Settings::mdns_query_timeout

    Settings::mdns_retry_interval

    Settings::new

    fn Settings::new(ice_credentials? :
    IceCredentials
    , ice_check_interval? :
    Duration
    , ice_initial_rto? :
    Duration
    , ice_reliable_timeout? :
    Duration
    , ice_max_retransmissions? : Int, ice_disconnected_timeout? :
    Duration
    , ice_failed_timeout? :
    Duration
    , ice_keepalive_interval? :
    Duration
    , mdns_retry_interval? :
    Duration
    , mdns_query_timeout? :
    Duration
    , mdns_local_name? : String, answering_dtls_role? :
    Role
    , verify_peer_fingerprint? : Bool, dtls_certificate_key_type? :
    CertificateKeyType
    , dtls_cipher_suites? : Array[
    CipherSuite
    ], dtls_psk? : Bytes, dtls_psk_identity? : Bytes, dtls_srtp_profiles? : Array[
    SrtpProtectionProfile
    ], dtls_flight_interval? :
    Duration
    , dtls_mtu? : Int, dtls_replay_window? : Int, srtp_replay_window? : Int, srtcp_replay_window? : Int, sctp_max_payload_size? : Int, sctp_send_buffer_size? : UInt64, sctp_receive_buffer_size? : UInt, sctp_initial_rto? :
    Duration
    , receive_mtu? : Int, ignore_rid_pause_for_recv? : Bool, write_ssrc_attributes_for_simulcast? : Bool) -> Settings raise RtcError

    Settings::receive_mtu

    fn Settings::receive_mtu(self : Settings) -> Int

    Settings::sctp_initial_rto

    Settings::sctp_max_payload_size

    fn Settings::sctp_max_payload_size(self : Settings) -> Int

    Settings::sctp_receive_buffer_size

    fn Settings::sctp_receive_buffer_size(self : Settings) -> UInt

    Settings::sctp_send_buffer_size

    fn Settings::sctp_send_buffer_size(self : Settings) -> UInt64

    Settings::srtcp_replay_window

    fn Settings::srtcp_replay_window(self : Settings) -> Int

    Settings::srtp_replay_window

    fn Settings::srtp_replay_window(self : Settings) -> Int

    Settings::verify_peer_fingerprint

    fn Settings::verify_peer_fingerprint(self : Settings) -> Bool

    Settings::write_ssrc_attributes_for_simulcast

    fn Settings::write_ssrc_attributes_for_simulcast(self : Settings) -> Bool

    StatsReport

    pub(all) struct StatsReport {
    timestamp :
    WallTime

    bytes_sent : UInt64
    bytes_received : UInt64
    packets_sent : UInt64
    packets_received : UInt64
    packets_lost : UInt64
    data_channels_opened : UInt
    data_channels_closed : UInt
    } derive(Eq,
    Debug
    )

    TransportStats

    pub(all) struct TransportStats {
    id : String
    ice_state :
    IceConnectionState

    dtls_state :
    State
    ?
    selected_candidate_pair_id : String?
    bytes_sent : UInt64
    bytes_received : UInt64
    packets_sent : UInt64
    packets_received : UInt64
    } derive(Eq,
    Debug
    )