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
//! Async Rust client SDK for the satd Streaming Consumption API
//! (`satd.events.v1`).
//!
//! It wraps the generated tonic client with a typed event model, auth metadata
//! injection, and cursor capture, so a consumer writes against [`StreamClient`]
//! instead of hand-rolling channels, metadata, and protobuf unwrapping.
//!
//! ```no_run
//! use satd_events_client::{StreamClient, SubscribeOptions, Categories, Event};
//!
//! # async fn run() -> Result<(), Box<dyn std::error::Error>> {
//! let mut client = StreamClient::builder("http://127.0.0.1:50051")
//! // Loopback, so the token never leaves the host. Off-host, use
//! // `.bearer_token(..)` with an `https://` endpoint — it refuses to send
//! // a credential over an unencrypted connection.
//! .insecure_bearer_token("…")
//! .keepalive_default()
//! .connect()
//! .await?;
//!
//! let mut events = client.subscribe(SubscribeOptions {
//! categories: Categories::MEMPOOL | Categories::CHAIN,
//! ..Default::default()
//! }).await?;
//!
//! while let Some(event) = events.message().await? {
//! match event {
//! Event::BlockConnected { height, .. } => println!("block {height}"),
//! Event::MempoolEnter { fee, vsize, .. } => println!("tx {fee}/{vsize}"),
//! Event::Lagged { resume_cursor, .. } => {
//! // reconnect with `resume_cursor` to recover the gap
//! let _ = resume_cursor;
//! }
//! _ => {}
//! }
//! }
//! # Ok(())
//! # }
//! ```
//!
//! ## Scope (this revision)
//!
//! - [`StreamClient::subscribe`] — the full firehose with `from_cursor` replay
//! and cursor capture.
//! - [`StreamClient::watch`] — opens the bidirectional stream and returns a
//! [`WatchHandle`] with typed helpers for every watch kind: `add_scripts`
//! (with per-script `min_value` floors), `add_outpoints`, `add_tx_lifecycle` /
//! `add_depth_alarms`, `add_descriptor`, and `add_script_prefixes` (the
//! privacy-preserving prefix watch), plus `set_categories` / `set_cursor` and
//! the matching `remove_*`. [`send_control`](WatchHandle::send_control) remains
//! for raw access.
//!
//! - [`StreamClient::resilient_subscribe`] — the firehose wrapped in a
//! [`ResilientSubscription`] that reconnects with backoff, persists and
//! replays the durable cursor (via a [`CursorStore`]), recovers from `Lagged`
//! per a [`LagPolicy`], and surfaces replay-truncation gaps.
//! - [`StreamClient::resilient_watch`] — the bidirectional `Watch` stream
//! wrapped in a [`ResilientWatch`] that mirrors the watch-set and
//! re-registers it on reconnect (watch-sets are per-connection), then
//! re-anchors off the deterministic [`Event::CursorAccepted`] /
//! [`Event::CursorRejected`] results, retrying transient rejects in place and
//! surfacing the rest for a resnapshot. Integrators with a durable watch-set
//! source-of-truth can install a
//! [`watch_set_loader`](ResilientWatchConfig::watch_set_loader) to rebuild the
//! canonical set from that truth on every (re)connect.
//! - [`PrefixWatcher`] (default-on `bitcoin` feature) — the privacy-preserving
//! prefix-watch local re-filter: decodes a [`PrefixMatch`]'s `raw_tx` and
//! recomputes `sha256(scriptPubKey)` to keep only true matches.
//!
//! ## TLS / mTLS (default-on `tls` feature)
//!
//! Builder methods encrypt the gRPC transport so a bearer token (and the event
//! stream) never travels in cleartext:
//!
//! - [`tls`](StreamClientBuilder::tls) — TLS with the bundled Mozilla roots
//! (public-CA servers).
//! - [`tls_ca_pem`](StreamClientBuilder::tls_ca_pem) — pin a private / self-signed
//! CA, the usual case for a satd node serving its own certificate.
//! - [`tls_client_identity`](StreamClientBuilder::tls_client_identity) — present a
//! client certificate for mutual TLS (`-eventsgrpcmtls`).
//! - [`tls_domain`](StreamClientBuilder::tls_domain) — override the verified
//! certificate name (SNI) when connecting by IP or through a proxy.
//!
//! ```no_run
//! # #[cfg(feature = "tls")]
//! # use satd_events_client::StreamClient;
//! # #[cfg(feature = "tls")]
//! # async fn run() -> Result<(), Box<dyn std::error::Error>> {
//! let ca = std::fs::read("node-ca.pem")?;
//! let client = StreamClient::builder("https://node.example:50051")
//! .tls_ca_pem(ca)
//! .bearer_token("…")
//! .connect()
//! .await?;
//! # let _ = client;
//! # Ok(())
//! # }
//! ```
//!
//! TLS uses the `ring` rustls provider. Opt out with `default-features = false`
//! for a plaintext-only build — but note that such a build cannot encrypt
//! *anything*: tonic only honours the `https://` scheme when it too is compiled
//! with TLS, so without this feature an `https://` endpoint connects in
//! cleartext. [`bearer_token`](StreamClientBuilder::bearer_token) therefore
//! refuses every endpoint in a build without `tls`. The raw wire types are
//! re-exported under [`proto`] for low-level use.
//!
//! ## Stability & versioning
//!
//! The SDK tracks the **additive `satd.events.v1` wire schema**, not the node's
//! release cadence: new optional fields and event / watch kinds are added
//! without breaking existing consumers, and the crate follows [semver]
//! independently of the satd node version — a node and SDK do **not** need
//! matching versions. The generated wire types are re-exported under [`proto`]
//! so you can pin to the schema directly when a typed helper does not yet cover
//! your case. Minimum supported Rust version (**MSRV**) is **1.93**; an MSRV
//! bump is treated as a minor-version change.
//!
//! See the [wire/streaming spec][spec] for the underlying gRPC contract and the
//! [`satd-events-proto`][proto-crate] crate for the generated types.
//!
//! [semver]: https://semver.org/
//! [spec]: https://github.com/epochbtc/satd/blob/master/docs/api/streaming.md
//! [proto-crate]: https://docs.rs/satd-events-proto
pub use ;
pub use StreamError;
pub use ;
/// Parse a raw wire txid into a typed [`bitcoin::Txid`]. Requires the `bitcoin`
/// feature.
pub use parse_txid;
pub use ;
pub use ;
pub use ;
/// The generated `satd.events.v1` wire types, for low-level control-message
/// construction until typed helpers cover every case.
pub use v1 as proto;