mosaik 0.4.2

A Rust runtime for building self-organizing, leaderless distributed systems.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
//! # Functions
//!
//! Leaderless remote procedure calls. A [`Caller`](caller::Caller) invokes
//! a function identified by a [`FunctionId`] without knowing which peer
//! serves it — the network discovers peers that registered a
//! [`Handler`](handler::Handler) for that function through the
//! [`discovery`](crate::discovery) subsystem, connects to the best
//! candidate, invokes the function, and returns the typed result.
//!
//! Handlers advertise the functions they serve in their signed catalog
//! entry. Callers rank eligible handler peers by measured round-trip time
//! and automatically fail over to the next candidate on transport
//! failures. Application-level errors returned by the function are typed
//! end-to-end and never trigger failover.
//!
//! Both sides can authorize their counterparty with ticket validators and
//! arbitrary peer predicates. Authorization is part of the function
//! identity: the validators' signatures are folded into the effective wire
//! id, so two functions with different authorization rules are different
//! functions on the network.
use {
	crate::{
		discovery::{Discovery, PeerEntry},
		network::{
			self,
			LocalNode,
			ProtocolProvider,
			link::{self, Protocol},
		},
		primitives::{Datum, Digest, UniqueId},
		tickets::TicketValidator,
	},
	accept::Acceptor,
	handler::Registry,
	iroh::protocol::RouterBuilder,
	std::sync::Arc,
};

mod accept;
pub mod caller;
mod config;
pub mod handler;
pub mod status;

pub use {
	caller::{CallError, Caller},
	config::{Config, ConfigBuilder, ConfigBuilderError},
	handler::Handler,
};

/// A unique identifier for a callable function within the Mosaik network.
///
/// By default this id is derived from the function's input and output
/// types, and the signatures of any configured ticket validators are
/// folded into the effective wire id.
pub type FunctionId = UniqueId;

/// A callable function that can be registered with the network and served
/// to remote callers.
///
/// For simple functions, prefer the closure-based
/// [`serve`](handler::Builder::serve) API — it is an adapter over this
/// trait. Implement `Function` directly when the function carries state or
/// requires startup logic: construct the instance however you like
/// (including async warmup work), then register it with
/// [`serve_fn`](handler::Builder::serve_fn). The function is only
/// advertised on the network once registered, so a not-yet-ready function
/// is simply not yet discoverable.
pub trait Function: Send + Sync + 'static {
	/// The function's input type.
	type Req: Datum;

	/// The function's output type.
	type Res: Datum;

	/// The function's application-level error type. Errors of this type are
	/// carried back to the caller typed end-to-end and never trigger
	/// failover to another handler.
	type Err: Datum;

	/// Invokes the function with the given request.
	///
	/// The [`CallContext`] carries the authenticated identity of the remote
	/// caller.
	fn call(
		&self,
		req: Self::Req,
		ctx: CallContext,
	) -> impl Future<Output = Result<Self::Res, Self::Err>> + Send;

	/// The base identity of this function on the network, consistent with
	/// [`TicketValidator::signature`] and
	/// [`StateMachine::signature`](crate::groups::StateMachine::signature).
	///
	/// The default derives the id from the implementing type's name and the
	/// input and output type names. The signatures of any configured ticket
	/// validators are folded on top of this base id to produce the
	/// effective wire id.
	fn signature() -> UniqueId {
		Digest::from_parts(&[
			core::any::type_name::<Self>(),
			core::any::type_name::<Self::Req>(),
			core::any::type_name::<Self::Res>(),
		])
	}
}

/// Trait for function definitions that provide a handler constructor.
///
/// Implemented automatically by the [`function!`](crate::function) macro.
/// The generated implementation bakes in the declared function id and any
/// `require`, `require_ticket`, or other handler configuration specified
/// in the macro invocation.
pub trait FunctionHandler {
	type Req: Datum;
	type Res: Datum;
	type Err: Datum;
	type Handler;

