Skip to main content

rvoip_core/
lib.rs

1//! # rvoip-core
2//!
3//! Transport-agnostic spine for RVoIP. Carries the voip-3 vocabulary
4//! ([`Conversation`], [`Session`], [`Connection`], [`MediaStream`], [`Message`],
5//! [`Participant`]), the [`ConnectionAdapter`] trait that adapter crates implement,
6//! the cross-transport [`BridgeManager`], and the [`Orchestrator`] entry point.
7//!
8//! See `INTERFACE_DESIGN.md` §3, §6, §7.5, §10.2 for the canonical design.
9//!
10//! ## Two-layer surface
11//!
12//! - **Pure-SIP consumers** (carriers, SIP softphones, SIP-only B2BUAs) stay on
13//!   `rvoip_sip::api::*` and `rvoip_sip::server::*` — they don't touch this
14//!   crate. See `rvoip-sip` for that surface.
15//! - **Cross-transport consumers** (Thelve, future CPaaS, anyone planning to
16//!   add WebRTC / QUIC adapters) build a [`Orchestrator`] here and register
17//!   adapters against it. SIP is one such adapter today (`rvoip_sip::SipAdapter`);
18//!   `rvoip-webrtc` and `rvoip-quic` register against the same handle when
19//!   they ship.
20//!
21//! ## Cross-transport entry point
22//!
23//! ```rust,no_run
24//! use rvoip_core::{Config, Orchestrator, Transport};
25//! # use std::sync::Arc;
26//! # struct MyAdapter;
27//! # #[async_trait::async_trait]
28//! # impl rvoip_core::ConnectionAdapter for MyAdapter {
29//! #     fn transport(&self) -> Transport { Transport::Sip }
30//! #     fn kind(&self) -> rvoip_core::AdapterKind { rvoip_core::AdapterKind::Interop }
31//! #     async fn originate(&self, _: rvoip_core::OriginateRequest) -> rvoip_core::Result<rvoip_core::ConnectionHandle> { unimplemented!() }
32//! #     async fn accept(&self, _: rvoip_core::ConnectionId) -> rvoip_core::Result<()> { Ok(()) }
33//! #     async fn reject(&self, _: rvoip_core::ConnectionId, _: rvoip_core::RejectReason) -> rvoip_core::Result<()> { Ok(()) }
34//! #     async fn end(&self, _: rvoip_core::ConnectionId, _: rvoip_core::EndReason) -> rvoip_core::Result<()> { Ok(()) }
35//! #     async fn hold(&self, _: rvoip_core::ConnectionId) -> rvoip_core::Result<()> { Ok(()) }
36//! #     async fn resume(&self, _: rvoip_core::ConnectionId) -> rvoip_core::Result<()> { Ok(()) }
37//! #     async fn transfer(&self, _: rvoip_core::ConnectionId, _: rvoip_core::TransferTarget) -> rvoip_core::Result<()> { Ok(()) }
38//! #     async fn streams(&self, _: rvoip_core::ConnectionId) -> rvoip_core::Result<Vec<Arc<dyn rvoip_core::MediaStream>>> { Ok(vec![]) }
39//! #     async fn send_message(&self, _: rvoip_core::ConnectionId, _: rvoip_core::Message) -> rvoip_core::Result<()> { Ok(()) }
40//! #     async fn send_dtmf(&self, _: rvoip_core::ConnectionId, _: &str, _: u32) -> rvoip_core::Result<()> { Ok(()) }
41//! #     async fn renegotiate_media(&self, _: rvoip_core::ConnectionId, _: rvoip_core::CapabilityDescriptor) -> rvoip_core::Result<rvoip_core::NegotiatedCodecs> { Ok(Default::default()) }
42//! #     fn subscribe_events(&self) -> tokio::sync::mpsc::Receiver<rvoip_core::AdapterEvent> { tokio::sync::mpsc::channel(1).1 }
43//! #     fn capabilities(&self) -> rvoip_core::CapabilityDescriptor { Default::default() }
44//! #     async fn verify_request_signature(&self, _: rvoip_core::ConnectionId, _: rvoip_core::SignatureHeaders) -> rvoip_core::Result<rvoip_core::IdentityAssurance> { Ok(rvoip_core::IdentityAssurance::Anonymous) }
45//! # }
46//! # async fn example(my_adapter: Arc<MyAdapter>) -> rvoip_core::Result<()> {
47//! let orchestrator = Orchestrator::new(Config::default());
48//! orchestrator.register(my_adapter)?;
49//!
50//! let mut events = orchestrator.subscribe_events();
51//! // events.recv() yields normalized rvoip-core Events (Connection*, Session*,
52//! // Conversation*) translated from each adapter's protocol-native AdapterEvents.
53//! # let _ = events;
54//! # Ok(())
55//! # }
56//! ```
57//!
58//! [`Orchestrator::register`] dispatches every per-connection command
59//! ([`Orchestrator::route_inbound_connection`], [`Orchestrator::prepare_outbound_connection`],
60//! [`PreparedOutboundConnection::commit`], [`Orchestrator::originate_connection`],
61//! [`Orchestrator::end_connection`], [`Orchestrator::hold`], [`Orchestrator::resume`],
62//! [`Orchestrator::transfer_connection`], [`Orchestrator::send_dtmf`],
63//! [`Orchestrator::mute`], [`Orchestrator::unmute`],
64//! [`Orchestrator::play_audio`]) through the adapter for the
65//! connection's [`Transport`]. Cross-transport bridging
66//! (cross-codec frame-pump per `INTERFACE_DESIGN.md` §10.2) is fully
67//! wired, including hot-swap on `renegotiate_media` and DTMF auto-
68//! route across legs.
69//!
70//! ## Conversation / Session / Participant lifecycle
71//!
72//! Beyond per-Connection dispatch, the Orchestrator owns live
73//! Conversation/Session/Participant state. See
74//! [`Orchestrator::open_conversation`], [`Orchestrator::start_session`],
75//! [`Orchestrator::join_session`], [`Orchestrator::leave_session`],
76//! [`Orchestrator::end_session`], [`Orchestrator::close_conversation`]
77//! and the cross-substrate messaging methods
78//! [`Orchestrator::send_message_to_conversation`] +
79//! [`Orchestrator::list_messages`] + [`Orchestrator::mark_message_read`].
80//!
81//! ## vCon, recording, transcription, AI harness
82//!
83//! With the `vcon` feature enabled, every Session gets a
84//! [`DefaultVconBuilder`] auto-bound at `start_session`; on `end_session` the
85//! snapshot is encoded, persisted via [`VconStore`], and emitted as
86//! `Event::VconReady`. Recording and transcription dispatch via
87//! consumer-registered providers
88//! ([`Orchestrator::register_recording_sink`],
89//! [`Orchestrator::register_asr_provider`]); the AI harness path
90//! includes barge-in support (`Event::BargeInDetected`).
91//!
92//! ## Tenant scoping, capacity, observability
93//!
94//! [`Config::TenantQuotas`](crate::config::TenantQuotas) enforces
95//! per-tenant maxes on concurrent sessions, recordings, and AI
96//! attachments. Periodic emit cadences live in
97//! [`Orchestrator::spawn_capacity_scheduler`],
98//! [`Orchestrator::spawn_media_quality_sampler`], and
99//! [`Orchestrator::spawn_idle_closer`] (Ephemeral Conversation
100//! close-after-idle driver).
101//!
102//! See `examples/sip_only_orchestrator.rs` for the working SIP-adapter
103//! flow end-to-end, and [`GAP_PLAN.md`](../../docs/GAP_PLAN.md)
104//! for the phased-roadmap status.
105//!
106//! ## Layering rule
107//!
108//! Per `INTERFACE_DESIGN.md` §18: this crate never imports an adapter crate.
109//! Adapters depend on `rvoip-core`, not the other way round. The only
110//! external dep that's transport-flavored is `rvoip-media-core` (a common
111//! crate, not an adapter), used by [`bridge`] for the SIP-fast-path bridge
112//! handle.
113
114pub mod adapter;
115pub mod bridge;
116pub mod broadcast;
117pub mod capability;
118pub mod commands;
119pub mod config;
120pub mod connection;
121pub mod conversation;
122pub mod error;
123pub mod events;
124pub mod harness;
125pub mod identity;
126pub mod ids;
127pub mod inbound_admission;
128pub mod media_graph;
129pub mod message;
130pub mod operational_events;
131pub mod orchestrator;
132pub mod participant;
133pub mod session;
134pub mod signing;
135pub mod store;
136pub mod stream;
137pub mod subscriptions;
138pub mod vcon;
139pub mod virtual_publisher;
140
141pub use adapter::{
142    AdapterEvent, AdapterKind, ConnectionAdapter, ConnectionHandle, EndReason,
143    ExternalConnectionReference, ExternalConnectionReferenceError, InboundConnectionContext,
144    InboundContextError, InboundRoutingHint, InboundSignalingMetadata, OriginateContext,
145    OriginateRequest, OutboundActivation, PlaybackHandle, RejectReason, SignatureHeaders,
146    TransferAttemptId, TransferStatus, TransferTarget, MAX_EXTERNAL_CONNECTION_REFERENCES,
147    MAX_EXTERNAL_REFERENCE_KIND_BYTES, MAX_EXTERNAL_REFERENCE_VALUE_BYTES,
148    MAX_INBOUND_ROUTING_HINT_BYTES,
149};
150pub use bridge::{BridgeError, BridgeHandle, BridgeManager, DirectionalMediaBridgePlan};
151pub use broadcast::{
152    BroadcastDescriptor, BroadcastDrainDescriptor, BroadcastDrainReason, BroadcastDrainRequest,
153    BroadcastDrainState, BroadcastEndpoint, BroadcastHealthDescriptor, BroadcastHealthIssue,
154    BroadcastHealthStatus, BroadcastLifecycleDescriptor, BroadcastLifecycleState,
155    BroadcastProtocolDescriptor, BroadcastProtocolFamily, BroadcastPublisher, BroadcastRelayHop,
156    BroadcastRelayRole, BroadcastResource, BroadcastSanitizedEvent,
157    BroadcastSanitizedEventCapability, BroadcastSanitizedEventError, BroadcastSanitizedEventKind,
158    BroadcastSubstrate, BroadcastTransport, MAX_BROADCAST_EVENT_JSON_INTEGER,
159};
160pub use capability::{CapabilityDescriptor, CapabilityIntersection, CodecInfo, NegotiatedCodecs};
161pub use commands::{
162    AttachmentRef, AudioSource, Command, InboundAction, ListenerSink, ListenerTarget,
163    MuteDirection, RecordingSink, RecordingTarget,
164};
165pub use config::Config;
166pub use connection::{Connection, ConnectionState, Direction, Transport};
167pub use conversation::{Conversation, ConversationPolicy, ConversationState};
168pub use error::{Result, RvoipError};
169pub use events::{AnomalyKind, ConnectionProgressKind, Event, SessionQualityReport, UsageKind};
170pub use identity::{
171    AuthenticatedPrincipal, AuthenticationMethod, BearerAuthError, Credential, CredentialKind,
172    Device, DtlsFingerprint, Identity, IdentityAssurance, IdentityProvider, Jwk,
173    PrincipalOwnershipKey,
174};
175pub use ids::{
176    AiAttachmentId, AttachmentId, BridgeId, ConnectionId, ConversationId, DeviceId, IdentityId,
177    ListenerId, MediaRouteId, MessageId, ParticipantId, PlaybackId, RecordingId, SessionId,
178    StreamId, TenantId, TranscriptionId,
179};
180pub use inbound_admission::{
181    InboundAdmission, InboundAdmissionTermination, ProvisionalMediaRoute, StagedInboundDataChannel,
182    StagedInboundDataPolicy, StagedInboundDataReceiver, StagedInboundDataSender,
183    MAX_STAGED_INBOUND_DATA_CAPACITY, MAX_STAGED_INBOUND_DATA_LABELS,
184};
185pub use media_graph::{
186    start_media_graph, MediaGraphActivityObservation, MediaGraphHandle, MediaGraphPolicy,
187    DEFAULT_MEDIA_GRAPH_MAX_SINKS, MEDIA_GRAPH_ACTIVITY_OBSERVATION_INTERVAL,
188};
189pub use message::{ContentType, Message, MessageOrigin, MessageRecipients};
190pub use operational_events::{
191    OperationalEndReason, OperationalEvent, OperationalEventKind, OperationalEventStreamHealth,
192    OperationalEventStreamHealthSubscription, OperationalFailureReason, OperationalTransferOutcome,
193    OperationalTransferTarget,
194};
195pub use orchestrator::{Orchestrator, PreparedOutboundConnection};
196pub use participant::{Participant, ParticipantKind, ParticipantRole};
197pub use rvoip_core_traits::data::{
198    DataMessage, DataMessageValidationError, DataReliability, MAX_CONTENT_TYPE_BYTES,
199    MAX_DATA_LABEL_BYTES, MAX_DATA_MESSAGE_BYTES, MAX_DATA_MESSAGE_ID_BYTES,
200};
201pub use session::{Session, SessionMedium, SessionState};
202pub use store::{
203    ConversationFilter, ConversationStore, MemoryConversationStore, MemoryMessageStore,
204    MemoryVconStore, MessageFilter, MessagePage, MessageStore, PageCursor, VconStore,
205};
206pub use stream::{
207    MediaFrame, MediaReceiverReservation, MediaStream, MediaStreamHandle, QualitySnapshot,
208    StreamKind,
209};
210pub use vcon::{
211    DefaultVconBuilder, VconAnalysis, VconAnalysisKind, VconAttachment, VconBuilderHandle,
212    VconDialog, VconDialogKind, VconParty, VconRef, VconSnapshot,
213};
214pub use virtual_publisher::{
215    ManagedVirtualPublisher, VirtualPublisherDescriptor, DEFAULT_VIRTUAL_PUBLISHER_QUEUE_CAPACITY,
216};
217
218// V2.A.8 — when `vcon-signing` is enabled, re-export the
219// `rvoip-vcon` crate's surface so consumers can explicitly sign
220// vCons without adding rvoip-vcon as a separate Cargo dependency.
221// Core's automatic end-session emission remains unsigned.
222#[cfg(feature = "vcon-signing")]
223pub mod signed_vcon {
224    //! V2.A.8 — feature-gated re-export of `rvoip-vcon` so consumers
225    //! enabling `vcon-signing` get the signing surface from a single
226    //! dep on rvoip-core.
227    pub use rvoip_vcon::*;
228}