From 1ab027454746b5c8a90c1aca503a392599af0607 Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 3 Nov 2026 13:39:21 +0100 Subject: [PATCH] The wanted accounts include the ledger kind, held in the tool runner's ledgers, in step with mesh-sdk's buscompose (issue 1966 D5b) --- modules/mesh-grants/grants_test.go | 22 +++++++++++++++++++ .../internal/buscompose/accounts.go | 14 +++++++++--- .../mesh-grants/internal/grants/accounts.go | 7 +++--- 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/modules/mesh-grants/grants_test.go b/modules/mesh-grants/grants_test.go index 27c2d4b4..6488719b 100644 --- a/modules/mesh-grants/grants_test.go +++ b/modules/mesh-grants/grants_test.go @@ -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 diff --git a/modules/mesh-grants/internal/buscompose/accounts.go b/modules/mesh-grants/internal/buscompose/accounts.go index 1402804f..9fcbe630 100644 --- a/modules/mesh-grants/internal/buscompose/accounts.go +++ b/modules/mesh-grants/internal/buscompose/accounts.go @@ -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-` 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: diff --git a/modules/mesh-grants/internal/grants/accounts.go b/modules/mesh-grants/internal/grants/accounts.go index c5847f2e..4c93df54 100644 --- a/modules/mesh-grants/internal/grants/accounts.go +++ b/modules/mesh-grants/internal/grants/accounts.go @@ -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 }