diff --git a/modules/mesh-issues/go.mod b/modules/mesh-issues/go.mod index 992631ae..9b0e32bb 100644 --- a/modules/mesh-issues/go.mod +++ b/modules/mesh-issues/go.mod @@ -3,7 +3,7 @@ module mesh-issues go 1.25.0 require ( - git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261011031108-d9e24e95a242 + git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261011091942-4cf45b5ca0cf github.com/nats-io/nats-server/v2 v2.11.17 github.com/nats-io/nats.go v1.51.0 ) diff --git a/modules/mesh-issues/go.sum b/modules/mesh-issues/go.sum index 4a4207f4..70e80d38 100644 --- a/modules/mesh-issues/go.sum +++ b/modules/mesh-issues/go.sum @@ -1,7 +1,5 @@ -git.novox.be/novox/mesh-sdk/go v0.1.12 h1:QvmLlT88F+OYt8MOytMGM5ZEOo0fKmNZvNVeJe6gnTk= -git.novox.be/novox/mesh-sdk/go v0.1.12/go.mod h1:GFuZUElBZ9A++mxgIKo97aXXo+kV0uJ/UkbhQPPIbrY= -git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261011031108-d9e24e95a242 h1:vpYUOltncgA+GCh/KI8e6azffB1K7768Ka+VuvqXjiE= -git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261011031108-d9e24e95a242/go.mod h1:GFuZUElBZ9A++mxgIKo97aXXo+kV0uJ/UkbhQPPIbrY= +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261011091942-4cf45b5ca0cf h1:ED/+RszKwm86U7H/NZRbS4b5LzQ8n7rubqKmeFqFtvM= +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261011091942-4cf45b5ca0cf/go.mod h1:GFuZUElBZ9A++mxgIKo97aXXo+kV0uJ/UkbhQPPIbrY= github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0= github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI= github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo= diff --git a/modules/mesh-knowledge/README.md b/modules/mesh-knowledge/README.md index ea173edf..8c4fc963 100644 --- a/modules/mesh-knowledge/README.md +++ b/modules/mesh-knowledge/README.md @@ -29,10 +29,11 @@ accepted only on the operator's Approve, given on a channel that proves who answ `ask`, `get`, `holds` and `history` stay within `bound` bytes (4096 unless asked for, at most 65536) and say what they cut and how to read the rest. The module's own tool `knowledge_health` answers its health (below). -**Who acted** is the caller as the runtime proves it, never an argument: no verb takes one. Today the runtime -does not hand a module its caller. The SDK's tool call carries only a name and arguments, so proving the caller -needs an SDK change first (hq issue 365). Until then every change is recorded as by `unproven`. In this module -the caller is read in one place, `callerOf` in `main.go`, so the SDK change touches that one function. +**Who acted** is the caller as the runtime proves it, never an argument: no verb takes one. The tool runner takes +the caller's bus user from the caller-named subject a call arrived on and hands it through the SDK +(`stdio.CallerOf`, hq issue 365). A call on the subject that names no caller (served beside it for one release, hq +issue 464), or through a runtime from before issue 365, is recorded as by `unproven`. In this module the caller is +read in one place, `callerOf` in `main.go`. The rules about callers: @@ -147,7 +148,8 @@ the consolidated schema for a fresh database is `schema.sql`. A test holds the t - assignment to the control-node, and a postgres provider for its `postgres-database` provision; - the issue tracker serving `issue-tracker.reserve` (hq issue 424, merged as mesh-catalog#204) on the mesh; -- the SDK and the runtime proving the caller (hq issue 365) before any review is taken; +- the runtime proving the caller (hq issue 365: mesh-tools#27 and mesh-controller#224 delivered) before any review + is taken; - the import run once, then checked against hq record by record (hq issue 426). ## Tests diff --git a/modules/mesh-knowledge/cmd/mesh-knowledge/caller_test.go b/modules/mesh-knowledge/cmd/mesh-knowledge/caller_test.go new file mode 100644 index 00000000..6c7daf3a --- /dev/null +++ b/modules/mesh-knowledge/cmd/mesh-knowledge/caller_test.go @@ -0,0 +1,65 @@ +package main + +import ( + "bufio" + "encoding/json" + "io" + "testing" + "time" + + stdio "git.novox.be/novox/mesh-sdk/go" +) + +// whoCalled serves one tool that answers callerOf of its arguments, over the SDK's stdio loop as the runtime +// launches a bundle, and sends it one call with params. +func whoCalled(t *testing.T, params map[string]any) string { + t.Helper() + inR, inW := io.Pipe() + outR, outW := io.Pipe() + go func() { + _ = stdio.ServeOn("mesh-knowledge", []stdio.Tool{{Name: "who", Run: func(args map[string]any) (any, error) { + return callerOf(args), nil + }}}, inR, outW) + }() + defer inW.Close() + b, _ := json.Marshal(map[string]any{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": params}) + _, _ = inW.Write(append(b, '\n')) + got := make(chan string, 1) + go func() { + lines := bufio.NewScanner(outR) + if lines.Scan() { + var m struct { + Result struct { + Content []struct { + Text string `json:"text"` + } `json:"content"` + } `json:"result"` + } + _ = json.Unmarshal(lines.Bytes(), &m) + var who string + if len(m.Result.Content) > 0 { + _ = json.Unmarshal([]byte(m.Result.Content[0].Text), &who) + } + got <- who + } + }() + select { + case who := <-got: + return who + case <-time.After(3 * time.Second): + t.Fatal("the call was not answered") + return "" + } +} + +// **Who called is the caller the runtime proved** (hq issue 365): read from the call's runtime-written `_meta`, +// never from its arguments; Unproven where the runtime named none. +func TestTheCallerIsTheOneTheRuntimeProved(t *testing.T) { + if got := whoCalled(t, map[string]any{"name": "who", "arguments": map[string]any{"caller": "novox.node-tools"}, + "_meta": map[string]any{stdio.MetaCaller: "shanks.node-tools"}}); got != "shanks.node-tools" { + t.Errorf("callerOf answered %q, want the runtime's caller", got) + } + if got := whoCalled(t, map[string]any{"name": "who", "arguments": map[string]any{"caller": "novox.node-tools"}}); got != Unproven { + t.Errorf("a call the runtime named no caller for answered %q, want %s", got, Unproven) + } +} diff --git a/modules/mesh-knowledge/cmd/mesh-knowledge/kb.go b/modules/mesh-knowledge/cmd/mesh-knowledge/kb.go index 34b353f6..7afdba34 100644 --- a/modules/mesh-knowledge/cmd/mesh-knowledge/kb.go +++ b/modules/mesh-knowledge/cmd/mesh-knowledge/kb.go @@ -15,11 +15,13 @@ import ( "strings" "sync" "time" + + stdio "git.novox.be/novox/mesh-sdk/go" ) -// Unproven is who acted, as recorded, while the runtime does not tell a module who called it (hq issue 365). The -// caller is never taken from an argument. -const Unproven = "unproven" +// Unproven is who acted, as recorded, where the runtime proves no caller (hq issue 365): the SDK's word. The caller +// is never taken from an argument. +const Unproven = stdio.Unproven // proven says whether a caller is one the runtime proved. func proven(caller string) bool { return caller != "" && caller != Unproven } diff --git a/modules/mesh-knowledge/cmd/mesh-knowledge/main.go b/modules/mesh-knowledge/cmd/mesh-knowledge/main.go index 9b6783f5..62564673 100644 --- a/modules/mesh-knowledge/cmd/mesh-knowledge/main.go +++ b/modules/mesh-knowledge/cmd/mesh-knowledge/main.go @@ -103,11 +103,10 @@ func serve(k *KB, status string) { } } -// callerOf is who called, as the runtime proves it: the one place the caller is read, so the change that brings it -// is this one function. The runtime does not hand a module its caller yet (hq issue 365): the SDK's tool call carries -// a name and arguments only, so the SDK must first pass the runtime's proven caller to a tool. Until it does, every -// caller is Unproven. It is never read from the call's arguments, so no caller can name another. -func callerOf(map[string]any) string { return Unproven } +// callerOf is who called, as the runtime proves it: the one place the caller is read. The tool runner takes the +// caller's bus user from the subject the call arrived on and hands it through the SDK (hq issue 365); Unproven where +// it proves none. It is never read from the call's arguments, so no caller can name another. +func callerOf(args map[string]any) string { return stdio.CallerOf(args) } // hear subscribes to the router's word on this module's asks, asked again until the runtime takes it. func hear(k *KB) { diff --git a/modules/mesh-knowledge/go.mod b/modules/mesh-knowledge/go.mod index 0478dd45..29635555 100644 --- a/modules/mesh-knowledge/go.mod +++ b/modules/mesh-knowledge/go.mod @@ -3,7 +3,7 @@ module mesh-knowledge go 1.25.0 require ( - git.novox.be/novox/mesh-sdk/go v0.1.12 + git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261011091942-4cf45b5ca0cf github.com/jackc/pgx/v5 v5.11.0 github.com/nats-io/nats-server/v2 v2.11.17 github.com/nats-io/nats.go v1.51.0 diff --git a/modules/mesh-knowledge/go.sum b/modules/mesh-knowledge/go.sum index 4cfd952c..d21d5726 100644 --- a/modules/mesh-knowledge/go.sum +++ b/modules/mesh-knowledge/go.sum @@ -1,5 +1,5 @@ -git.novox.be/novox/mesh-sdk/go v0.1.12 h1:QvmLlT88F+OYt8MOytMGM5ZEOo0fKmNZvNVeJe6gnTk= -git.novox.be/novox/mesh-sdk/go v0.1.12/go.mod h1:GFuZUElBZ9A++mxgIKo97aXXo+kV0uJ/UkbhQPPIbrY= +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261011091942-4cf45b5ca0cf h1:ED/+RszKwm86U7H/NZRbS4b5LzQ8n7rubqKmeFqFtvM= +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261011091942-4cf45b5ca0cf/go.mod h1:GFuZUElBZ9A++mxgIKo97aXXo+kV0uJ/UkbhQPPIbrY= github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0= github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=