The node agent hands the vault what runs of an own secret its shadow answer says another value of, through move-in's replace_running, while it applies the controller's compositions (issue 1126, cause C4)
A new step, internal/ownresync, after each shadow answer: for each own secret masked as another value, while the node applies the controller's compositions and runs the one its want names, it asks the vault's move-in with replace_running: true and the running value sealed to the call's key, once per value, and says what it asked and what the vault answered. The answer written to the node's have key carries values_differ. Pins the group's mesh-sdk head 1062b33 (go.sum is filled once that head is pushed).
This commit is contained in:
@@ -556,9 +556,15 @@ func nodeAgent(ctx context.Context, opts options, signer ed25519.PublicKey, sess
|
||||
// its own key, `<node>.node`, under candidates.
|
||||
shadowHooks := hooks
|
||||
shadowHooks.refused = nil
|
||||
shadow := selfcompose.NewShadow(composerEnv(session, node, say, shadowHooks),
|
||||
shadowEnv := composerEnv(session, node, say, shadowHooks)
|
||||
// And, after each answer, what runs of an own secret the answer says the vault holds another value of, handed to
|
||||
// the vault while the node applies the controller's compositions (issue 1126, cause C4; ownresync).
|
||||
composes := &composesNow{}
|
||||
resync := ownResync(session, node, say, shadowEnv, composes)
|
||||
shadow := selfcompose.NewShadow(shadowEnv,
|
||||
func(ctx context.Context, answers map[string]delivery.CandidateAnswer) {
|
||||
session.NoteCandidates(ctx, answers)
|
||||
session.NoteCandidates(ctx, delivery.WithValuesDiffer(answers))
|
||||
go resync.take(ctx, answers)
|
||||
})
|
||||
go shadow.Keep(run, link.Backoff) // this run's alone: a switch begins a run, and its shadow, again
|
||||
composer, why := selfComposer(linked, session, node, say, hooks)
|
||||
@@ -569,12 +575,15 @@ func nodeAgent(ctx context.Context, opts options, signer ed25519.PublicKey, sess
|
||||
continue
|
||||
}
|
||||
if composer != nil {
|
||||
composes.set(delivery.ComposesOwn)
|
||||
env.Composed, env.SelfComposed, env.Trusted, env.Policy = composer, true, nil, composer.Policy
|
||||
session.NoteComposes(linked, delivery.ComposesOwn, why)
|
||||
// And once the catalogue no longer places it, the agent begins again on the controller's compositions: the
|
||||
// placement key deleted ends this run, the way back (issue 1126, the seed's revert).
|
||||
go endWhenUnplaced(run, session, node, stopRun, say)
|
||||
} else {
|
||||
composes.set(delivery.ComposesController)
|
||||
go resync.again(run)
|
||||
session.NoteComposes(linked, delivery.ComposesController, why)
|
||||
// And once the catalogue places it, the agent begins again composing its own: the placement key appearing ends
|
||||
// this run, so a node does not wait for a restart to follow the seed of its placement (ADR 0549 §4).
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"sync"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
|
||||
"github.com/novox/mesh-host/internal/identity"
|
||||
"github.com/novox/mesh-host/internal/link"
|
||||
"github.com/novox/mesh-host/internal/ownresync"
|
||||
"github.com/novox/mesh-host/internal/selfcompose"
|
||||
)
|
||||
|
||||
// composesNow is whose compositions this run of the agent applies, once decided ("" before).
|
||||
type composesNow struct {
|
||||
mu sync.Mutex
|
||||
now string
|
||||
}
|
||||
|
||||
func (c *composesNow) set(s string) { c.mu.Lock(); c.now = s; c.mu.Unlock() }
|
||||
func (c *composesNow) get() string { c.mu.Lock(); defer c.mu.Unlock(); return c.now }
|
||||
|
||||
// resyncStep is the step that hands the vault what runs of an own secret a shadow answer says another value of (issue
|
||||
// 1126, cause C4; internal/ownresync): one pass at a time, and the last answers taken again once the agent has decided
|
||||
// whose compositions it applies.
|
||||
type resyncStep struct {
|
||||
r *ownresync.Resync
|
||||
one sync.Mutex
|
||||
mu sync.Mutex
|
||||
last map[string]delivery.CandidateAnswer
|
||||
}
|
||||
|
||||
func ownResync(session *link.Session, node string, say func(string), env selfcompose.Env, composes *composesNow) *resyncStep {
|
||||
return &resyncStep{r: ownresync.New(ownresync.Env{Node: node, Composes: composes.get, Running: env.Running,
|
||||
Get: env.Get, Unseal: env.Unseal, Seal: identity.Seal, Say: say,
|
||||
Ask: func(ctx context.Context, verb string, args map[string]any) (json.RawMessage, error) {
|
||||
return session.AskVault(ctx, verb, args)
|
||||
}})}
|
||||
}
|
||||
|
||||
// take keeps answers as the last and takes them.
|
||||
func (s *resyncStep) take(ctx context.Context, answers map[string]delivery.CandidateAnswer) {
|
||||
s.mu.Lock()
|
||||
s.last = answers
|
||||
s.mu.Unlock()
|
||||
s.again(ctx)
|
||||
}
|
||||
|
||||
// again takes the last answers.
|
||||
func (s *resyncStep) again(ctx context.Context) {
|
||||
s.one.Lock()
|
||||
defer s.one.Unlock()
|
||||
s.mu.Lock()
|
||||
last := s.last
|
||||
s.mu.Unlock()
|
||||
if last != nil {
|
||||
s.r.Take(ctx, last)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user