Skip to content

Releases

NimRTC v1.1.0

Choose a tag to compare

@github-actions github-actions released this 09 Nov 04:37
v1.1.0
50358b8

[1.1.0] - 2026-11-07

Status: Release candidate

The source version is 1.1.0 (project(VERSION) in CMakeLists.txt,
NIMRTC_API_VERSION = 10100, NIMRTC_API_VERSION_PRE_RELEASE = "").
The v1.1 / v1.2 / v1.3 plan drafts were consolidated into
docs/plan/v1.1-plan.md (BDFL, 2026-11-06); the version ladder it
records (§5.6) is: v1.1.0 is the only feature-bearing release of the
v1.1 line, v1.1.x carries fixes only, and new behaviour starts at
v1.2.0.

The published stable cut is still v1.0.2; the v1.1.0 tag is cut when
the remaining release-hardening gates close (see "Known limitations").

Fixed

  • The GMSSL TLCP backend corrupted the heap on every open() and crashed
    the process.
    DtlsSessionGmSSL allocated its TLS_CTX and
    TLS_CONNECT objects in fixed-size byte buffers (128 KiB / 256 KiB) whose
    sizes came from stale measurements of an older GmSSL. The real structs are
    much larger — sizeof(TLS_CTX) is 201,128 bytes and sizeof(TLS_CONNECT)
    is 189,280 bytes on the vendored v3.2.0 tree — and GmSSL's
    tls_ctx_init() / tls_init() memset the whole struct. The 128 KiB
    TLS_CTX buffer was therefore ~70 KiB too small: the memset ran past
    the end of the allocation and smashed the heap, so
    test_dtls_gmssl_tlcp died with a SEGV before the handshake even
    started. The buffers are now sized from the real sizeof() plus a 64 KiB
    margin, and a compile-time #error guard makes the build fail loudly if
    the GmSSL feature macros (ENABLE_SM9 / ENABLE_SECP256R1 / ENABLE_LMS
    / ENABLE_XMSS / ENABLE_SPHINCS / ENABLE_KYBER) ever stop reaching
    this translation unit — those macros decide the size of the X509_KEY
    union, so a mismatch would silently change the struct layout and turn
    the same memset into heap corruption again. With the fix the TLCP
    handshake completes and the whole suite is 697 tests, 0 failures.

  • The vendored Opus library was built against a different C runtime than
    the rest of the project, so Debug builds crashed on the audio path.

    src/third_party/libopus/CMakeLists.txt tried to pin the Opus target to
    the project's /MD runtime by setting a target property named
    CMAKE_MSVC_RUNTIME_LIBRARY — but that is the variable name upstream
    reads, not the target property. The property is MSVC_RUNTIME_LIBRARY, so
    the override was a silent no-op and upstream's own scope kept the Debug
    runtime (MultiThreadedDebugDLL → MSVCRTD) for opus.lib, while every
    other NimRTC target used MultiThreadedDLL → MSVCRT. Linking two CRT
    heaps into one program means an allocation in one runtime and the matching
    free in the other: the engine tests that touch the Opus path
    (test_engine_video_feedback and its five cases) died with an access
    violation before main() even printed a line, and the link emitted
    LNK4098. The property name is fixed and the compile-flag rewrite now maps
    /MTd and /MT to /MD; opus.lib and the rest of the tree agree on
    MSVCRT, the LNK4098 warning is gone, and the full suite is 696 tests,
    0 failures.

  • Commits and tags were signed with an email GitHub does not recognise, so
    every signature showed up as unverified.
    The release key
    114154CFEC3F5EE5A8D2D313BEACFBB55871CC06 carried the Gitee noreply
    address 15608750+video-reflow@user.noreply.gitee.com as its only user ID.
    No GitHub account owns that address, so GitHub answered no_user for a
    signed commit and never rendered a "Verified" badge. The key's user ID is
    now NimRTC Release <realdesktop@163.com>, the account address of
    NimRTC, and the key's public block was re-exported with a stock
    gpg --export into build/nimrtc-release-signing.pub.asc. The fingerprint
    is unchanged, so signatures made before the rename still verify and the
    published v1.0.2 assets keep verifying against the same key; the account's
    GitHub GPG key list must be re-pointed at the new block before GitHub
    accepts the key (see docs/plan/v1.1-plan.md Track C6).

  • The exported public key was not a valid OpenPGP block. The copy on disk
    had been written by a PowerShell redirection, which encodes stdout as
    UTF-16LE with a BOM; GnuPG rejects it with "partial length invalid for
    packet type 63" and gpg --import fails with "Invalid keyring", so no
    standard tool could read it. Writing the export through GnuPG's own
    --output flag produces a plain ASCII armoured block that imports cleanly
    and whose self-signature reports "Good signature".

  • The offline release-signature verifier rejected valid signatures.
    tools/verify_release_signature.py re-implements Ed25519 in pure
    Python so a release can be checked without GnuPG. Two defects made
    it report every valid signature as invalid: the twisted-Edwards
    curve check used X^2 + Y^2 - Z^2 = d*X^2*Y^2 instead of the
    RFC 8032 form -X^2 + Y^2 - Z^2 - d*T^2 = 0, and the §5.1.7
    equation was checked as R == [S]B + [k]A instead of
    R + [k]A == [S]B. Both are fixed, and --self-test now runs the
    RFC 8032 §7.1 vectors plus a tampered-signature rejection case, so
    the regression cannot come back silently; CI runs it on every push.
    With the fix the published v1.0.2 artefacts all verify (four
    detached signatures, four SHA-256 sidecars) — and so does the
    signing key's UID self-signature: GnuPG 2.4.8 reports "Good
    signature" both for the key import and for every detached
    signature, so the published nimrtc-signing-key.asc needs no
    re-export.

  • The release body's 3A status table did not match the published
    bundles.
    The table was derived from build/3a-status-<platform>.txt,
    which records the build-time APM mode (whether the WebRTC APM
    prebuild succeeded), not what the tarball ships. The v1.0.2 body
    therefore claimed "WebRTC APM (real 3A) bundled" for the three
    platforms whose prebuild had succeeded, while none of the four
    published bundles contains lib/webrtc_apm/ at all, and its
    windows line blamed a failed prebuild although that step succeeded
    in the final v1.0.2 release run. release.yml now lists the
    published tarball and reports the build mode and the shipped content
    separately: "real 3A bundled" is only claimed when lib/webrtc_apm/
    is actually inside the artefact, and a platform whose bundle is
    missing or carries no APM archives says so. The published v1.0.2
    body has been corrected accordingly.

  • The installed bundle was missing the shared jitter-buffer strategy.
    nimrtc::jb_strategy (v1.1 Q2) is linked by both nimrtc::jb and
    nimrtc::video_jb, but the install rules did not know about it:
    cmake/NimRTCInstall.cmake shipped neither libnimrtc_jb_strategy.a
    nor the module's headers, and cmake/NimRTCConfig.cmake.in never
    created the nimrtc::jb_strategy imported target. A downstream
    consumer that linked nimrtc::jb (or the engine, which fans in every
    archive) therefore failed at link time with undefined symbols from
    the shared strategy. Both files now carry the module.

  • The video jitter buffer silently dropped complete frames. In
    video_jb, tick() treated every frame older than max_wait_ms the
    same way: a frame that never completed was force-emitted (correctly
    tagged complete = false), but a frame that had completed was
    marked as emitted and counted in frames_emitted without ever
    being handed out
    — the bitstream was dropped on the floor while
    the statistics claimed the frame had been played. The trigger was
    rare with the old fixed-window behaviour (the frame was normally
    popped long before the timeout), but it becomes the normal case once
    a frame waits for its playout deadline, which by design can exceed
    max_wait_ms. The timeout now only applies to frames that never
    completed; a complete frame stays buffered until it is popped.
    Two related defects in the same path are fixed as well: the
    in-flight cap evicted the oldest frame unconditionally (so a
    complete frame that was only waiting for its deadline could be
    evicted by the very delay it was waiting for — the cap now grows
    with the adaptive delay window and prefers evicting a frame that
    never completed), and a freshly created frame bucket was still
    flagged "no marker seen" when the cap ran, so the cap could evict
    the frame that had just been inserted.

  • The Windows WebRTC APM prebuild reported a successful build as a
    failure.
    tools/build_webrtc_apm.cmd carried three defects that
    together made the windows release leg fall back to NimRTC's built-in
    stub 3A instead of shipping the real AEC/ANS/AGC:

    • the script was checked out with LF endings (the * text=auto rule
      follows the local core.autocrlf setting) and cmd.exe mis-parses
      an LF-only batch file — it split the quoted vcvars64.bat path at
      spaces and never reached the ninja step;
    • the ninja call was wrapped in powershell -Command with
      $ErrorActionPreference = 'Stop', and PowerShell turns a native
      command's stderr into error records, so ninja's first progress line
      (ninja: Entering directory ...) aborted the wrapper with rc=1;
    • ninja was invoked without call, so a .cmd/.bat wrapper on
      PATH took over control and the script exited silently with no
      diagnostic at all.

    .gitattributes now pins *.cmd / *.bat to CRLF in the working
    tree, the ninja call uses cmd.exe's own redirection
    (call ninja -C ... > log 2>&1) so the real exit code survives, and
    the failure diagnostics moved from an if (...) block to a goto
    label so a syntax error in the triage path can no longer break the
    success path. The meson/ninja version echo now runs the tools instead
    of expanding !meson --version! as a delayed-expansion variable
    reference (which always printed nothing). Verified on Windows/MSVC:
    the success path exits 0 and records the link order, the failure path
    exits 3 and prints the...

