The provisioner holds only a bad module, the vault keeps bus accounts by declaration, and silent faults from the release sweep speak up (issue 774) #565

Merged
jschoubben merged 18 commits from fix/sweep-2026-10-30 into main 2026-10-30 18:12:53 +00:00
2 changed files with 51 additions and 5 deletions
Showing only changes of commit b7f878226e - Show all commits
+24 -5
View File
@@ -189,12 +189,17 @@ func (v *Vault) mintOnce(w buscompose.WantedAccount, by string) (Minted, error)
v.holdBack(w.User, err.Error())
return Minted{}, err
}
if _, held, err := v.Store.Read(secret); err != nil {
replacing := false
if rec, held, err := v.Store.Read(secret); err != nil {
return Minted{}, err
} else if held {
// Its value is held (the controller's, handed in, or one the vault minted): never minted anew (§4).
v.noLongerHeld(w.User)
return Minted{}, errHeldBack{secret + " is held already: an account the vault holds a value for is never minted anew (ADR 0561 §4)"}
// A held value that is no account (kept before the vault judged it, the sweep of 2026-10-30) has no hash and
// logs in nowhere on the bus: it is replaced by a minted account. Any other held value is never minted anew (§4).
if value, err := openAtRest(rec, v); err != nil || BusAccountValue(secret, value) {
v.noLongerHeld(w.User)
return Minted{}, errHeldBack{secret + " is held already: an account the vault holds a value for is never minted anew (ADR 0561 §4)"}
}
replacing = true
}
if v.Accounts == nil {
v.holdBack(w.User, "the vault has no state of bus accounts to write the hash to")
@@ -230,11 +235,25 @@ func (v *Vault) mintOnce(w buscompose.WantedAccount, by string) (Minted, error)
v.holdBack(w.User, err.Error())
return Minted{}, err
}
c, err := v.keepLocked(secret, string(value), "the vault", "", OriginMade, keepOptions{neverReplace: true, quiet: true})
opt := keepOptions{neverReplace: true, quiet: true}
if replacing {
// Only the value that is no account is replaced, judged again under the store's lock; the one replaced is kept.
opt = keepOptions{quiet: true, keepPrevious: true, refuse: func(before Record) string {
if was, err := openAtRest(before, v); err != nil || BusAccountValue(secret, was) {
return secret + " is held as an account meanwhile: it is never minted anew (ADR 0561 §4)"
}
return ""
}}
}
c, err := v.keepLocked(secret, string(value), "the vault", "", OriginMade, opt)
if err != nil {
v.holdBack(w.User, "its value could not be kept: "+err.Error())
return Minted{}, err
}
if replacing {
v.log("%s held a value that is no bus account, which no hash was taken of and which logs in nowhere: replaced by a "+
"minted account (generation %d), the value it replaces kept beside it", secret, c.Generation)
}
out := Minted{User: w.User, Secret: secret, Generation: c.Generation, At: c.At}
// The hash first, then account-issued, then secret-changed: the grants part composes the user before the node asks.
if _, err := v.HashAccount(secret, false); err != nil {
@@ -242,3 +242,30 @@ func TestAFailedMintIsTriedAgainAtThePass(t *testing.T) {
t.Errorf("not minted at the pass after the failure: %+v", v.MintState())
}
}
// A held value that is no account (kept before the vault judged it: broker-nats-tools, broker-address and
// broker-management on 2026-10-30, issue 774) is replaced by a minted account when the grants part wants its user;
// a held account still never is.
func TestAHeldValueThatIsNoAccountIsReplacedByAMintedOne(t *testing.T) {
v, accounts, _ := mintingVault(t, true)
w := aWant("mod-x")
if _, err := v.Put("own/ace/mod-x/broker", `{"password":"pw"}`, "the test"); err != nil {
t.Fatal(err)
}
heldPlain(t, v.Vault, "own/ace/mod-x/broker", "a-random-value-the-controller-made")
v.WantHeard(w.User, wantRaw(t, w), false)
rec, _, _ := v.Store.Read("own/ace/mod-x/broker")
value, err := openAtRest(rec, v.Vault)
if err != nil || !BusAccountValue("own/ace/mod-x/broker", value) || rec.Generation != 3 || rec.Previous == nil {
t.Fatalf("not replaced by a minted account: generation %d, previous kept %v, %v", rec.Generation, rec.Previous != nil, err)
}
var got struct{ Password string }
_ = json.Unmarshal([]byte(value), &got)
if !accounts.hashOf(w.User, got.Password, 3) {
t.Errorf("%s's hash is not the minted password's: %+v", w.User, accounts.keys[w.User])
}
v.WantHeard(w.User, wantRaw(t, w), false)
if again, _, _ := v.Store.Read("own/ace/mod-x/broker"); again.Generation != 3 {
t.Errorf("a held account was minted anew: generation %d", again.Generation)
}
}