Restart what reads a secret family member when the member is given again (issue 405)
mesh/merge-gate pass: builds build-agent, mesh-controller → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without …
mesh/repo-check fail: its merge-check.sh failed: --- FAIL: TestTheSecondControllerWaitsAndTakesAHigherEpochOnHandover (4.67s)
mesh/delivery superseded: a newer head of the same pull request

secretsReadBy looked only at secrets declared by name, so a container
mounting a member kept the value it started with.
This commit is contained in:
2026-10-11 02:51:11 +02:00
parent 1dc9961c43
commit d6b867a87a
2 changed files with 56 additions and 4 deletions
+18 -4
View File
@@ -1076,7 +1076,7 @@ func (r Resolution) compose(with Rendering, owner map[string]string,
// credential the mesh had replaced, because its manifest restarted it on its
// environment file and nobody had thought to name the credential too. Composed here so
// no manifest has to say it, for a container or a daemon that names the secret's path.
if reads := secretsReadBy(copied, m); len(reads) > 0 {
if reads := secretsReadBy(copied, m, with.Needed[m.Module]); len(reads) > 0 {
copied["restart-on"] = withRestartOn(copied["restart-on"], reads)
}
// **A version prepares its state before it runs** (novox/hq ADR 0135). Derived from the
@@ -2392,7 +2392,12 @@ func portOfEndpoint(values map[string]any, ports map[string]int) {
// — named in its volumes, its environment or its env-files by the secret's placed path — as
// restart-on ids. Nothing for other shapes, and nothing for a scheduled or run-once process, which
// the host refuses a restart-on for (it runs again anyway, and reads the file afresh).
func secretsReadBy(resource map[string]any, m Manifest) []string {
//
// **A member of a secret family given here is read the same way** (novox/hq issue 405, ADR 0283 decision 6):
// it is an own secret placed at its own path, and a container that mounted it would keep the value it
// started with when the operator gives it again. given is the module's own secrets sealed to this
// machine; a member not given is no file, so nothing restarts on it.
func secretsReadBy(resource map[string]any, m Manifest, given map[string]string) []string {
kind := fmt.Sprint(resource["type"])
if kind != "container" && kind != "process" {
return nil
@@ -2404,9 +2409,18 @@ func secretsReadBy(resource map[string]any, m Manifest) []string {
for _, key := range []string{"volumes", "env", "env-file"} {
mentioned = append(mentioned, stringsIn(resource[key])...)
}
paths := map[string]string{}
for name, own := range m.OwnSecrets.Plain() {
paths[name] = own.Path
}
for name, sealed := range given {
if own, family, ok := m.OwnSecrets.Lookup(name); ok && family != "" && sealed != "" {
paths[name] = own.Path
}
}
var out []string
for _, name := range sortedKeys(m.OwnSecrets.Plain()) {
path := m.OwnSecrets[name].Path
for _, name := range sortedKeys(paths) {
path := paths[name]
if path == "" {
continue
}