Read more

NimRTC v1.0.2

Choose a tag to compare

@github-actions github-actions released this 03 Nov 03:42

[1.0.2] - 2026-11-02

Status: Stable

Patch release fixing two defects in v1.0.1. v1.0.1 remains
published and its tag is unchanged; per RELEASING.md §2.3 a
post-announcement fix becomes a new patch version. Public API is
unchanged (NIMRTC_API_VERSION = 10002, NIMRTC_API_VERSION_PRE_RELEASE
= "").

Fixed

  • WebTransport ALPN mismatch (NimRTC ↔ NimRTC handshakes were
    rejected).
    The listener advertised the HTTP/3 token "h3"
    (RFC 9114 §3.1) while the session advertised the legacy draft
    token "sm", so every NimRTC-to-NimRTC WebTransport handshake was
    refused during ALPN negotiation (Chrome interop was unaffected —
    Chrome offers "h3"). Both sides now share one definition in
    src/web_transport/src/web_transport_msquic_alpn.hpp
    (session_alpn() / listener_alpn()), and
    src/web_transport/tests/test_web_transport_alpn.cpp pins the
    invariant so the two cannot drift apart again.
  • Windows release bundle contained the whole CI workspace
    (822 MB).
    release.yml packed the Windows bundle with
    7z a -ttar -so "$staging" | 7z a -txz -si "$bundle". With -so,
    7-Zip consumes the first positional argument as the archive name
    (discarded, because the archive goes to stdout) and, with no file
    list left, it packs the current working directory — so the
    published v1.0.1 Windows asset shipped the sources, .git and the
    complete MSVC build tree instead of the install staging tree
    (822,079,464 bytes against 2.3-2.9 MB for the other three
    platforms). The Windows job now uses
    tools/make_release_bundle.py, which packs exactly the staging
    tree with members relative to the staging root — the same layout as
    the tar -C ... . of the Linux/macOS jobs — and all four jobs now
    refuse to publish a bundle larger than 64 MB.
  • The signing public key is now shipped with the release. v1.0.1
    published detached .asc signatures while the signing key stayed
    private, so gpg --verify failed for every downstream consumer.
    The publish job now exports the public half of the signing key as
    the nimrtc-signing-key.asc release asset, pushes it to
    keyserver.ubuntu.com and keys.openpgp.org, and the release body
    carries the verification recipe together with the fingerprint
    (114154CFEC3F5EE5A8D2D313BEACFBB55871CC06).

