lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

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.