enigma-sfu 0.2.0

In-memory SFU core for rooms, participants, tracks, and subscriptions.
Documentation
# Enigma SFU

Generic in-memory SFU core for managing rooms, participants, tracks, and subscriptions. It focuses on deterministic state only; media forwarding and signaling transports are left to adapters you plug in.

## Highlights

- Pure in-memory state machine with validated identifiers
- Room, participant, track, and subscription management with event hooks
- No hard participant or track caps; callers enforce policy
- Transport adapter trait to bridge WebRTC or any media layer later

## Quickstart

```rust
use enigma_sfu::{ParticipantId, ParticipantMeta, RoomId, Sfu, TrackKind, VecEventSink};

fn main() -> Result<(), enigma_sfu::SfuError> {
    let sink = VecEventSink::new();
    let sfu = Sfu::new(sink);
    let room = RoomId::new("demo")?;
    let alice = ParticipantId::new("alice")?;
    let bob = ParticipantId::new("bob")?;
    sfu.create_room(room.clone(), 0)?;
    sfu.join(room.clone(), alice.clone(), ParticipantMeta::default(), 1)?;
    sfu.join(room.clone(), bob.clone(), ParticipantMeta::default(), 2)?;
    let track = sfu.publish_track(room.clone(), alice.clone(), TrackKind::Audio, None, 3)?;
    sfu.subscribe(room.clone(), bob.clone(), track.clone())?;
    sfu.unsubscribe(room.clone(), bob.clone(), track)?;
    sfu.leave(room.clone(), bob)?;
    sfu.leave(room, alice)?;
    Ok(())
}
```

## Events and adapters

Use `EventSink` to capture room events (for logging, metrics, or async dispatch). Implement `SfuTransportAdapter` to react when tracks are published or subscriptions change so a media stack can wire transports.

## Compatibility

Legacy types and engine wrappers remain available for callers that relied on earlier interfaces, but the core API is `Sfu`, `RoomId`/`ParticipantId`/`TrackId`, and the event-driven state machine shown above.