Vault: take and hash a process's own bus account (broker-<process id>) as a covered account, so take-over is not held by every process user
mesh/delivery merged: source.main moved to novox/mesh-catalog@22381d75224f
mesh/check failed: integration: meshbuildnode/cmd/mesh-build-node.TestAFixtureBundleBuiltTwiceHasOneDigest: no toolchain of this lock is installed und…

The controller hands each process account of a core or moved module with origin issued (ADR 0436 §2); the vault took
only broker and provisioner-broker, refused these and never hashed them. Owed hashes are now said once per pass, and
account_hash says who asked when it refuses too (review round 1).

Issue: 1945
This commit is contained in:
2026-10-26 02:35:57 +01:00
parent 32b912cfe5
commit 1c55e5d44d
3 changed files with 40 additions and 8 deletions
@@ -275,15 +275,19 @@ func (v *Vault) HashOwed() {
names = append(names, n)
}
sort.Strings(names)
still := map[string]string{}
for _, n := range names {
if _, held, err := v.Store.Read(n); err == nil && !held {
v.hashes.paid(n)
continue
}
if _, err := v.HashAccount(n, false); err != nil {
v.log("the hash of %s is still owed: %v", n, err)
still[n] = err.Error()
}
}
if len(still) > 0 {
v.log("%d of %d owed bus-account hashes are still owed, tried again at the next pass: %v", len(still), len(names), still)
}
}
// HashUser is the tool account_hash: the covered account whose value names user, hashed again now.
@@ -308,10 +312,13 @@ func (v *Vault) HashUser(user, caller string) (Hashed, error) {
h, err := v.HashAccount(r.Name, true)
if err == nil {
v.log("hashed %s again (%s, generation %d), asked by %s", user, r.Name, h.Generation, someoneOr(caller, "a tool"))
} else {
v.log("did not hash %s again (%s), asked by %s: %v", user, r.Name, someoneOr(caller, "a tool"), err)
}
return h, err
}
}
v.log("did not hash %s again, asked by %s: the vault holds no bus account of it", user, someoneOr(caller, "a tool"))
return Hashed{}, fmt.Errorf("the vault holds no bus account of %s: an account it holds no value for is adopted, "+
"never hashed here", user)
}
+18 -7
View File
@@ -32,7 +32,6 @@ import (
"errors"
"fmt"
"regexp"
"slices"
"strings"
"time"
@@ -197,15 +196,27 @@ func zero(b []byte) {
// CoveredSecrets are the names, in a module's own secrets, of the bus accounts the controller mints for a core module
// or a module marked moved and hands to the vault (novox/hq ADR 0374, issue 787): the module's account on the bus
// (`broker`) and, for the machine's provisioner client, its own (`provisioner-broker`).
var CoveredSecrets = []string{"broker", "provisioner-broker"}
// (`broker`), for the machine's provisioner client its own (`provisioner-broker`), and for each process of its bus
// declarations its own (`broker-<process id>`, ADR 0436 §2), named here by ProcessSecretPrefix.
var CoveredSecrets = []string{"broker", "provisioner-broker", ProcessSecretPrefix + "<process id>"}
// Covered says whether secret is a covered account: a module's own secret named one of CoveredSecrets. The vault
// cannot tell core or moved from a module's manifest; the controller, which can, is the only caller that may hand
// one in, and the name keeps what it hands to these.
// ProcessSecretPrefix starts a process's own bus account's secret, `broker-<process id>` (the controller's
// processSecretName).
const ProcessSecretPrefix = "broker-"
// Covered says whether secret is a covered account: a module's own secret named `broker`, `provisioner-broker` or
// `broker-<process id>`. The vault cannot tell core or moved from a module's manifest; the controller, which can, is the
// only caller that may hand one in, and the name keeps what it hands to these.
func Covered(secret string) bool {
parts := strings.Split(secret, "/")
return len(parts) == 4 && parts[0] == "own" && slices.Contains(CoveredSecrets, parts[3])
if len(parts) != 4 || parts[0] != "own" {
return false
}
name := parts[3]
if id, process := strings.CutPrefix(name, ProcessSecretPrefix); process {
return id != ""
}
return name == "broker" || name == "provisioner-broker"
}
// AcceptOpen opens a call to take a value given at the controller's terminal for secret, which the terminal
@@ -181,6 +181,20 @@ func TestAHandedBusAccountIsHashedOncePerGeneration(t *testing.T) {
}
return err
}, "pw-3", 3, 6, 1},
{"a process's own account (ADR 0436 §2) is covered and hashed", func() error {
_, err := handIssued(t, v, ControllerCaller, "own/ace/mesh-build-go/broker-worker",
`{"node":"ace","module":"mesh-build-go","user":"ace.mesh-build-go.worker","password":"pw-w"}`)
if !acc.hashOf("ace.mesh-build-go.worker", "pw-w", 1) {
return errors.New("ace.mesh-build-go.worker is not hashed")
}
if _, rerr := v.Rotate(ControllerCaller, "own/ace/mesh-build-go/broker-worker", "jochen", "x"); rerr == nil {
return errors.New("the vault rotated a process's covered account")
}
if Covered("own/ace/mesh-build-go/broker-") || Covered("own/ace/mesh-build-go/brokers") {
return errors.New("a name that is no process account is covered")
}
return err
}, "pw-3", 3, 7, 1},
} {
before := len(v.bus.events)
if err := step.do(); err != nil {