The wanted accounts include the ledger kind, held in the tool runner's ledgers, in step with mesh-sdk's buscompose (issue 1966 D5b)

This commit is contained in:
2026-11-03 13:39:21 +01:00
parent 07af86b153
commit 1ab0274547
3 changed files with 37 additions and 6 deletions
+22
View File
@@ -2202,6 +2202,28 @@ func TestGrants(t *testing.T) {
if res := bare.Compose(); res.Accounts != nil || !strings.Contains(strings.Join(res.Errors, " "), "no bus address") {
t.Errorf("a part with no bus address wrote %+v: %v", res.Accounts, res.Errors)
}
// 1966/AC20 (issue 1966 D5, D5b; novox/hq ADR 0597 §9): a module asking for a ledger in `requires` is wanted a ledger
// account on each machine it is assigned to, minted by the vault like every kind and held in the tool runner's
// `ledgers`; the account is retired once the module leaves that machine.
l := fixture()
l.manifestOf("mod-x", strings.Replace(xManifest, `"state"`, `"requires":[{"name":"records","storage":"ledger","template":"ledger",`+
`"types":[{"name":"decision","schema":{},"operations":[{"name":"propose","effect":"set-fields"}]}]}],"state"`, 1))
l.assign("b", "mod-x")
l.loaded = shadowOf(t, l)
lp := partOn(l)
lp.Bus = grants.BusAddress{URL: "nats://a.mesh:4222"}
lp.Compose()
for _, user := range []string{"ledger.a.mod-x", "ledger.b.mod-x"} {
w, err := buscompose.ReadWantedAccount(user, l.own[grants.StateAccounts][user])
if err != nil || w.Kind != buscompose.KindLedger || w.OwnSecret() != "ledgers" || w.Module != "mod-x" {
t.Errorf("%s is wanted as %+v (%v)", user, w, err)
}
}
l.unassign("b", "mod-x")
if res := lp.Compose(); res.Accounts == nil || !slices.Equal(res.Accounts.Deleted, []string{"b.mod-x", "ledger.b.mod-x"}) ||
l.own[grants.StateAccounts]["ledger.a.mod-x"] == nil {
t.Errorf("after mod-x left b: %+v", res.Accounts)
}
})
// 774/AC5 (novox/hq ADR 0561 §1): the vault's account-issued wakes a pass, so the next list carries the new user's hash
@@ -50,7 +50,8 @@ type ClaimedSeat struct {
// WantedAccount is one key of the grants part's state `accounts`: an account the mesh needs, without its password.
type WantedAccount struct {
User string `json:"user"`
// Kind is KindModule (KindNodeTools for the runtime, named as a module is), KindProcess or KindProvisioner.
// Kind is KindModule (KindNodeTools for the runtime, named as a module is), KindProcess, KindProvisioner or
// KindLedger.
Kind Kind `json:"kind"`
Node string `json:"node"`
Module string `json:"module"`
@@ -65,8 +66,12 @@ type WantedAccount struct {
At time.Time `json:"at"`
}
// LedgersSecret is the tool runner's own secret holding its machine's ledger accounts, a map of module to credential
// (ADR 0597 §9).
const LedgersSecret = "ledgers"
// mintedKinds are the kinds of account the vault mints (ADR 0561 §1).
var mintedKinds = []Kind{KindModule, KindNodeTools, KindProcess, KindProvisioner}
var mintedKinds = []Kind{KindModule, KindNodeTools, KindProcess, KindProvisioner, KindLedger}
// principal is the account as the composer names it.
func (w WantedAccount) principal() Principal {
@@ -112,9 +117,12 @@ func (w WantedAccount) Same(o WantedAccount) bool {
}
// OwnSecret is the module's own secret the account is held in: `broker`, `broker-<process id>` or `provisioner-broker`
// (the controller's names, ADR 0436 §2, ADR 0349 §11).
// (the controller's names, ADR 0436 §2, ADR 0349 §11); a ledger account in the tool runner's `ledgers`, which keeps every
// ledger account of its machine, by module (ADR 0597 §9).
func (w WantedAccount) OwnSecret() string {
switch w.Kind {
case KindLedger:
return LedgersSecret
case KindProcess:
return "broker-" + w.Process
case KindProvisioner:
@@ -46,8 +46,9 @@ type BusAddress struct {
Fingerprint string
}
// WantedAccounts is every account the vault mints that the records compose, keyed by user, with the bus's address. A
// node, the controller, an enrolment user or a person is never one (ADR 0561 §4, §5). Pure.
// WantedAccounts is every account the vault mints that the records compose, keyed by user, with the bus's address: a
// module's, a process's, a machine's provisioner client's and a module assignment's ledger account (issue 1966 D5b, ADR
// 0597 §9). A node, the controller, an enrolment user or a person is never one (ADR 0561 §4, §5). Pure.
func WantedAccounts(in Inputs, recs buscompose.Records, bus BusAddress) (map[string]buscompose.WantedAccount, error) {
principals, err := buscompose.Users(recs)
if err != nil {
@@ -62,7 +63,7 @@ func WantedAccounts(in Inputs, recs buscompose.Records, bus BusAddress) (map[str
if m, ok := manifestOn(in, p.Node, p.Module); ok {
w.Claims = ClaimsOf(m)
}
case buscompose.KindProcess, buscompose.KindProvisioner:
case buscompose.KindProcess, buscompose.KindProvisioner, buscompose.KindLedger:
default:
continue
}