// time-sync's tools bundle (novox/hq to-be 42 Phase 1, research 026/05), and its step. // // Served by the node's runtime over MCP on stdio through the Go SDK (ADR 0188, ADR 0193) when it is // started with no arguments. Started as `time-sync-tools retire …` it is the module's step, // which the host runs once as root for every version of the bundle (timesync.go says why). package main import ( "context" "fmt" "os" "strings" stdio "git.novox.be/novox/mesh-sdk/go" ) // binaryName is what the build names this bundle's executable: the manifest's `binary`. const binaryName = "time-sync-tools" func bg() context.Context { return context.Background() } func main() { m := ThisMachine() if len(os.Args) > 1 { if os.Args[1] != "retire" || len(os.Args) < 3 { fmt.Fprintf(os.Stderr, "usage: %s [retire …]\n", binaryName) os.Exit(2) } r, err := m.Retire(os.Args[2:], Wants, Dangling, os.Remove) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } fmt.Printf("disabled: %s; dangling links taken out: %s\n", orNone(r.Disabled), orNone(r.Removed)) return } // An empty name serves as the module the runtime names (MESH_SERVED_MODULE): time-sync. if err := stdio.Serve("", tools(m)); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } func orNone(list []string) string { if len(list) == 0 { return "none" } return strings.Join(list, ", ") } func tools(m *Machine) []stdio.Tool { return []stdio.Tool{ { Name: "time_sync_status", Description: "Whether the clock is synchronised and how well: NTP on, synchronised, timesyncd's unit state, the server " + "it uses, offset, delay and jitter in milliseconds, stratum and packet count (timedatectl timesync-status, with " + "its every line), and any other time daemon installed beside it. timesyncd not answering is said, not failed.", Input: schema(map[string]any{}), Run: func(map[string]any) (any, error) { return m.Status() }, }, { Name: "time_sync_servers", Description: "The servers timesyncd uses (the one now, the configured, the fallback, the link's and the runtime's) " + "and every configuration file in the order it reads them with the NTP= and FallbackNTP= each sets; which file " + "decides, and a note when a drop-in sorting after the mesh's wins.", Input: schema(map[string]any{}), Run: func(map[string]any) (any, error) { return m.Servers() }, }, { Name: "time_sync_sync_now", Description: "Restart timesyncd (sudo -n), which asks its server at once, and answer the status after up to ten " + "seconds of waiting for its first packet.", Input: schema(map[string]any{}), Run: func(map[string]any) (any, error) { return m.SyncNow() }, }, } }