// postgres-provider: postgres's code, one binary the node's runtime launches and speaks MCP to over // stdio through the Go SDK (novox/hq ADR 0188, 0193). It serves postgres's tools and the mesh-store // seat's verbs and, beside them, runs long: the provisioner that makes postgres the provider of the // mesh `postgres-database` interface, and an audit line for each database it provisions or withdraws. // // stdout is the MCP channel; everything this module says, it says on stderr. package main import ( "context" "encoding/json" "fmt" "os" "time" stdio "git.novox.be/novox/mesh-sdk/go" ) func say(format string, args ...any) { fmt.Fprintf(os.Stderr, "[postgres] "+format+"\n", args...) } func main() { pg, err := ClientFromEnv(os.Getenv) if err != nil { // Without the server there is nothing to serve and nothing to provision; said, not fatal, // so the runtime does not restart a process that cannot do better. say("%v; serving no tools and provisioning nothing", err) if err := stdio.Serve("", nil); err != nil { say("%v", err) os.Exit(1) } return } var h *Harness if consumers := handedConsumersFile(os.Getenv, os.Stat); consumers != "" { // Handed to the provisioner (novox/hq ADR 0373): the provider helper answers the consumers its // machine's client fills the file with, and the grant file still gives the consumers not moved // (issue 937). Retirement is the harness's, never run, so its guards and its tools stand (issue 780). h = &Harness{ Resource: ProvisionName, Receives: os.Getenv("MESH_RECEIVES"), Adapter: provisioner{pg: pg, announce: announce}, Log: func(format string, args ...any) { fmt.Fprintf(os.Stderr, format+"\n", args...) }, Announce: func(event string, body map[string]any) { if err := stdio.Emit(event, body); err != nil { say("emit %s failed: %v", event, err) } }, Node: os.Getenv("MESH_NODE"), } run, p, bus, err := moved(consumers, h) if err != nil { say("%v", err) } else { go func() { if err := run.Run(context.Background(), p, bus); err != nil { say("the provider helper stopped: %v", err) } }() } } else if receives := os.Getenv("MESH_RECEIVES"); receives == "" { say("MESH_RECEIVES is not set — the provisioner cannot run without it") } else { h = &Harness{ Resource: "postgres-database", Receives: receives, Adapter: provisioner{pg: pg, announce: announce}, Log: func(format string, args ...any) { fmt.Fprintf(os.Stderr, format+"\n", args...) }, // A consumer failed for minutes is said on the bus, where the controller hears it and // `status` names it (novox/hq ADR 0224). Announce: func(event string, body map[string]any) { if err := stdio.Emit(event, body); err != nil { say("emit %s failed: %v", event, err) } }, Node: os.Getenv("MESH_NODE"), } go h.Run(context.Background()) } // Started on the grant path while the module is to be handed, it restarts itself when its client first fills // the consumers' file, so the first fill moves it onto the helper without a hand restart (issue 780): `restart-on` // names a service or a container, and the only one here is the database. if at := os.Getenv("MESH_CONSUMERS_FILE"); at != "" && handedConsumersFile(os.Getenv, os.Stat) == "" { go exitWhenFilled(at, os.Stat, 5*time.Second, func() { say("%s is filled: restarting to answer from it", at) os.Exit(0) }) } go audit() // The store's reader is narrowed to what modules declare readable, at once and every minute, whether // or not the provisioner runs: without a contributions file it is given nothing (novox/hq issue 337). go pg.RunReader(context.Background(), os.Getenv("MESH_RECEIVES"), time.Minute, say) // The retirement tools beside postgres's own: what is retired here, approving or rejecting what waits // for a person, and deleting a retired consumer (novox/hq ADR 0230). if err := stdio.Serve("", append(Tools(pg), RetirementTools(h)...)); err != nil { say("%v", err) os.Exit(1) } } // handedConsumersFile is the consumers' file when postgres is handed to the provisioner: the file MESH_CONSUMERS_FILE // names, which the controller places only where it hands the module. Empty, where it is not set or the file is not // there, and postgres then reads the grant directory exactly as it always did (issue 780). func handedConsumersFile(getenv func(string) string, stat func(string) (os.FileInfo, error)) string { at := getenv("MESH_CONSUMERS_FILE") if at == "" { return "" } if _, err := stat(at); err != nil { return "" } return at } // exitWhenFilled calls filled once the file is there, asking every so often. func exitWhenFilled(at string, stat func(string) (os.FileInfo, error), every time.Duration, filled func()) { for { if _, err := stat(at); err == nil { filled() return } time.Sleep(every) } } // announce emits a lifecycle event without letting a broker hiccup fail the provisioning itself. func announce(event string, body map[string]string) { if err := stdio.Emit(event, body); err != nil { say("emit %s failed: %v", event, err) } } // audit keeps a line for each database granted and withdrawn — observability the provider is best // placed to log. The events are its own, read back from its consumer (`consumes`). func audit() { subscribe := func() error { return stdio.Subscribe("postgres.database.*", func(e stdio.Envelope) error { var body struct { Consumer string `json:"consumer"` Database string `json:"database"` } _ = json.Unmarshal(e.Body, &body) switch e.Key { case "postgres.database.provisioned": say("database provisioned for %s (db %s)", body.Consumer, body.Database) case "postgres.database.deprovisioned": say("database retired, kept (db %s)", body.Database) } return nil }) } // Asked again until the runtime takes it: the first ask may come before Serve is running. for wait := time.Second; ; wait = min(wait*2, time.Minute) { if err := subscribe(); err == nil { say("auditing database lifecycle events") return } else if wait >= 8*time.Second { say("subscribing to the lifecycle events failed, will retry: %v", err) } time.Sleep(wait) } }