Known issues

  • The v1.0.1 Windows asset stays broken. The published
    nimrtc-1.0.1-windows-x86_64-msvc.tar.xz is still the 822 MB
    archive that contains the CI workspace rather than the install
    tree; v1.0.1 is immutable now, so the usable Windows artefact is
    the one in this release.
  • The bundles do not contain NimRTC's own headers, static library,
    or NimRTCConfig.cmake.
    Each platform bundle currently ships
    only the vendored third-party dependency stack (wolfSSL /
    libsrtp3 / libjuice / msquic headers and libraries); no module in
    src/modules/ declares an install() rule, and no CMake package
    config is generated, so find_package(NimRTC) does not resolve
    against a downloaded bundle and linking NimRTC itself still requires
    a source build. This predates v1.0.1 (its Linux bundle has the
    same shape). Adding per-module install() rules plus a generated
    NimRTCConfig.cmake / NimRTCConfigVersion.cmake is tracked for
    v1.1.0 (Track D in docs/plan/v1.1-plan.md); README.md "Download prebuilt" has been corrected to
    describe what the bundles actually contain.
  • The bundles carry no WebRTC APM archives, so no bundle provides a
    usable 3A.
    The four v1.0.2 tarballs contain no
    lib/webrtc_apm/ directory at all (verified 2026-11-05 against the
    published nimrtc-1.0.2-{windows-x86_64-msvc,linux-x86_64-gcc}.tar.xz
    assets): they ship the vendored third-party stack only. The APM
    archives are installed by cmake/NimRTCInstall.cmake (Track D), which
    landed after the v1.0.2 cut, so the v1.0.2 bundles predate it.