	/// Registers the given closure as the function's implementation with
	/// the baked-in configuration and advertises it on the network.
	fn handler<F, Fut>(
		network: &crate::Network,
		serve: F,
	) -> Result<Self::Handler, handler::BuilderError>
	where
		F: Fn(Self::Req, CallContext) -> Fut + Send + Sync + 'static,
		Fut: Future<Output = Result<Self::Res, Self::Err>> + Send + 'static;
}

/// Trait for function definitions that provide a caller constructor.
///
/// Implemented automatically by the [`function!`](crate::function) macro.
/// The generated implementation bakes in the declared function id and any
/// `require`, `require_ticket`, or other caller configuration specified
/// in the macro invocation.
pub trait FunctionCaller {
	type Caller;

	fn caller(network: &crate::Network) -> Self::Caller;

	/// Creates a caller and waits for it to come online (by default, at
	/// least one eligible handler available).
	fn online_caller(
		network: &crate::Network,
	) -> impl Future<Output = Self::Caller> + Send + Sync + 'static;
}

/// Convenience type alias for the handler type of a function definition.
pub type HandlerOf<F> = <F as FunctionHandler>::Handler;

/// Convenience type alias for the caller type of a function definition.
pub type CallerOf<F> = <F as FunctionCaller>::Caller;

