# Design

The crate follows three layers:

1. **Signaling layer** produces canonical JSON payloads (`SignalingMessage`) through `encode_signaling` and `decode_signaling`. Validation rejects empty SDP bodies or malformed candidates to keep transport-agnostic adapters simple.
2. **Session layer** wraps the validated signaling primitives inside `CallSession`. It keeps the authoritative `CallState`, exposes imperative methods for offers/answers/ICE, and pushes `RtcEvent` items into an async queue consumed by the application.
3. **Engine layer** defines the `RtcEngine` trait. The default `WebRtcEngine` bridges Enigma's deterministic state machine with `webrtc-rs`, emitting local ICE candidates, tracking media toggles, and validating SDP through the underlying library.

The layers communicate only through strongly typed messages:

- `CallSession` never performs networking; instead, it requests SDP or ICE operations from an `RtcEngine` implementation.
- `RtcEngine` never mutates session state; it pushes `RtcEvent::LocalIceCandidate` messages via a shared channel, while state transitions remain the responsibility of `CallSession`.

Because the engine is behind a trait, downstream projects can supply their own implementations (for example, a native mobile stack or a deterministic simulator) without touching the signaling/state machine code.
