{"id":15762355,"url":"https://github.com/williamvenner/searchlight","last_synced_at":"2025-04-28T16:11:23.761Z","repository":{"id":64938142,"uuid":"579552117","full_name":"WilliamVenner/searchlight","owner":"WilliamVenner","description":"📡 Rust mDNS library designed with user interfaces in mind","archived":false,"fork":false,"pushed_at":"2023-09-26T13:54:34.000Z","size":149,"stargazers_count":29,"open_issues_count":2,"forks_count":0,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-10-11T11:33:20.065Z","etag":null,"topics":["client","discovery","dns","dns-sd","lan","mdns","mdns-sd","rust","server"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/WilliamVenner.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-12-18T04:02:19.000Z","updated_at":"2024-10-04T18:13:24.000Z","dependencies_parsed_at":"2024-10-25T10:18:03.899Z","dependency_job_id":"61ce3057-f8d1-4751-9d64-30582551a40e","html_url":"https://github.com/WilliamVenner/searchlight","commit_stats":{"total_commits":41,"total_committers":2,"mean_commits":20.5,"dds":"0.36585365853658536","last_synced_commit":"cc0cca24b6d3223d48eb7ce14881f369c7409284"},"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WilliamVenner%2Fsearchlight","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WilliamVenner%2Fsearchlight/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WilliamVenner%2Fsearchlight/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WilliamVenner%2Fsearchlight/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/WilliamVenner","download_url":"https://codeload.github.com/WilliamVenner/searchlight/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251342724,"owners_count":21574245,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["client","discovery","dns","dns-sd","lan","mdns","mdns-sd","rust","server"],"created_at":"2024-10-04T11:08:48.182Z","updated_at":"2025-04-28T16:11:23.736Z","avatar_url":"https://github.com/WilliamVenner.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n    \u003cimg alt=\"License\" src=\"https://img.shields.io/crates/l/searchlight\"/\u003e\n    \u003ca href=\"https://crates.io/crates/searchlight\"\u003e\u003cimg alt=\"crates.io\" src=\"https://img.shields.io/crates/v/searchlight.svg\"/\u003e\u003c/a\u003e\n    \u003ca href=\"https://docs.rs/searchlight/\"\u003e\u003cimg alt=\"docs.rs\" src=\"https://docs.rs/searchlight/badge.svg\"/\u003e\u003c/a\u003e\n    \u003ca href=\"https://github.com/WilliamVenner/searchlight/actions/workflows/ci.yml\"\u003e\u003cimg alt=\"Workflow Status\" src=\"https://github.com/WilliamVenner/searchlight/workflows/ci/badge.svg\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003e📡 Searchlight\u003c/h1\u003e\n\nSearchlight is an mDNS server \u0026 client library designed to be simple, lightweight and easy to use,\neven if you just have basic knowledge about mDNS.\n\nIn layman's terms, Searchlight is a library for broadcasting and discovering \"services\" on a local network.\nThis technology is part of the same technology used by Chromecast, AirDrop, Phillips Hue, and et cetera.\n\n**Searchlight is designed with user interfaces in mind.**\nThe defining feature of this library is that it keeps track of the presence of services on the network,\nand notifies you when they come and go, allowing you to update your user interface accordingly,\nproviding a user experience that is responsive, intuitive and familiar to a scanning list for\nWiFi, Bluetooth, Chromecast, etc.\n\n- **🌐 IPv4 and IPv6** - Support for both IPv4 and IPv6.\n- **✨ OS support** - Support for Windows, macOS and most UNIX systems.\n- **📡 Broadcasting** - Send out service announcements to the network and respond to discovery requests. (mDNS server)\n- **👽 Discovery** - Discover services on the network and keep track of their presence. (mDNS client)\n- **🧵 Single threaded** - Searchlight operates on just a single thread, thanks to the [Tokio](https://tokio.rs/) async runtime \u0026 task scheduler.\n- **🤸 Flexible API** - No async, no streams, no channels, no bullsh*t. Just provide an event handler function and bridge the gap between your application and Searchlight however you like.\n- **👻 Background runtime** - Discovery and broadcasting can both run in the background on separate threads, providing a handle to gracefully shut down if necessary.\n- **📨 UDP** - All networking, including discovery and broadcasting, is connectionless and done over UDP.\n- **🔁 Loopback** - Support for receiving packets sent by the same socket, intended to be used in tests.\n- **🎯 Interface targeting** - Support for targeting specific network interface(s) for discovery and broadcasting.\n\n# Usage\n\nAdd Searchlight to your [`Cargo.toml`](https://doc.rust-lang.org/cargo/reference/manifest.html) file:\n\n```toml\n[dependencies]\nsearchlight = \"0.3.2\"\n```\n\nTo learn more about how to use Searchlight, see the [documentation](https://docs.rs/searchlight/).\n\n# Examples\n\n## 👽 Discovery\n\nFind all Chromecasts on the network.\n\n```rust\nuse searchlight::{\n    discovery::{DiscoveryBuilder, DiscoveryEvent},\n    dns::{op::DnsResponse, rr::RData},\n    net::IpVersion,\n};\n\nfn get_chromecast_name(dns_packet: \u0026DnsResponse) -\u003e String {\n    dns_packet\n        .additionals()\n        .iter()\n        .find_map(|record| {\n            if let Some(RData::SRV(_)) = record.data() {\n                let name = record.name().to_utf8();\n                let name = name.strip_suffix('.').unwrap_or(\u0026name);\n                let name = name.strip_suffix(\"_googlecast._tcp.local\").unwrap_or(\u0026name);\n                let name = name.strip_suffix('.').unwrap_or(\u0026name);\n                Some(name.to_string())\n            } else {\n                None\n            }\n        })\n        .unwrap_or_else(|| \"Unknown\".into())\n}\n\nDiscoveryBuilder::new()\n    .service(\"_googlecast._tcp.local.\")\n    .unwrap()\n    .build(IpVersion::Both)\n    .unwrap()\n    .run(|event| match event {\n        DiscoveryEvent::ResponderFound(responder) =\u003e {\n            println!(\n                \"Found Chromecast {} at {}\",\n                get_chromecast_name(\u0026responder.last_response),\n                responder.addr.ip()\n            );\n        }\n        DiscoveryEvent::ResponderLost(responder) =\u003e {\n            println!(\n                \"Chromecast {} at {} has gone away\",\n                get_chromecast_name(\u0026responder.last_response),\n                responder.addr.ip()\n            );\n        }\n        DiscoveryEvent::ResponseUpdate { .. } =\u003e {}\n    })\n    .unwrap();\n```\n\n## 📡 Broadcasting\n\nBroadcast a service on the network, and verify that it can be discovered.\n\n```rust\nuse searchlight::{\n    broadcast::{BroadcasterBuilder, ServiceBuilder},\n    discovery::{DiscoveryBuilder, DiscoveryEvent},\n    net::IpVersion,\n};\nuse std::{\n    net::{IpAddr, Ipv4Addr},\n    str::FromStr,\n};\n\nlet (found_tx, found_rx) = std::sync::mpsc::sync_channel(0);\n\nlet broadcaster = BroadcasterBuilder::new()\n    .loopback()\n    .add_service(\n        ServiceBuilder::new(\"_searchlight._udp.local.\", \"HELLO-WORLD\", 1234)\n            .unwrap()\n            .add_ip_address(IpAddr::V4(Ipv4Addr::from_str(\"192.168.1.69\").unwrap()))\n            .add_txt_truncated(\"key=value\")\n            .add_txt_truncated(\"key2=value2\")\n            .build()\n            .unwrap(),\n    )\n    .build(IpVersion::V4)\n    .unwrap()\n    .run_in_background();\n\nlet discovery = DiscoveryBuilder::new()\n    .loopback()\n    .service(\"_searchlight._udp.local.\")\n    .unwrap()\n    .build(IpVersion::V4)\n    .unwrap()\n    .run_in_background(move |event| {\n        if let DiscoveryEvent::ResponderFound(responder) = event {\n            found_tx.try_send(responder).ok();\n        }\n    });\n\nprintln!(\"Waiting for discovery to find responder...\");\n\nprintln!(\"{:#?}\", found_rx.recv().unwrap());\n\nprintln!(\"Shutting down...\");\n\nbroadcaster.shutdown().unwrap();\ndiscovery.shutdown().unwrap();\n\nprintln!(\"Done!\");\n```\n\n# Contribution\n\nUnless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the MIT license, shall be dual licensed as above, without any additional terms or conditions.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwilliamvenner%2Fsearchlight","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwilliamvenner%2Fsearchlight","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwilliamvenner%2Fsearchlight/lists"}