Notes

  • Public API is unchanged vs v1.0.1; NIMRTC_API_VERSION = 10002,
    NIMRTC_API_VERSION_PRE_RELEASE = "".
  • The 3A status table in this release's body was wrong, and so was
    the reason it gave for the windows line.
    The table is generated
    from the per-platform build-time APM mode
    (build/3a-status-<platform>.txt, which records whether the WebRTC
    APM prebuild succeeded), not from what the published bundle carries.
    The "WebRTC APM (real 3A) bundled" lines for linux/macOS therefore
    described the build, not the artefact: none of the four bundles ships
    the APM archives (see the known issue above). The windows line
    blamed a failed prebuild, but the Prebuild WebRTC APM step
    succeeded in the final v1.0.2 release run (release / build / windows → Prebuild WebRTC APM → success); the persistent Windows
    stub-3A outcome through v1.0.2 came from defects in
    tools/build_webrtc_apm.cmd — LF-only line endings that cmd.exe
    mis-parses, a powershell -Command wrapper that turned ninja's
    stderr progress line into a terminating error, and a ninja invocation
    without call — all fixed in 9c3c522, i.e. before this cut. The
    generator now lists the published tarball and reports the build mode
    and the shipped content separately, so "real 3A bundled" is only
    claimed when lib/webrtc_apm/ is actually present in the artefact.

Verifying the signature

The public half of the signing key ships with this
release as nimrtc-signing-key.asc. To verify a bundle:

gpg --import nimrtc-signing-key.asc
gpg --verify nimrtc-<version>-<platform>.tar.xz.asc nimrtc-<version>-<platform>.tar.xz

The signing key fingerprint is 114154CFEC3F5EE5A8D2D313BEACFBB55871CC06.
Check it against the fingerprint shown by gpg --fingerprint
after importing — do not trust the key file blindly.


