Koi makes containers, applications, and devices delightfully discoverable, secure, and interconnected on your private network.
Bring container workloads onto mDNS. Give services meaningful .internal names.
Establish shared identity and trust. Connect applications across container, host, and
device boundaries. Observe them as they arrive, change, and leave.
One open-source, cross-platform binary. No hosted control plane. Built to work with the tools and networks you already have — even when the internet is unavailable.
Koi's promise has three parts:
| Outcome | What it means | |
|---|---|---|
| 🔎 | Find | Discover services over mDNS/DNS-SD, give them stable .internal names, and keep the view honest through leases and lifecycle events |
| 🔐 | Trust | Give local names and peers shared identity through a private CA, guided enrollment, OS trust-store installation, mTLS, and diagnosis |
| 🔗 | Connect | Let containers, host applications, devices, proxies, resolvers, monitors, and agents participate across their usual network boundaries |
Label a container and Koi can announce, name, certify, and watch it without touching the image. A container can also discover and watch LAN services through ordinary HTTP, or bridge UDP without giving up network isolation. Participation works both ways.
Everything is reachable three ways: a CLI built for discoverability, an HTTP API with interactive docs, and a web dashboard with a live mDNS network browser.
koi mdns discover # what's on this network?
koi dns add grafana 10.0.0.42 # give it a friendly name
koi certmesh create # mint a private CA (guided)
koi status # one view of everything
koi launch # open the dashboardOr over HTTP, from any language or script:
# Reads are open on localhost:
curl "http://localhost:5641/v1/mdns/discover?type=_http._tcp"
# Writes carry the daemon token (the CLI does this for you):
curl -X POST -H "x-koi-token: $TOKEN" http://localhost:5641/v1/mdns/announce \
-d '{"name": "My App", "type": "_http._tcp", "port": 8080}'The token lives in a breadcrumb file next to the daemon — see the security model for the two-line recipe per OS.
Install with one line (detects your OS/arch, verifies the checksum, puts koi
on your PATH):
curl -fsSL https://raw.githubusercontent.com/sylin-org/koi/main/install.sh | sh # Linux / macOSirm https://raw.githubusercontent.com/sylin-org/koi/main/install.ps1 | iex # WindowsThen:
koi mdns discover # works instantly, no daemon, no configFor always-on naming, trust, connectivity, and observation, run the daemon — foreground or as a system service:
koi --daemon # foreground (Ctrl+C to stop)
sudo koi install # or: install as a service (Linux/macOS)koi install # Windows (run as Administrator)The daemon listens on 127.0.0.1:5641. Bare koi shows live status plus the full
command catalog; koi <domain> shows curated examples; any command + ?
(e.g. koi mdns announce?) opens a detail page.
Koi began as an mDNS bridge for containers. Containers live on the network, but their default bridge networks do not carry multicast, so they cannot naturally see or join the local discovery fabric. A small host service could speak mDNS on the physical LAN and expose it to isolated workloads through ordinary interfaces.
That solved the first gap and exposed the rest. A workload that can be found still needs a useful name. A named service still needs identity and trust. Containers, applications, and devices still need practical ways to communicate across their boundaries, and the entire picture has to change when something arrives, becomes unhealthy, or leaves.
mDNS itself illustrates why those seams exist. It is the invisible backbone of local networking — printers, smart speakers, AirPlay, Chromecast, and IoT devices use it — but using it programmatically is surprisingly painful:
- Windows has native mDNS since Windows 10, but the Win32 APIs are poorly documented, 64-bit only, and don't expose full DNS-SD. The alternative — Apple's Bonjour — is effectively abandoned, with redistribution-prohibiting licensing.
- Linux has Avahi: Linux-only, semi-maintained, deeply coupled to D-Bus and systemd. systemd-resolved's mDNS is famously flaky.
- Containers can't do mDNS at all — bridge networks don't forward multicast,
and every workaround (
--network=host, macvlan, reflectors) sacrifices isolation or adds fragility. - Cross-platform libraries exist, but they're libraries: you re-implement discovery per language, and then hit the container multicast wall anyway.
Point solutions exist for most individual layers. The unowned problem is the connective tissue: letting a participant arrive, receive a name, be discovered, be reached, be trusted, report health, change, and leave cleanly. Koi owns those seams once, at the host and network boundaries, so every container and application does not have to solve them again.
Koi is the substrate under the tools you already run, not a replacement for them:
- Keep your DNS ad-blocker. Delegate one zone via conditional forwarding —
Pi-hole/AdGuard/dnsmasq forward
*.internalto Koi; everything else stays put. - Keep Tailscale. Point a tailnet split-DNS rule at Koi's resolver and remote devices resolve your LAN names; Koi covers the printers, TVs, and guests the tailnet can't see.
- Keep your reverse proxy. Certmesh certs land as files with reload hooks for Caddy/Traefik/NPM today; an ACME endpoint (RFC 8555, dns-01, port 5643) lets your proxy renew against Koi like a local Let's Encrypt — see the ACME guide.
- Ships now: Prometheus service-discovery export (
GET /v1/sd/prometheus), MCP tools so AI agents can discover and trust local services, and reading thetraefik.*/ caddy labels your containers already carry.
The doctrine: export in their formats, consume what you already wrote, and make every capability easy to leave — tools that are easy to stop using are easy to start using.
Find, Trust, and Connect are the product model. The implementation is a set of focused domains that compose at the daemon boundary:
Foundation:
| Capability | What it does | CLI | Guide |
|---|---|---|---|
| mDNS | Service discovery with lease lifecycle | koi mdns … |
mDNS guide |
| DNS | Local resolver; names from three sources | koi dns … |
DNS guide |
| Certmesh | Private CA, guided enrollment, truststore install | koi certmesh … |
Certmesh guide |
| Runtime | Container lifecycle → announce/name/cert/watch via labels | --runtime |
Runtime guide |
Connectivity and operations:
| Capability | What it does | CLI | Guide |
|---|---|---|---|
| Proxy | TLS endpoint for certmesh certs | koi proxy … |
Proxy guide |
| Health | HTTP/TCP checks feeding status & dashboard | koi health … |
Health guide |
| UDP | Host UDP sockets for bridge-networked containers | koi udp … |
UDP guide |
| Trust | Install/list/remove CA roots in the OS trust store; export the certmesh root; koi trust diagnose is the one-command trust-doctor |
koi trust … |
Trust protocol |
| MCP | Expose the LAN to AI agents. koi mcp serve is the stdio transport; the running daemon also serves the same surface over Streamable HTTP at /v1/mcp (token-authed; default on, --no-mcp-http to disable) |
koi mcp serve |
MCP guide |
| ACME | RFC 8555 server (dns-01, port 5643) so standard clients get certs from the CA | koi certmesh acme … |
ACME guide |
Every capability is runtime-toggleable (--no-dns, KOI_NO_DNS=1, …). The daemon
also exports for tools you already run — a Prometheus HTTP-SD endpoint
(GET /v1/sd/prometheus) and a DNS zone export (GET /v1/dns/zone?format=hosts|dnsmasq|json) —
and serves the dashboard (/), the mDNS network browser (/mdns-browser),
and interactive API docs (/docs).
Every node carries a posture — Open until it has a mesh identity, mTLS once it
does — and the same API behaves the same in both modes: messages can be signed
(verified offline against the mesh root) or sealed, and listeners flip live between
plaintext and mTLS without dropping connections. The category's defining failure is
silent trust state (certs that expire unnoticed, mesh that's secretly plaintext), so
Koi makes it loud: koi trust diagnose is the one-command health check — posture,
identity and renewal health, integrity, revocation, CA-trust-install, clock skew, each
with an exact remedy, exiting non-zero on anything red. The language-neutral wire
contract is in the trust protocol reference.
koi-embedded ships every backend by default. A lean consumer (e.g. a headless
container that only needs discovery/DNS) can drop the heavy, version-locked ones with a
single line — and re-arm any subset à la carte:
| Cargo feature | Default | Pulls in | Off → fallback |
|---|---|---|---|
docker |
on | bollard Docker/Podman client (=-pinned stubs) |
runtime backend → BackendUnavailable |
keyring |
on | OS keychain / Secret Service / D-Bus | vault uses its passphrase backend |
qr |
on | qrcode + image PNG codec |
enrollment prints the otpauth:// URI |
# everything (default) — unchanged
koi-embedded = "1.0.0-rc.1"
# lean: no bollard, no OS-keychain/D-Bus, no image codec
koi-embedded = { version = "1.0.0-rc.1", default-features = false }
# à la carte
koi-embedded = { version = "1.0.0-rc.1", default-features = false, features = ["docker"] }See ADR-014. The koi binary always ships
all backends.
Koi's container story — host daemon speaks multicast, containers speak plain HTTP —
is the design center. The HTTP API binds to loopback by default, so it works out of
the box with Docker Desktop (host.docker.internal); on native Linux, expose
it deliberately with --http-bind bridge (or 0.0.0.0) and hand containers the
token with koi token write (mutations still require it). CONTAINERS.md
has the patterns, the secure exposure recipe, and the label-driven runtime adapter
(the zero-code path).
OrbStack delivers a similar container-discovery inner loop, but only on macOS and as proprietary software; Koi is the open-source, cross-platform answer — the same story on Windows, Linux, and macOS.
| Platform | mDNS engine | Service integration |
|---|---|---|
| Windows | Pure Rust (no Bonjour) | Windows Service (SCM) + firewall rules |
| Linux | Pure Rust (no Avahi) | systemd unit |
| macOS | Pure Rust (no Bonjour) | launchd plist |
Zero OS dependencies, single static binary, and — unusual for this space — Windows is a first-class citizen.
Install script (recommended) — picks the right release archive for your
OS/arch, verifies its SHA-256, and installs onto your PATH. No root needed for
the default per-user location; set KOI_INSTALL_DIR or KOI_VERSION to override.
curl -fsSL https://raw.githubusercontent.com/sylin-org/koi/main/install.sh | sh # Linux / macOSirm https://raw.githubusercontent.com/sylin-org/koi/main/install.ps1 | iex # WindowsContainer — multi-arch (amd64/arm64) image on GHCR:
docker run --rm ghcr.io/sylin-org/koi:latest version
docker run -d --name koi -p 5641:5641 ghcr.io/sylin-org/koi:latest # daemonPrebuilt binaries: download from
GitHub Releases, extract, put koi
(or koi.exe) on your PATH.
crates.io: cargo install koi-net remains the source-build escape hatch (the
package is named koi-net — see Name below — and installs a koi
binary). It needs a Rust toolchain; the recommended installer above does not.
The v1 release-candidate line carries an artifact manifest and cargo-binstall
metadata, so Rust users can install the official prebuilt archive without compiling.
Prereleases are deliberately explicit: use cargo binstall koi-net --version 1.0.0-rc.1
or npx @sylin/koi@1.0.0-rc.1. Stable unqualified commands remain on the latest stable
release until Koi 1.0.0 ships.
See ADR-025 for the
artifact-first channel contract and its deliberately honest rollout states.
Build from source — requires Rust 1.92 or later:
git clone https://github.com/sylin-org/koi.git
cd koi
cargo build --releaseVerify — every release binary and the container image carry a signed build-provenance attestation. A trust tool should let you verify its own supply chain in one line:
gh attestation verify koi-v1.0.0-rc.1-x86_64-unknown-linux-musl.tar.gz --repo sylin-org/koi
gh attestation verify oci://ghcr.io/sylin-org/koi:1.0.0-rc.1 --repo sylin-org/koiKoi is at v1.0.0-rc.1: feature-complete for the v1 contract and ready for real-network evaluation. The release candidate brings the Find / Trust / Connect promise, artifact-first distribution, self-managing certmesh trust, truthful runtime recovery, and repeatable physical Windows/Linux validation into one candidate (CHANGELOG).
An RC is not the stable declaration: pin 1.0.0-rc.1, read the
upgrade guide, and report anything that disagrees with the docs.
The remaining road to 1.0 is defect discovery and contract hardening—not another expansion of
scope. The assessment and validation record remain public under
docs/assessment/ and docs/prompts/.
Start here: the documentation hub is the goal-keyed map of every guide and reference. New to Koi? The Getting started tutorial goes from install to a visible result in about a minute, and Trusted HTTPS across two machines is the headline end-to-end journey.
Using Koi: User Guide · Container Guide · Security model
Capability deep-dives: mDNS · DNS · DNS coexistence · Certmesh · ACME · Runtime · Proxy · Health · UDP · MCP · Integrations · System · Embedded (Rust)
For AI agents: koi mcp serve runs a Model Context Protocol
server over stdio (the daemon also serves the same surface over Streamable HTTP at
/v1/mcp, token-authed), turning the LAN into a substrate an agent can read and act on —
discover services, resolve and add names, take inventory, and announce the agent's
own service (auto-heartbeated, auto-retracted on exit). See the MCP guide
for the copy-paste client config.
Reference: Architecture · HTTP API · CLI · Wire protocol · Ceremony protocol · Envelope encryption
Decisions & direction: Find, Trust, Connect · Release channels · All ADRs · Assessment & roadmap
Koi (鯉) are the fish that live in garden ponds. They're visible — they surface, they announce themselves by simply existing. You look into the pond and see what's there. That was the original service-discovery idea: the network is the pond, the services are the koi. The project grew beyond seeing what is there, but the name still fits its purpose—helping everything in the local environment participate rather than remain hidden behind a boundary.
The binary is koi. The crates.io package is koi-net because koi was taken.
Koi's mDNS heavy lifting happens in mdns-sd, a pure-Rust mDNS/DNS-SD implementation by @keepsimple1 — probing, conflict resolution, known-answer suppression, goodbye packets, and all the multicast plumbing. Koi gives it a friendly front door and builds the naming and trust layers on top.
Dual licensed under Apache-2.0 and MIT. See LICENSE-APACHE and LICENSE-MIT. Free to use, embed, bundle, and redistribute, commercially or otherwise — just link back to this project somewhere reasonable.
See CONTRIBUTING.md — including how AI-assisted sessions should work in this repo (we eat our own dog food: see docs/prompts/CHARTER.md).
