Revert L3 (mesh-host, merge 0a4c35f): its node-engine removed every held module on novox, the bus server among them, at 08:54 CET on 2026-10-29 (issue 774)
mesh/delivery merged: source.main moved to novox/mesh-host@1ec73915b807
mesh/delivery-group group revert/774-l3 merged: every member is merged
mesh/check being carried to f81e6147aee1 on 0a4c35f731a7 | from mesh-catalog 1555241393fe | novox/mesh-host#174 in group revert/774-l3

L3 lands again with two fixes: a held module is never removed, and the grants cover L3's new subjects.
This commit is contained in:
2026-10-29 09:42:29 +01:00
parent 0a4c35f731
commit f81e6147ae
27 changed files with 59 additions and 2689 deletions
-77
View File
@@ -12,9 +12,7 @@ package selfcompose
import (
"context"
"crypto/ed25519"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
@@ -30,7 +28,6 @@ import (
"git.novox.be/novox/mesh-sdk/go/compose"
"git.novox.be/novox/mesh-sdk/go/compose/frombus"
"git.novox.be/novox/mesh-sdk/go/delivery"
"git.novox.be/novox/mesh-sdk/go/overlay"
"github.com/nats-io/nats.go"
@@ -78,13 +75,6 @@ type Env struct {
// Fetch reads an archive at a URL in the artifact store: a moved module's templates archive, whose texts its built
// manifest carries (issue 1126). Read once per digest. Nil reads none, and a moved module does not compose.
Fetch func(ctx context.Context, url string) ([]byte, error)
// VaultAuthority is the vault's authority as this node read it from the vault's `signers` (ADR 0550 §2), composed
// beside the controller's as the mesh's authority (frombus.SignersReader); "" or nil composes the controller's alone.
VaultAuthority func() string
// Certify asks the vault's `sign` for this node's own certificate, keeps it and writes the node's record again (ADR
// 0550 §2): asked only when a module placed here asks for a certificate and the record holds none from an authority
// the node trusts. Nil asks nothing, and such a module stands in, as before.
Certify func(ctx context.Context) error
}
// VaultSecretChanged is the vault's event that a secret changed: a name and its generation, never a value.
@@ -109,8 +99,6 @@ 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
// certified is the serving key a certificate was last asked for successfully, so it is asked once per key.
certified string
said string
refusal string
// held is what the composer's last pass held: each module refused alone, and the node's own ("node") when it could
@@ -513,8 +501,6 @@ func (c *Composer) compose(ctx context.Context) []agent.Update {
if c.env.Running != nil {
running = c.env.Running()
}
// Its own certificate, asked of the vault when a module here asks for one and the record holds none it trusts.
c.certifyIfWanted(ctx, r, own)
scope := frombus.Scope{Node: c.env.Node, KeptAt: kept, InOwn: inOwn, Running: running}
missed := c.missed.Load()
compositions, _, held, err := frombus.Compose(ctx, r, c.env.Mesh, scope, fps)
@@ -760,69 +746,6 @@ func (c *Composer) Policy(ctx context.Context, module string) string {
// reader is the composer's view of the bus as frombus reads it.
type reader struct{ c *Composer }
// VaultAuthority is the vault's authority the node follows (frombus.SignersReader).
func (r reader) VaultAuthority(context.Context) string {
if r.c.env.VaultAuthority == nil {
return ""
}
return r.c.env.VaultAuthority()
}
var _ frombus.SignersReader = reader{}
// certificateWanted says whether a module the placement p puts on this node asks for a certificate for the machine's
// internal name (its manifest's `certificate`), read from the indexer's manifests; a manifest that cannot be read is
// taken as asking none, and the composition says what it misses.
func (c *Composer) certificateWanted(ctx context.Context, r reader, p delivery.Placement) bool {
for _, m := range p.Modules {
c.mu.Lock()
digest := c.modules[m.Module].Manifest
c.mu.Unlock()
if digest == "" {
continue
}
raw, err := r.Manifest(ctx, digest)
if err != nil {
continue
}
if parsed, err := catalogue.ParseManifestForInputs(raw); err == nil && parsed.Certificate != nil {
return true
}
}
return false
}
// certifyIfWanted asks Certify once per serving key when a module placed here asks for a certificate and this node's
// record holds none for its internal name and serving key from an authority it trusts (the one it holds, or the vault's).
func (c *Composer) certifyIfWanted(ctx context.Context, r reader, p delivery.Placement) {
if c.env.Certify == nil || !c.certificateWanted(ctx, r, p) {
return
}
c.mu.Lock()
rec, ok := c.records[c.env.Node]
asked := c.certified
c.mu.Unlock()
if !ok || rec.ServingKey == "" || asked == rec.ServingKey {
return
}
key, err := base64.StdEncoding.DecodeString(rec.ServingKey)
if err != nil || len(key) != ed25519.PublicKeySize {
return
}
trusted := []string{rec.Authority, r.VaultAuthority(ctx)}
if rec.Certificate != "" && delivery.CheckNodeCertificate(rec.Certificate, trusted, overlay.InternalName(c.env.Node),
ed25519.PublicKey(key), time.Now()) == nil {
return
}
if err := c.env.Certify(ctx); err != nil {
c.sayOnce(fmt.Sprintf("a module here asks for a certificate, and the vault's sign gave none: %v", err))
return
}
c.mu.Lock()
c.certified = rec.ServingKey
c.mu.Unlock()
}
func (r reader) Placements(context.Context) (map[string]delivery.Placement, error) {
r.c.mu.Lock()
defer r.c.mu.Unlock()