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}