The node composes again when the vault says a secret changed, so a rotated secret or credential reaches it at once (issue 1126, ADR 0547 §2)
mesh/delivery closed: a newer head of the same pull request
mesh/check failed: integration: meshbuildgo/cmd/mesh-build-go.TestIntegrationLevelRunsAgainstARealStoreAndBusAndLeavesNothing: result = &{Level:integr…

This commit is contained in:
2026-10-26 19:26:25 +01:00
parent f8afaefba0
commit bb46a6ad0f
5 changed files with 50 additions and 5 deletions
+35 -2
View File
@@ -42,8 +42,14 @@ type Env struct {
// Own asks the vault's `own` in this node's engine's name, with the arguments given: its modules' own secrets and the
// pair credentials asked, sealed to its key (ADR 0550 §5). Nil asks nothing, and a module with either does not compose.
Own func(ctx context.Context, args map[string]any) (json.RawMessage, error)
// Hear subscribes a subject: here the vault's word that a secret changed (VaultSecretChanged), on which the node
// composes again, so a rotated secret or credential reaches it at once (ADR 0547 §2). Nil hears nothing.
Hear func(subject string) (<-chan []byte, func(), error)
}
// VaultSecretChanged is the vault's event that a secret changed: a name and its generation, never a value.
const VaultSecretChanged = "mesh.mod.mesh-vault.event.secret-changed"
// Composer composes the node and is the agent's watcher of its compositions (agent.Env.Composed).
type Composer struct {
env Env
@@ -128,6 +134,30 @@ func (c *Composer) Watch(ctx context.Context, _ string) (<-chan agent.Update, er
}
}(i, ch)
}
if c.env.Hear != nil {
heard, stop, err := c.env.Hear(VaultSecretChanged)
if err != nil {
return nil, fmt.Errorf("the node composes again when a secret changes, and cannot hear the vault say so: %w", err)
}
go func() {
defer stop()
for {
select {
case <-ctx.Done():
return
case _, ok := <-heard:
if !ok {
return
}
select {
case merged <- tagged{in: -1, ok: true}:
case <-ctx.Done():
return
}
}
}
}()
}
out := make(chan agent.Update, 64)
go func() {
defer close(out)
@@ -144,9 +174,12 @@ func (c *Composer) Watch(ctx context.Context, _ string) (<-chan agent.Update, er
c.env.Say(fmt.Sprintf("the watch of %s ended; the node composes no more until its agent starts again", ins[t.in].bucket))
return
}
if t.u.Caught {
switch {
case t.in < 0:
dirty = true // a secret changed: its new generation is asked of the vault at the compose
case t.u.Caught:
caught[t.in] = true
} else {
default:
ins[t.in].take(c, t.u)
dirty = true
}