Review of L2: a node that cannot compose says so in each wanted module's have, failed, while it keeps what it runs (H2); the composer keeps the box the node runs when it opens to the vault's value, so neither the switch nor a restart moves a composition (H1); pin mesh-sdk 3968849, whose node asks the vault only for a module declaring a secret (H8's failure) (issue 1126)
mesh/delivery closed: a newer head of the same pull request
mesh/check failed: unit: ..test: ✔ a transient empty list for four passes retires nothing; five stable passes retire, data kept (290.408814ms) | 58de6…

This commit is contained in:
2026-10-26 20:09:40 +01:00
parent cdd3f3fd4f
commit 58de6aa10a
7 changed files with 207 additions and 15 deletions
+27 -3
View File
@@ -497,7 +497,23 @@ func nodeAgent(ctx context.Context, opts options, signer ed25519.PublicKey, sess
// The node composes its own modules once the catalogue places it (novox/hq ADR 0547 §2, ADR 0549, issue 1126): its
// placement key is read at the agent's start; until then it applies the controller's compositions as before.
run, stopRun := context.WithCancel(linked)
if composer := selfComposer(linked, session, node, say); composer != nil {
// What the node runs and how it opens a sealed value, for the composer to keep the boxes it runs; and the agent
// told when the node cannot compose, so each wanted module's have says it.
hooks := composerHooks{
running: func() []delivery.Composed {
if now := current.get(); now != nil {
return now.Everything()
}
return nil
},
unseal: sealOpener(opts.state),
refused: func(why string) {
if now := current.get(); now != nil {
now.NotComposed(linked, why)
}
},
}
if composer := selfComposer(linked, session, node, say, hooks); composer != nil {
env.Composed, env.SelfComposed, env.Trusted, env.Policy = composer, true, nil, composer.Policy
} else {
// And once the catalogue places it, the agent begins again composing its own: the placement key appearing ends
@@ -788,7 +804,14 @@ func applyContext(ctx context.Context) (context.Context, context.CancelFunc) {
// selfComposer is the node's own composer when the catalogue places the node (its key `placement.<node>` in the
// indexer's state), and nil, said once, while it does not or the key cannot be read.
func selfComposer(ctx context.Context, session *link.Session, node string, say func(string)) *selfcompose.Composer {
// composerHooks are what the composer reads of the agent and the machine (selfcompose.Env Running, Unseal, Refused).
type composerHooks struct {
running func() []delivery.Composed
unseal func(string) ([]byte, error)
refused func(string)
}
func selfComposer(ctx context.Context, session *link.Session, node string, say func(string), hooks composerHooks) *selfcompose.Composer {
key, err := delivery.PlacementKey(node)
if err != nil {
say("this node applies the controller's compositions: " + err.Error())
@@ -822,7 +845,8 @@ func selfComposer(ctx context.Context, session *link.Session, node string, say f
return session.AskVault(ctx, "own", args)
},
// And the vault's word that a secret changed, on which it composes again (ADR 0547 §2).
Hear: session.Hear})
Hear: session.Hear,
Running: hooks.running, Unseal: hooks.unseal, Refused: hooks.refused})
}
// endWhenPlaced calls stop once this node's placement key appears in the indexer's placement (a value after the watch