From 8899b62cd4305c0d3dbc5086e2f7de0b5a492e93 Mon Sep 17 00:00:00 2001 From: jochen Date: Fri, 13 Nov 2026 09:20:46 +0100 Subject: [PATCH] A-342 judge-node-env: a resource's own MESH_NODE is refused The node-engine sets MESH_NODE to its node's name in every process it runs; the judge refuses a resource whose env sets it, so the mesh's value is the only one. Builds: design 47 G A-342 Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h --- modules/mesh-delivery-indexer/judge/rules.go | 20 +++++++++++++++++++ .../mesh-delivery-indexer/judge/rules_test.go | 5 +++++ 2 files changed, 25 insertions(+) diff --git a/modules/mesh-delivery-indexer/judge/rules.go b/modules/mesh-delivery-indexer/judge/rules.go index f43908c0..1ea632ba 100644 --- a/modules/mesh-delivery-indexer/judge/rules.go +++ b/modules/mesh-delivery-indexer/judge/rules.go @@ -2845,6 +2845,8 @@ func parseManifest(raw []byte, predefined catalogue.PredefinedList) (catalogue.M // And the config values its resources are declared under (design 42 zfs-owns-zfs ยง2). problems = append(problems, IfConfigProblems(m)...) problems = append(problems, ConfigLinesProblems(m)...) + // And the node's name, which the node-engine sets in every process's env itself (design 47 G A-342). + problems = append(problems, NodeEnvProblems(m)...) // And what it requires optionally, and the files it delivers only while bound (novox/hq ADR 0330). problems = append(problems, OptionalProblems(m)...) // And which setting keys each mergeable file takes (setting_takes.go, novox/hq ADR 0326). @@ -4125,6 +4127,24 @@ func ProcessBusProblems(m catalogue.Manifest) []string { return problems } +// NodeEnv is the variable the node-engine sets to its node's name in the env of every process it runs (design 47 G +// A-342): a resource's own value of it is refused, so the mesh's is the only one. +const NodeEnv = "MESH_NODE" + +// NodeEnvProblems are the resources whose env sets NodeEnv themselves. +func NodeEnvProblems(m catalogue.Manifest) []string { + var problems []string + for _, r := range m.Resources { + if env, ok := r["env"].(map[string]any); ok { + if _, sets := env[NodeEnv]; sets { + problems = append(problems, fmt.Sprintf("%s's resource %v sets %s in its env, which the node-engine sets to "+ + "its node's name in every process it runs (design 47 G A-342)", m.Module, r["id"], NodeEnv)) + } + } + } + return problems +} + func filepathDir(p string) string { if i := strings.LastIndex(p, "/"); i > 0 { return p[:i] diff --git a/modules/mesh-delivery-indexer/judge/rules_test.go b/modules/mesh-delivery-indexer/judge/rules_test.go index 5c79bf82..f5e0c4a5 100644 --- a/modules/mesh-delivery-indexer/judge/rules_test.go +++ b/modules/mesh-delivery-indexer/judge/rules_test.go @@ -2456,6 +2456,11 @@ func caseAProcessBusNamesOnlyTheModulesOwn(t *testing.T) { t.Errorf("%s: %v; want a refusal naming %q", name, err, c.says) } } + // Design 47 G A-342: a resource setting the node's name in its own env is refused naming it. + ownNode := strings.Replace(mailSenderProcessManifest(t, ""), `"user":"mesh-mail-send"}`, `"user":"mesh-mail-send","env":{"MESH_NODE":"ace"}}`, 1) + if _, err := ParseManifest([]byte(ownNode)); err == nil || !strings.Contains(err.Error(), "MESH_NODE") { + t.Errorf("a process setting MESH_NODE itself: %v; want a refusal naming MESH_NODE", err) + } // Design 47 G A-340: an event the module consumes, its process may hear. consumed := strings.Replace(mailSenderProcessManifest(t, `{"consumes":["billing.paid"]}`), `"resources"`, `"consumes":["billing.paid"],"resources"`, 1) if _, err := ParseManifest([]byte(consumed)); err != nil {