3A (AEC/ANS/AGC) status per platform

  • linux-aarch64 — the build used the real WebRTC APM, but this bundle ships no APM archives (lib/webrtc_apm/ is absent), so it provides no usable 3A
  • linux-x86_64 — the build used the real WebRTC APM, but this bundle ships no APM archives (lib/webrtc_apm/ is absent), so it provides no usable 3A
  • macos-arm64 — the build used the real WebRTC APM, but this bundle ships no APM archives (lib/webrtc_apm/ is absent), so it provides no usable 3A
  • windows — the build used the real WebRTC APM, but this bundle ships no APM archives (lib/webrtc_apm/ is absent), so it provides no usable 3A

The WebRTC APM is prebuilt out-of-tree with meson. Where
that prebuild fails, the build falls back to NimRTC's
built-in stub 3A path instead of aborting the release.
The status above is checked against the published bundle:
a platform counts as "real 3A bundled" only when
lib/webrtc_apm/ is actually present in the tarball.

NimRTC v1.0.1

Choose a tag to compare

@github-actions github-actions released this 02 Nov 10:00

[1.0.1] - 2026-11-02

Status: Stable

First published stable release. v1.0.0 (f740f2e, 2026-10-30)
is retained as a source-tree milestone tag only; it was never
published as a GitHub Release. v1.0.1 is the first 1.0-line
release cut through .github/workflows/release.yml with
four-platform artefacts + SBOM + detached GPG signatures.

Fixed

  • Chrome forward DTLS handshake stall (Case D). Tick-thread
    WebTransport session creation with an empty peer host blocked ICE
    drain, so ClientHello never reached feed_inbound. create_data_channel
    now fails fast, and --no-datachannel clears both the datachannel name
    and backend so tick keeps draining DTLS/SRTP/RTP. DTLS outbound re-pump
    is gated on empty drain to stop ServerHello triple flight; single
    keylog writer on handshake complete. (5a582c5)
  • CI e2e acceptance on push. interop.yml now runs the Case D
    acceptance gate on push and emits build/e2e/case_d_results.json;
    proxy/demo-p2p stderr and DTLS artefacts route under build/e2e for
    upload; macOS arm64 CadenceProducesApproxFps bound relaxed 8→4 for
    runner stutter. (4e312e3, 5a582c5)

Notes

  • Public API is unchanged vs v1.0.0; NIMRTC_API_VERSION = 10001,
    NIMRTC_API_VERSION_PRE_RELEASE = "".

Verification

  • Each platform bundle ships with .sha256 and detached GPG .asc.
  • Signing key fingerprint: 114154CFEC3F5EE5A8D2D313BEACFBB55871CC06.
  • Verify with sha256sum -c *.sha256 and gpg --verify *.asc.
  • The signing public key was not shipped with this release, so
    gpg --verify could not succeed out of the box. It is published on
    keyserver.ubuntu.com and keys.openpgp.org, and also ships as the
    nimrtc-signing-key.asc asset of the v1.0.2 release. Import it with
    gpg --keyserver keyserver.ubuntu.com --recv-keys 114154CFEC3F5EE5A8D2D313BEACFBB55871CC06
    before verifying.

3A (AEC/ANS/AGC) status per platform

  • linux-aarch64 — WebRTC APM (real 3A) bundled
  • linux-x86_64 — WebRTC APM (real 3A) bundled
  • macos-arm64 — WebRTC APM (real 3A) bundled
  • windows — stub 3A path (WebRTC APM prebuild did not succeed)

The WebRTC APM is prebuilt out-of-tree with meson. Where
that prebuild fails, the bundle falls back to NimRTC's
built-in stub 3A path instead of aborting the release.


Known issues in this release

