{"id":15651862,"url":"https://github.com/tenderlove/zeroconf","last_synced_at":"2026-01-23T02:42:28.316Z","repository":{"id":212747494,"uuid":"732213718","full_name":"tenderlove/zeroconf","owner":"tenderlove","description":"Multicast DNS client and server written in pure Ruby","archived":false,"fork":false,"pushed_at":"2025-05-21T17:05:59.000Z","size":78,"stargazers_count":36,"open_issues_count":0,"forks_count":4,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-12-03T18:29:13.731Z","etag":null,"topics":["multicast-dns","ruby"],"latest_commit_sha":null,"homepage":"","language":"Ruby","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/tenderlove.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-12-16T00:03:39.000Z","updated_at":"2025-11-05T11:27:58.000Z","dependencies_parsed_at":"2024-08-15T18:03:33.058Z","dependency_job_id":"88101f91-acc3-4f2b-b388-290b1d43fadf","html_url":"https://github.com/tenderlove/zeroconf","commit_stats":{"total_commits":50,"total_committers":3,"mean_commits":"16.666666666666668","dds":0.09999999999999998,"last_synced_commit":"57ec4249ba5e01388e3f7a243eac10772b0e00f4"},"previous_names":["tenderlove/zeroconf"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/tenderlove/zeroconf","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tenderlove%2Fzeroconf","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tenderlove%2Fzeroconf/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tenderlove%2Fzeroconf/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tenderlove%2Fzeroconf/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tenderlove","download_url":"https://codeload.github.com/tenderlove/zeroconf/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tenderlove%2Fzeroconf/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28679138,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-23T01:00:35.747Z","status":"online","status_checked_at":"2026-01-23T02:00:08.296Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["multicast-dns","ruby"],"created_at":"2024-10-03T12:40:26.987Z","updated_at":"2026-01-23T02:42:28.311Z","avatar_url":"https://github.com/tenderlove.png","language":"Ruby","readme":"# ZeroConf: a Pure Ruby multicast DNS client and server\n\nZeroConf is a multicast DNS client and server written in pure Ruby.  Use it to\nfind multicast services, or advertise your own!\n\n## Client Example\n\nI've got some Elgato Key lights that can be controlled via the network.\nIn this example, we're going to use ZeroConf to find them on the network,\nthen we'll connect with `net/http` and control them.\n\nLets start by getting a list of Multicast DNS services on the network:\n\n```ruby\nall_services = ZeroConf.find_services\nservice = all_services.grep(/_elg/).first # =\u003e \"_elg._tcp.local\"\n```\n\nWe've found the Elgato service name on the network, next lets get the host\nnames and connection information:\n\n```ruby\naddr_infos = ZeroConf.find_addrinfos(service)\n```\n\nI have two lights on my network, and both of them have an IPv4 and IPv6 addresses,\nso we get back an array with 4 tuples:\n\n```\n[[\"elgato-key-light-2d93.local\", #\u003cAddrinfo: 10.0.1.249:9123 (elgato-key-light-2d93.local)\u003e],\n [\"elgato-key-light-2d93.local\", #\u003cAddrinfo: [fe80::3e6a:9dff:fe19:b313]:9123 (elgato-key-light-2d93.local)\u003e],\n [\"elgato-key-light-48c6.local\", #\u003cAddrinfo: 10.0.1.151:9123 (elgato-key-light-48c6.local)\u003e],\n [\"elgato-key-light-48c6.local\", #\u003cAddrinfo: [fe80::3e6a:9dff:fe19:3a99]:9123 (elgato-key-light-48c6.local)\u003e]]\n ```\n\nWe can connect either via IP address or host name.  In this example, I'm just\ngoing to get the unique host names and ports, connect to those, and shut off\nthe lights.\n\n```ruby\nrequire \"uri\"\nrequire \"net/http\"\nrequire \"json\"\n\n# Get unique host / port\nconnection_info = addr_infos.uniq(\u0026:first).map { |host, addr| [host, addr.ip_port] }\n\n# Turn the lights off\nconnection_info.each { |host, port|\n  req = Net::HTTP::Put.new(\"/elgato/lights\")\n  req.body = { \"numberOfLights\": 1, \"lights\": [ { \"on\": 0 } ] }.to_json\n  Net::HTTP.start(host, port) { |http| http.request(req) }\n}\n```\n\nHere is the whole script:\n\n```ruby\nrequire \"zeroconf\"\nrequire \"uri\"\nrequire \"net/http\"\nrequire \"json\"\n\n# Find all services\nall_services = ZeroConf.find_services\n\n# Look for Elgato\nservice = all_services.grep(/_elg/).first || raise # =\u003e \"_elg._tcp.local\"\n\n# Get addrinfo objects associated with that service\naddr_infos = ZeroConf.find_addrinfos(service)\n\n# Get unique host / port\nconnection_info = addr_infos.uniq(\u0026:first).map { |host, addr| [host, addr.ip_port] }\n\n# Turn the lights off\nconnection_info.each { |host, port|\n  req = Net::HTTP::Put.new(\"/elgato/lights\")\n  req.body = { \"numberOfLights\": 1, \"lights\": [ { \"on\": 0 } ] }.to_json\n  Net::HTTP.start(host, port) { |http| http.request(req) }\n}\n```\n\nOnce you've discovered the host names of the devices, you really don't need to\nuse the ZeroConf library anymore.  You can just hardcode the host names in your\nscript.  If the host names ever change, just rediscover them with ZeroConf.\n\n## Server Example\n\nAdvertising a service requires the service name, the hostname, and the port\nyou want to advertise on the network.\n\nFor example, we want to advertise an HTTP service running on port 8080, and\nwe want the hostname to be \"test-hostname.local\".\n\nUse the following code (note that the hostname should _omit_ `.local`):\n\n```ruby\nrequire \"zeroconf\"\n\nZeroConf.service \"_http._tcp.local.\", 8080, \"test-hostname\"\n```\n\nWhile this script is running, you should be able to ping `test-hostname.local`.\n\n`ZeroConf.service` can be run in a thread, so we could combine it with WEBrick\nto run an HTTP server and simultaneously advertise the server:\n\n```ruby\nrequire \"zeroconf\"\nrequire \"webrick\"\n\nport = 8080\nhost = \"test-hostname\"\n\nRactor.new(port, host) { |port, host|\n  ZeroConf.service \"_http._tcp.local.\", port, host\n}\n\nserver = WEBrick::HTTPServer.new(:Port =\u003e port,\n                                 :SSLEnable =\u003e false,\n                                 :ServerAlias =\u003e host + \".local\")\n\nserver.mount_proc '/' do |req, res|\n  res.body = 'Hello, world!'\nend\n\ntrap 'INT' do server.shutdown end\n\nserver.start\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftenderlove%2Fzeroconf","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftenderlove%2Fzeroconf","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftenderlove%2Fzeroconf/lists"}