A drifted own secret is read from a resource id as a node runs it, its module's name first (only-office.needs-jwt), so the node agent finds what to hand the vault (issue 1126, cause C4; review round 2)
mesh/delivery merged: source.main moved to novox/mesh-sdk@79eb58efe4b0
mesh/delivery-group group fix/1126-seed-same merged: every member is merged
mesh/check failed: integration: github.com/novox/mesh-host/cmd/mesh-host.TestNatsANodeThePlacementNamesComposesAndAppliesItsOwn: the node did not comp…

delivery.OwnNeedName reads <module>.needs-<name> and the bare needs-<name>; OwnValuesDiffer and the node agent use it.
The fixtures carry the id shape captured from novox's shadow answer.
This commit is contained in:
2026-10-28 10:00:29 +01:00
parent 1062b33d32
commit 3028c7ec4e
2 changed files with 30 additions and 10 deletions
+14 -3
View File
@@ -27,16 +27,27 @@ type OwnValueDiffers struct {
// ownNeedPrefix starts the id of the file a module's own secret is placed in (catalogue.NeedID). // ownNeedPrefix starts the id of the file a module's own secret is placed in (catalogue.NeedID).
var ownNeedPrefix = catalogue.NeedID("") var ownNeedPrefix = catalogue.NeedID("")
// OwnNeedName is the name of module's own secret whose file a resource id names, and whether it names one. On a node a
// resource's id carries its module's name (`only-office.needs-jwt`, as a live shadow answer shows it); the bare form a
// declaration composes (`needs-jwt`, catalogue.NeedID) is read too. An id of another module's is not this module's.
func OwnNeedName(module, id string) (string, bool) {
if module != "" {
id = strings.TrimPrefix(id, module+".")
}
name, own := strings.CutPrefix(id, ownNeedPrefix)
return name, own && name != "" && !strings.Contains(name, ".")
}
// OwnValuesDiffer is every own secret a's shown bodies say holds another value composed than running: a body of a // OwnValuesDiffer is every own secret a's shown bodies say holds another value composed than running: a body of a
// module's own secret's file (catalogue.NeedID) masked MaskedAnotherValue on either side. Sorted by module and name, each // module's own secret's file (OwnNeedName: `<module>.needs-<name>` on a node) masked MaskedAnotherValue on either side. Sorted by module and name, each
// once. A body the answer did not show (beyond MaxBodies) is not read. // once. A body the answer did not show (beyond MaxBodies) is not read.
func OwnValuesDiffer(a CandidateAnswer) []OwnValueDiffers { func OwnValuesDiffer(a CandidateAnswer) []OwnValueDiffers {
seen := map[OwnValueDiffers]bool{} seen := map[OwnValueDiffers]bool{}
var out []OwnValueDiffers var out []OwnValueDiffers
for _, d := range a.Differs { for _, d := range a.Differs {
for _, b := range d.Bodies { for _, b := range d.Bodies {
name, own := strings.CutPrefix(b.ID, ownNeedPrefix) name, own := OwnNeedName(d.Module, b.ID)
if !own || name == "" || !anotherValue(b) { if !own || !anotherValue(b) {
continue continue
} }
o := OwnValueDiffers{Module: d.Module, Name: name} o := OwnValueDiffers{Module: d.Module, Name: name}
+16 -7
View File
@@ -356,19 +356,28 @@ func TestPlacement(t *testing.T) {
} }
// Issue 1126, cause C4: the own secrets whose two sides open to two values are read from the shown bodies, and // Issue 1126, cause C4: the own secrets whose two sides open to two values are read from the shown bodies, and
// every body so masked is counted, own or not. // every body so masked is counted, own or not.
drift := CompareComposedWith(map[string]Composed{"k": comp(sealedFile("needs-jwt", "box-1", "0600"), // The ids are as a node runs them, its module's name first (captured from novox's shadow answer, 2026-10-28:
sealedFile("needs-salt", "box-4", "0600"), sealedFile("conf", "box-6", "0600"))}, // `only-office.needs-jwt`); another module's need, named here, is not nats's.
[]Composed{comp(sealedFile("needs-jwt", "box-2", "0600"), sealedFile("needs-salt", "box-5", "0600"), drift := CompareComposedWith(map[string]Composed{"k": comp(sealedFile("nats.needs-jwt", "box-1", "0600"),
sealedFile("conf", "box-7", "0600"))}, sealedFile("nats.needs-salt", "box-4", "0600"), sealedFile("nats.conf", "box-6", "0600"),
sealedFile("other.needs-key", "box-8", "0600"))},
[]Composed{comp(sealedFile("nats.needs-jwt", "box-2", "0600"), sealedFile("nats.needs-salt", "box-5", "0600"),
sealedFile("nats.conf", "box-7", "0600"), sealedFile("other.needs-key", "box-9", "0600"))},
func(a, b string) (same, known bool) { return a == "box-4", true }) func(a, b string) (same, known bool) { return a == "box-4", true })
got := CandidateAnswer{Head: "h", Differs: drift.Differs} got := CandidateAnswer{Head: "h", Differs: drift.Differs}
if own := OwnValuesDiffer(got); len(own) != 1 || own[0] != (OwnValueDiffers{Module: "nats", Name: "jwt"}) { if own := OwnValuesDiffer(got); len(own) != 1 || own[0] != (OwnValueDiffers{Module: "nats", Name: "jwt"}) {
t.Errorf("the own secrets with another value are %+v, want nats jwt alone", own) t.Errorf("the own secrets with another value are %+v, want nats jwt alone", own)
} }
if n := ValuesDiffer(got); n != 2 { if n := ValuesDiffer(got); n != 3 {
t.Errorf("%d bodies hold another value, want 2 (jwt and conf)", n) t.Errorf("%d bodies hold another value, want 3 (jwt, conf and other's key)", n)
} }
if w := WithValuesDiffer(map[string]CandidateAnswer{"g": got}); w["g"].ValuesDiffer != 2 { for id, want := range map[string]string{"nats.needs-jwt": "jwt", "needs-jwt": "jwt", "other.needs-jwt": "",
"nats.conf": "", "nats.needs-": ""} {
if name, own := OwnNeedName("nats", id); name != want && (own || want != "") {
t.Errorf("%s names nats's own secret %q (%v), want %q", id, name, own, want)
}
}
if w := WithValuesDiffer(map[string]CandidateAnswer{"g": got}); w["g"].ValuesDiffer != 3 {
t.Errorf("the answer carries values_differ %d", w["g"].ValuesDiffer) t.Errorf("the answer carries values_differ %d", w["g"].ValuesDiffer)
} }
if c := CompareComposed(nil, []Composed{{Node: "novox", Resources: []json.RawMessage{}}}); len(c.Removed) != 1 || c.Removed[0] != "node" { if c := CompareComposed(nil, []Composed{{Node: "novox", Resources: []json.RawMessage{}}}); len(c.Removed) != 1 || c.Removed[0] != "node" {