Skip to content

feat(call): SFU relay support via optional backend - #159

Draft
HexaField wants to merge 1 commit into
devfrom
feat/sfu-integration
Draft

feat(call): SFU relay support via optional backend#159
HexaField wants to merge 1 commit into
devfrom
feat/sfu-integration

Conversation

@HexaField

Copy link
Copy Markdown
Contributor

What

Adds SFU (Selective Forwarding Unit) support to the call module as an opt-in backend. When a host provides an SfuBackend through the store's deps, the call connects through a relay server instead of building N−1 direct peer connections.

The peer-to-peer mesh remains the default and the fallback — the SFU path activates only when a backend appears in deps, and falls back to mesh automatically if the SFU join fails.

Why

The mesh topology scales to about 4–6 participants before upstream bandwidth saturates. An SFU receives each participant's media once and forwards it selectively, shifting the bottleneck from the weakest uplink to the relay's bandwidth. This unblocks larger calls without changing anything about smaller ones.

How

New: sfu.ts

  • SfuBackend — structural interface satisfied by SfuManager from @coasys/ad4m without importing it. The host constructs the manager (with neighbourhood proxy, room id, agent DID, ICE config) and passes it in. The call module carries no dependency on @coasys/ad4m.
  • createCallSfu() — adapter that wraps an SfuBackend behind the existing CallMesh interface. Joins the SFU, maps participant events to stream/state callbacks, and handles track replacement on the single RTCPeerConnection.

Modified: store.ts

  • sfuBackend?: SfuBackend added to CallStoreDeps
  • topology signal ('mesh' | 'sfu') — exposes the active topology for the template
  • qualityPreference signal + setQualityPreference() action — simulcast layer selection ('high' / 'medium' / 'low'), only meaningful in SFU mode
  • join() branches: SFU path acquires media first (the SFU needs tracks for the SDP offer), then joins the relay; mesh path unchanged
  • Automatic mesh fallback on SFU join failure
  • Proper teardown of SFU state

Modified: index.ts

  • Exports: createCallSfu, SfuBackend, SfuQuality, CallTopology
  • Updated module description

Integration

A host wiring up SFU support:

import { SfuManager, resolveTopology } from '@coasys/ad4m';

// Resolve topology from the neighbourhood's SFU config
const { topology, sfuPeer, config } = await resolveTopology(
  neighbourhood, neighbourhoodUrl, participantCount
);

// If SFU, construct the manager and pass it as the backend
const sfuBackend = topology !== 'mesh'
  ? new SfuManager(neighbourhood, roomName, agentDid, neighbourhoodUrl, undefined, config)
  : undefined;

const store = createCallStore({ ...deps, sfuBackend });

What the user sees

  • Topology indicator: store.topology() returns 'mesh' or 'sfu' — the host decides how to render this
  • Quality preference: store.setQualityPreference('medium') — the host adds a toggle if desired
  • Everything else (tiles, focus, media, screen share) works identically

Testing

  • All 70 existing tests pass (mesh path unchanged)
  • Type-check clean
  • Lint clean (lint-staged ran automatically)

🤖 Generated with Claude Code

@netlify

netlify Bot commented Aug 26, 2026

Copy link
Copy Markdown

Deploy Preview for coasys-we ready!

Name Link
🔨 Latest commit 216ec28
🔍 Latest deploy log https://app.netlify.com/projects/coasys-we/deploys/6a8f9b2284f2da000822a37f
😎 Deploy Preview https://deploy-preview-159--coasys-we.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

Add an SFU adapter that wraps any SfuManager-compatible backend behind
the existing CallMesh interface. The store branches on an optional
sfuBackend in deps — when provided, the call connects through the SFU
relay instead of building N-1 direct peer connections.

New files:
  sfu.ts — SfuBackend structural interface + createCallSfu() adapter

Store changes:
  - sfuBackend?: SfuBackend in CallStoreDeps
  - topology signal (mesh/sfu) for the template to surface mode
  - qualityPreference signal + setQualityPreference action (high/med/low)
  - SFU join with automatic mesh fallback on failure
  - Proper teardown of SFU state

Exports:
  - createCallSfu, SfuBackend, SfuQuality, CallTopology

The SfuBackend interface matches SfuManager from @coasys/ad4m without
importing it — the host constructs the manager and passes it in.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@HexaField
HexaField force-pushed the feat/sfu-integration branch from 8b01954 to 216ec28 Compare August 27, 2026 02:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant