Review of L2, round 2: a refusal at the first compose after a start keeps what the module's have said runs (1); each have of a self-composing node carries the values its composition holds sealed, and the composer reuses them after a restart, so a rotated secret moves no composition at a restart (2); pin mesh-sdk dd9d9d5 (issue 1126, ADR 0547 §3)
mesh/delivery closed: a newer head of the same pull request
mesh/check checking 4476e5808047 on ff9386dcccc2 | novox/mesh-host#149 in group feat/1126-nodes-compose

This commit is contained in:
2026-10-26 20:23:16 +01:00
parent 58de6aa10a
commit 4476e58080
9 changed files with 255 additions and 72 deletions
+32 -24
View File
@@ -55,6 +55,9 @@ type Env struct {
// keeps the vault's box.
Running func() []delivery.Composed
Unseal func(sealed string) ([]byte, error)
// Held is the sealed values a module's have says it runs (delivery.Have.Sealed), kept by the agent on the bus: what
// runs after a restart, before the agent applied anything, and after the switch once the kept declaration is old.
Held func(module string) []string
}
// VaultSecretChanged is the vault's event that a secret changed: a name and its generation, never a value.
@@ -431,39 +434,44 @@ func (c *Composer) refused(why string) {
}
}
// runningBox is the box the node already runs that opens to the same value as box, or box: read before c.mu is taken,
// since the agent's lock is taken to read what runs.
// runningBox is the box the node already runs that opens to the same value as box, or box: what the agent runs, and
// what each wanted module's have says it runs sealed (Held). Read before c.mu is taken, since the agent's lock is taken
// to read what runs.
func (c *Composer) runningBox(box string) string {
if c.env.Running == nil || c.env.Unseal == nil {
if c.env.Unseal == nil || (c.env.Running == nil && c.env.Held == nil) {
return box
}
value, err := c.env.Unseal(box)
if err != nil {
return box
}
for _, comp := range c.env.Running() {
for _, raw := range comp.Resources {
var r map[string]json.RawMessage
if json.Unmarshal(raw, &r) != nil {
continue
}
for _, field := range []string{"sealed", catalogue.VaultSealedField} {
f, ok := r[field]
if !ok {
continue
}
var held string
if json.Unmarshal(f, &held) != nil {
held = string(f) // a vault box is carried as its JSON
}
if held == "" || held == box {
continue
}
if v, err := c.env.Unseal(held); err == nil && bytes.Equal(v, value) {
return held
}
var held []string
if c.env.Running != nil {
for _, comp := range c.env.Running() {
held = append(held, agent.SealedIn(comp)...)
}
}
if c.env.Held != nil {
c.mu.Lock()
modules := make([]string, 0, len(c.wants))
for m := range c.wants {
if m != agent.NodeModule {
modules = append(modules, m)
}
}
c.mu.Unlock()
sort.Strings(modules)
for _, m := range modules {
held = append(held, c.env.Held(m)...)
}
}
for _, h := range held {
if h == box {
continue
}
if v, err := c.env.Unseal(h); err == nil && bytes.Equal(v, value) {
return h
}
}
return box
}