Hand a module exactly when the vault's own answer judged it handed, so the node never composes a secret the vault leaves to the provisioner (issue 774)
mesh/delivery merged: source.main moved to novox/mesh-host@a4713e2d28d7
mesh/delivery-group group feat/072-moves merged: every member is merged
mesh/check failed: shanks: the check of novox/mesh-controller#487 failed: go mod download in modules/route-proxy: go: git.novox.be/novox/mesh-controll…

This commit is contained in:
2026-10-31 09:49:14 +01:00
parent a4c0cee251
commit 8bdfddab4a
4 changed files with 37 additions and 2 deletions
+20
View File
@@ -19,6 +19,7 @@ import (
"encoding/json"
"errors"
"fmt"
"maps"
"reflect"
"slices"
"sort"
@@ -113,6 +114,10 @@ type Composer struct {
// sealedOwn is each own secret's sealed value as first handed, by module/name and generation: a value sealed again
// is other bytes, so the one first had is kept while the generation stands, and the composition stays the same.
sealedOwn map[string]ownSealed
// ownHanded is, by module, whether the vault judged it handed to the provisioner in its last own answer (its
// `handed`), nil when that answer said nothing of it (a vault older than the field): the composition hands exactly
// those (frombus.HandedReader, issue 774).
ownHanded map[string]bool
// certified is the serving key a certificate was last asked for successfully, so it is asked once per key.
certified string
said string
@@ -1279,6 +1284,7 @@ func (r reader) OwnSecrets(ctx context.Context, node string) ([]compose.OwnSecre
Generation int `json:"generation"`
Sealed json.RawMessage `json:"sealed"`
} `json:"own"`
Handed map[string]bool `json:"handed"`
}
if err := json.Unmarshal(raw, &a); err != nil {
return nil, fmt.Errorf("the vault's own answer is not one: %w", err)
@@ -1288,6 +1294,7 @@ func (r reader) OwnSecrets(ctx context.Context, node string) ([]compose.OwnSecre
}
r.c.mu.Lock()
defer r.c.mu.Unlock()
r.c.ownHanded = a.Handed
out := make([]compose.OwnSecretRow, 0, len(a.Own))
for _, o := range a.Own {
// The box first had is kept while its generation stands, so sealing it again moves no composition; the box the
@@ -1304,6 +1311,19 @@ func (r reader) OwnSecrets(ctx context.Context, node string) ([]compose.OwnSecre
return out, nil
}
// Handed is, of this node's modules, which the vault judged handed to the provisioner when it last answered own
// (frombus.HandedReader, issue 774): the node hands exactly those, so the vault and the node judge a module one way.
func (r reader) Handed(_ context.Context, node string) map[string]bool {
if node != r.c.env.Node {
return nil
}
r.c.mu.Lock()
defer r.c.mu.Unlock()
return maps.Clone(r.c.ownHanded)
}
var _ frombus.HandedReader = reader{}
func (r reader) Credentials(ctx context.Context, node string, asConsumer, asProvider []compose.CredentialKey) ([]compose.Credential, []compose.Issued, error) {
if node != r.c.env.Node || r.c.env.Own == nil {
return nil, nil, nil