openrtc 2.8.1

OpenRTC: a Rust-first P2P runtime for device discovery, signaling, and iroh/QUIC networking.
Documentation
//! Local process acceptance only; compiled out of released Node hosts.
use super::*;

#[derive(Deserialize, Serialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub(super) struct TestLane {
    run_id: String,
    control_plane: String,
    gateway: String,
    source_digest: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    relay_url: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    ticket_lifetime_ms: Option<u64>,
}

impl TestLane {
    pub(super) fn parse(arguments: &[String]) -> Result<Option<Self>> {
        if arguments.is_empty() {
            return Ok(None);
        }
        if arguments.len() != 2 || arguments[0] != "--test-lane" || arguments[1].len() > 2048 {
            bail!("invalid Node host test lane arguments");
        }
        let lane: Self =
            serde_json::from_str(&arguments[1]).context("decode local Node test lane")?;
        lane.validate(option_env!("OPENRTC_NODE_HOST_TEST_SOURCE_DIGEST").unwrap_or("unbound"))?;
        Ok(Some(lane))
    }

    fn validate(&self, compiled_digest: &str) -> Result<()> {
        if self.ticket_lifetime_ms.is_some_and(|value| value != 90_000) {
            bail!("local ticket renewal fixture requires exactly 90 seconds");
        }
        bounded_id("runId", self.run_id.clone(), 80)?;
        if self.source_digest.len() != 64
            || !self.source_digest.bytes().all(|b| b.is_ascii_hexdigit())
            || self.source_digest != compiled_digest
        {
            bail!("Node host test source is stale or unbound");
        }
        for value in [&self.control_plane, &self.gateway] {
            let url = reqwest::Url::parse(value)?;
            if url.scheme() != "http"
                || !url
                    .host_str()
                    .is_some_and(|host| matches!(host, "127.0.0.1" | "[::1]"))
                || url.port().is_none_or(|port| port == 0)
                || !url.username().is_empty()
                || url.password().is_some()
                || url.query().is_some()
                || url.fragment().is_some()
                || url.path() != "/"
            {
                bail!("Node host test endpoints must be explicit loopback HTTP origins");
            }
        }
        if let Some(value) = &self.relay_url {
            let url = reqwest::Url::parse(value)?;
            if url.scheme() != "https"
                || !matches!(url.host_str(), Some("127.0.0.1" | "localhost" | "[::1]"))
                || url.port().is_none_or(|port| port == 0)
                || !url.username().is_empty()
                || url.password().is_some()
                || url.query().is_some()
                || url.fragment().is_some()
                || url.path() != "/"
            {
                bail!("Node host test relay must be an explicit loopback HTTPS origin");
            }
        }
        Ok(())
    }

    pub(super) fn configure(
        &self,
        control: ControlPlane,
        config: &HostConfig,
    ) -> Result<ControlPlane> {
        let expected_secret = if self.relay_url.is_some() {
            "sk_test_integration_plutonium"
        } else {
            "sk_test_node_host_local"
        };
        if config.api_key != "pk_test_0000000000000000000000000000000000000000"
            || config.secret_key != expected_secret
            || config.iroh_relay != self.relay_url.is_some()
        {
            bail!(
                "local Node tests require disposable credentials and matching local relay policy"
            );
        }
        control.with_testing_endpoints(&self.control_plane, &self.gateway)
    }