/// Declares a named function definition with an optional compile-time
/// function id and baked-in configuration.
///
/// # Syntax
///
/// ```ignore
/// // Type-derived function id (from the marker and req/res type names):
/// function!(pub Echo = String -> String);
///
/// // Explicit function id:
/// function!(pub Echo = String -> String, "my.function.id");
///
/// // Typed application errors:
/// function!(pub Quote = QuoteRequest -> Result<QuoteResponse, QuoteError>);
///
/// // With configuration:
/// function!(pub Quote = QuoteRequest -> Result<QuoteResponse, QuoteError>,
///     "pricing.quote",
///     caller require_ticket: my_validator(),  // handlers validate callers
///     handler require: |peer| peer.tags().contains(&"pricer".into()),
///     timeout: Duration::from_secs(5),
///     max_concurrent: 32,
/// );
///
/// // Caller only:
/// function!(pub caller Quote = QuoteRequest -> QuoteResponse, "pricing.quote");
/// ```
///
/// # Configuration keys
///
/// Handler-side (inferred):
/// - `max_concurrent` — maximum number of concurrently executing calls
///
/// Caller-side (inferred):
/// - `timeout` — default end-to-end call timeout
/// - `online_when` — availability conditions for the caller to be online
///
/// Both sides:
/// - `require` — peer eligibility predicate; the `handler`/`caller` prefix
///   names who must satisfy it (AND-composed when repeated)
/// - `require_ticket` — ticket authorization; the prefix names which side must
///   present the ticket. Validator signatures fold into the effective wire id
///   on both generated sides, so handlers and callers declared through the same
///   `function!` always derive matching identities.
///
/// # Usage
///
/// ```ignore
/// use mosaik::functions::{FunctionCaller, FunctionHandler};
///
/// function!(pub Quote = QuoteRequest -> Result<QuoteResponse, QuoteError>,
///     "pricing.quote");
///
/// let handler = Quote::handler(&network, |req, _ctx| async move {
///     Ok(QuoteResponse::for_request(&req))
/// })?;
///
/// let caller = Quote::online_caller(&network).await;
/// let quote = caller.call(request).await?;
/// ```
#[macro_export]
macro_rules! function {
	(#[$($meta:tt)*] $($rest:tt)*) => {
		$crate::function! { @attrs [#[$($meta)*]] $($rest)* }
	};
	(@attrs [$($attrs:tt)*] #[$($meta:tt)*] $($rest:tt)*) => {
		$crate::function! { @attrs [$($attrs)* #[$($meta)*]] $($rest)* }
	};
	(@attrs [$($attrs:tt)*] $($rest:tt)*) => {
		$crate::__function_impl! { @$crate; $($attrs)* $($rest)* }
	};
	($($tt:tt)*) => {
		$crate::__function_impl! { @$crate; $($tt)* }
	};
}

/// Contextual information about an individual function invocation, passed
/// to the serving function alongside the request.
#[derive(Debug, Clone)]
pub struct CallContext {
	caller: PeerEntry,
}

impl CallContext {
	pub(crate) const fn new(caller: PeerEntry) -> Self {
		Self { caller }
	}

	/// The authenticated identity of the remote caller, as recorded in the
	/// local discovery catalog at the time of the call.
	pub const fn caller(&self) -> &PeerEntry {
		&self.caller
	}

	/// Consumes the context, returning the caller's peer entry.
	pub fn into_caller(self) -> PeerEntry {
		self.caller
	}
}

/// A function definition that can be used to create handlers and callers
/// for a given request/response type pair.
///
/// Usually used by libraries that want to expose a well-known function
/// interface.
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct FunctionDef<Req: Datum, Res: Datum, E: Datum = ()> {
	pub function_id: Option<FunctionId>,
	_marker: core::marker::PhantomData<fn(&Req, &Res, &E)>,
}

impl<Req: Datum, Res: Datum, E: Datum> Clone for FunctionDef<Req, Res, E> {
	fn clone(&self) -> Self {
		*self
	}
}
impl<Req: Datum, Res: Datum, E: Datum> Copy for FunctionDef<Req, Res, E> {}

impl<Req: Datum, Res: Datum, E: Datum> Default for FunctionDef<Req, Res, E> {
	fn default() -> Self {
		Self::new()
	}
}

impl<Req: Datum, Res: Datum, E: Datum> FunctionDef<Req, Res, E> {
	pub const fn new() -> Self {
		Self {
			function_id: None,
			_marker: core::marker::PhantomData,
		}
	}

	/// Overrides the default type-derived base function id for this
	/// function definition with a custom function id.
	#[must_use]
	pub const fn with_function_id(function_id: FunctionId) -> Self {
		Self {
			function_id: Some(function_id),
			_marker: core::marker::PhantomData,
		}
	}
}

/// Returns the default base function id for a request/response type pair,
/// derived from the two type names.
pub(crate) fn derived_function_id<Req: Datum, Res: Datum>() -> FunctionId {
	Digest::from_parts(&[
		core::any::type_name::<Req>(),
		core::any::type_name::<Res>(),
	])
}

/// Computes the effective wire id of a function by folding the signatures
/// of all configured ticket validators into the base id.
///
/// Authorization is part of the function identity: two functions with
/// different authorization rules have different wire ids, so mismatched
/// configuration is a hard partition rather than a runtime rejection. The
/// fold order is canonical — caller-authorization validators first, then
/// handler-authorization — and both roles must be configured with the same
/// full contract for their wire ids to match.
pub(crate) fn effective_function_id(
	base: FunctionId,
	caller_auth: &[Arc<dyn TicketValidator>],
	handler_auth: &[Arc<dyn TicketValidator>],
) -> FunctionId {
	let mut id = base;
	for validator in caller_auth {
		id = id.derive(validator.signature());
	}
	for validator in handler_auth {
		id = id.derive(validator.signature());
	}
	id
}

/// The functions subsystem for a Mosaik network.
///
/// Functions are the leaderless RPC primitive in Mosaik. Handlers register
/// callable functions and advertise them through discovery; callers invoke
/// them by function id without knowing which peer will serve the call.
pub struct Functions {
	/// Configuration for the functions subsystem.
	config: Arc<Config>,

	/// The local node instance associated with this functions subsystem.
	///
	/// This gives us access to the transport layer socket and identity.
	local: LocalNode,

	/// The discovery system used to announce registered functions and find
	/// remote function handlers.
	discovery: Discovery,

	/// Registry of locally served functions by function id.
	registry: Arc<Registry>,
}

/// Public API
impl Functions {
	/// Creates a new [`handler::Builder`] for the given request/response
	/// types to assemble a function handler configuration. The handler is
	/// registered and advertised when the builder's
	/// [`serve`](handler::Builder::serve) or
	/// [`serve_fn`](handler::Builder::serve_fn) method is called.
	///
	/// The type parameters are usually inferred from the serving closure or
	/// [`Function`] implementation, so no turbofish is needed.
	pub fn handler<Req: Datum, Res: Datum, E: Datum>(
		&self,
	) -> handler::Builder<'_, Req, Res, E> {
		handler::Builder::new(self)
	}

	/// Creates a new [`handler::Builder`] for the given function definition
	/// to assemble a function handler configuration.
	#[allow(clippy::needless_pass_by_value)]
	pub fn handler_of<Req: Datum, Res: Datum, E: Datum>(
		&self,
		def: FunctionDef<Req, Res, E>,
	) -> handler::Builder<'_, Req, Res, E> {
		let mut builder = self.handler::<Req, Res, E>();
		if let Some(function_id) = def.function_id {
			builder = builder.with_function_id(function_id);
		}
		builder
	}

	/// Creates a new [`caller::Builder`] for the given request/response
	/// types to assemble a caller configuration. The caller starts watching
	/// the catalog for eligible handlers when the builder's
	/// [`build`](caller::Builder::build) method is called.
	pub fn caller<Req: Datum, Res: Datum, E: Datum>(
		&self,
	) -> caller::Builder<'_, Req, Res, E> {
		caller::Builder::new(self)
	}

	/// Creates a new [`caller::Builder`] for the given function definition
	/// to assemble a caller configuration.
	#[allow(clippy::needless_pass_by_value)]
	pub fn caller_of<Req: Datum, Res: Datum, E: Datum>(
		&self,
		def: FunctionDef<Req, Res, E>,
	) -> caller::Builder<'_, Req, Res, E> {
		let mut builder = self.caller::<Req, Res, E>();
		if let Some(function_id) = def.function_id {
			builder = builder.with_function_id(function_id);
		}
		builder
	}

	/// Creates a new [`caller::Builder`] pre-configured for the given
	/// [`Function`] implementation — the base function id is taken from
	/// [`Function::signature`], so the caller derives the same identity as
	/// a handler registered with
	/// [`serve_fn`](handler::Builder::serve_fn) for the same type.
	pub fn caller_for<F: Function>(
		&self,
	) -> caller::Builder<'_, F::Req, F::Res, F::Err> {
		self
			.caller::<F::Req, F::Res, F::Err>()
			.with_function_id(F::signature())
	}
}

