// avahi's tools bundle (novox/hq to-be 42 Phase 1, research 026/05): a process the node's runtime // launches and speaks MCP over stdio to, through the Go SDK (ADR 0188, ADR 0193). It reads the // daemon, the name service's wiring and the packet filter's view of mDNS, browses the local network // for services, resolves a name, and lists what the machine publishes. It changes nothing. package main import ( "context" "fmt" "os" stdio "git.novox.be/novox/mesh-sdk/go" ) // binaryName is what the build names this bundle's executable: the manifest's `binary`. const binaryName = "avahi-tools" func bg() context.Context { return context.Background() } func main() { // An empty name serves as the module the runtime names (MESH_SERVED_MODULE): avahi. if err := stdio.Serve("", tools(ThisMachine())); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } func tools(m *Machine) []stdio.Tool { return []stdio.Tool{ { Name: "avahi_status", Description: "The daemon's state and version, its configuration as set, the name service switch's hosts line and whether mdns is on it, " + "whether nss-mdns is installed, whether the packet filter accepts inbound mDNS (UDP 5353), whether systemd-resolved runs beside it, " + "and notes naming what keeps discovery from working.", Input: schema(map[string]any{}), Run: func(map[string]any) (any, error) { return m.GetStatus() }, }, { Name: "avahi_browse", Description: "Listen on the local network for a few seconds (avahi-browse -prt) and answer every service announced, with interface, " + "protocol, name, type, host, address, port and TXT where it resolved; narrowed to one service type when given. Hearing nothing says why it may be.", Input: schema(map[string]any{ "seconds": map[string]any{"type": "integer", "description": "how long to listen (default 5, at most 15)"}, "type": map[string]any{"type": "string", "description": "one service type, e.g. _ssh._tcp (optional)"}, }), Run: func(args map[string]any) (any, error) { n, err := whole(args, "seconds", 5, 1, 15) if err != nil { return nil, err } kind, err := text(args, "type", false) if err != nil { return nil, err } return m.Browse(n, kind) }, }, { Name: "avahi_resolve", Description: "Resolve a .local name to its addresses through avahi, and through the name service (getent) beside it, or an address to its name. " + "An answer from avahi that the name service lacks shows nsswitch unwired; no answer says why it may be.", Input: schema(map[string]any{ "name": map[string]any{"type": "string", "description": "a host name; .local is added when missing"}, "address": map[string]any{"type": "string", "description": "an address to name instead"}, }), Run: func(args map[string]any) (any, error) { name, err := text(args, "name", false) if err != nil { return nil, err } address, err := text(args, "address", false) if err != nil { return nil, err } return m.Resolve(name, address) }, }, { Name: "avahi_services", Description: "What this machine publishes from /etc/avahi/services: each file with the service's name, types and ports.", Input: schema(map[string]any{}), Run: func(map[string]any) (any, error) { return m.Services() }, }, } }