plexus_rpc/lib.rs
1//! # Plexus RPC — umbrella crate
2//!
3//! The single dependency to add when building a Plexus backend. Re-exports
4//! the verified-compatible subcrate set:
5//!
6//! - [`auth_core`] — sealed identity, credential, tenant, forwarding, and audit
7//! primitives (`plexus-auth-core`).
8//! - [`core`] — `DynamicHub`, dispatch, `MethodSchema`, the `Activation` trait,
9//! credential wire envelope, ChildRouter (`plexus-core`).
10//! - [`macros`] — procedural macros for activations, methods, child routers,
11//! credentials, and auth resolvers (`plexus-macros`).
12//! - [`transport`] (feature `transport`, on by default) — WebSocket / HTTP / SSE
13//! server runtime (`plexus-transport`).
14//!
15//! ## Why one crate
16//!
17//! Tracking three or four subcrate version bumps across coordinated releases is
18//! error-prone. Depending on `plexus-rpc` pins the whole set at once:
19//!
20//! ```toml
21//! [dependencies]
22//! plexus-rpc = "0.1"
23//! ```
24//!
25//! ## Capability manifest
26//!
27//! Backends embed [`CAPABILITIES`] in their `_info` response so generic clients
28//! (synapse CLI, generated SDKs, agents) can negotiate features instead of
29//! guessing from version strings. The manifest names every bundled subcrate
30//! version plus a list of named feature flags shipped in this release. See
31//! `plans/UMB/UMB-3.md` for the full design.
32//!
33//! ## Doc-comment example (current canonical shape)
34//!
35//! ```ignore
36//! use plexus_rpc::macros::{activation, method};
37//!
38//! pub struct Echo;
39//!
40//! #[activation(namespace = "echo", version = "1.0.0")]
41//! impl Echo {
42//! /// Echo a message back the specified number of times.
43//! #[method]
44//! async fn echo(
45//! &self,
46//! /// The message to echo
47//! message: String,
48//! /// Number of times to repeat
49//! count: u32,
50//! ) -> impl futures::Stream<Item = EchoEvent> + Send + 'static {
51//! async_stream::stream! {
52//! for _ in 0..count {
53//! yield EchoEvent::Echo { message: message.clone() };
54//! }
55//! }
56//! }
57//! }
58//! ```
59
60/// Plexus RPC umbrella crate version, populated at compile time.
61pub const VERSION: &str = env!("CARGO_PKG_VERSION");
62
63// ---------------------------------------------------------------------------
64// Subcrate re-exports.
65// ---------------------------------------------------------------------------
66
67/// Sealed identity, credential, tenant, forwarding, and audit primitives.
68///
69/// Re-export of the `plexus-auth-core` crate. The types here are
70/// structurally sealed — third crates cannot fabricate `AuthContext`,
71/// `Principal`, `Credential<T>`, `Tenant`, etc. Construction is the
72/// framework's responsibility; activation code only ever receives sealed
73/// references.
74pub use plexus_auth_core as auth_core;
75
76/// Core dispatch and schema machinery.
77///
78/// Re-export of the `plexus-core` crate. Contains `DynamicHub`,
79/// `Activation`, `MethodSchema`, the credential wire envelope, the
80/// `ChildRouter` trait, and the `with_auth_capabilities` / `with_forward_policy`
81/// hub builders.
82pub use plexus_core as core;
83
84/// Procedural macros for activations, methods, child routers, credentials,
85/// and auth resolvers.
86///
87/// Re-export of the `plexus-macros` crate. The canonical entry points are
88/// `#[plexus_rpc::macros::activation(...)]` on an `impl` block,
89/// `#[plexus_rpc::macros::method]` on each method, and
90/// `#[derive(plexus_rpc::macros::Credentials)]` on credential-bearing
91/// return types.
92pub use plexus_macros as macros;
93
94/// WebSocket / HTTP / SSE server runtime (feature `transport`, default on).
95///
96/// Re-export of the `plexus-transport` crate. Drop the `transport` feature
97/// when building embedded / ahead-of-time codegen / WASM consumers that
98/// only need the type and dispatch surface.
99#[cfg(feature = "transport")]
100pub use plexus_transport as transport;
101
102// ---------------------------------------------------------------------------
103// Capability manifest (UMB-3).
104// ---------------------------------------------------------------------------
105
106/// Compile-time capability manifest describing the subcrate versions and
107/// feature flags bundled in this release of `plexus-rpc`.
108///
109/// Backends embed [`CAPABILITIES`] in their `_info` response. Synapse +
110/// synapse-cc decode it and branch on `features` rather than version
111/// strings, so adding a new feature is one entry on this struct and one
112/// branch in tooling — no version-string parsing involved.
113///
114/// `Capabilities` is **emit-only** — it holds `&'static` slices so it can
115/// be a `const`, which precludes `Deserialize`. Consumers should mirror
116/// the wire shape in their own owning struct.
117#[derive(Debug, Clone, Copy, serde::Serialize)]
118pub struct Capabilities {
119 /// Version of the `plexus-rpc` umbrella itself.
120 pub plexus_rpc: &'static str,
121 /// Bundled `plexus-auth-core` version.
122 pub plexus_auth_core: &'static str,
123 /// Bundled `plexus-core` version.
124 pub plexus_core: &'static str,
125 /// Bundled `plexus-macros` version. `plexus-macros` is a proc-macro
126 /// crate and cannot expose a `pub const VERSION`, so the umbrella's
127 /// build script reads the dependency pin from `Cargo.toml` and
128 /// populates this at compile time.
129 pub plexus_macros: &'static str,
130 /// Bundled `plexus-transport` version. `None` when the umbrella was
131 /// built without the `transport` feature.
132 pub plexus_transport: Option<&'static str>,
133 /// Named feature flags shipped in this release. Tooling branches on
134 /// these instead of parsing version strings.
135 pub features: &'static [&'static str],
136}
137
138/// The canonical compile-time capability manifest for this build of
139/// `plexus-rpc`.
140///
141/// Backends embed this in their `_info` payload so tooling (synapse,
142/// synapse-cc, generated SDKs) can branch on capability rather than
143/// inferring features from subcrate version strings.
144pub const CAPABILITIES: Capabilities = Capabilities {
145 plexus_rpc: VERSION,
146 plexus_auth_core: plexus_auth_core::VERSION,
147 plexus_core: plexus_core::VERSION,
148 plexus_macros: env!("PLEXUS_MACROS_VERSION"),
149 #[cfg(feature = "transport")]
150 plexus_transport: Some(plexus_transport::VERSION),
151 #[cfg(not(feature = "transport"))]
152 plexus_transport: None,
153 features: FEATURES,
154};
155
156/// The named feature flags this release of `plexus-rpc` advertises. Tooling
157/// (synapse, synapse-cc, generated SDKs) branches on these strings rather
158/// than parsing version numbers. New features get added to this list as
159/// they land.
160///
161/// Current entries reflect wave 2 (May 2026) of the AUTHZ epic:
162///
163/// - `"auth_capabilities"` — backend advertises auth mechanisms at `_info`
164/// (AUTHZ-CORE-3).
165/// - `"forward_policy"` — per-namespace `ForwardPolicy` consulted on every
166/// cross-boundary call (AUTHLANG-2 + AUTHLANG-3).
167/// - `"credentials"` — `Credential<T>` wire envelope (sentinel + sidecar)
168/// with `MethodSchema` projection (AUTHZ-CRED-CORE-1, -2, -3 +
169/// `#[derive(Credentials)]`).
170/// - `"tenant"` — sealed `Tenant` identity + `ClaimTenantResolver` +
171/// `Tenanted<S>` storage wrapper (AUTHZ-DATA-1-TYPES + WRAPPER).
172/// - `"audit"` — `AuditRecord` + `AuditSink` primitives + `TracingAuditSink`
173/// default (AUTHZ-PRIVACY-1).
174/// - `"doc_comments"` — `///` on function and parameters feeds the schema
175/// (AUTHZ-MACRO-PARAM-DOC-1).
176/// - `"optional_auth"` — `auth: Option<&AuthContext>` codegen support
177/// (AUTHZ-MACRO-OPTIONAL-AUTH-1).
178const FEATURES: &[&str] = &[
179 "auth_capabilities",
180 "forward_policy",
181 "credentials",
182 "tenant",
183 "audit",
184 "doc_comments",
185 "optional_auth",
186];
187
188#[cfg(test)]
189mod tests {
190 use super::*;
191
192 #[test]
193 fn version_const_is_populated() {
194 assert!(!VERSION.is_empty());
195 // Must match what's pinned in our Cargo.toml.
196 assert_eq!(VERSION, env!("CARGO_PKG_VERSION"));
197 }
198
199 #[test]
200 fn capabilities_const_reflects_subcrate_versions() {
201 assert_eq!(CAPABILITIES.plexus_rpc, VERSION);
202 assert_eq!(CAPABILITIES.plexus_auth_core, plexus_auth_core::VERSION);
203 assert_eq!(CAPABILITIES.plexus_core, plexus_core::VERSION);
204 assert_eq!(CAPABILITIES.plexus_macros, env!("PLEXUS_MACROS_VERSION"));
205 assert!(!CAPABILITIES.features.is_empty());
206 }
207
208 #[test]
209 fn capabilities_serializes_to_expected_json_keys() {
210 // Capabilities is emit-only (const, &'static slices). Verify the
211 // wire shape carries every field a consumer would expect — they
212 // mirror it into an owning struct of their own.
213 let v = serde_json::to_value(CAPABILITIES).expect("capabilities serialize");
214 for key in [
215 "plexus_rpc",
216 "plexus_auth_core",
217 "plexus_core",
218 "plexus_macros",
219 "plexus_transport",
220 "features",
221 ] {
222 assert!(
223 v.get(key).is_some(),
224 "wire shape must carry key {} so consumers can mirror it",
225 key
226 );
227 }
228 }
229
230 #[test]
231 #[cfg(feature = "transport")]
232 fn transport_version_is_present_when_feature_is_on() {
233 assert!(CAPABILITIES.plexus_transport.is_some());
234 assert_eq!(
235 CAPABILITIES.plexus_transport,
236 Some(plexus_transport::VERSION)
237 );
238 }
239
240 #[test]
241 #[cfg(not(feature = "transport"))]
242 fn transport_version_is_absent_when_feature_is_off() {
243 assert!(CAPABILITIES.plexus_transport.is_none());
244 }
245
246 #[test]
247 fn features_list_is_stable() {
248 // Document the v0.1 feature set so a casual reader of the test file
249 // sees what shipped. New features get appended here as they land.
250 for required in [
251 "auth_capabilities",
252 "forward_policy",
253 "credentials",
254 "tenant",
255 "audit",
256 "doc_comments",
257 "optional_auth",
258 ] {
259 assert!(
260 CAPABILITIES.features.contains(&required),
261 "feature flag {} missing from CAPABILITIES.features",
262 required
263 );
264 }
265 }
266}