This release is superseded by v1.0.2, which fixes the three defects
listed below. Prefer the v1.0.2 assets: the v1.0.1 tag and its assets
are immutable per RELEASING.md §2.3.

  • WebTransport ALPN mismatch -- NimRTC-to-NimRTC handshakes were
    rejected.
    The listener advertised the HTTP/3 token "h3"
    (RFC 9114 §3.1) while the session advertised the legacy draft token
    "sm", so every NimRTC-to-NimRTC WebTransport handshake was refused
    during ALPN negotiation. Chrome interop was unaffected (Chrome offers
    "h3"). Fixed in v1.0.2.
  • The Windows bundle contains the whole CI workspace (822 MB).
    nimrtc-1.0.1-windows-x86_64-msvc.tar.xz is 822,079,464 bytes and packs
    the sources, .git and the complete MSVC build tree instead of the
    install staging tree; the other three platforms are 2.3-2.9 MB. Do not
    use this asset. Fixed in v1.0.2.
  • The signing public key was not published, so gpg --verify failed
    for every downstream consumer. See "Verification" above for where to get
    the key now. Fixed in v1.0.2.

NimRTC v0.14.0

Choose a tag to compare

@github-actions github-actions released this 17 Oct 14:24

[0.14.0] - 2026-10-16

Status: Tech Preview

First pre-release of the v0.14.x cycle — the
v1.0.0-alpha.2 pre-release per docs/plan/v1.0-plan.md
§8 + §12.2 + §12.3.2. The marketing version stays anchored
to the v0.x line ("Tech Preview" banner unchanged from
v0.13.0); the NIMRTC_API_VERSION_PRE_RELEASE macro cycles
to "alpha.2". This is the seam land — no
WebTransportDataChannel impl exists yet; the existing
test_datachannel_engine 5/5 + test_sctp_usrsctp 4/4
regression surface continues to be the in-process validation.
The data-plane wire-up is the v0.14.0-beta.1 work per
docs/plan/v0.14-plan.md §2.1.2.

Tag cut: git tag -s v0.14.0 -m "v0.14.0 (alias: v1.0.0-alpha.2) — ...".
The tag message body documents the alias relationship to
v1.0.0-alpha.2 and the v0.x banner unchanged.

