fix: fill a ptr-secret placeholder from the file-owner's credential, not the last consumer's

The provider-seal-key gate: on a node with two modules requiring the same provision (baserow
and letta both consuming postgres), sealedFor matched a need by provision NAME alone, so a
file's ${secret:X} placeholder took whichever consumer's sealed credential came last in
r.Needs -- the OTHER module's password. baserow was handed letta's password and could not
authenticate. The secrets:-map delivery path already guards this (For == m.Module, novox/hq
04-ISSUES/022); the ${secret:...} placeholder path did not. Added the same guard.

Also dedups the contributions file: when provider and consumer are co-located, grantsFor
enumerates the same-node consumer, so a consumer was emitted twice into the provider's
receives file (once full with its grant, once partial). The m.Contributes loop now skips a
(provision, module) the grants loop already carried; non-grant contributions (routes) still emit.

Regression test added: two consumers of one provision each get their own credential. Proven
end-to-end on a two-node lab install (mesh-lab assigned-two-node-db): baserow and letta on one
node, substrate on another, each authenticates with its own minted password.

Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
This commit is contained in:
2026-09-06 13:46:55 +02:00
parent 3b0f17b8fd
commit 99a753994e
3 changed files with 68 additions and 2 deletions
+5 -1
View File
@@ -71,7 +71,11 @@ func sealedFor(m Manifest, needs []Needed, with Rendering) (map[string]string, e
"${secret:%s} could mean either — rename one of them", m.Module, to, to, to)
}
for i := range needs {
if needs[i].Name == to && needs[i].Sealed != "" {
// `For == m.Module`, not name alone: on a node with two modules requiring the same
// provision, both appear in `needs`, and matching by name would fill ${secret:X} with
// whichever came last — the other module's credential (novox/hq 04-ISSUES/022). The
// `secrets:`-map path already guards this way; the ${secret:…} placeholder path did not.
if needs[i].Name == to && needs[i].For == m.Module && needs[i].Sealed != "" {
sealed[to] = needs[i].Sealed
}
}