README.md (9649B)
1 # radroots_transport 2 3 `radroots_transport` is the transport-neutral host SPI for moving verified 4 Radroots events between explicit targets. It owns extensible transport and 5 target identities, separate source and sink capabilities, bounded fetch and 6 delivery requests, bounded live subscriptions, provenance, partial outcomes, 7 and normalized errors. 8 9 The crate is `no_std` with `alloc`. It does not own network clients, storage, 10 outbox claiming, retry loops, schedulers, timers, or fallback policy. Concrete 11 adapters such as `radroots_transport_nostr` implement the SPI; applications 12 compose those adapters in `radroots_sdk` or their own host layer. 13 14 The authoritative package charter is the 15 [`radroots_transport` section of the Release V1 specification](../../contracts/crates/release_v1/radroots_crates_release_v1.toml). 16 17 ## Typical flow 18 19 1. A host selects a validated [`TransportId`] and constructs one or more 20 canonical [`Target`] values. 21 2. [`TargetSet`] rejects an empty, oversized, or duplicate-fingerprint set. 22 3. The caller creates a bounded [`FetchRequest`], [`SubscriptionRequest`], or 23 [`DeliveryRequest`] with a request identity and absolute deadline. Inbound 24 operations may carry a validated [`FetchSelector`] for exact kinds, 25 authors, indexed single-letter tag values, and inclusive event-time bounds. 26 4. A dyn-compatible [`EventSource`], [`EventSubscriber`], or [`EventSink`] 27 implementation performs only the requested operation. 28 5. The caller validates [`FetchPage`] or [`DeliveryReceipt`] against the 29 originating request and decides whether normalized retryable outcomes merit 30 another explicit operation. 31 32 [`TransportId`]: crate::TransportId 33 [`Target`]: crate::Target 34 [`TargetSet`]: crate::TargetSet 35 [`FetchRequest`]: crate::FetchRequest 36 [`SubscriptionRequest`]: crate::SubscriptionRequest 37 [`FetchSelector`]: crate::source::FetchSelector 38 [`DeliveryRequest`]: crate::DeliveryRequest 39 [`EventSource`]: crate::EventSource 40 [`EventSubscriber`]: crate::EventSubscriber 41 [`EventSink`]: crate::EventSink 42 [`FetchPage`]: crate::FetchPage 43 [`DeliveryReceipt`]: crate::DeliveryReceipt 44 45 ```rust 46 use radroots_transport::{Error, Target, TargetSet, TransportId}; 47 48 let transport_id = TransportId::parse("example")?; 49 let target = Target::new(transport_id, "https://transport.example/events")?; 50 let targets = TargetSet::new(vec![target])?; 51 52 assert_eq!(targets.len(), 1); 53 # Ok::<(), Error>(()) 54 ``` 55 56 The complete externally implementable SPI example is 57 [`examples/host_transport.rs`](examples/host_transport.rs). 58 59 ## Host SPI contract 60 61 `EventSource`, `EventSubscriber`, and `EventSink` are independent, externally 62 implementable, dyn-compatible `Send + Sync` traits. An adapter may implement 63 any subset. Their methods return boxed `Future + Send` values so this package 64 does not select an async runtime or require an async-trait macro. 65 66 - `status` is observational and must not begin fetch or delivery work. 67 - `fetch` returns one bounded page plus per-target outcomes and explicit 68 continuation state. Returned events must satisfy the request selector; 69 request-bound page validation rejects adapter drift. 70 - `subscribe` returns a sealed live-operation capability. Its `next` method 71 returns request-bound events with target checkpoints or one stable terminal 72 result; its `cancel` method returns that same terminal result once ended. 73 - `deliver` returns one receipt for the exact request and every requested 74 target; it never performs an implicit retry. 75 - Implementations must not install an executor or spawn hidden workers. The 76 composing host owns polling, scheduling, retries, and process lifecycle. 77 - Concrete backend failures are normalized to `radroots_transport::Error`. 78 Native sources may be retained by adapters, but public outcome codes and 79 messages must remain bounded and secret-safe. 80 81 `EventSink::deliver_selected` accepts an exact nonempty subset while preserving 82 the full original request and receipt target set. An adapter must not attempt 83 unselected targets. The default accepts only all original targets; a proper 84 subset returns `target_selection_unsupported` without delivery. Callers hold 85 empty selections without invoking transport. 86 87 ## Targets and extensible identity 88 89 `TransportId` is a validated open identity, not a closed enum. The built-in 90 `LOCAL`, `NOSTR`, `RETICULUM`, and `RADROOTSD` constants are conveniences; 91 future adapters can use validated custom IDs without changing this crate. 92 93 Targets contain a transport ID, canonical endpoint URI, optional scope and 94 human label, and a derived SHA-256 fingerprint. The label is descriptive and 95 does not participate in identity. The transport ID, endpoint, and scope do. 96 Target-set construction preserves caller order and rejects duplicate 97 fingerprints instead of silently deduplicating them. 98 99 The ordinary Nostr target constructor admits TLS endpoints and exact loopback 100 cleartext only. Physical-device callers must select 101 [`TargetNetworkPolicy::PrivateDevice`] explicitly; that policy admits only 102 literal RFC1918 IPv4 or ULA IPv6 endpoints. This is a passive construction 103 boundary, not connection authority. Concrete adapters must retain the selected 104 policy, revalidate every resolved address, and pin the validated destination 105 when opening a socket. This generic package does not resolve names, open 106 connections, export relay URL types, or own network fallback. 107 108 [`TargetNetworkPolicy::PrivateDevice`]: crate::TargetNetworkPolicy::PrivateDevice 109 110 ## Bounds, deadlines, cancellation, and commit points 111 112 Request IDs, endpoint values, cursors, target sets, outcome details, page 113 sizes, and live event counts are bounded by public constants. Fetch, 114 subscription, and delivery requests carry an absolute Unix-millisecond 115 deadline; constructors validate the deadline but do not read a clock. 116 117 Dropping a returned future requests cancellation. Before an adapter publishes 118 or commits a remote operation, cancellation must leave no durable effect. 119 After that boundary, cancellation does not imply rollback: the adapter must 120 preserve and report the final remote state when it can be observed. Each 121 concrete adapter documents its exact publication or commit point. 122 123 Live subscriptions additionally retain canonical per-target checkpoints in 124 request target order. Reconnect callers pass only an explicit checkpoint 125 subset; adapters must not invent hidden replay state. Once a subscription 126 returns its terminal result, subsequent `next` and `cancel` calls return the 127 same result. 128 129 ## Outcomes, partial success, and retry 130 131 Fetch pages and delivery receipts preserve target-local results. Delivery 132 satisfaction is explicit (`any`, `all`, quorum, or required fingerprints) and 133 is evaluated against the originating request. Retryability and terminality are 134 normalized data on outcomes, never an automatic loop. Authentication, 135 unavailability, rejection, cancellation, partial progress, and unknown remote 136 state must not be rewritten as success or silent fallback. 137 138 ## Serialization and provenance 139 140 With `serde`, passive identities, requests, subscriptions, pages, receipts, 141 status values, and outcomes use validated representations. Deserialization 142 rechecks canonical identity, bounds, request/result cardinality, and provenance 143 rather than trusting serialized fingerprints or counts. 144 145 Inbound events carry the transport ID, exact target fingerprint, observation 146 time, and optional continuation cursor that produced them. A page cannot claim 147 events or outcomes for targets outside its request. A live event additionally 148 binds its exact originating request and requires its resulting target 149 checkpoint to match the event's provenance cursor. 150 151 ## Security and side effects 152 153 - Target endpoint syntax is validated before an adapter receives a request; 154 adapters remain responsible for scheme, DNS/IP, TLS, and private-network 155 policy before connection. 156 - Delivery accepts a validated signed event, not arbitrary bytes. 157 - Receipt, page, and subscription constructors reject missing, duplicate, 158 unexpected, or forged target evidence. 159 - This crate forbids unsafe code and contains no socket, filesystem, database, 160 global state, timer, executor, or retry implementation. 161 - No transport may silently route through another transport. 162 163 ## Features 164 165 | Feature | Default | Contract | 166 | --- | --- | --- | 167 | `std` | yes | Enables standard-library support in canonical dependency values; it adds no I/O, runtime, or global initialization. | 168 | `serde` | yes | Adds validated serialization for passive identities, requests, subscriptions, status, provenance, pages, receipts, policies, and outcomes. | 169 170 Features are additive. `--no-default-features` provides the `no_std + alloc` 171 core, and `serde` is supported independently of `std`. 172 173 ## Intended consumers 174 175 - `radroots_transport_nostr` provides the concrete native Nostr adapter. 176 - `radroots_storage` persists transport-neutral outbox and evidence values. 177 - `radroots_sync` plans bounded operations and applies explicit retry policy. 178 - `radroots_sdk` composes storage, signing, source, and sink implementations. 179 - Service and future adapter hosts implement any of the SPIs and own runtime, 180 network, cancellation, and lifecycle behavior. 181 182 Applications that only need ordinary Radroots operations should normally use 183 `radroots` or `radroots_sdk`; implement this package directly when providing a 184 new transport adapter or advanced host composition. 185 186 ## Copyright 187 188 Except as otherwise noted, all files in the `radroots_transport` distribution are 189 190 Copyright (c) 2025 Tyson Lupul 191 192 For information on usage and redistribution, and for a DISCLAIMER OF ALL 193 WARRANTIES, see LICENSE included in the `radroots_transport` distribution.