Tag day follow-ups (2026-10-16): the v0.14.0 tag was
force-re-pointed twice on tag day to fold in two release.yml
fixes that the tag-cut force-push surfaced:

  1. PR #20 (b5da037) — added -DNIMRTC_VENDORED_WEBRTC_APM=OFF
    to the four release.yml Configure steps so audio3a links
    against the same stub path that ci.yml uses, instead of
    chasing absl/base/config.h in the vendored WebRTC APM
    prebuild.
  2. dc15ad8 — added if: ${{ false }} to the four
    Prebuild WebRTC APM steps in release.yml (windows-msvc +
    linux-gcc + linux-aarch64 + macos-clang) so the meson /
    abseil-cpp subproject is no longer built before
    Configure. The long-term fix
    (target_link_libraries(audio3a PUBLIC webrtc_apm::webrtc_apm)
    so abseil's INCLUDE_DIRECTORIES propagate to the consumer) is
    tracked in a follow-up; until that lands, release.yml ships
    with the audio3a stub path, byte-equivalent to ci.yml.

RELEASING.md §2.3 tag immutability is preserved because the
v0.14.0 GitHub Release was a no-artefact draft at first cut
(release / publish was skipped / cancelled in
run #38898093864); no public announcement had been made. Each
re-point landed before the corresponding release / publish
re-run.

Highlights

  • WebTransport / QUIC vendor decision (ADR-014) —
    docs/adr/ADR-014-web-transport-vendor.md (Status:
    Accepted). msquic chosen as the WebTransport / QUIC backend
    for the v0.14.0-beta.1 WebTransportMsQuicSession impl. The
    5 evaluation criteria in v1.0-plan.md §10.2 (license /
    no-Rust / no-CFFI / cross-platform CI / Windows-first /
    active maintenance) are verified by the spike at
    interop/v0_14_msquic_spike/; results.txt records
    per-criterion PASS/FAIL with timestamps + commit SHAs.
    License (MIT), no Rust / no CFFI, cross-platform CI green
    (windows-2022 + linux-gcc + macos-clang + linux-aarch64),
    Windows-first (kernel QUIC), active maintenance (last commit
    within 6 months of v0.14.0 tag cut). Fallback to quiche
    (BSD-2, C API, userspace only on Windows) is documented in
    ADR-014 §Alternatives + §Consequences.

  • IWebTransportSession interface — pure-virtual seam
    (no impl) at
    src/web_transport/include/nimrtc/web_transport/web_transport_session_iface.hpp.
    Companion headers:

    • bidi_stream_iface.hpp — IBidirectionalStream
    • uni_stream_iface.hpp — IUnidirectionalStream
    • datagram_facade.hpp — DatagramFacade (utility wrapper)
    • web_transport_config.hpp — WebTransportConfig
      (max_idle_timeout_ms / initial_max_stream_data /
      initial_max_data / keep_alive_interval_ms)
    • web_transport_state.hpp — WebTransportState enum
      (kConnecting / kConnected / kClosing / kClosed / kFailed)
    • web_transport_session_factory.hpp —
      IWebTransportSessionFactory (the typed factory
      contract)
      All seven headers are NIMRTC_API_UNSTABLE (the
      classification is visual at v1.0.0-alpha.2; the "v1.0.0
      stable" banner does not flip until v1.0.0 final).
  • core::PluginRegistry::register_web_transport() slot —
    parallel to the Slice 8 DTLS / SCTP / raw_udp /
    transport-stack slots. The factory slot exists at the
    alpha.2 cut; the msquic factory lands in beta.1 alongside
    the msquic integration. list_web_transports() returns an
    empty vector at the alpha.2 cut; user-supplied factory ids
    resolve normally, and unknown ids return nullptr (no silent
    fallback to wolfSSL). Two new subtests in
    test_engine_plugin_loading.cpp:

    • wt014_registry_slot_is_typed_for_web_transport_factory
    • wt014_registry_default_registrars_omits_web_transport_until_beta1
  • Compile-only seam test —
    tests/test_web_transport_seam.cpp (new). Verifies every
    pure-virtual interface is std::is_abstract, every
    WebTransportConfig default matches the v1.0-plan.md
    §10.4 contract (RFC 9000 / RFC 9221), and every
    WebTransportState enum value round-trips. 5 subtests
    PASS on Windows MSVC. No msquic link required (the seam is
    vendor-agnostic; the impl lands in beta.1).

  • v0.14.x spike-validation program — 6 spike programs at
    interop/v0_14_msquic_spike/ exercise the full msquic C
    API surface against the planned IWebTransportSession
    methods:

    • echo_client.cpp / echo_server.cpp — 0-RTT echo
      round-trip latency < 50 ms
    • stream_reset.cpp — MsQuicStreamSendComplete + reset
    • datagram_roundtrip.cpp — MsQuicDatagramSend / recv
    • concurrent_streams.cpp — 16-stream fanout
      The results.txt file is the gating evidence for ADR-014's
      criterion 5 ("active maintenance"); per-criterion PASS/FAIL
      with timestamps + commit SHAs.

Added

  • src/web_transport/include/nimrtc/web_transport/*.hpp
    (7 new public headers) — the WebTransport / QUIC seam.
    All headers are NIMRTC_API_UNSTABLE (the v1.0.0-alpha.2
    classification; stable banner does not flip until v1.0.0
    final).

  • IWebTransportSession interface in
    web_transport_session_iface.hpp. Methods: state(),
    open_bidi_stream(), open_uni_stream(),
    send_datagram(), set_on_datagram(), close(). Pure
    virtual; no impl.

  • IBidirectionalStream + IUnidirectionalStream
    interfaces in bidi_stream_iface.hpp /
    uni_stream_iface.hpp. Bidi methods: send / recv /
    set_on_data / close / reset. Uni methods: send /
    close / reset.

  • WebTransportConfig in web_transport_config.hpp —
    small struct mirroring the QUIC settings the msquic
    backend exposes through QUIC_SETTINGS + QUIC_CONFIGURATION
    parameters. Defaults match RFC 9000 / RFC 9221.

  • WebTransportState enum in
    web_transport_state.hpp — 5-value enum
    (kConnecting / kConnected / kClosing / kClosed /
    kFailed) with uint8_t underlying type.

  • DatagramFacade in datagram_facade.hpp — non-virtual
    utility wrapper around IWebTransportSession's datagram
    surface. Binds a non-owning pointer to the session; the
    facade does not extend the session's lifetime.

  • IWebTransportSessionFactory in
    web_transport_session_factory.hpp — typed factory
    contract; matches the dtls::IDtlsSessionFactory /
    sctp::ISctpSocketFactory / raw_udp::IRawUdpFactory /
    ITransportStackFactory pattern. No factory is
    auto-registered at the alpha.2 cut; the msquic factory
    lands in beta.1.

  • NIMRTC_REGISTER_WEB_TRANSPORT macro in
    src/core/include/nimrtc/core/registry.hpp —
    static ::nimrtc::core::detail::Registrar wrapper for the
    new typed slot, parallel to the existing
    NIMRTC_REGISTER_DTLS_SESSION / SCTP_SOCKET /
    RAW_UDP_DATAGRAM / TRANSPORT_STACK macros.

  • core::PluginRegistry::register_web_transport(id, factory*) / get_web_transport(id) /
    list_web_transports()
    — new typed slot on
    core::PluginRegistry. Forward-declared in registry.hpp;
    no msquic headers leak into core (Layout Invariant 1
    preserved).

  • tests/test_web_transport_seam.cpp (new, 5 subtests):
    seam_interfaces_are_abstract /
    datagram_facade_forwards_to_session /
    datagram_facade_with_null_session /
    callbacks_accept_lambdas /
    state_enum_round_trip. Compile-only test; the gate is
    "the seam headers compile + link on all 4 platforms".

  • 2 new EnginePluginLoading subtests in
    tests/test_engine_plugin_loading.cpp:

    • wt014_registry_slot_is_typed_for_web_transport_factory —
      verifies the slot exists, accepts a user-registered
      factory, and resolves by id.
    • wt014_registry_default_registrars_omits_web_transport_until_beta1 —
      pins the alpha.2 → beta.1 transition: no factory is
      auto-registered at the alpha.2 cut; the msquic factory
      lands in beta.1.
  • NIMRTC_API_VERSION_PRE_RELEASE cycles to "alpha.2"
    in src/core/include/nimrtc/core/api_version.hpp.

  • interop/v0_14_msquic_spike/ (new) — spike-validation
    program for ADR-014. README.md documents the 5-criterion
    check; results.txt records per-criterion PASS/FAIL with
    timestamps + commit SHAs. 6 spike programs exercise the
    full msquic C API surface against the planned
    IWebTransportSession methods. No msquic source code
    lands in v0.14.0 alpha.2 (the integration lands in beta.1).

  • docs/adr/ADR-014-web-transport-vendor.md (new) —
    msquic vendor decision. 5 evaluation criteria in
    v1.0-plan.md §10.2 verified by the spike at
    interop/v0_14_msquic_spike/. Fallback to quiche
    documented in §Alternatives + §Consequences.

Changed

  • src/core/include/nimrtc/core/registry.hpp — adds the
    register_web_transport / get_web_transport /
    list_web_transports methods (parallel to the Slice 8
    DTLS / SCTP / raw_udp / transport-stack slots), the
    NIMRTC_REGISTER_WEB_TRANSPORT macro, the kWebTransport
    Category enum entry, and the matchi...
Read more