/// Internal construction API
impl Functions {
	/// Internally used by [`super::NetworkBuilder`] to create a new Functions
	/// subsystem instance as part of the overall [`super::Network`] instance.
	pub(crate) fn new(
		local: LocalNode,
		discovery: &Discovery,
		config: Config,
	) -> Self {
		Self {
			local: local.clone(),
			config: Arc::new(config),
			discovery: discovery.clone(),
			registry: Arc::new(Registry::new(local, discovery.clone())),
		}
	}
}

impl ProtocolProvider for Functions {
	fn install(&self, protocols: RouterBuilder) -> RouterBuilder {
		protocols.accept(Self::ALPN, Acceptor::new(self))
	}
}

impl link::Protocol for Functions {
	/// ALPN identifier for the functions protocol.
	const ALPN: &'static [u8] = b"/mosaik/functions/1.0";
}

network::error::make_close_reason!(
	/// The requested function is not registered on the handler node.
	struct FunctionNotFound, 11_404);

network::error::make_close_reason!(
	/// The remote peer is not allowed to invoke the requested function.
	struct NotAllowed, 11_403);

network::error::make_close_reason!(
	/// The handler has reached its maximum number of concurrent calls and
	/// cannot accept any new invocations.
	struct NoCapacity, 11_509);

network::error::make_close_reason!(
	/// The handler failed internally while producing a reply. This indicates
	/// a handler-side fault (e.g. reply encoding failure), not an
	/// application-level error returned by the function.
	struct HandlerFailure, 11_500);