Batteries-included, runtime-agnostic mDNS / DNS-SD for Rust — a Sans-I/O protocol core with pluggable async drivers.
Introduction
hick is the high-level entry point to the hick mDNS family. It wires the
Sans-I/O protocol core (mdns-proto) to a ready-to-use async driver
(hick-reactor) so you can publish and discover services on the local
network in a few lines, with tokio out of the box.
mDNS (Multicast DNS, RFC 6762) and DNS-SD (RFC 6763) let peers discover one another on a local link with no central DNS server — the basis of "zeroconf" service discovery. Note that many cloud and shared-infrastructure networks restrict multicast, so mDNS is best suited to office, home, or private networks.
Highlights
- Sans-I/O core. All protocol logic lives in
mdns-protoas pure state machines — no sockets, threads, or clocks — making it deterministic and exhaustively unit-tested. The drivers only shuttle bytes and time in and out. - Runtime-agnostic. Drive it from
tokio,smol, orcompio(thread-per-core) with no change to protocol behavior. no_stdand bare-metal. The core runs onalloc— orheapless, with no allocator at all — andhick-smoltcp/hick-embassybring full mDNS to embedded targets over smoltcp.- RFC 6762 / 6763 conformant. Probing and announcing, name-conflict detection with automatic renaming, known-answer and duplicate-question suppression, TTL-accurate caching, and TTL=0 goodbyes on withdrawal.
unsafe-minimal. The protocol core is#![forbid(unsafe_code)];unsafeis confined to the platform socket and control-message plumbing.- Observable, à la carte. Opt into
tracingspans,metricscounters, pollablestats, ordefmton embedded — each behind a feature flag and compiled out when unused.
The family
The crates split protocol logic from I/O, mirroring the quinn layering:
| Crate | Role |
|---|---|
hick |
this crate — batteries-included facade (core + default driver) |
mdns-proto |
Sans-I/O protocol state machines (no_std-capable) |
hick-udp |
multicast UDP socket setup |
hick-reactor |
runtime-agnostic async driver (tokio & smol) |
hick-compio |
compio (thread-per-core) async driver |
hick-smoltcp |
bare-metal mDNS engine over smoltcp (no_std + alloc) |
hick-embassy |
embassy async driver, built on hick-smoltcp (no_std + alloc) |
Installation
[]
= "0.1" # tokio runtime by default
For smol instead of tokio:
[]
= { = "0.1", = false, = ["smol"] }
For the compio (completion-based, thread-per-core) runtime:
[]
= { = "0.1", = false, = ["compio"] }
For bare-metal (no_std) targets, enable smoltcp (the runtime-agnostic
engine) or embassy (the embassy-net async driver) — neither pulls in std:
[]
= { = "0.1", = false, = ["embassy"] }
Each driver is also published as a standalone crate (hick-compio,
hick-smoltcp, hick-embassy) if you would rather depend on it directly.
The minimum supported Rust version (MSRV) is 1.91.0 (edition 2024).
Example
Common, runtime-agnostic types (Name, QuerySpec, ServiceSpec, …) live at the
crate root; the runtime-pinned entry point and driver handles live in the per-runtime
module (hick::tokio, hick::smol).
use ;
use ;
async
Feature flags
tokio(default) — drive thetokioruntime viahick-reactor.smol— drive thesmolruntime.compio— drive thecompio(thread-per-core) runtime viahick-compio.smoltcp— expose the bare-metalhick-smoltcpengine (no_std+alloc).embassy— expose thehick-embassyasync driver (no_std+alloc).tracing— forward structuredtracingspans/events from every enabled driver and the proto core.stats— enableno_std-safe atomic counters; read a snapshot viaendpoint.stats().metrics— bridge counters to themetricsfacade (Prometheus/StatsD exporters). Impliesstats.defmt— forwarddefmtlog events to the bare-metal drivers (hick-smoltcp/hick-embassy). Has no effect when only std drivers are enabled.
Observability
tracing — structured events and spans
Add features = ["tracing"] and install a subscriber in main:
The driver task and mdns-proto emit tracing events at DEBUG / INFO /
WARN level during probing, service registration, conflict resolution, cache
updates, and query completion.
metrics — Prometheus / StatsD counters
Add features = ["metrics"] and install a recorder before starting the
endpoint:
// Example using the prometheus exporter from the `metrics-exporter-prometheus` crate:
new.install.unwrap;
All mDNS counters (packets, bytes, cache hits, queries, …) are forwarded
automatically under mdns_* metric names.
stats — polling counters without a recorder
When you only need periodic read-outs without a full metrics exporter,
enable stats and call endpoint.stats():
use ;
let endpoint = server.await?;
// … run for a while …
let snap = endpoint.stats;
println!;
stats() returns a hick_trace::stats::StatsSnapshot — a plain Copy
struct of u64 fields covering packets, bytes, cache, queries, services,
probes, conflicts, and more. See the hick-trace crate for the full list.
defmt — bare-metal embedded logging
For no_std targets using hick-smoltcp or hick-embassy, enable defmt
instead of (or alongside) stats. Log events are forwarded through the
defmt framework. On std drivers this feature is a no-op.
Per-crate observability details: hick-trace (shim) · mdns-proto ·
hick-reactor · hick-compio · hick-smoltcp · hick-embassy ·
hick-udp.
License
hick is under the terms of both the MIT license and the Apache License
(Version 2.0).
See LICENSE-APACHE, LICENSE-MIT for details.
Copyright (c) 2025 Al Liu.