claude-code: refuse registering what a session runs, so no agent can plant it in the operator's sessions
mesh/merge-gate pass: builds claude-code → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request

The tool runner is not told who called, so a hook, a managed setting, a
permission rule or a tool server registered through these tools could come
from an agent confined on another node and run as the operator there
(hq issue 340). They are set at the controller's terminal now; a skill,
subagent or command that runs something by itself is refused too, kept
hooks and settings are never rendered, and claude_code_registrations
lists what is registered and by which node.
This commit is contained in:
jochen
2026-10-09 02:30:11 +02:00
parent a1406e7d79
commit 736743fedc
11 changed files with 650 additions and 336 deletions
@@ -318,6 +318,18 @@ func (b *bus) Delete(key string) error {
return nil
}
func (b *bus) Get(key string) (json.RawMessage, bool, error) {
v, ok := b.kept[key]
if !ok {
return nil, false, nil
}
raw, err := json.Marshal(v)
return raw, true, err
}
// existing is a server registered before registering was refused (novox/hq issue 340), as the state still holds it.
func (b *bus) existing(key string, entry map[string]any) { _ = b.Put(key, entry) }
func (b *bus) Keys() ([]string, error) {
var out []string
for k := range b.kept {
@@ -338,27 +350,15 @@ func (b *bus) join(p Paths, w map[string]string) *ServerView {
func noOthers() ([]string, error) { return nil, nil }
func TestRegisteringHerePutsItUnderThisNodesKeyAndAsksAboutTheOthers(t *testing.T) {
p, w := node(t, "laptop")
b := &bus{kept: map[string]map[string]any{}}
v := b.join(p, w)
r, err := RegisterServer(p, Registration{Name: "search", Entry: map[string]any{"type": "http", "url": "https://s.example/mcp"}}, b, v, writer(w),
func() ([]string, error) { return []string{"laptop", "server", "desktop"}, nil })
if err != nil || r["here"] != "changed" || !strings.Contains(r["also"].(string), "server, desktop") || b.kept["laptop.search"] == nil {
t.Fatalf("%v %v %v", r, err, b.kept)
}
if !strings.Contains(w["managed-mcp.json"], `"search"`) {
t.Fatal("not rendered")
}
}
// A server registered before stays (novox/hq issue 340): every node takes what the state holds for it, a node
// that joins later reads the current set, and an unregistration reaches every node.
func TestEveryNodeRegistrationReachesTheOthersAndALateNodeReadsIt(t *testing.T) {
a, wa := node(t, "laptop")
s, ws := node(t, "server")
b := &bus{kept: map[string]map[string]any{}}
va := b.join(a, wa)
b.join(s, ws)
_, _ = RegisterServer(a, Registration{Name: "docs", Entry: map[string]any{"type": "stdio", "command": "docs-mcp"}, Nodes: []string{"all"}}, b, va, writer(wa), noOthers)
b.existing("all.docs", map[string]any{"type": "stdio", "command": "docs-mcp"})
if Registered(s)["docs"] == nil || !strings.Contains(ws["managed-mcp.json"], "docs-mcp") {
t.Fatalf("the other node did not take it: %v", Registered(s))
}
@@ -379,8 +379,8 @@ func TestANodesOwnRegistrationOverridesTheOneForEveryNode(t *testing.T) {
b := &bus{kept: map[string]map[string]any{}}
va := b.join(a, wa)
b.join(s, ws)
_, _ = RegisterServer(a, Registration{Name: "x", Entry: map[string]any{"type": "http", "url": "https://all"}, Nodes: []string{"all"}}, b, va, writer(wa), noOthers)
_, _ = RegisterServer(a, Registration{Name: "x", Entry: map[string]any{"type": "http", "url": "https://laptop"}}, b, va, writer(wa), noOthers)
b.existing("all.x", map[string]any{"type": "http", "url": "https://all"})
b.existing("laptop.x", map[string]any{"type": "http", "url": "https://laptop"})
if Registered(a)["x"]["url"] != "https://laptop" || Registered(s)["x"]["url"] != "https://all" {
t.Fatalf("%v %v", Registered(a), Registered(s))
}
@@ -390,13 +390,13 @@ func TestANodesOwnRegistrationOverridesTheOneForEveryNode(t *testing.T) {
}
}
func TestABadEntryIsRefusedBeforeAnythingIsPut(t *testing.T) {
func TestABadEntryIsNeverTaken(t *testing.T) {
p, w := node(t, "laptop")
b := &bus{kept: map[string]map[string]any{}}
v := b.join(p, w)
r, _ := RegisterServer(p, Registration{Name: "mesh", Entry: map[string]any{"type": "http", "url": "https://x"}}, b, v, writer(w), noOthers)
if r["registered"] != false || len(b.kept) != 0 {
t.Fatalf("%v %v", r, b.kept)
b.existing("laptop.mesh", map[string]any{"type": "http", "url": "https://x"})
if Registered(p)["mesh"] != nil || strings.Contains(w["managed-mcp.json"], "https://x") {
t.Fatalf("the mesh's own entry was replaced: %v", Registered(p))
}
if done, _ := OnServerChange(v, ServerChange{Key: "server.b", Op: "put", Value: map[string]any{"type": "http", "url": "https://b"}}, p, writer(w)); done != "" {
t.Fatal("another node's key changed this one")