darkbio_wire/protocol/mod.rs
1// wire-rs: encrypted protocol between Ark and host
2// Copyright 2026 Dark Bio AG. All rights reserved.
3//
4// Use of this source code is governed by a BSD-style
5// license that can be found in the LICENSE file.
6
7//! Bidirectional requests over the transport, with pipelining and explicit sessions.
8//!
9//! A reader receives messages from each connection. Each session also has a writer
10//! that sends queued messages and a deadline worker that times out operations.
11//! Incoming requests and unread responses stay encoded until `recv()` or `wait()`.
12//! Invalid payloads close their original session when decoded.
13//!
14//! Each session limits accepted peer requests and buffered incoming bytes. Set
15//! both with [`Session::set_inbound_limits`] or [`Server::set_inbound_limits`].
16//! Exceeding a limit closes the session. The reader never waits for the application
17//! to make room. These limits are local; no flow control is negotiated with the
18//! peer. The outgoing queue has no capacity limit.
19//!
20//! The application opens a [`crate::transport::Stream`]; this layer constructs and
21//! owns its transport. [`connect`] establishes one client session. [`Server`] owns
22//! a persistent stream and accepts successive server sessions. Each [`Session`]
23//! owns its receive queue and closes when dropped. Its [`Requester`] and
24//! [`Responder`] handles always target that session, even after it closes and
25//! another session connects.
26//! Both sides use the same handle types. Sessions exchange [`Message`], the union
27//! of every body in the [`schema`]. The [`schema::host_to_ark::Content`] and
28//! [`schema::ark_to_host::Content`] oneofs hold what each side may send. Convert
29//! a received [`Message`] into the peer's oneof to dispatch on it exhaustively.
30//! Callers select response types when waiting on [`Promise<Message>`]. A request
31//! is refused with a [`schema::Error`], an application's own error type converting
32//! into one through [`CodedError`]. A request whose content this build does not
33//! know is refused as `UNKNOWN` by the session itself, so a newer peer learns what
34//! an older one serves. A response of unknown content is malformed, no request
35//! having asked for it.
36//!
37//! Requests and replies return promises without waiting for I/O. Their deadlines
38//! include time in the outgoing queue; `wait()` does not restart the timeout.
39//! Transport write timeouts are independent: a request can still reach the peer
40//! after its promise expires. The reader and writer run independently of the
41//! application, but the application must keep receiving and answering requests
42//! while its own requests wait for replies. All waiting is blocking; no async
43//! runtime is required. Every request expects a reply, including notifications.
44//!
45//! Closing a session fails its pending promises and discards queued messages.
46//! Completed promises keep their results. A write already in progress may still
47//! reach the peer.
48
49mod closer;
50mod envelope;
51mod error;
52mod message;
53mod operation;
54mod promise;
55mod requester;
56mod responder;
57mod server;
58mod session;
59mod worker;
60
61#[cfg(any(test, feature = "fuzz"))]
62#[cfg_attr(coverage_nightly, coverage(off))]
63#[doc(hidden)]
64pub mod mock;
65
66pub use closer::Closer;
67pub use error::{CodedError, Error};
68pub use message::Message;
69pub use promise::Promise;
70pub use requester::Requester;
71pub use responder::Responder;
72pub use server::Server;
73pub use session::{Session, connect};
74
75use std::time::Duration;
76
77/// Default timeout for sending an automatic reply, `UNANSWERED` when a responder
78/// is dropped or `UNKNOWN` to a request this build does not know. Starts when
79/// the responder is dropped or the request arrives and includes time in the
80/// outgoing queue. Configure it with [`Session::set_autoreply_timeout`] or
81/// [`Server::set_autoreply_timeout`]. Transport write timeouts are independent.
82pub const DEFAULT_AUTOREPLY_TIMEOUT: Duration = Duration::from_secs(5);
83
84/// Default limit of 1,024 accepted peer requests per session. A request counts
85/// while queued, held by a responder, or waiting to send its reply. The slot is
86/// freed when the writer takes the reply or the reply is discarded.
87/// Configure it with [`Session::set_inbound_limits`] or
88/// [`Server::set_inbound_limits`]. Zero admits no peer requests.
89pub const DEFAULT_MAX_INBOUND_REQUESTS: usize = 1024;
90
91/// Default limit of 16 MiB for queued requests and unread response promises.
92/// Counts their full encoded envelopes. Taking or dropping an envelope reduces
93/// the byte count.
94/// Decoded application data, outgoing messages and transport buffers are excluded.
95/// Configure it with [`Session::set_inbound_limits`] or
96/// [`Server::set_inbound_limits`]. Zero permits no retained envelope bytes.
97pub const DEFAULT_MAX_INBOUND_BYTES: usize = 16 * 1024 * 1024;
98
99/// Protobuf bindings of the protocol, generated from `proto/wire.proto` and
100/// excluded from the lints of handwritten code. Every message implements
101/// `prost::Message` for raw encoding. Applications exchange the bodies through
102/// [`Message`], converting to the `host_to_ark` and `ark_to_host` oneofs to
103/// dispatch on one direction. The `HostToArk` and `ArkToHost` envelopes are the
104/// wire form, handled by the session internally.
105#[allow(clippy::all)]
106#[allow(rustdoc::broken_intra_doc_links)]
107pub mod schema {
108 include!("generated/darkbio.wire.rs");
109}