2026-07-07 11:07:59 +00:00
|
|
|
<?php
|
|
|
|
|
|
feat: full package — typed rooms (public/private/presence/open-presence) + call recording
Rooms
- Rooms\RoomType derived from the name prefix (like laravel-websockets channels):
public, private-* (authorized), presence-* (authorized + member list),
open-presence-* (public + member list). Room/RoomManager carry per-member info.
- Contracts\RoomAuthorizer + secure DefaultRoomAuthorizer (denies private/presence
until the host binds one). Presence rooms broadcast the member list + info.
Recording (store the FULL call audio, in PHP, today)
- Pure-P2P audio is DTLS-SRTP end-to-end, so the server can't capture it. Instead
the browser MediaRecords its mic and streams chunks over the WS as BINARY frames
(needs the new laravel-ws onBinary); RelaySignalingHandler appends them per
participant via a RecordingStore. Recording\FileRecordingStore -> <room>/<peer>.webm.
welcome/joined carry a record flag telling the client to stream.
- Contracts\CallEventListener call-log seam (peer/room/recording lifecycle) so calls
are auditable even when unrecorded.
Wiring
- RelaySignalingHandler(authorizer, recorder, events); webrtc:serve + provider +
config/webrtc.php (recording enabled/path/format, authorizer, events).
Tests 35/99 incl on-disk FileRecordingStore, presence member lists, auth gating,
binary recording+finalize+events, RoomType, and the real WS round-trip. README
documents room types + the browser MediaRecorder->WS recording flow.
rel learn-atc #1060 #1055 #1057 #1051
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 07:01:12 +00:00
|
|
|
use Blax\WebRtc\Authorization\DefaultRoomAuthorizer;
|
|
|
|
|
use Blax\WebRtc\Events\NullCallEventListener;
|
feat: WebRTC MVP to Blax standard (namespace, README, tests, scaffolding)
- Rename namespace BlaxSoftware\LaravelWebRtc -> Blax\WebRtc (house standard) +
update kernel imports to Blax\ReactPhpKernel
- README rewritten to the Blax OSS package principles: OSS banner, title+badges,
emoji feature list, quick start, wire shape, config, architecture, star history
- Add PHPUnit suite (8 tests / 18 assertions, green): SignalingHandler routing
(offer/ice/record/connect/bridge/close/error paths via a fake engine) +
NullMediaEngine behaviour
- Add pint.json (laravel preset, applied), composer scripts, .gitattributes,
test.sh (nix), CHANGELOG
- Verified end-to-end: composer install resolves blax-software/reactphp-kernel
from Forgejo VCS and the suite runs green on it
rel learn-atc #1055 #1051 #1019
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 11:28:49 +00:00
|
|
|
use Blax\WebRtc\Media\NullMediaEngine;
|
feat: Rust SFU data plane — backend-hosted rooms, plug-and-play sidecar binary
rust/ becomes a standalone static binary (blax-webrtc-sfu, tokio-free
std-thread loop on str0m): terminates ICE/DTLS/SRTP, forwards Opus between
room peers without decoding, records per-peer Ogg/Opus (ffprobe-verified),
server-side mute/kick, stats + peer_connected/peer_left usage events, all
driven by PHP over a JSON-lines Unix control socket. Renegotiation rides the
browser data channel (str0m chat model) — signaling only carries the initial
offer/answer.
PHP side: RustMediaEngine (MediaEngine contract + room-aware API) is
plug-and-play — first use downloads the sha256-verified prebuilt binary
(BinaryManager, RoadRunner pattern) and spawns it detached, flock-guarded
(SidecarSupervisor). webrtc:install / webrtc:sidecar provided for explicit
control; config gains the webrtc.sfu block. Replaces the ext-php-rs
Str0mMediaEngine seam.
Tests: 61 PHP (incl. a real PHP<->Rust E2E that spawns the sidecar from
PHPUnit) + 7 Rust (incl. an end-to-end control-socket integration test).
build-release.sh produces the static-musl release + .sha256 (verified:
static-pie, 9.2MB, runs).
Card #1083 (rel #1051 #1060 #1064).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 09:29:26 +00:00
|
|
|
use Blax\WebRtc\Rooms\Stores\ArrayRoomStore;
|
2026-07-07 11:07:59 +00:00
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
/*
|
|
|
|
|
|--------------------------------------------------------------------------
|
|
|
|
|
| Signaling bind
|
|
|
|
|
|--------------------------------------------------------------------------
|
|
|
|
|
| Where the signaling server (on the shared ReactPHP kernel) listens. For
|
|
|
|
|
| browser clients you will typically front this with the laravel-websockets
|
|
|
|
|
| WS transport instead; this raw endpoint drives + tests the media engine.
|
|
|
|
|
*/
|
|
|
|
|
'host' => env('WEBRTC_HOST', '127.0.0.1'),
|
|
|
|
|
'port' => (int) env('WEBRTC_PORT', 8090),
|
|
|
|
|
|
|
|
|
|
// ReactPHP TLS context; leave both unset for plain TCP.
|
|
|
|
|
'tls' => array_filter([
|
|
|
|
|
'local_cert' => env('WEBRTC_TLS_CERT'),
|
|
|
|
|
'local_pk' => env('WEBRTC_TLS_KEY'),
|
|
|
|
|
]),
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|--------------------------------------------------------------------------
|
|
|
|
|
| Media engine
|
|
|
|
|
|--------------------------------------------------------------------------
|
|
|
|
|
| The backend that terminates media (ICE/DTLS/SRTP/RTP/Opus), records, and
|
feat: Rust SFU data plane — backend-hosted rooms, plug-and-play sidecar binary
rust/ becomes a standalone static binary (blax-webrtc-sfu, tokio-free
std-thread loop on str0m): terminates ICE/DTLS/SRTP, forwards Opus between
room peers without decoding, records per-peer Ogg/Opus (ffprobe-verified),
server-side mute/kick, stats + peer_connected/peer_left usage events, all
driven by PHP over a JSON-lines Unix control socket. Renegotiation rides the
browser data channel (str0m chat model) — signaling only carries the initial
offer/answer.
PHP side: RustMediaEngine (MediaEngine contract + room-aware API) is
plug-and-play — first use downloads the sha256-verified prebuilt binary
(BinaryManager, RoadRunner pattern) and spawns it detached, flock-guarded
(SidecarSupervisor). webrtc:install / webrtc:sidecar provided for explicit
control; config gains the webrtc.sfu block. Replaces the ext-php-rs
Str0mMediaEngine seam.
Tests: 61 PHP (incl. a real PHP<->Rust E2E that spawns the sidecar from
PHPUnit) + 7 Rust (incl. an end-to-end control-socket integration test).
build-release.sh produces the static-musl release + .sha256 (verified:
static-pie, 9.2MB, runs).
Card #1083 (rel #1051 #1060 #1064).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 09:29:26 +00:00
|
|
|
| bridges to external realtime providers. NullMediaEngine = signaling only
|
|
|
|
|
| (peer-to-peer mesh); RustMediaEngine = BACKEND-HOSTED rooms on the Rust
|
|
|
|
|
| SFU sidecar (plug-and-play — see the `sfu` block below).
|
2026-07-07 11:07:59 +00:00
|
|
|
*/
|
|
|
|
|
'media_engine' => env('WEBRTC_MEDIA_ENGINE', NullMediaEngine::class),
|
|
|
|
|
|
feat: Rust SFU data plane — backend-hosted rooms, plug-and-play sidecar binary
rust/ becomes a standalone static binary (blax-webrtc-sfu, tokio-free
std-thread loop on str0m): terminates ICE/DTLS/SRTP, forwards Opus between
room peers without decoding, records per-peer Ogg/Opus (ffprobe-verified),
server-side mute/kick, stats + peer_connected/peer_left usage events, all
driven by PHP over a JSON-lines Unix control socket. Renegotiation rides the
browser data channel (str0m chat model) — signaling only carries the initial
offer/answer.
PHP side: RustMediaEngine (MediaEngine contract + room-aware API) is
plug-and-play — first use downloads the sha256-verified prebuilt binary
(BinaryManager, RoadRunner pattern) and spawns it detached, flock-guarded
(SidecarSupervisor). webrtc:install / webrtc:sidecar provided for explicit
control; config gains the webrtc.sfu block. Replaces the ext-php-rs
Str0mMediaEngine seam.
Tests: 61 PHP (incl. a real PHP<->Rust E2E that spawns the sidecar from
PHPUnit) + 7 Rust (incl. an end-to-end control-socket integration test).
build-release.sh produces the static-musl release + .sha256 (verified:
static-pie, 9.2MB, runs).
Card #1083 (rel #1051 #1060 #1064).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 09:29:26 +00:00
|
|
|
/*
|
|
|
|
|
|--------------------------------------------------------------------------
|
|
|
|
|
| Rust SFU sidecar (RustMediaEngine)
|
|
|
|
|
|--------------------------------------------------------------------------
|
|
|
|
|
| The `blax-webrtc-sfu` binary from this package's rust/ directory: one
|
|
|
|
|
| process per host that terminates ICE/DTLS/SRTP and forwards audio between
|
|
|
|
|
| room peers (no decoding), records Ogg/Opus per peer, and reports usage.
|
|
|
|
|
|
|
|
|
|
|
| Plug-and-play defaults: the first RustMediaEngine call DOWNLOADS the
|
|
|
|
|
| checksummed prebuilt binary for this platform (auto_install) and SPAWNS
|
|
|
|
|
| it detached, flock-guarded (auto_spawn). For supervised deployments run
|
|
|
|
|
| `php artisan webrtc:sidecar` as a service and disable auto_spawn.
|
|
|
|
|
|
|
|
|
|
|
| udp_port 0 = ephemeral (fine same-host); pin it (and publish it on the
|
|
|
|
|
| container) for production, and set public_ip to the address browsers
|
|
|
|
|
| can reach — it is advertised in the ICE host candidate.
|
|
|
|
|
*/
|
|
|
|
|
'sfu' => [
|
|
|
|
|
'socket' => env('WEBRTC_SFU_SOCKET', storage_path('app/webrtc/sfu.sock')),
|
|
|
|
|
'udp_ip' => env('WEBRTC_SFU_UDP_IP', '0.0.0.0'),
|
|
|
|
|
'udp_port' => (int) env('WEBRTC_SFU_UDP_PORT', 0),
|
|
|
|
|
'public_ip' => env('WEBRTC_SFU_PUBLIC_IP'),
|
|
|
|
|
|
|
|
|
|
'auto_spawn' => (bool) env('WEBRTC_SFU_AUTO_SPAWN', true),
|
|
|
|
|
'auto_install' => (bool) env('WEBRTC_SFU_AUTO_INSTALL', true),
|
|
|
|
|
'spawn_timeout' => (float) env('WEBRTC_SFU_SPAWN_TIMEOUT', 10),
|
|
|
|
|
'log' => env('WEBRTC_SFU_LOG', storage_path('logs/webrtc-sfu.log')),
|
|
|
|
|
|
|
|
|
|
// Binary resolution: explicit path > local rust/target build > install.
|
|
|
|
|
'binary' => env('WEBRTC_SFU_BINARY'),
|
|
|
|
|
'install_path' => env('WEBRTC_SFU_INSTALL_PATH', storage_path('app/webrtc/bin')),
|
|
|
|
|
'version' => env('WEBRTC_SFU_VERSION'), // default: the version pinned by this package
|
|
|
|
|
'download_url' => env(
|
|
|
|
|
'WEBRTC_SFU_DOWNLOAD_URL',
|
2026-08-11 08:45:57 +00:00
|
|
|
'https://git.blax.at/blax-software/laravel-webrtc/releases/download/{version}/blax-webrtc-sfu-{platform}',
|
feat: Rust SFU data plane — backend-hosted rooms, plug-and-play sidecar binary
rust/ becomes a standalone static binary (blax-webrtc-sfu, tokio-free
std-thread loop on str0m): terminates ICE/DTLS/SRTP, forwards Opus between
room peers without decoding, records per-peer Ogg/Opus (ffprobe-verified),
server-side mute/kick, stats + peer_connected/peer_left usage events, all
driven by PHP over a JSON-lines Unix control socket. Renegotiation rides the
browser data channel (str0m chat model) — signaling only carries the initial
offer/answer.
PHP side: RustMediaEngine (MediaEngine contract + room-aware API) is
plug-and-play — first use downloads the sha256-verified prebuilt binary
(BinaryManager, RoadRunner pattern) and spawns it detached, flock-guarded
(SidecarSupervisor). webrtc:install / webrtc:sidecar provided for explicit
control; config gains the webrtc.sfu block. Replaces the ext-php-rs
Str0mMediaEngine seam.
Tests: 61 PHP (incl. a real PHP<->Rust E2E that spawns the sidecar from
PHPUnit) + 7 Rust (incl. an end-to-end control-socket integration test).
build-release.sh produces the static-musl release + .sha256 (verified:
static-pie, 9.2MB, runs).
Card #1083 (rel #1051 #1060 #1064).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 09:29:26 +00:00
|
|
|
),
|
|
|
|
|
// sha256 verification is REQUIRED when set; null opts out explicitly.
|
|
|
|
|
'checksum_url' => env(
|
|
|
|
|
'WEBRTC_SFU_CHECKSUM_URL',
|
2026-08-11 08:45:57 +00:00
|
|
|
'https://git.blax.at/blax-software/laravel-webrtc/releases/download/{version}/blax-webrtc-sfu-{platform}.sha256',
|
feat: Rust SFU data plane — backend-hosted rooms, plug-and-play sidecar binary
rust/ becomes a standalone static binary (blax-webrtc-sfu, tokio-free
std-thread loop on str0m): terminates ICE/DTLS/SRTP, forwards Opus between
room peers without decoding, records per-peer Ogg/Opus (ffprobe-verified),
server-side mute/kick, stats + peer_connected/peer_left usage events, all
driven by PHP over a JSON-lines Unix control socket. Renegotiation rides the
browser data channel (str0m chat model) — signaling only carries the initial
offer/answer.
PHP side: RustMediaEngine (MediaEngine contract + room-aware API) is
plug-and-play — first use downloads the sha256-verified prebuilt binary
(BinaryManager, RoadRunner pattern) and spawns it detached, flock-guarded
(SidecarSupervisor). webrtc:install / webrtc:sidecar provided for explicit
control; config gains the webrtc.sfu block. Replaces the ext-php-rs
Str0mMediaEngine seam.
Tests: 61 PHP (incl. a real PHP<->Rust E2E that spawns the sidecar from
PHPUnit) + 7 Rust (incl. an end-to-end control-socket integration test).
build-release.sh produces the static-musl release + .sha256 (verified:
static-pie, 9.2MB, runs).
Card #1083 (rel #1051 #1060 #1064).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 09:29:26 +00:00
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
|
2026-07-07 11:07:59 +00:00
|
|
|
/*
|
|
|
|
|
|--------------------------------------------------------------------------
|
feat: full package — typed rooms (public/private/presence/open-presence) + call recording
Rooms
- Rooms\RoomType derived from the name prefix (like laravel-websockets channels):
public, private-* (authorized), presence-* (authorized + member list),
open-presence-* (public + member list). Room/RoomManager carry per-member info.
- Contracts\RoomAuthorizer + secure DefaultRoomAuthorizer (denies private/presence
until the host binds one). Presence rooms broadcast the member list + info.
Recording (store the FULL call audio, in PHP, today)
- Pure-P2P audio is DTLS-SRTP end-to-end, so the server can't capture it. Instead
the browser MediaRecords its mic and streams chunks over the WS as BINARY frames
(needs the new laravel-ws onBinary); RelaySignalingHandler appends them per
participant via a RecordingStore. Recording\FileRecordingStore -> <room>/<peer>.webm.
welcome/joined carry a record flag telling the client to stream.
- Contracts\CallEventListener call-log seam (peer/room/recording lifecycle) so calls
are auditable even when unrecorded.
Wiring
- RelaySignalingHandler(authorizer, recorder, events); webrtc:serve + provider +
config/webrtc.php (recording enabled/path/format, authorizer, events).
Tests 35/99 incl on-disk FileRecordingStore, presence member lists, auth gating,
binary recording+finalize+events, RoomType, and the real WS round-trip. README
documents room types + the browser MediaRecorder->WS recording flow.
rel learn-atc #1060 #1055 #1057 #1051
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 07:01:12 +00:00
|
|
|
| Recording (store the full call audio)
|
2026-07-07 11:07:59 +00:00
|
|
|
|--------------------------------------------------------------------------
|
feat: full package — typed rooms (public/private/presence/open-presence) + call recording
Rooms
- Rooms\RoomType derived from the name prefix (like laravel-websockets channels):
public, private-* (authorized), presence-* (authorized + member list),
open-presence-* (public + member list). Room/RoomManager carry per-member info.
- Contracts\RoomAuthorizer + secure DefaultRoomAuthorizer (denies private/presence
until the host binds one). Presence rooms broadcast the member list + info.
Recording (store the FULL call audio, in PHP, today)
- Pure-P2P audio is DTLS-SRTP end-to-end, so the server can't capture it. Instead
the browser MediaRecords its mic and streams chunks over the WS as BINARY frames
(needs the new laravel-ws onBinary); RelaySignalingHandler appends them per
participant via a RecordingStore. Recording\FileRecordingStore -> <room>/<peer>.webm.
welcome/joined carry a record flag telling the client to stream.
- Contracts\CallEventListener call-log seam (peer/room/recording lifecycle) so calls
are auditable even when unrecorded.
Wiring
- RelaySignalingHandler(authorizer, recorder, events); webrtc:serve + provider +
config/webrtc.php (recording enabled/path/format, authorizer, events).
Tests 35/99 incl on-disk FileRecordingStore, presence member lists, auth gating,
binary recording+finalize+events, RoomType, and the real WS round-trip. README
documents room types + the browser MediaRecorder->WS recording flow.
rel learn-atc #1060 #1055 #1057 #1051
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 07:01:12 +00:00
|
|
|
| When enabled, the browser streams its mic to the server as binary WS frames
|
|
|
|
|
| and RelaySignalingHandler appends them per participant via a RecordingStore
|
|
|
|
|
| (FileRecordingStore by default: <path>/<room>/<peer>.<format>). See README.
|
2026-07-07 11:07:59 +00:00
|
|
|
*/
|
|
|
|
|
'recording' => [
|
feat: full package — typed rooms (public/private/presence/open-presence) + call recording
Rooms
- Rooms\RoomType derived from the name prefix (like laravel-websockets channels):
public, private-* (authorized), presence-* (authorized + member list),
open-presence-* (public + member list). Room/RoomManager carry per-member info.
- Contracts\RoomAuthorizer + secure DefaultRoomAuthorizer (denies private/presence
until the host binds one). Presence rooms broadcast the member list + info.
Recording (store the FULL call audio, in PHP, today)
- Pure-P2P audio is DTLS-SRTP end-to-end, so the server can't capture it. Instead
the browser MediaRecords its mic and streams chunks over the WS as BINARY frames
(needs the new laravel-ws onBinary); RelaySignalingHandler appends them per
participant via a RecordingStore. Recording\FileRecordingStore -> <room>/<peer>.webm.
welcome/joined carry a record flag telling the client to stream.
- Contracts\CallEventListener call-log seam (peer/room/recording lifecycle) so calls
are auditable even when unrecorded.
Wiring
- RelaySignalingHandler(authorizer, recorder, events); webrtc:serve + provider +
config/webrtc.php (recording enabled/path/format, authorizer, events).
Tests 35/99 incl on-disk FileRecordingStore, presence member lists, auth gating,
binary recording+finalize+events, RoomType, and the real WS round-trip. README
documents room types + the browser MediaRecorder->WS recording flow.
rel learn-atc #1060 #1055 #1057 #1051
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 07:01:12 +00:00
|
|
|
'enabled' => (bool) env('WEBRTC_RECORDING', false),
|
|
|
|
|
'path' => env('WEBRTC_RECORDING_PATH', storage_path('app/webrtc')),
|
|
|
|
|
'format' => env('WEBRTC_RECORDING_FORMAT', 'webm'),
|
2026-07-07 11:07:59 +00:00
|
|
|
],
|
|
|
|
|
|
2026-07-11 08:53:54 +00:00
|
|
|
/*
|
|
|
|
|
|--------------------------------------------------------------------------
|
|
|
|
|
| Backend-defined rooms (the room registry)
|
|
|
|
|
|--------------------------------------------------------------------------
|
|
|
|
|
| The rooms clients are allowed to join. A join to anything NOT listed here is
|
|
|
|
|
| rejected — clients can't invent rooms. Each room configures its transport:
|
|
|
|
|
| 'p2p' — browsers mesh, the server only relays signaling (no server media)
|
|
|
|
|
| 'sfu' — the server terminates media (the Rust SFU; enables recording/scale)
|
|
|
|
|
|
|
|
|
|
|
| Shapes (all equivalent granularity):
|
|
|
|
|
| 'just-talk' => 'p2p',
|
|
|
|
|
| 'lobby' => ['mode' => 'sfu', 'limit' => 50, 'type' => 'presence'],
|
|
|
|
|
| 'private-desk' => ['mode' => 'p2p'], // membership type inferred from prefix
|
|
|
|
|
|
|
|
|
|
|
| For DYNAMIC rooms (created/edited at runtime), point `room_registry` at your
|
|
|
|
|
| own Contracts\RoomRegistry (e.g. an Eloquent-backed one); the static `rooms`
|
|
|
|
|
| map below is the fallback used when no registry class is bound.
|
|
|
|
|
*/
|
|
|
|
|
'room_registry' => env('WEBRTC_ROOM_REGISTRY'),
|
|
|
|
|
|
|
|
|
|
'rooms' => [],
|
|
|
|
|
|
feat: full package — typed rooms (public/private/presence/open-presence) + call recording
Rooms
- Rooms\RoomType derived from the name prefix (like laravel-websockets channels):
public, private-* (authorized), presence-* (authorized + member list),
open-presence-* (public + member list). Room/RoomManager carry per-member info.
- Contracts\RoomAuthorizer + secure DefaultRoomAuthorizer (denies private/presence
until the host binds one). Presence rooms broadcast the member list + info.
Recording (store the FULL call audio, in PHP, today)
- Pure-P2P audio is DTLS-SRTP end-to-end, so the server can't capture it. Instead
the browser MediaRecords its mic and streams chunks over the WS as BINARY frames
(needs the new laravel-ws onBinary); RelaySignalingHandler appends them per
participant via a RecordingStore. Recording\FileRecordingStore -> <room>/<peer>.webm.
welcome/joined carry a record flag telling the client to stream.
- Contracts\CallEventListener call-log seam (peer/room/recording lifecycle) so calls
are auditable even when unrecorded.
Wiring
- RelaySignalingHandler(authorizer, recorder, events); webrtc:serve + provider +
config/webrtc.php (recording enabled/path/format, authorizer, events).
Tests 35/99 incl on-disk FileRecordingStore, presence member lists, auth gating,
binary recording+finalize+events, RoomType, and the real WS round-trip. README
documents room types + the browser MediaRecorder->WS recording flow.
rel learn-atc #1060 #1055 #1057 #1051
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 07:01:12 +00:00
|
|
|
/*
|
|
|
|
|
|--------------------------------------------------------------------------
|
|
|
|
|
| Room authorization + call-event log
|
|
|
|
|
|--------------------------------------------------------------------------
|
|
|
|
|
| `authorizer` gates private-* / presence-* joins (bind your own that checks a
|
|
|
|
|
| token / the authenticated user; the default DENIES them). `events` receives
|
|
|
|
|
| call-lifecycle events so you can log the call (bind your own to persist).
|
|
|
|
|
*/
|
|
|
|
|
'authorizer' => env('WEBRTC_AUTHORIZER', DefaultRoomAuthorizer::class),
|
|
|
|
|
'events' => env('WEBRTC_EVENTS', NullCallEventListener::class),
|
|
|
|
|
|
2026-07-08 09:22:53 +00:00
|
|
|
/*
|
|
|
|
|
|--------------------------------------------------------------------------
|
|
|
|
|
| Room membership store
|
|
|
|
|
|--------------------------------------------------------------------------
|
|
|
|
|
| Where room membership + presence lives. The default ArrayRoomStore keeps it
|
|
|
|
|
| in process memory (right for the long-lived bundled server). A fork-per-
|
|
|
|
|
| message host (a classic Laravel WebSocket handling each frame in a fresh
|
|
|
|
|
| worker) binds a Cache/Redis-backed RoomStore so state survives across
|
|
|
|
|
| workers — that is how a host runs this relay on its existing WS.
|
|
|
|
|
*/
|
feat: Rust SFU data plane — backend-hosted rooms, plug-and-play sidecar binary
rust/ becomes a standalone static binary (blax-webrtc-sfu, tokio-free
std-thread loop on str0m): terminates ICE/DTLS/SRTP, forwards Opus between
room peers without decoding, records per-peer Ogg/Opus (ffprobe-verified),
server-side mute/kick, stats + peer_connected/peer_left usage events, all
driven by PHP over a JSON-lines Unix control socket. Renegotiation rides the
browser data channel (str0m chat model) — signaling only carries the initial
offer/answer.
PHP side: RustMediaEngine (MediaEngine contract + room-aware API) is
plug-and-play — first use downloads the sha256-verified prebuilt binary
(BinaryManager, RoadRunner pattern) and spawns it detached, flock-guarded
(SidecarSupervisor). webrtc:install / webrtc:sidecar provided for explicit
control; config gains the webrtc.sfu block. Replaces the ext-php-rs
Str0mMediaEngine seam.
Tests: 61 PHP (incl. a real PHP<->Rust E2E that spawns the sidecar from
PHPUnit) + 7 Rust (incl. an end-to-end control-socket integration test).
build-release.sh produces the static-musl release + .sha256 (verified:
static-pie, 9.2MB, runs).
Card #1083 (rel #1051 #1060 #1064).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 09:29:26 +00:00
|
|
|
'room_store' => env('WEBRTC_ROOM_STORE', ArrayRoomStore::class),
|
2026-07-08 09:22:53 +00:00
|
|
|
|
2026-07-07 11:07:59 +00:00
|
|
|
/*
|
|
|
|
|
|--------------------------------------------------------------------------
|
|
|
|
|
| ICE servers (STUN/TURN) advertised to browsers
|
|
|
|
|
|--------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
'ice_servers' => [
|
|
|
|
|
// ['urls' => 'stun:stun.l.google.com:19302'],
|
|
|
|
|
// ['urls' => 'turn:turn.example.com:3478', 'username' => '...', 'credential' => '...'],
|
|
|
|
|
],
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|--------------------------------------------------------------------------
|
|
|
|
|
| External realtime bridge (provider hidden from the browser)
|
|
|
|
|
|--------------------------------------------------------------------------
|
2026-07-08 09:22:53 +00:00
|
|
|
| OpenAiRealtimeBridge (or the media engine) dials these; the browser only ever
|
|
|
|
|
| talks to us, so the AI provider is invisible and the model is swappable
|
|
|
|
|
| server-side. `instructions` is the persona (the host injects its own — e.g.
|
|
|
|
|
| an ATC prompt) and `voice` the spoken voice.
|
2026-07-07 11:07:59 +00:00
|
|
|
*/
|
|
|
|
|
'bridge' => [
|
|
|
|
|
'openai' => [
|
2026-07-08 09:22:53 +00:00
|
|
|
'endpoint' => env('WEBRTC_OPENAI_ENDPOINT', 'wss://api.openai.com/v1/realtime'),
|
2026-07-07 11:07:59 +00:00
|
|
|
'model' => env('WEBRTC_OPENAI_MODEL', 'gpt-realtime'),
|
|
|
|
|
'api_key' => env('OPENAI_API_KEY'),
|
2026-07-08 09:22:53 +00:00
|
|
|
'voice' => env('WEBRTC_OPENAI_VOICE', 'alloy'),
|
|
|
|
|
'instructions' => null,
|
2026-07-07 11:07:59 +00:00
|
|
|
],
|
|
|
|
|
],
|
|
|
|
|
];
|