- New module crates/sprout-relay/src/iroh_relay.rs (~290 lines incl. tests).
- pub fn spawn(state, bind_addr) constructs an iroh_relay::server::Server
with AccessConfig::Restricted set to a closure that:
1. Pulls the Bearer token from ClientRequest::auth_token().
2. base64-decodes (accepts STANDARD + URL_SAFE, padded or not).
3. Calls sprout_auth::verify_nip98_event against canonical URL
(= sprout_auth::nip98_canonical_url(public_url, '/relay')).
4. Runs check_relay_membership against the NIP-98 pubkey.
Anything other than Member/ViaOwner/OpenRelay -> Deny.
Per Max's review notes: fail-closed on missing/invalid token, run
membership only after NIP-98 verifies the pubkey, no caching.
- Returns Ok(None) gracefully when SPROUT_IROH_RELAY_PUBLIC_URL is unset
(the canonical URL can't be built without it).
- patched-iroh-relay feature flag reserved for upstream PR C's per-client
max-lifetime hook (kept behind cfg so unpatched rc.0 still compiles).
- MSRV bumped from 1.88.0 -> 1.91.0 (iroh-relay rc.0's MSRV). Repo's
rust-toolchain.toml already pins 1.95.0 so builds are unaffected; the
bump just keeps Cargo.toml honest with the actual transitive floor.
- README updated: 'Rust 1.88+' -> 'Rust 1.91+'.
- crates/sprout-relay/Cargo.toml: added
iroh-relay = { version = "=1.0.0-rc.0", features = ["server"] }
plus the patched-iroh-relay feature.
Tests (rustc 1.95, via rust-toolchain.toml; also verified independently
on 1.91.1):
- sprout-relay --lib: 195 -> 206 (+11 iroh_relay tests covering valid
admission, missing/empty/non-base64/wrong-method/wrong-URL/wrong-kind/
stale-timestamp denials, and bearer-encoding round-trips).
- cargo clippy --workspace --all-targets -- -D warnings: clean.
- cargo fmt --all -- --check: clean.
Signed-off-by: Tyler Longwell <109685178+tlongwell-block@users.noreply.github.com>
Co-authored-by: Dawn (sprout agent) <c6237ef84fa537c78dcee78efd2d4e59f728859c7f194da42ac51ededfa0be05@sprout-oss.stage.blox.sqprod.co>