HIPsHanzo Proposals
Back to HIPs
HIP-0069FinalStandards TrackInfrastructure

Service Discovery & Auto-Bridge

Hanzo AI
Created: 2026-05-08
Requires: HIP-0007, HIP-0010, HIP-0068

HIP-0069: Service Discovery & Auto-Bridge

Abstract

Every Hanzo-domain service on the local network advertises itself via multicast DNS (RFC 6762) under one canonical service type. hanzo-mcp auto-discovers neighbours, fetches their tool manifests, and exposes each as a namespaced tool family on its own MCP surface. New service joining the LAN → its tools light up in Claude automatically. Service leaves → tools drop on the next refresh.

This HIP closes the gap between HIP-007 (ZAP transport), HIP-0010 (MCP integration) and HIP-0068 (Ingress), removing every hard-coded URL, port range, lockfile registry and service-name env-var from the Hanzo stack.

Specification

Service type

_hanzo._tcp.local.

ONE type for the entire mesh. Roles differentiate via TXT, not by service-type. A consumer that wants e.g. only role=kms browses once and filters.

TXT record

keyrequiredexample
role✓mcp / iam / kms / mpc / base / engine / browser / node / desktop / gateway / static
server_id✓kms-laptop.local-12345
orgdefault hanzohanzo / lux / zoo / osage
version✓0.5.1
proto✓zap/1 / http/1.1 / grpc/1
capabilities✓sign,verify,encrypt
agent_labelkms-prod
authdefault nonenone / iam / mtls

Per-role contracts

Each role MUST accept the listed methods over its declared proto.

role=mcp

  • tools/list, tools/call, prompts/list, prompts/get, resources/list, resources/read

role=iam (Hanzo IAM)

  • iam.login, iam.token.exchange, iam.user.get, iam.user.list, iam.session.refresh, iam.session.revoke
  • HTTP equivalents under /v1/iam/*

role=kms (key store + sign)

  • kms.kv.get/put/list/delete, kms.sign, kms.verify, kms.encrypt, kms.decrypt, kms.key.generate/list/delete
  • auth=iam or auth=mtls REQUIRED

role=mpc (threshold signing)

  • mpc.session.start/join/contribute/finalize/status
  • capabilities lists supported curves (secp256k1,ed25519,bls12-381)

role=base (record store, IAM-native)

  • base.collection.list/get, base.record.list/get/create/update/delete, base.subscribe

role=engine (LLM serving)

  • engine.completion, engine.chat, engine.embed, engine.tokenize, engine.models.list

role=browser (extension or browser endpoint)

  • CDP-flavoured: Page.navigate, Runtime.evaluate, hanzo.listTabs, hanzo.screenshot

role=node (Hanzo Node)

  • node.status, node.peers.list, node.task.submit/status

role=desktop (Electron host)

  • desktop.window.list/focus, desktop.notify, desktop.shell.open

role=gateway (api.hanzo.ai-style ingress, see HIP-0068)

  • proto=http/1.1; mDNS used only for LAN-local discovery. Routes follow the platform's /v1/<role>/* convention.

role=static (CDN-style asset serving)

  • proto=http/1.1; capabilities: etag,range,gzip,brotli

hanzo-mcp auto-bridge

hanzo-mcp MUST:

  1. On startup, after registering its built-in tools, browse _hanzo._tcp.local. for ≥ 2 s.
  2. For every peer service whose role appears in the role enum (and whose org matches the local org or is unspecified), open a ZAP handshake, read the tool manifest from MSG_HANDSHAKE_OK, and register every tool under the namespace {role}.{toolName} (e.g. iam.login, kms.sign).
  3. Re-poll every 30 s. Register new peers; deregister vanished peers.
  4. NOT register tools from the local server (skip on server_id match).

For role=mcp peers, tools merge in unprefixed (deduplicated by name) so multiple cooperating MCPs appear as one surface.

Single source of truth

Every binding (Python, TypeScript, Go, Rust, Swift) MUST own:

  • The constant _hanzo._tcp.local.
  • The TXT key list above
  • The role enum

…in ONE module per language. No hand-rolled mDNS records anywhere in the tree.

languagepackage
Pythonhanzo-zap-mdns (pip install)
TypeScript@hanzo/zap-mdns (npm)
Gogithub.com/zap-proto/mdns (not yet published)
Rusthanzo-zap-mdns (crates.io)
SwiftHanzoZapMDNS (SwiftPM)

The Go binding is written but not yet a fetchable module: the source is mdns/go in luxfi/zap, and the import path above is the one it declares once tagged.

Backwards compatibility

None. _hanzo._tcp.local. is the record; there is no second one to also advertise. A host with no mDNS responder discovers nothing, which is the correct answer rather than a reason to keep a port probe.

Reference implementation

  • Python: ~/work/zap/mdns/ — hanzo_zap_mdns package, including expand_mcp_with_neighbors for the auto-bridge.
  • Spec doc lives alongside the implementation at ~/work/zap/mdns/SPEC.md.

Security considerations

  • mDNS announcements are visible to anyone on the same broadcast domain. Roles that handle secrets (kms, iam, base) MUST set auth=iam or auth=mtls; consumers MUST honour the auth field and refuse unauthenticated requests.
  • TXT capability advertisements are NOT a substitute for capability negotiation; consumers MUST verify each call against the peer's actual auth-checked tool list.
  • org SHOULD be honoured: an org=lux consumer SHOULD NOT auto-bridge org=osage services without explicit operator opt-in.

Adoption order

  1. Python: hanzo-mcp, hanzo-tools-browser ✓ (shipped)
  2. TypeScript / Node: hanzo-iam, hanzo-desktop, @hanzo/extension
  3. Go: hanzo-kms, hanzo-base, hanzo-mpc, hanzo-ingress, hanzo-gateway, hanzo-static
  4. Rust: hanzo-engine, hanzo-node
  5. Swift: future iOS / macOS native clients

Each adoption lands as a 5-line change in the service's startup path — import the binding, call publish(port, role, server_id, …). The canonical example for each language ships in the binding repo's examples/ directory.