lib

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

commit 99bc178f53b7dea0d5b839141e7d77fc988dd718
parent 39952362558f6e2ae1acdc78e966cc0f304ba2fa
Author: triesap <tyson@radroots.org>
Date:   Mon, 10 Aug 2026 19:05:28 +0000

merge: reconcile canonical relay authority

- Merge the current public Lib master into the service-hardening history.
- Preserve canonical relay read-write behavior across mobile and SDK surfaces.
- Normalize libc metadata for contract-parser compatibility.
- Verify affected tests, contracts, Clippy, and rustdoc through extbuild.

Diffstat:
MCargo.toml | 2+-
Mcrates/mobile_core/src/runtime/builder.rs | 4++--
Mcrates/mobile_ffi/tests/runtime_delegation.rs | 4++--
Mcrates/sdk/src/client.rs | 2+-
Mcrates/transport_nostr/src/client.rs | 2+-
Mcrates/transport_nostr/src/profile.rs | 20++++++++++----------
Mcrates/transport_nostr/src/status.rs | 72++++++++++++++++++++++++++++++++++++++++++++++--------------------------
7 files changed, 63 insertions(+), 43 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml @@ -214,7 +214,7 @@ keyring = { version = "3.6.3", default-features = false, features = [ "linux-native-sync-persistent", "vendored", ] } -libc = "0.2" +libc = { version = "0.2" } nostr = { version = "0.44.7", default-features = false } nostr-relay-pool = { version = "0.44.0" } nostr-sdk = { version = "0.44.1" } diff --git a/crates/mobile_core/src/runtime/builder.rs b/crates/mobile_core/src/runtime/builder.rs @@ -124,9 +124,9 @@ mod tests { assert_eq!(report.state, "configured"); assert_eq!(report.relays.len(), 1); assert_eq!(report.relays[0].relay_url, "wss://radroots.org"); - assert_eq!(report.relays[0].access, "read_only"); + assert_eq!(report.relays[0].access, "read_write"); assert_eq!(report.relays[0].read_state, "unobserved"); - assert_eq!(report.relays[0].write_state, "unsupported"); + assert_eq!(report.relays[0].write_state, "unobserved"); } runtime.shutdown().await.expect("shutdown"); } diff --git a/crates/mobile_ffi/tests/runtime_delegation.rs b/crates/mobile_ffi/tests/runtime_delegation.rs @@ -39,9 +39,9 @@ async fn native_boundary_delegates_the_complete_core_surface() { assert_eq!(public.read_availability, "unavailable"); assert_eq!(public.write_availability, "unavailable"); assert_eq!(public.relays.len(), 1); - assert_eq!(public.relays[0].access, "read_only"); + assert_eq!(public.relays[0].access, "read_write"); assert_eq!(public.relays[0].read_state, "unobserved"); - assert_eq!(public.relays[0].write_state, "unsupported"); + assert_eq!(public.relays[0].write_state, "unobserved"); runtime .configure_public_relays(vec!["wss://write.example".to_owned()]) diff --git a/crates/sdk/src/client.rs b/crates/sdk/src/client.rs @@ -710,7 +710,7 @@ mod tests { .expect("delivery capability"); assert!(fetch.is_configured()); assert_eq!(fetch.availability(), Availability::Unavailable); - assert!(!delivery.is_configured()); + assert!(delivery.is_configured()); assert_eq!(delivery.availability(), Availability::Unavailable); client diff --git a/crates/transport_nostr/src/client.rs b/crates/transport_nostr/src/client.rs @@ -325,7 +325,7 @@ mod tests { .expect("connections"); assert_eq!(config.relays().len(), 3); assert_eq!(config.read_relays().count(), 3); - assert_eq!(config.write_relays().count(), 2); + assert_eq!(config.write_relays().count(), 3); assert_eq!(config.profile_kind(), RelayProfileKind::Public); assert_eq!(config.connect_timeout_ms(), 1); assert_eq!(config.request_timeout_ms(), 2); diff --git a/crates/transport_nostr/src/profile.rs b/crates/transport_nostr/src/profile.rs @@ -3,7 +3,7 @@ use crate::{Error, RelayUrl, RelayUrlPolicy}; use std::collections::BTreeSet; -/// Bundled public relay used for read discovery only. +/// Bundled canonical Radroots relay used for reads and publication. pub const DEFAULT_PUBLIC_RELAY: &str = "wss://radroots.org"; /// Directional access authorized for one configured relay. @@ -34,7 +34,7 @@ impl RelayAccess { #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] #[non_exhaustive] pub enum RelayProfileKind { - /// Public-Internet profile with the bundled read-only relay. + /// Public-Internet profile with the bundled canonical relay. Public, /// Development-only profile restricted to exact loopback destinations. Simulator, @@ -92,10 +92,9 @@ pub struct RelayProfile { impl RelayProfile { /// Builds the ordinary public profile. /// - /// `wss://radroots.org/` is always present as read-only. Every supplied + /// `wss://radroots.org/` is always present as read-write. Every additional /// writable relay must be a TLS public-Internet destination. Supplying the - /// bundled relay as writable is rejected rather than silently broadening - /// its authority. + /// bundled relay again is rejected as a duplicate. pub fn public<I, S>(writable_relays: I) -> Result<Self, Error> where I: IntoIterator<Item = S>, @@ -104,7 +103,7 @@ impl RelayProfile { let mut endpoints = vec![RelayEndpoint::new( DEFAULT_PUBLIC_RELAY, RelayUrlPolicy::Public, - RelayAccess::ReadOnly, + RelayAccess::ReadWrite, )?]; endpoints.extend(parse_endpoints( writable_relays, @@ -133,7 +132,7 @@ impl RelayProfile { /// Builds a physical-device profile from explicit writable TLS endpoints. /// - /// The bundled public relay remains read-only. Writable endpoints may + /// The bundled canonical relay remains read-write. Additional endpoints may /// resolve to public or private addresses, but loopback, unspecified, and /// multicast destinations remain forbidden before and after resolution. pub fn device<I, S>(writable_relays: I) -> Result<Self, Error> @@ -144,7 +143,7 @@ impl RelayProfile { let mut endpoints = vec![RelayEndpoint::new( DEFAULT_PUBLIC_RELAY, RelayUrlPolicy::Public, - RelayAccess::ReadOnly, + RelayAccess::ReadWrite, )?]; endpoints.extend(parse_endpoints( writable_relays, @@ -208,12 +207,12 @@ mod tests { use super::*; #[test] - fn public_profile_never_promotes_the_bundled_relay_to_writable() { + fn public_profile_authorizes_the_canonical_relay_for_publication() { let profile = RelayProfile::public(["wss://write.example"]).expect("public profile"); assert_eq!(profile.kind(), RelayProfileKind::Public); assert_eq!(profile.endpoints().len(), 2); assert_eq!(profile.endpoints()[0].url().as_str(), DEFAULT_PUBLIC_RELAY); - assert_eq!(profile.endpoints()[0].access(), RelayAccess::ReadOnly); + assert_eq!(profile.endpoints()[0].access(), RelayAccess::ReadWrite); assert_eq!(profile.endpoints()[1].access(), RelayAccess::ReadWrite); assert!(RelayProfile::public([DEFAULT_PUBLIC_RELAY]).is_err()); assert!(RelayProfile::public(["ws://public.example"]).is_err()); @@ -232,6 +231,7 @@ mod tests { fn device_profile_requires_tls_and_rejects_duplicate_authority() { let profile = RelayProfile::device(["wss://10.0.0.5:7447"]).expect("device profile"); assert_eq!(profile.kind(), RelayProfileKind::Device); + assert_eq!(profile.endpoints()[0].access(), RelayAccess::ReadWrite); assert_eq!( profile.endpoints()[1].policy(), RelayUrlPolicy::PrivateNetwork diff --git a/crates/transport_nostr/src/status.rs b/crates/transport_nostr/src/status.rs @@ -644,9 +644,9 @@ mod tests { let config = Config::from_profile(RelayProfile::public(["wss://write.example"]).expect("profile")) .with_reconnect_backoff(ReconnectBackoff::new(10, 40).expect("backoff")); - let read_only = config.relays()[0].clone(); + let canonical = config.relays()[0].clone(); let writable = config.relays()[1].clone(); - (StatusTracker::new(&config), read_only, writable) + (StatusTracker::new(&config), canonical, writable) } #[test] @@ -672,20 +672,20 @@ mod tests { #[test] fn status_requires_directional_evidence_and_backoff_is_monotonic() { - let (tracker, read_only, writable) = tracker(); + let (tracker, canonical, writable) = tracker(); let initial = tracker.report(); assert_eq!(initial.read_availability(), Availability::Unavailable); assert_eq!(initial.write_availability(), Availability::Unavailable); assert_eq!(initial.state(), RelayAggregateState::Configured); assert_eq!( initial.relays()[0].write().state(), - RelayEvidenceState::Unsupported + RelayEvidenceState::Unobserved ); - assert!(!tracker.may_write(&read_only, 100)); + assert!(tracker.may_write(&canonical, 100)); - tracker.begin_read(&read_only, 100); + tracker.begin_read(&canonical, 100); assert_eq!(tracker.report().state(), RelayAggregateState::Connecting); - tracker.record_read(&read_only, true, false, 100); + tracker.record_read(&canonical, true, false, 100); tracker.record_read(&writable, false, true, 100); tracker.record_write(&writable, false, true, 100); let partial = tracker.report(); @@ -706,14 +706,29 @@ mod tests { RelayEvidenceState::Unavailable ); tracker.record_write(&writable, true, false, 130); - let available = tracker.report(); - assert_eq!(available.write_availability(), Availability::Available); - assert_eq!(available.relays()[1].write().consecutive_failures(), 0); + let partially_available = tracker.report(); assert_eq!( - available.relays()[1].write().last_success_unix_ms(), + partially_available.write_availability(), + Availability::Degraded + ); + assert_eq!( + partially_available.relays()[1] + .write() + .consecutive_failures(), + 0 + ); + assert_eq!( + partially_available.relays()[1] + .write() + .last_success_unix_ms(), Some(130) ); assert!(tracker.may_write(&writable, 130)); + tracker.record_write(&canonical, true, false, 130); + assert_eq!( + tracker.report().write_availability(), + Availability::Available + ); assert_eq!( source_status(&tracker).availability(), Availability::Degraded @@ -724,7 +739,9 @@ mod tests { ); tracker.record_write(&writable, false, false, 140); + tracker.record_write(&canonical, false, false, 140); assert!(!tracker.may_write(&writable, u64::MAX)); + assert!(!tracker.may_write(&canonical, u64::MAX)); assert_eq!( tracker.report().relays()[1] .write() @@ -762,25 +779,26 @@ mod tests { } #[test] - fn all_success_is_available_and_no_writable_relay_is_honestly_unavailable() { + fn canonical_relay_read_and_write_success_is_available() { let config = Config::from_profile(RelayProfile::public(Vec::<String>::new()).expect("profile")); let tracker = StatusTracker::new(&config); let relay = config.relays()[0].clone(); tracker.record_read(&relay, true, false, 1); + tracker.record_write(&relay, true, false, 1); assert_eq!( source_status(&tracker).availability(), Availability::Available ); let sink = sink_status(&tracker); - assert!(!sink.is_configured()); - assert_eq!(sink.availability(), Availability::Unavailable); - assert_eq!(tracker.report().state(), RelayAggregateState::ReadOnly); + assert!(sink.is_configured()); + assert_eq!(sink.availability(), Availability::Available); + assert_eq!(tracker.report().state(), RelayAggregateState::Writable); } #[test] fn aggregate_states_and_unconfigured_targets_cover_fail_closed_edges() { - let (live, read_only, writable) = tracker(); + let (live, canonical, writable) = tracker(); let unknown = RelayUrl::parse("wss://unknown.example", crate::RelayUrlPolicy::Public).expect("relay"); @@ -788,16 +806,16 @@ mod tests { live.begin_write(&unknown, 1); live.record_read(&unknown, true, false, 1); live.record_write(&unknown, true, false, 1); - live.begin_write(&read_only, 1); - live.record_write(&read_only, true, false, 1); + live.begin_write(&canonical, 1); + live.record_write(&canonical, true, false, 1); assert_eq!( live.report().relays()[0].write().state(), - RelayEvidenceState::Unsupported + RelayEvidenceState::Available ); - live.begin_read(&read_only, 2); - live.begin_read(&read_only, 1); - live.record_read(&read_only, true, false, 2); + live.begin_read(&canonical, 2); + live.begin_read(&canonical, 1); + live.record_read(&canonical, true, false, 2); live.record_read(&writable, true, false, 2); live.record_write(&writable, true, false, 2); let writable_report = live.report(); @@ -808,15 +826,17 @@ mod tests { Availability::Available ); - let (offline, read_only, writable) = tracker(); - offline.record_read(&read_only, false, true, 10); + let (offline, canonical, writable) = tracker(); + offline.record_read(&canonical, false, true, 10); offline.record_read(&writable, false, true, 10); + offline.record_write(&canonical, false, true, 10); offline.record_write(&writable, false, true, 10); assert_eq!(offline.report().state(), RelayAggregateState::Offline); - let (failed, read_only, writable) = tracker(); - failed.record_read(&read_only, false, false, 10); + let (failed, canonical, writable) = tracker(); + failed.record_read(&canonical, false, false, 10); failed.record_read(&writable, false, false, 10); + failed.record_write(&canonical, false, false, 10); failed.record_write(&writable, false, false, 10); assert_eq!(failed.report().state(), RelayAggregateState::Failed);