    pub(super) fn prepare_ticket(&self, client: &Client, ticket: String) -> Result<String> {
        let Some(lifetime) = self.ticket_lifetime_ms else {
            return Ok(ticket);
        };
        use openrtc::session_token::{decode_token_payload, expiring_ticket, split_ticket};
        let (endpoint, suffix) = split_ticket(&ticket);
        let payload = decode_token_payload(suffix.context("missing local ticket payload")?)
            .context("invalid local ticket payload")?;
        let expires = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)?
            .as_millis() as u64
            + lifetime;
        client.register_token_until(
            payload.token.clone(),
            payload.scope.to_string(),
            payload.max_connections,
            expires,
        );
        Ok(expiring_ticket(
            endpoint,
            &payload.token,
            payload.scope,
            payload.max_connections,
            Some(expires),
        ))
    }

    pub(super) async fn endpoint(&self, client: &Client, secret: &[u8; 32]) -> Result<String> {
        // No n0 publisher/resolver, public relay, carrier provider or LAN discovery.
        // The emulator owns the optional self-signed relay and its lifetime.
        let builder = iroh::Endpoint::builder(iroh::endpoint::presets::Minimal)
            .secret_key(iroh::SecretKey::from_bytes(secret))
            .relay_mode(iroh::RelayMode::Disabled)
            .alpns(vec![openrtc::native_node::PlutoniumProtocol::ALPN.to_vec()])
            .bind_addr("127.0.0.1:0".parse::<std::net::SocketAddr>()?)?;
        let builder = if let Some(url) = &self.relay_url {
            #[cfg(not(feature = "test-relay-client"))]
            bail!("local relay requires the test-relay-client feature: {url}");
            #[cfg(feature = "test-relay-client")]
            builder
                .relay_mode(iroh::RelayMode::custom([url.parse::<iroh::RelayUrl>()?]))
                .ca_tls_config(iroh::tls::CaTlsConfig::insecure_skip_verify())
        } else {
            builder
        };
        let endpoint = builder.bind().await?;
        if self.relay_url.is_some() {
            // As in Client's production initializer, publish only after the
            // relay address is available; browsers cannot use the UDP address.
            tokio::time::timeout(std::time::Duration::from_secs(10), endpoint.online())
                .await
                .context("local Node relay did not become ready before publication")?;
        }
        let id = endpoint.id().to_string();
        client
            .adopt_endpoint_with_router_mode(endpoint, true)
            .await?;
        Ok(id)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn local_lane_rejects_remote_origins_and_stale_source() {
        let mut lane = TestLane {
            run_id: "run-1".into(),
            control_plane: "http://127.0.0.1:5004".into(),
            gateway: "http://127.0.0.1:8787".into(),
            source_digest: "a".repeat(64),
            relay_url: None,
            ticket_lifetime_ms: None,
        };
        lane.validate(&"a".repeat(64)).unwrap();
        lane.ticket_lifetime_ms = Some(90_000);
        lane.validate(&"a".repeat(64)).unwrap();
        lane.ticket_lifetime_ms = Some(0);
        assert!(lane.validate(&"a".repeat(64)).is_err());
        lane.ticket_lifetime_ms = None;
        assert!(lane.validate("unbound").is_err());
        for origin in [
            "http://example.test:8080",
            "https://127.0.0.1:8080",
            "http://localhost:8080",
            "http://127.0.0.1",
            "http://u:p@127.0.0.1:8080",
            "http://127.0.0.1:8080/?token=x",
        ] {
            lane.gateway = origin.into();
            assert!(lane.validate(&"a".repeat(64)).is_err(), "{origin}");
        }
    }

    #[test]
    fn local_lane_rejects_public_or_credentialed_relays() {
        let mut lane = TestLane {
            run_id: "run-1".into(),
            control_plane: "http://127.0.0.1:5004".into(),
            gateway: "http://127.0.0.1:8787".into(),
            source_digest: "a".repeat(64),
            relay_url: Some("https://localhost:9444".into()),
            ticket_lifetime_ms: None,
        };
        lane.validate(&"a".repeat(64)).unwrap();
        for url in [
            "https://relay.example:9444",
            "http://127.0.0.1:9444",
            "https://localhost",
            "https://u:p@localhost:9444",
            "https://localhost:9444/?token=x",
        ] {
            lane.relay_url = Some(url.into());
            assert!(lane.validate(&"a".repeat(64)).is_err(), "{url}");
        }
    }
}