diff --git a/modules/mesh-delivery-assigner/asks/asks.go b/modules/mesh-delivery-assigner/asks/asks.go index d255408b..7fe58160 100644 --- a/modules/mesh-delivery-assigner/asks/asks.go +++ b/modules/mesh-delivery-assigner/asks/asks.go @@ -5,7 +5,7 @@ // view may answer them: the hold of a fingerprint whose health check failed (`hold..`: release it or // keep it held), the restore of a data module set back (`restore...`: restore and lose newer // writes, or leave it stopped, ADR 0429 §6), a backup that failed before an apply, a move a person must allow for a module of -// policy record, and the authorising ask of a proposal of placement. +// policy record, and the authorising ask of a proposal of assignments. package asks import ( @@ -14,8 +14,8 @@ import ( "fmt" "strings" + "git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient" "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" - "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds" @@ -34,15 +34,15 @@ const ( // AskKindRecord is the kind of the assigner's ask about a move a person must allow, for a module of policy record: a // composition judged to recreate a container or move data, or one that cannot be judged (ADR 0539 clause 14). AskKindRecord = "record" - // AskKindPlacement is the kind of the authorising ask about a proposal of placement (novox/hq ADR 0567 §5): - // `placement.`. The kind needs no proof, as the bus's put does not: its yes is an Approve, which only a + // AskKindAssignments is the kind of the authorising ask about a proposal of assignments (novox/hq ADR 0567 §5): + // `assignments.`. The kind needs no proof, as the bus's put does not: its yes is an Approve, which only a // channel proving who answered takes and the view never does (ViewAnswerFits). - AskKindPlacement = "placement" + AskKindAssignments = "assignments" ) // AskKey is an ask's key read whole: its Kind, and the parts its kind has: a merge's Group and heads Hash, a hold's Module // and fingerprint FP, a restore's and a backup's Node, Module and FP, a record's Node, Module and the digest Composed of the -// composition it would allow, a placement's Proposal. +// composition it would allow, the assignments' Proposal. type AskKey struct { Proposal string Kind string @@ -60,11 +60,11 @@ func AskNeedsProof(kind string) (bool, error) { switch kind { case meshdeliveryclient.AskKindMerge: return true, nil - case AskKindHold, AskKindRestore, AskKindBackup, AskKindRecord, AskKindPlacement: + case AskKindHold, AskKindRestore, AskKindBackup, AskKindRecord, AskKindAssignments: return false, nil } return false, fmt.Errorf("asks: %q is not a kind of ask, %s, %s, %s, %s, %s or %s", kind, meshdeliveryclient.AskKindMerge, AskKindHold, - AskKindRestore, AskKindBackup, AskKindRecord, AskKindPlacement) + AskKindRestore, AskKindBackup, AskKindRecord, AskKindAssignments) } // HoldAskKey is the key of the ask about a module's held fingerprint: `hold..`. @@ -103,12 +103,12 @@ func BackupAskKey(node, module, fingerprint string) (string, error) { return AskKindBackup + "." + key, nil } -// PlacementAskKey is the key of the authorising ask about a proposal of placement: `placement.` (ADR 0567 §5). -func PlacementAskKey(proposal string) (string, error) { +// AssignmentsAskKey is the key of the authorising ask about a proposal of assignments: `assignments.` (ADR 0567 §5). +func AssignmentsAskKey(proposal string) (string, error) { if err := meshmoduleassignerclient.CheckProposalID(proposal); err != nil { return "", err } - return AskKindPlacement + "." + proposal, nil + return AskKindAssignments + "." + proposal, nil } // RecordAskKey is the key of the ask about a move of a module on a node to the composition whose digest it names: @@ -124,7 +124,7 @@ func RecordAskKey(node, module, composed string) (string, error) { return AskKindRecord + "." + have + "." + strings.TrimPrefix(composed, "sha256:"), nil } -// ParseAnyAskKey reads an ask key of any kind (a merge, hold, restore, backup, record or placement ask); it refuses an unknown +// ParseAnyAskKey reads an ask key of any kind (a merge, hold, restore, backup, record or assignments ask); it refuses an unknown // kind and a key whose parts do not fit its kind, naming the key. func ParseAnyAskKey(key string) (AskKey, error) { kind, rest, _ := strings.Cut(key, ".") @@ -167,7 +167,7 @@ func ParseAnyAskKey(key string) (AskKey, error) { return refused(err) } return AskKey{Kind: kind, Node: node, Module: module, FP: fp}, nil - case AskKindPlacement: + case AskKindAssignments: if err := meshmoduleassignerclient.CheckProposalID(rest); err != nil { return refused(err) } diff --git a/modules/mesh-delivery-assigner/asks/asks_test.go b/modules/mesh-delivery-assigner/asks/asks_test.go index bfa79231..c522584c 100644 --- a/modules/mesh-delivery-assigner/asks/asks_test.go +++ b/modules/mesh-delivery-assigner/asks/asks_test.go @@ -17,17 +17,17 @@ func TestTheAssignersAskKeysKeepTheirFormsAndTheValueItsRules(t *testing.T) { hold, _ := HoldAskKey("gitea", fp) restore, _ := RestoreAskKey("n1", "gitea", fp) backup, _ := BackupAskKey("n1", "novox.be", fp) - placement, _ := PlacementAskKey("p-20261028t183000-1a2b3c") + assignments, _ := AssignmentsAskKey("p-20261028t183000-1a2b3c") record, _ := RecordAskKey("n1", "gitea", digest) for key, want := range map[string]struct { got string back AskKey }{ - "hold": {hold, AskKey{Kind: "hold", Module: "gitea", FP: fp}}, - "restore": {restore, AskKey{Kind: "restore", Node: "n1", Module: "gitea", FP: fp}}, - "backup": {backup, AskKey{Kind: "backup", Node: "n1", Module: "novox.be", FP: fp}}, - "placement": {placement, AskKey{Kind: "placement", Proposal: "p-20261028t183000-1a2b3c"}}, - "record": {record, AskKey{Kind: "record", Node: "n1", Module: "gitea", Composed: digest}}, + "hold": {hold, AskKey{Kind: "hold", Module: "gitea", FP: fp}}, + "restore": {restore, AskKey{Kind: "restore", Node: "n1", Module: "gitea", FP: fp}}, + "backup": {backup, AskKey{Kind: "backup", Node: "n1", Module: "novox.be", FP: fp}}, + "assignments": {assignments, AskKey{Kind: "assignments", Proposal: "p-20261028t183000-1a2b3c"}}, + "record": {record, AskKey{Kind: "record", Node: "n1", Module: "gitea", Composed: digest}}, } { back, err := ParseAnyAskKey(want.got) if err != nil || back != want.back { @@ -35,7 +35,7 @@ func TestTheAssignersAskKeysKeepTheirFormsAndTheValueItsRules(t *testing.T) { } } for got, want := range map[string]string{hold: "hold.gitea." + hex, restore: "restore.n1.gitea." + hex, backup: "backup.n1.novox=2ebe." + hex, - placement: "placement.p-20261028t183000-1a2b3c", record: "record.n1.gitea." + strings.Repeat("cd", 32)} { + assignments: "assignments.p-20261028t183000-1a2b3c", record: "record.n1.gitea." + strings.Repeat("cd", 32)} { if got != want { t.Errorf("key %q, want %q", got, want) } @@ -45,7 +45,7 @@ func TestTheAssignersAskKeysKeepTheirFormsAndTheValueItsRules(t *testing.T) { t.Errorf("a merge ask key: %+v, %v", merge, err) } for _, bad := range []string{"", "other.x", "hold.gitea", "hold.gitea.src2:" + hex, "restore.n1", "backup.n1.node", "backup.n1.gitea." + hex[:60], - "placement.AB", "record.n1.gitea." + hex[:10], "record.n1.node." + hex, "merge.g"} { + "assignment.AB", "record.n1.gitea." + hex[:10], "record.n1.node." + hex, "merge.g"} { if _, err := ParseAnyAskKey(bad); err == nil { t.Errorf("ParseAnyAskKey(%q) reads a wrong key", bad) } diff --git a/modules/mesh-delivery-assigner/asks/go.mod b/modules/mesh-delivery-assigner/asks/go.mod index 6cf7bd65..0bb052f8 100644 --- a/modules/mesh-delivery-assigner/asks/go.mod +++ b/modules/mesh-delivery-assigner/asks/go.mod @@ -4,7 +4,7 @@ go 1.26.0 require ( git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient v0.0.0-00010101000000-000000000000 - git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds v0.0.0-00010101000000-000000000000 @@ -14,7 +14,7 @@ require ( require ( git.novox.be/novox/mesh-contracts/go/seats/nodeengine v0.0.0-00010101000000-000000000000 // indirect - git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner v0.0.0-00010101000000-000000000000 // indirect + git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner v0.0.0-00010101000000-000000000000 // indirect git.novox.be/novox/mesh-delivery/contracts/go/clients/containerimageregistryclient v0.0.0-00010101000000-000000000000 // indirect git.novox.be/novox/mesh-delivery/contracts/go/seats/containerimageregistry v0.0.0-00010101000000-000000000000 // indirect git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog v0.0.0-00010101000000-000000000000 // indirect diff --git a/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/integration_test.go b/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/integration_test.go index 6f3f28a0..08351fcf 100644 --- a/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/integration_test.go +++ b/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/integration_test.go @@ -9,8 +9,8 @@ import ( "fmt" "io" "maps" + "mesh-delivery-assigner/internal/assignment" "mesh-delivery-assigner/internal/buckets" - "mesh-delivery-assigner/internal/placement" "mesh-delivery-assigner/internal/puts" "os" "path/filepath" @@ -21,10 +21,10 @@ import ( "time" "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" - mwc "git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" mdcc "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliverycheckerclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient" + mwc "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-assigner/asks" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds" @@ -37,10 +37,10 @@ import ( "mesh-delivery-assigner/internal/judge" + mmac "git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient" + mma "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" occ "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/clients/operatorchannelclient" oc "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel" - mmac "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" - mma "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient" stdio "git.novox.be/novox/mesh-sdk/go" "git.novox.be/novox/mesh-sdk/go/announce" @@ -56,7 +56,7 @@ import ( "git.novox.be/novox/mesh-sdk/go/views" "mesh-delivery-assigner/internal/assign" - plc "mesh-delivery-assigner/internal/placement" + plc "mesh-delivery-assigner/internal/assignment" ) // bed is an embedded bus with JetStream, every bucket the assigner reads and keeps, and the stream the mesh's events @@ -67,7 +67,7 @@ type bed struct { ctx context.Context dir string buckets map[string]jetstream.KeyValue - // placed is, by node, the modules the bed's placement names on it (place). + // placed is, by node, the modules the bed's assignments names on it (place). placed map[string][]string } @@ -229,7 +229,7 @@ func saysWhatItKeeps(t *testing.T, b *bed, done <-chan error) { func TestTheAssignerDoesNotStartWithoutABucketItReads(t *testing.T) { for _, missing := range readBuckets { if startsWithout(missing) { - continue // it starts without it (the placement and the grants' source: main_test.go; the puts: below) + continue // it starts without it (the assignments and the grants' source: main_test.go; the puts: below) } b := startBed(t, missing) select { @@ -278,26 +278,36 @@ func TestTheAssignerDoesNotStartWithoutABucketItReads(t *testing.T) { } } - // Without the seat's placement state it starts all the same, and once the bucket is made (the controller makes + // Without the seat's assignments state it starts all the same, and once the bucket is made (the controller makes // it from the manifest) it is read with no restart: a node it names gets its own want, the node composing its own. - pb := startBed(t, plc.PlacementBucket) + pb := startBed(t, plc.AssignmentsBucket) pdone := pb.start(t) select { case err := <-pdone: - t.Fatalf("without %s the assigner stopped: %v", plc.PlacementBucket, err) + t.Fatalf("without %s the assigner stopped: %v", plc.AssignmentsBucket, err) case <-time.After(2 * time.Second): } - placement, err := pb.js.CreateKeyValue(pb.ctx, jetstream.KeyValueConfig{Bucket: plc.PlacementBucket, History: 5}) + // ADR 0641: the state the seat's assignments were kept in before holds n1's keys, and the form of one key per node; the + // assigner copies the keys into the state made after its start, drops the second form, and leaves the old state empty. + values, _ := mmac.AssignmentsValues(mma.Assignments{Node: "n1", Modules: []mma.PlacedModule{{Module: "web"}}}) + for _, k := range slices.Sorted(maps.Keys(values)) { // a key per module, then the node key + pb.putJSON(t, retiredBucket, k, values[k]) + } + pb.putJSON(t, retiredBucket, "placement.n1", values["n1.node"]) + assignments, err := pb.js.CreateKeyValue(pb.ctx, jetstream.KeyValueConfig{Bucket: plc.AssignmentsBucket, History: 5}) if err != nil { t.Fatal(err) } - pl, _ := mmac.EncodePlacement(mma.Placement{Node: "n1", Modules: []mma.PlacedModule{{Module: "web"}}}) - plkey, _ := mmac.LegacyKey("n1") // the form before, read beside the new (research 072 row 37) - if _, err := placement.Put(pb.ctx, plkey, pl); err != nil { - t.Fatal(err) - } if nw, err := mwc.DecodeNodeWant(pb.get(t, buckets.Wants, "want.n1.node")); err != nil || !nw.Self { - t.Fatalf("the placement state made after the start was not read: n1's own want = %+v, %v", nw, err) + t.Fatalf("the assignments state made after the start was not read: n1's own want = %+v, %v", nw, err) + } + for _, k := range slices.Sorted(maps.Keys(values)) { + if e, err := assignments.Get(pb.ctx, k); err != nil || !slices.Equal(e.Value(), values[k]) { + t.Errorf("%s was not copied into %s: %v", k, plc.AssignmentsBucket, err) + } + } + if left, err := pb.buckets[retiredBucket].Keys(pb.ctx); err == nil || !errors.Is(err, jetstream.ErrNoKeysFound) { + t.Errorf("the retired state still holds %v (%v)", left, err) } } @@ -309,8 +319,8 @@ func (b *bed) putJSON(t *testing.T, bucket, key string, value []byte) { } } -// place adds module to the seat's placement of node, written as the seat writes it: one node key and a key per module -// (research 072 row 37). The placement alone names where modules run (ADR 0567 §8). +// place adds module to the seat's assignments of node, written as the seat writes it: one node key and a key per module +// (research 072 row 37). The assignments alone names where modules run (ADR 0567 §8). func (b *bed) place(t *testing.T, node, module string) { t.Helper() if b.placed == nil { @@ -319,16 +329,16 @@ func (b *bed) place(t *testing.T, node, module string) { if !slices.Contains(b.placed[node], module) { b.placed[node] = append(b.placed[node], module) } - pl := mma.Placement{Node: node} + pl := mma.Assignments{Node: node} for _, m := range b.placed[node] { pl.Modules = append(pl.Modules, mma.PlacedModule{Module: m}) } - values, err := mmac.PlacementValues(pl) + values, err := mmac.AssignmentsValues(pl) if err != nil { t.Fatal(err) } for key, raw := range values { - b.putJSON(t, plc.PlacementBucket, key, raw) + b.putJSON(t, plc.AssignmentsBucket, key, raw) } } @@ -426,11 +436,11 @@ func TestWantsAreWrittenAndNoDifferenceIs(t *testing.T) { // indexer its runtime of its own answers each first-view tool to the readers its manifest's callers lists name, at // its mark, and refuses every other account, the assigner's own included. firstViewsBeforeAnyNewFormState(t, b, map[string][]string{ - "placement": {"n1.mesh-grants", "n1.node-tools", "n1.mesh-delivery-indexer"}, + "assignments": {"n1.mesh-grants", "n1.node-tools", "n1.mesh-delivery-indexer"}, "seat-holders": {"n1.node-tools"}, "wants": {"n1.mesh-grants"}, }) - graphKey := meshconditionsclient.Key(meshconditionsclient.ScopeModule, placement.Module+"."+buckets.IndexerModule+"."+graph.State, views.UnansweredKind) + graphKey := meshconditionsclient.Key(meshconditionsclient.ScopeModule, assignment.Module+"."+buckets.IndexerModule+"."+graph.State, views.UnansweredKind) graphRaised := func() bool { e, err := b.buckets[assign.FindingsBucket].Get(b.ctx, graphKey) return err == nil && strings.Contains(string(e.Value()), buckets.IndexerModule+".graph") @@ -492,16 +502,17 @@ func TestWantsAreWrittenAndNoDifferenceIs(t *testing.T) { t.Errorf("the condition of web on neither list on g14 was not cleared once g14 got its want") } - // Issue 1126 (ADR 0547 §2): the catalogue's placement reaches the indexer's state naming web on every node the + // Issue 1126 (ADR 0547 §2): the catalogue's assignments reaches the indexer's state naming web on every node the // assignments name it on. Each node then composes its own: its want names the build alone (Self, no composition), and // its own want says it composes its own, which the node agent reads (mesh-host's TestNats...ComposesAndAppliesItsOwn). for _, node := range []string{"ace", "g14", "shanks"} { - pl, err := mmac.EncodePlacement(mma.Placement{Node: node, Modules: []mma.PlacedModule{{Module: "web"}}}) + values, err := mmac.AssignmentsValues(mma.Assignments{Node: node, Modules: []mma.PlacedModule{{Module: "web"}}}) if err != nil { t.Fatal(err) } - pkey, _ := mmac.LegacyKey(node) - b.putJSON(t, plc.PlacementBucket, pkey, pl) + for _, k := range slices.Sorted(maps.Keys(values)) { + b.putJSON(t, plc.AssignmentsBucket, k, values[k]) + } } if w := b.wantNow(t, "ace", "web", "ace's want once placed: the build alone, composed by the node", func(w mwc.Want) bool { return w.Self }); w.Composed != "" || w.Fp != fNew { t.Errorf("ace's want once placed = %+v", w) @@ -521,7 +532,7 @@ func TestWantsAreWrittenAndNoDifferenceIs(t *testing.T) { // ADR 0603 (L3, D15): one source revision changes a provider (db), its new consumer (app) and a build module (tool, whose // recipe moved: ace runs its first build and the second, under the agreed toolchain, is the one the source revision names). // The keys are written as their writers write them: the indexer's modules and manifests, the builder's builds, the - // controller's grants, the placement seat's placement and the provisioner's provisions. The build module's want moves at + // controller's grants, the assignments seat's assignments and the provisioner's provisions. The build module's want moves at // once and the provider's with it; the consumer's waits for its provision, with the words naming it, and moves once the // provisioner provides it (rule 8, ADR 0570 §4): nothing waits for a turn or an order, only for a key. offer := `"provides":[{"name":"database","scope":"mesh","answered-by":"helper"}]` @@ -1095,7 +1106,7 @@ func (s bedStore) kv(state string) jetstream.KeyValue { if strings.Contains(state, ".") { return s.b.buckets[strings.Replace(state, ".", "_", 1)] } - return s.b.buckets[placement.Module+"_"+state] + return s.b.buckets[assignment.Module+"_"+state] } func (s bedStore) get(state, key string) ([]byte, uint64, bool, error) { @@ -1125,23 +1136,23 @@ func (s bedStore) history(string, string) ([]stdio.StateRevision, error) { retur // 1126 (ADR 0567 §2-§6), the seat mesh-module-assigner whole on the bus: a proposal moving web from the anchor to the laptop is // proposed through the verb, the assigner finds both nodes must answer, they answer the same in their haves, the -// operator is asked and approves, and each key is written at a new revision while placement-changed is heard on the +// operator is asked and approves, and each key is written at a new revision while assignments-changed is heard on the // assigner's stream naming both nodes. A second proposal laid over the old revision of a key moved since is laid again, // and declined, writes nothing and is withdrawn. func TestAProposalIsAnsweredApprovedAndWritten(t *testing.T) { b := startBed(t, "") - if _, err := b.js.CreateStream(b.ctx, jetstream.StreamConfig{Name: "MOD_" + placement.Module + "_placement", - Subjects: placementSubjects()}); err != nil { // the seat's four events: the views family has a stream of its own + if _, err := b.js.CreateStream(b.ctx, jetstream.StreamConfig{Name: "MOD_" + assignment.Module + "_assignments", + Subjects: assignmentsSubjects()}); err != nil { // the seat's four events: the views family has a stream of its own t.Fatal(err) } - heard := make(chan mma.PlacementEvent, 16) + heard := make(chan mma.AssignmentsEvent, 16) nc, err := nats.Connect(b.srv.ClientURL()) if err != nil { t.Fatal(err) } t.Cleanup(nc.Close) - if _, err := nc.Subscribe(seatdef.EventSubject(mma.Seat, mma.EventPlacementChanged), func(m *nats.Msg) { - var e mma.PlacementEvent + if _, err := nc.Subscribe(seatdef.EventSubject(mma.Seat, mma.EventAssignmentsChanged), func(m *nats.Msg) { + var e mma.AssignmentsEvent if json.Unmarshal(m.Data, &e) == nil { heard <- e } @@ -1155,38 +1166,34 @@ func TestAProposalIsAnsweredApprovedAndWritten(t *testing.T) { b.putJSON(t, buckets.Modules, "web", []byte(`{"v":1}`)) b.putJSON(t, buckets.Modules, "shell", []byte(`{"v":1}`)) b.putJSON(t, buckets.Modules, "mesh-enrolment", []byte(`{"v":1}`)) - // place writes a node's placement as another writer would, in its node and module keys (research 072 row 37), and - // answers its node key's revision; legacy writes it in the form before, placement., alone. - place := func(node string, legacy bool, modules ...string) uint64 { - p := mma.Placement{Node: node, Modules: []mma.PlacedModule{}} + // place writes a node's assignments as another writer would, in its node and module keys (research 072 row 37), and + // answers its node key's revision. + place := func(node string, modules ...string) uint64 { + p := mma.Assignments{Node: node, Modules: []mma.PlacedModule{}} for _, m := range modules { p.Modules = append(p.Modules, mma.PlacedModule{Module: m}) if m == "mesh-enrolment" { // the module that holds the seat mesh-node-enroller: who may add a node is read from it p.Holds = append(p.Holds, mma.Holding{Claim: "mesh-node-enroller", Scope: "mesh", Module: m}) } } - values, _ := mmac.PlacementValues(p) + values, _ := mmac.AssignmentsValues(p) key, _ := mmac.NodeKey(node) - if legacy { - key, _ = mmac.LegacyKey(node) - values = map[string][]byte{key: values[key]} - } for _, k := range slices.Sorted(maps.Keys(values)) { if k == key { continue } - if _, err := b.buckets[plc.PlacementBucket].Put(b.ctx, k, values[k]); err != nil { + if _, err := b.buckets[plc.AssignmentsBucket].Put(b.ctx, k, values[k]); err != nil { t.Fatal(err) } } - rev, err := b.buckets[plc.PlacementBucket].Put(b.ctx, key, values[key]) + rev, err := b.buckets[plc.AssignmentsBucket].Put(b.ctx, key, values[key]) if err != nil { t.Fatal(err) } return rev } - place("anchor", true, "web") - laptopRev := place("laptop", false, "shell") + place("anchor", "web") + laptopRev := place("laptop", "shell") bound := judge.AnswerBound t.Cleanup(func() { judge.AnswerBound = bound }) judge.AnswerBound = 2 * time.Second @@ -1194,9 +1201,9 @@ func TestAProposalIsAnsweredApprovedAndWritten(t *testing.T) { // The holder converts anchor, held in the form before alone, to its node and module keys once at its start. var anchorRev uint64 for deadline := time.Now().Add(15 * time.Second); anchorRev == 0; time.Sleep(50 * time.Millisecond) { - if e, err := b.buckets[plc.PlacementBucket].Get(b.ctx, "anchor.node"); err == nil { + if e, err := b.buckets[plc.AssignmentsBucket].Get(b.ctx, "anchor.node"); err == nil { anchorRev = e.Revision() - if _, err := b.buckets[plc.PlacementBucket].Get(b.ctx, "anchor.web"); err != nil { + if _, err := b.buckets[plc.AssignmentsBucket].Get(b.ctx, "anchor.web"); err != nil { t.Fatalf("anchor converted without its module key: %v", err) } } @@ -1250,33 +1257,29 @@ func TestAProposalIsAnsweredApprovedAndWritten(t *testing.T) { } answerAll(p) p = proposalNow(p.ID, "asked", func(p mma.Proposal) bool { return p.State == mmac.ProposalAsked }) - akey, _ := asks.PlacementAskKey(p.ID) + akey, _ := asks.AssignmentsAskKey(p.ID) b.answer(t, akey, assign.OptionYes) select { case e := <-heard: if e.Proposal.ID != p.ID || len(e.Nodes) != 2 || changeIn(e.Nodes[0]).Before != anchorRev || changeIn(e.Nodes[1]).Before != laptopRev || changeIn(e.Nodes[0]).After <= anchorRev || e.Warrant == nil || !slices.Equal(changeIn(e.Nodes[0]).Removed, []string{"web"}) { - t.Errorf("placement-changed = %+v", e) + t.Errorf("assignments-changed = %+v", e) } case <-time.After(15 * time.Second): - t.Fatal("no placement-changed heard") + t.Fatal("no assignments-changed heard") } for node, want := range map[string][]string{"anchor": {}, "laptop": {"shell", "web"}} { - // The node key names the modules, each module key carries its own, and the mirror of the form before follows. + // The node key names the modules, each module key carries its own. values := map[string][]byte{} - for _, key := range []string{node + ".node", "placement." + node, node + ".web", node + ".shell"} { - if e, err := b.buckets[plc.PlacementBucket].Get(b.ctx, key); err == nil { + for _, key := range []string{node + ".node", node + ".web", node + ".shell"} { + if e, err := b.buckets[plc.AssignmentsBucket].Get(b.ctx, key); err == nil { values[key] = e.Value() } } - got, err := mmac.PlacementsOf(values) + got, err := mmac.AssignmentsOf(values) if err != nil || !slices.Equal(mmac.Assigned(got[node]), want) { t.Errorf("%s = %v, %v, want %v", node, mmac.Assigned(got[node]), err, want) } - mirror, _ := mmac.DecodePlacement(values["placement."+node]) - if !slices.Equal(mmac.Assigned(mirror), want) { - t.Errorf("the mirror placement.%s = %v, want %v", node, mmac.Assigned(mirror), want) - } } if b.hasKey(plc.ProposalsBucket, p.ID) { t.Error("an applied proposal stays in the state") @@ -1288,13 +1291,13 @@ func TestAProposalIsAnsweredApprovedAndWritten(t *testing.T) { t.Fatal(err) } moved := q.(proposed).Proposal - place("laptop", false, "shell", "web") // another writer moved the key + place("laptop", "shell", "web") // another writer moved the key relaid := proposalNow(moved.ID, "laid again", func(p mma.Proposal) bool { return strings.Contains(p.Words, "laid again") && len(p.Must) > 0 }) answerAll(relaid) proposalNow(moved.ID, "asked", func(p mma.Proposal) bool { return p.State == mmac.ProposalAsked }) - akey, _ = asks.PlacementAskKey(moved.ID) + akey, _ = asks.AssignmentsAskKey(moved.ID) b.answer(t, akey, assign.OptionNo) for deadline := time.Now().Add(15 * time.Second); b.hasKey(plc.ProposalsBucket, moved.ID); time.Sleep(50 * time.Millisecond) { if time.Now().After(deadline) { @@ -1302,7 +1305,7 @@ func TestAProposalIsAnsweredApprovedAndWritten(t *testing.T) { } } key, _ := mmac.NodeKey("laptop") - if e, _ := b.buckets[plc.PlacementBucket].Get(b.ctx, key); e == nil || !strings.Contains(string(e.Value()), "web") { + if e, _ := b.buckets[plc.AssignmentsBucket].Get(b.ctx, key); e == nil || !strings.Contains(string(e.Value()), "web") { t.Error("a declined proposal wrote its key") } @@ -1320,7 +1323,7 @@ func TestAProposalIsAnsweredApprovedAndWritten(t *testing.T) { } // 774/S59 (ADR 0587 §7): a joining node is written on the enrolment's warrant with no node to answer and no ask raised, - // and placement-changed carries that warrant. The assigner reads no lease (ADR 0567 §8): the enrolment's own issue waits + // and assignments-changed carries that warrant. The assigner reads no lease (ADR 0567 §8): the enrolment's own issue waits // for the lease write (ADR 0587 §4). for len(heard) > 0 { <-heard @@ -1329,7 +1332,7 @@ func TestAProposalIsAnsweredApprovedAndWritten(t *testing.T) { // now, on a node of its own. hubRecord, _ := nodeengineclient.EncodeHaveNode(nodeengine.HaveByNodeNodeValue{Record: &nodeengine.NodeRecord{Node: "hub"}}) b.putJSON(t, buckets.Have, "hub.node", hubRecord) - place("hub", false, "mesh-enrolment") + place("hub", "mesh-enrolment") joined, err := verbs.doPropose(map[string]any{"why": "enrolment of ace", "warrant": `{"node":"ace","address":"10.9.0.2","ask":"enrol-ace-1","warrant":"enrol-ace-1@2026-10-28T20:00:00Z"}`, "changes": `[{"op":"add-node","node":"ace","decided":{"address":"10.9.0.2"},"keys":{"overlay":"dHVubmVs"}}]`}, "novox.mesh-enrolment") if err != nil { @@ -1338,19 +1341,19 @@ func TestAProposalIsAnsweredApprovedAndWritten(t *testing.T) { select { case e := <-heard: if e.Proposal.ID != joined.(proposed).Proposal.ID || e.Warrant == nil || e.Warrant.Warrant != "enrol-ace-1@2026-10-28T20:00:00Z" || len(e.Nodes) != 1 || changeIn(e.Nodes[0]).Node != "ace" { - t.Errorf("placement-changed for the joining node = %+v", e) + t.Errorf("assignments-changed for the joining node = %+v", e) } case <-time.After(15 * time.Second): - t.Fatal("no placement-changed heard for the joining node") + t.Fatal("no assignments-changed heard for the joining node") } - if ace, err := b.buckets[plc.PlacementBucket].Get(b.ctx, "ace.node"); err != nil || !strings.Contains(string(ace.Value()), "10.9.0.2") { + if ace, err := b.buckets[plc.AssignmentsBucket].Get(b.ctx, "ace.node"); err != nil || !strings.Contains(string(ace.Value()), "10.9.0.2") { t.Errorf("the joining node's key = %v, %v", ace, err) } - if akey, _ := asks.PlacementAskKey(joined.(proposed).Proposal.ID); b.hasKey(buckets.Asks, akey) || b.hasKey(plc.ProposalsBucket, joined.(proposed).Proposal.ID) { + if akey, _ := asks.AssignmentsAskKey(joined.(proposed).Proposal.ID); b.hasKey(buckets.Asks, akey) || b.hasKey(plc.ProposalsBucket, joined.(proposed).Proposal.ID) { t.Error("an ask was raised for the joining node, or its proposal stays open") } // Its enrolment ends before it confirms: a remove-node on the same warrant deletes its key at once, with no ask, and - // placement-changed carries the warrant (the operator's decision of 2026-11-02 01:05). + // assignments-changed carries the warrant (the operator's decision of 2026-11-02 01:05). for len(heard) > 0 { <-heard } @@ -1361,12 +1364,12 @@ func TestAProposalIsAnsweredApprovedAndWritten(t *testing.T) { select { case e := <-heard: if e.Warrant == nil || len(e.Nodes) != 1 || changeIn(e.Nodes[0]).Node != "ace" { - t.Errorf("placement-changed for the removed node = %+v", e) + t.Errorf("assignments-changed for the removed node = %+v", e) } case <-time.After(15 * time.Second): - t.Fatal("no placement-changed heard for the removed node") + t.Fatal("no assignments-changed heard for the removed node") } - if _, err := b.buckets[plc.PlacementBucket].Get(b.ctx, "ace.node"); err == nil { + if _, err := b.buckets[plc.AssignmentsBucket].Get(b.ctx, "ace.node"); err == nil { t.Error("the removed node's key stays") } } @@ -1382,8 +1385,8 @@ func (b *bed) viewsHold(t *testing.T, state, key string) { t.Fatal(err) } for deadline := time.Now().Add(20 * time.Second); ; time.Sleep(50 * time.Millisecond) { - f := &views.Follower{Owner: placement.Module, Tool: state, State: state, Ask: func(context.Context, string) (views.Answer, error) { - return views.FirstView(placement.Module, state, src(kv), src(marks), "", time.Now()) + f := &views.Follower{Owner: assignment.Module, Tool: state, State: state, Ask: func(context.Context, string) (views.Answer, error) { + return views.FirstView(assignment.Module, state, src(kv), src(marks), "", time.Now()) }} if err := f.Start(b.ctx); err != nil { t.Fatal(err) @@ -1392,7 +1395,7 @@ func (b *bed) viewsHold(t *testing.T, state, key string) { gap := false for seq := info.State.FirstSeq; seq > 0 && seq <= info.State.LastSeq; seq++ { m, err := st.GetMsg(b.ctx, seq) - if owner, _ := views.OwnerOfEvent(m.Subject); err != nil || owner != placement.Module || views.StateOfEvent(m.Subject) != state { + if owner, _ := views.OwnerOfEvent(m.Subject); err != nil || owner != assignment.Module || views.StateOfEvent(m.Subject) != state { continue } e, err := views.DecodeEvent(m.Data) @@ -1446,11 +1449,11 @@ func (s bedSource) Get(key string) (*views.Entry, bool, error) { return &views.Entry{Value: e.Value(), Revision: e.Revision()}, true, nil } -// placementSubjects are the subjects of the seat's four events on the assigner's stream placement. -func placementSubjects() []string { +// assignmentsSubjects are the subjects of the seat's four events on the assigner's stream assignments. +func assignmentsSubjects() []string { var out []string for _, e := range mma.Events() { - out = append(out, process.ModuleStreamSubject(placement.Module, e)) + out = append(out, process.ModuleStreamSubject(assignment.Module, e)) } return out } @@ -1553,7 +1556,7 @@ func firstViewsBeforeAnyNewFormState(t *testing.T, b *bed, granted map[string][] outR, outW := io.Pipe() t.Cleanup(func() { inW.Close(); outW.Close() }) go func() { - _ = stdio.ServeOn(placement.Module, viewTools(func(k string) string { return env[k] }, state, time.Now), inR, outW) + _ = stdio.ServeOn(assignment.Module, viewTools(func(k string) string { return env[k] }, state, time.Now), inR, outW) }() answers := make(chan map[string]any, 16) go func() { @@ -1574,7 +1577,7 @@ func firstViewsBeforeAnyNewFormState(t *testing.T, b *bed, granted map[string][] t.Fatal(err) } t.Cleanup(nc.Close) - sub, err := nc.Subscribe("mesh.mod."+placement.Module+".call.*.*", func(msg *nats.Msg) { + sub, err := nc.Subscribe("mesh.mod."+assignment.Module+".call.*.*", func(msg *nats.Msg) { one <- struct{}{} defer func() { <-one }() tokens := strings.Split(msg.Subject, ".") @@ -1606,7 +1609,7 @@ func firstViewsBeforeAnyNewFormState(t *testing.T, b *bed, granted map[string][] ask := func(user, tool string) (views.Answer, error) { return views.AskVia(func(key string, body any) (json.RawMessage, error) { data, _ := json.Marshal(body) - msg, err := nc.Request("mesh.mod."+placement.Module+".call."+tool+"."+strings.ReplaceAll(user, ".", "~"), data, 5*time.Second) + msg, err := nc.Request("mesh.mod."+assignment.Module+".call."+tool+"."+strings.ReplaceAll(user, ".", "~"), data, 5*time.Second) if err != nil { return nil, err } @@ -1622,7 +1625,7 @@ func firstViewsBeforeAnyNewFormState(t *testing.T, b *bed, granted map[string][] t.Errorf("%s's first view of %s before any new-form state: %v", user, tool, err) case slices.Contains(allowed, user): keys, _ := b.buckets[viewedStates[tool]].Keys(b.ctx) - if a.Owner != placement.Module || a.State != tool || len(a.Values) != len(keys) { + if a.Owner != assignment.Module || a.State != tool || len(a.Values) != len(keys) { t.Errorf("%s's first view of %s = %+v, the state holds %v", user, tool, a, keys) } case err == nil || !strings.Contains(err.Error(), "refused"): @@ -1632,7 +1635,7 @@ func firstViewsBeforeAnyNewFormState(t *testing.T, b *bed, granted map[string][] } } -// changeIn reads one entry of a placement-changed event's nodes. +// changeIn reads one entry of assignments-changed event's nodes. func changeIn(body map[string]any) plc.Change { raw, _ := json.Marshal(body) var c plc.Change diff --git a/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/main.go b/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/main.go index 99ad4ee2..3ff57552 100644 --- a/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/main.go +++ b/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/main.go @@ -12,8 +12,8 @@ import ( "fmt" "io" "log" + "mesh-delivery-assigner/internal/assignment" "mesh-delivery-assigner/internal/buckets" - "mesh-delivery-assigner/internal/placement" "os" "os/signal" "slices" @@ -23,10 +23,10 @@ import ( "github.com/nats-io/nats.go" "github.com/nats-io/nats.go/jetstream" + mma "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" mcc "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient" occ "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/clients/operatorchannelclient" oc "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel" - mma "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-sdk/go/announce" "git.novox.be/novox/mesh-sdk/go/process" @@ -34,7 +34,7 @@ import ( "git.novox.be/novox/mesh-sdk/go/views" "mesh-delivery-assigner/internal/assign" - plc "mesh-delivery-assigner/internal/placement" + plc "mesh-delivery-assigner/internal/assignment" ) func main() { @@ -75,29 +75,29 @@ var readFamilies = []string{buckets.ComposedRead} // ownBuckets are the assigner's own states, as its manifest declares them: the seat mesh-module-assigner's three among them // (ADR 0567 §1), named after the assigner as every module state is. var ownBuckets = []string{buckets.Asks, buckets.Differences, - buckets.Puts, buckets.Wants, plc.PlacementBucket, plc.ProposalsBucket, + buckets.Puts, buckets.Wants, plc.AssignmentsBucket, plc.ProposalsBucket, plc.CandidatesBucket, plc.PulsesBucket, assign.FindingsBucket, assign.PoliciesBucket, plc.SeatHoldersBucket, - engineReadsBucket} + engineReadsBucket, retiredBucket} // engineReadsState is the assigner's state of what each node's engine states it reads (ADR 0618 §3), one key ``, as // the verb manifest-reads last answered it or the fallback read it: what assigner_engine_reads lists and asks again. const engineReadsState = "engine-reads" // engineReadsBucket is that state's bucket. -const engineReadsBucket = placement.Module + "_" + engineReadsState +const engineReadsBucket = assignment.Module + "_" + engineReadsState // seatBuckets are the seat's states, new with it: until the controller has made them from the manifest and the bus grants // them, the assigner starts without them and binds each once the bus says it was created (issue 1790's kind). -var seatBuckets = []string{plc.PlacementBucket, plc.ProposalsBucket, plc.CandidatesBucket, +var seatBuckets = []string{plc.AssignmentsBucket, plc.ProposalsBucket, plc.CandidatesBucket, plc.PulsesBucket} // keptStates is what the assigner keeps and who may read it (ADR 0495 §2-§3): its wants by the watchman and the view // (and the node agent from release 6), its differences by the view, its asks by the view -// and the teller, its puts by whoever follows one (ADR 0546), and the seat mesh-module-assigner's placement, proposals and -// candidates by the node agents, the grants part, mesh-config and the view (ADR 0567 §9), and its placement by +// and the teller, its puts by whoever follows one (ADR 0546), and the seat mesh-module-assigner's assignments, proposals and +// candidates by the node agents, the grants part, mesh-config and the view (ADR 0567 §9), and its assignments by // the builder, the checker and the indexer, through mesh-sdk's toolchain gate (issue 774, ADR 0549 §3). var keptStates = []announce.State{ - {Name: mma.StatePlacement, Readable: true}, + {Name: mma.StateAssignments, Readable: true}, {Name: mma.StateProposals, Readable: true}, {Name: mma.StateCandidates, Readable: true}, {Name: mma.StatePulses, Readable: true}, @@ -111,6 +111,7 @@ var keptStates = []announce.State{ {Name: views.MarksState, Readable: true}, // what its views family said each state up to (views.go) {Name: views.StatusState, Readable: true}, // its first views of the indexer's states (views.go) {Name: engineReadsState, Readable: true}, + {Name: retiredState}, // read only by the copy into assignments (migrate.go) } // kvSet is every bucket the assigner opened, by name. @@ -129,7 +130,7 @@ func open(ctx context.Context, js process.BucketOpener, logf func(string, ...any continue } if slices.Contains(seatBuckets, name) && err != nil { - // The seat's states come in the same delivery as this assigner (ADR 0567 §1): without placement no module is placed. + // The seat's states come in the same delivery as this assigner (ADR 0567 §1): without assignments no module is placed. logf("the bucket %s cannot be opened (%v): it is bound once the bus says it was made", name, err) continue } @@ -169,6 +170,12 @@ func open(ctx context.Context, js process.BucketOpener, logf func(string, ...any logf("the bucket %s cannot be opened (%v): what each node's engine reads is kept in memory only until it starts again", name, err) continue } + if name == retiredBucket && err != nil { + // The retired state is read only for the copy into `assignments` (migrate.go): a mesh made after the switch has + // none, and nothing is copied. + logf("the bucket %s cannot be opened (%v): nothing is copied from it", name, err) + continue + } if name == buckets.Grants && err != nil { // The grants part's grants (ADR 0571 clause 9): without them no module counts as granted until it is bound. logf("the bucket %s cannot be opened (%v): no grant is read until it is bound once the bus says it was made", name, err) @@ -187,7 +194,7 @@ func open(ctx context.Context, js process.BucketOpener, logf func(string, ...any func startsWithout(name string) bool { switch name { case plc.SeatHoldersBucket, buckets.Provisions, assign.PoliciesBucket, assign.FindingsBucket, - buckets.Puts, buckets.Source, buckets.Merges, engineReadsBucket: + buckets.Puts, buckets.Source, buckets.Merges, engineReadsBucket, retiredBucket: return true } return slices.Contains(seatBuckets, name) || name == buckets.Grants @@ -271,7 +278,7 @@ var watched = []string{ buckets.Grants, buckets.Answers, buckets.Builds, - plc.PlacementBucket, + plc.AssignmentsBucket, plc.ProposalsBucket, plc.CandidatesBucket, plc.PulsesBucket, @@ -419,11 +426,11 @@ func follow(ctx context.Context, js jetstream.JetStream, nc process.Subscriber, name string kv jetstream.KeyValue } - placementAppeared := make(chan appeared, len(seatBuckets)+2) + assignmentsAppeared := make(chan appeared, len(seatBuckets)+2) for _, name := range seatBuckets { if kvs[name] == nil && js != nil { process.AwaitBucket(ctx, js, nc, name, "the seat mesh-module-assigner waits for it", - logger.Printf).Ready(func(kv jetstream.KeyValue) { placementAppeared <- appeared{name, kv} }) + logger.Printf).Ready(func(kv jetstream.KeyValue) { assignmentsAppeared <- appeared{name, kv} }) } } // And the two that choose where grants are read from, with the same delivery as the grants part that writes one. @@ -431,16 +438,20 @@ func follow(ctx context.Context, js jetstream.JetStream, nc process.Subscriber, if kvs[name] == nil && js != nil { name := name process.AwaitBucket(ctx, js, nc, name, "until then the bucket is not read", - logger.Printf).Ready(func(kv jetstream.KeyValue) { placementAppeared <- appeared{name, kv} }) + logger.Printf).Ready(func(kv jetstream.KeyValue) { assignmentsAppeared <- appeared{name, kv} }) } } - takePlacement := func(a appeared) error { + takeAssignments := func(a appeared) error { if kvs[a.name] != nil { return nil // bound already, by the other of its two events } kvs[a.name] = a.kv logger.Printf("the bucket %s is open and watched", a.name) - return watch(a.name) + if err := watch(a.name); err != nil { + return err + } + wake() // a state made empty brings no change of its own: the pass copies the retired state into it (migrate.go) + return nil } for range started { select { @@ -476,8 +487,8 @@ func follow(ctx context.Context, js jetstream.JetStream, nc process.Subscriber, case err := <-errs: return err case <-settled: - case kv := <-placementAppeared: - if err := takePlacement(kv); err != nil { + case kv := <-assignmentsAppeared: + if err := takeAssignments(kv); err != nil { return err } case <-dirty: @@ -491,7 +502,7 @@ func follow(ctx context.Context, js jetstream.JetStream, nc process.Subscriber, continue } if kv, err := js.KeyValue(ctx, name); err == nil { - if err := takePlacement(appeared{name, kv}); err != nil { + if err := takeAssignments(appeared{name, kv}); err != nil { return err } continue @@ -511,7 +522,7 @@ var PutsReopenEvery = time.Minute // (ADR 0437 clause 9). func sayVia(ctx context.Context, js jetstream.JetStream) func(oc.Condition) error { return func(c oc.Condition) error { - subject, err := occ.ConditionSubject(placement.Module) + subject, err := occ.ConditionSubject(assignment.Module) if err != nil { return err } @@ -528,7 +539,7 @@ func sayVia(ctx context.Context, js jetstream.JetStream) func(oc.Condition) erro // consumer on EVENTS, and asks the node it names again; the hub also hands the indexer's views to the followers of // indexerViews, and starts once both are registered. func hearReadsChanged(ctx context.Context, js jetstream.JetStream, hub *process.EventHub, reads *enginereads.Keeper, findings assign.Findings, logf func(string, ...any)) { - account := process.Account{Node: thisNode, Module: placement.Module} + account := process.Account{Node: thisNode, Module: assignment.Module} w := notHeardWriter(findings, logf) handle := func(m jetstream.Msg) { if err := m.Ack(); err != nil { diff --git a/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/main_test.go b/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/main_test.go index 8b35b7b9..f1a66b44 100644 --- a/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/main_test.go +++ b/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/main_test.go @@ -2,8 +2,8 @@ package main import ( "mesh-delivery-assigner/internal/assign" + plc "mesh-delivery-assigner/internal/assignment" "mesh-delivery-assigner/internal/buckets" - plc "mesh-delivery-assigner/internal/placement" "context" "errors" @@ -20,9 +20,9 @@ import ( "github.com/nats-io/nats.go/jetstream" + mmac "git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient" + mma "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" oc "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel" - mmac "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" - mma "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-sdk/go/catalogue" "git.novox.be/novox/mesh-sdk/go/process" ) @@ -112,12 +112,12 @@ func TestOpensWhatItsManifestNames(t *testing.T) { for _, name := range append(append([]string{}, readBuckets...), ownBuckets...) { kvs[name] = &watchKV{memKV: &memKV{values: map[string][]byte{}}} } - placed, err := mmac.PlacementValues(mma.Placement{Node: "ace"}) + placed, err := mmac.AssignmentsValues(mma.Assignments{Node: "ace"}) if err != nil { t.Fatal(err) } for key, raw := range placed { - kv := kvs[plc.PlacementBucket].(*watchKV) + kv := kvs[plc.AssignmentsBucket].(*watchKV) kv.entries = append(kv.entries, kvEntry{key: key, value: raw}) } kvs[buckets.Puts].(*watchKV).closed = true @@ -192,3 +192,8 @@ func (e kvEntry) Value() []byte { return e.value } func (e kvEntry) Operation() jetstream.KeyValueOp { return jetstream.KeyValuePut } func (e kvEntry) Revision() uint64 { return 1 } func (e kvEntry) Created() time.Time { return time.Time{} } + +// Keys lists nothing: the retired state this watch stands for holds no key. +func (w *watchKV) Keys(context.Context, ...jetstream.WatchOpt) ([]string, error) { + return nil, jetstream.ErrNoKeysFound +} diff --git a/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/migrate.go b/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/migrate.go new file mode 100644 index 00000000..5f33b899 --- /dev/null +++ b/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/migrate.go @@ -0,0 +1,126 @@ +package main + +import ( + "context" + "errors" + "slices" + "strings" + + "github.com/nats-io/nats.go/jetstream" + + plc "mesh-delivery-assigner/internal/assignment" + + mmac "git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient" +) + +// The state this seat's assignments were kept in before ADR 0641 gave it its name. The assigner reads it here and nowhere +// else: at its start it copies each key into `assignments` (copyRetired) and keeps no reader of it afterwards. The +// manifest still declares the state, so the assigner's account may read it for the copy; a later release drops it. +const ( + retiredState = "placement" + retiredBucket = plc.Module + "_" + retiredState +) + +// CopiedEvent is the module's event saying the copy was made, once: mesh.mod.mesh-delivery-assigner.event.assignments-copied. +const CopiedEvent = "assignments-copied" + +// copiedSaid is what the event carries: the keys copied, those already in `assignments` (kept as they are), and the keys of +// the form `placement.` that were dropped (their node key holds the value). +type copiedSaid struct { + Copied int `json:"copied"` + Present int `json:"present"` + Dropped int `json:"dropped"` + Skipped int `json:"skipped"` +} + +// copyRetired is the holder's switch from the retired state to `assignments`, with no gap: for each key of the retired state +// in the form `.node` or `.` it copies the raw value into `assignments` when the key is missing there, +// the module keys first and the node keys, which commit a node, last. A key copied or already there is then deleted from the +// retired state, so a restart never brings back what `assignments` has dropped since. The keys of the form +// `placement.` are dropped, and a key that fits no form is left and counted. It answers whether the seat may go on: a +// copy that failed is tried again at the next pass. +func (p *passer) copyRetired(ctx context.Context) bool { + if p.copied { + return true + } + old, next := p.kvs[retiredBucket], p.kvs[plc.AssignmentsBucket] + if next == nil { + return false + } + if old == nil { + p.logf("assignments: the retired state %s cannot be read, so nothing is copied from it", retiredBucket) + p.copied = true + return true + } + names, err := old.Keys(ctx) + if err != nil && !errors.Is(err, jetstream.ErrNoKeysFound) { + p.logf("assignments: the keys of %s cannot be listed, and are tried again at the next pass: %v", retiredBucket, err) + return false + } + // A node's module keys before its node key: the node key is the commit the readers wait for. + slices.SortStableFunc(names, func(a, b string) int { + return strings.Compare(sortForm(a), sortForm(b)) + }) + said := copiedSaid{} + for _, key := range names { + _, _, kind, perr := mmac.ParseKey(key) + switch { + case strings.HasPrefix(key, retiredState+"."): + // The node named placement could not exist before, so this is the form that held a whole node in one key. + said.Dropped++ + case perr != nil || (kind != mmac.NodeKind && kind != mmac.ModuleKind): + said.Skipped++ + continue + default: + entry, err := old.Get(ctx, key) + if errors.Is(err, jetstream.ErrKeyNotFound) { + continue + } + if err != nil { + p.logf("assignments: %s cannot be read, and the copy is tried again at the next pass: %v", key, err) + return false + } + if entry.Operation() != jetstream.KeyValuePut { + continue + } + rev, err := next.Create(ctx, key, entry.Value()) + switch { + case errors.Is(err, jetstream.ErrKeyExists): + said.Present++ + case err != nil: + p.logf("assignments: %s is not copied, and the copy is tried again at the next pass: %v", key, err) + return false + default: + said.Copied++ + p.w.putEntry(plc.AssignmentsBucket, key, entry.Value(), rev, p.clock()) + } + } + if err := old.Delete(ctx, key); err != nil { + p.logf("assignments: %s is not deleted from %s after its copy, and the copy is tried again at the next pass: %v", key, retiredBucket, err) + return false + } + } + p.copied = true + if said == (copiedSaid{}) { + return true + } + p.logf("assignments: copied %d keys of %s into %s (%d already there kept as they are, %d of the form placement. dropped, %d fitting no form left)", + said.Copied, retiredBucket, plc.AssignmentsBucket, said.Present, said.Dropped, said.Skipped) + if said.Copied+said.Present+said.Dropped == 0 { + return true // only keys left behind: the line above says them at each start, and the event was said with the copy + } + if p.seat != nil { + if err := p.seat.bus.Emit(CopiedEvent, said); err != nil { + p.logf("assignments: the copy is not said as an event: %v", err) + } + } + return true +} + +// sortForm orders `.` keys before `.node`. +func sortForm(key string) string { + if node, ok := strings.CutSuffix(key, ".node"); ok { + return node + "\x7f" + } + return key +} diff --git a/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/pass.go b/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/pass.go index 0a672ffa..eac3a2b1 100644 --- a/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/pass.go +++ b/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/pass.go @@ -12,7 +12,7 @@ import ( "time" "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" - mwc "git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient" + mwc "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-assigner/asks" "git.novox.be/novox/mesh-delivery/modules/mesh-grants/grantsstate" @@ -26,7 +26,7 @@ import ( "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient/enginereads" "mesh-delivery-assigner/internal/assign" - plc "mesh-delivery-assigner/internal/placement" + plc "mesh-delivery-assigner/internal/assignment" ) // passer makes the assigner's passes: from what the world holds now, it writes each key whose value differs from the @@ -74,8 +74,8 @@ type passer struct { seat *seatActs publish publisher saidProposed map[string]bool - // convertedAll says every node in the form before was converted to its node and module keys (convertPlacement). - convertedAll bool + // copied says the retired state was copied into `assignments` by this process, or found empty (copyRetired). + copied bool // saidWaits is the last said of the seat's wait for its states (sayWaits). saidWaits string intended map[string]map[string]map[string]bool @@ -123,7 +123,7 @@ func (p *passer) clock() time.Time { } func (p *passer) pass(ctx context.Context) { - p.placementSeat(ctx) + p.assignmentsSeat(ctx) p.wants(ctx) p.puts(ctx) p.nodeWants(ctx) @@ -1071,7 +1071,7 @@ func (p *passer) provisionsMissing(ctx context.Context, missing map[string]bool) } // unplacedFindings writes, from the lease write, the finding `machine..unplaced` for each node that runs but no -// placement names, which gets no want (ADR 0571 §6), and deletes it once the node is placed. +// assignments names, which gets no want (ADR 0571 §6), and deletes it once the node is placed. func (p *passer) unplacedFindings() { if p.findings.Put == nil { return @@ -1106,20 +1106,20 @@ func (p *passer) unplacedFindings() { } } -// seatHolders writes the seat mesh-module-assigner's state seat-holders from every node's placement (ADR 0571 §4): one key per +// seatHolders writes the seat mesh-module-assigner's state seat-holders from every node's assignments (ADR 0571 §4): one key per // seat held once, ``, and one per node of a node seat, `.`, written only where it differs, and a key no -// placement holds any more deleted. A pass follows each placement change and each manifest the indexer takes in, so no +// assignments holds any more deleted. A pass follows each assignments change and each manifest the indexer takes in, so no // timer writes it. func (p *passer) seatHolders(ctx context.Context) { kv := p.kvs[plc.SeatHoldersBucket] if kv == nil { return } - placements := p.w.placements() - if len(placements) == 0 { - return // no placement read yet: nothing is known of who holds what, and nothing is deleted + assignments := p.w.assignments() + if len(assignments) == 0 { + return // no assignments read yet: nothing is known of who holds what, and nothing is deleted } - want := plc.SeatHoldersOf(placements) + want := plc.SeatHoldersOf(assignments) have := p.w.read(plc.SeatHoldersBucket) for key, sh := range want { value, err := json.Marshal(sh) diff --git a/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/pass_test.go b/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/pass_test.go index b039ad13..7bf9d849 100644 --- a/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/pass_test.go +++ b/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/pass_test.go @@ -15,9 +15,9 @@ import ( "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" wdc "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshwatchdogsclient" - mwc "git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" mdc "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient" + mwc "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" mdseat "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdelivery" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-assigner/asks" @@ -26,10 +26,10 @@ import ( "github.com/nats-io/nats.go/jetstream" + mmac "git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient" + mma "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" occ "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/clients/operatorchannelclient" oc "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel" - mmac "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" - mma "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-sdk/go/busretry" "git.novox.be/novox/mesh-sdk/go/catalogue" @@ -38,7 +38,7 @@ import ( "git.novox.be/novox/mesh-delivery/sdk/graph" "mesh-delivery-assigner/internal/assign" - plc "mesh-delivery-assigner/internal/placement" + plc "mesh-delivery-assigner/internal/assignment" ) // memKV is the part of a bucket a pass writes: puts and deletes. @@ -70,20 +70,20 @@ func newPasser(kvs kvSet, at time.Time) *passer { now: func() time.Time { return at }} } -// placeOn puts the seat's placement of node, naming the modules, into the world: one node key and a key per module +// placeOn puts the seat's assignments of node, naming the modules, into the world: one node key and a key per module // (research 072 row 37). func placeOn(t *testing.T, w *world, node string, modules ...string) { t.Helper() - pl := mma.Placement{Node: node} + pl := mma.Assignments{Node: node} for _, m := range modules { pl.Modules = append(pl.Modules, mma.PlacedModule{Module: m}) } - values, err := mmac.PlacementValues(pl) + values, err := mmac.AssignmentsValues(pl) if err != nil { t.Fatal(err) } for key, raw := range values { - w.put(plc.PlacementBucket, key, raw) + w.put(plc.AssignmentsBucket, key, raw) } } @@ -491,7 +491,7 @@ func putOfTheBus(t *testing.T) { // the journal, so it is loud with no operator channel. func TestAModuleOnNeitherListIsRaisedOnceAndClearedOnce(t *testing.T) { // A pair only one source names is a condition of the same kind (issue 1126): folded here to hold the count. - aPlacementAloneNamesWhereModulesRun(t) + aAssignmentsAloneNamesWhereModulesRun(t) // The verb edges a want waits on come from the graph and what runs (ADR 0614 §2, §3): folded here to hold the count. aWantWaitsOnTheVerbEdgesTheGraphKeeps(t) t.Run("gathers", testThePassGathersTheModulesOnNeitherList) @@ -972,18 +972,18 @@ func aReleasesApproveAnswersTheMoveOfItsBuildAlone(t *testing.T) { } } -// 774/S26/AC1 (ADR 0549 §5, ADR 0567 §8): the seat's placement alone names where modules run: no controller state is read, so a -// module in an assignment only is no module of its node, and before any placement is read nothing is placed. -func aPlacementAloneNamesWhereModulesRun(t *testing.T) { +// 774/S26/AC1 (ADR 0549 §5, ADR 0567 §8): the seat's assignments alone names where modules run: no controller state is read, so a +// module in an assignment only is no module of its node, and before any assignments is read nothing is placed. +func aAssignmentsAloneNamesWhereModulesRun(t *testing.T) { p := newPasser(kvSet{}, time.Date(2026, 10, 26, 18, 0, 0, 0, time.UTC)) p.w.put("mesh-controller_assignments", "g14.b", []byte(`{"node":"g14","module":"b","build":"b-1"}`)) // read no more if got := p.w.assigned(); len(got) != 0 { - t.Fatalf("before any placement: assigned %v", got) + t.Fatalf("before any assignments: assigned %v", got) } placeOn(t, p.w, "g14", "c", "a") placeOn(t, p.w, "n2", "d") if got := p.w.assigned(); !slices.Equal(got["g14"], []string{"a", "c"}) || !slices.Equal(got["n2"], []string{"d"}) || len(got) != 2 { - t.Errorf("the placement alone: assigned %v", got) + t.Errorf("the assignments alone: assigned %v", got) } } @@ -1000,8 +1000,8 @@ func aPlacedNodesWantsNameTheBuildAloneAndItsYesComesFromItsHave(t *testing.T) { mod, _ := meshcatalogclient.EncodeModule(meshcatalog.ModulesByModuleValue{Fp2: fpTest, Repository: "novox/web", Commit: strings.Repeat("1", 40), Path: meshcatalogclient.RootPath}) put(buckets.Modules, "web", mod) // the trunk's build is not done: rule 1 holds g14 at fpOld placeOn(t, p.w, "g14", "web") - pl, _ := mmac.EncodePlacement(mma.Placement{Node: "g14", Modules: []mma.PlacedModule{{Module: "web"}}}) - put(plc.PlacementBucket, "placement.g14", pl) + pl, _ := mmac.EncodeAssignments(mma.Assignments{Node: "g14", Modules: []mma.PlacedModule{{Module: "web"}}}) + put(plc.AssignmentsBucket, "assignments.g14", pl) on, _ := presenceValue(true) put(buckets.Presence, "g14", on) have, _ := nodeengineclient.EncodeHave(nodeengine.HaveByNodeModuleValue{Fp: fpOld, Composed: was, Phase: nodeengineclient.PhaseRunning, Health: nodeengineclient.HealthOK}) @@ -1076,19 +1076,19 @@ func aPlacedNodesWantsNameTheBuildAloneAndItsYesComesFromItsHave(t *testing.T) { } } -// 774/S25/AC1 (ADR 0567 §8): an approved placement change is written and followed with its warrant recorded and -// placement-changed said, and no verb of the controller is called; the refused-call conditions a previous process raised are +// 774/S25/AC1 (ADR 0567 §8): an approved assignments change is written and followed with its warrant recorded and +// assignments-changed said, and no verb of the controller is called; the refused-call conditions a previous process raised are // cleared once, and said again at a later pass while the findings cannot be read. -func TestAnApprovedPlacementCallsNoControllerVerb(t *testing.T) { +func TestAnApprovedAssignmentsCallsNoControllerVerb(t *testing.T) { p := newPasser(kvSet{}, time.Date(2026, 11, 2, 9, 0, 0, 0, time.UTC)) bus := &fakeBus{calls: make(chan string, 8)} p.seat = newSeatActs(bus, func() {}, func(string, ...any) {}) var said []string p.publish = func(event string, _ any, _ string) error { said = append(said, event); return nil } prop := mma.Proposal{ID: "p-one", By: "operator@laptop", Why: "move web"} - change := plc.ChangeOf(mma.Placement{Node: "anchor"}, mma.Placement{Node: "anchor", Modules: []mma.PlacedModule{{Module: "web"}}}) + change := plc.ChangeOf(mma.Assignments{Node: "anchor"}, mma.Assignments{Node: "anchor", Modules: []mma.PlacedModule{{Module: "web"}}}) p.followWrite(prop, mdc.Answer{}, []plc.Change{change}) - if len(bus.calls) != 0 || !slices.Equal(said, []string{mma.EventPlacementChanged}) { + if len(bus.calls) != 0 || !slices.Equal(said, []string{mma.EventAssignmentsChanged}) { t.Fatalf("calls = %d, said = %v", len(bus.calls), said) } diff --git a/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/puts.go b/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/puts.go index ec85652a..492f4219 100644 --- a/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/puts.go +++ b/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/puts.go @@ -13,8 +13,8 @@ import ( "time" "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" - mwc "git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient" + mwc "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-assigner/asks" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds" "git.novox.be/novox/mesh-delivery/modules/mesh-grants/grantsstate" diff --git a/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/seat.go b/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/seat.go index c84dc005..f9f47447 100644 --- a/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/seat.go +++ b/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/seat.go @@ -6,8 +6,8 @@ import ( "errors" "fmt" "maps" + "mesh-delivery-assigner/internal/assignment" "mesh-delivery-assigner/internal/buckets" - "mesh-delivery-assigner/internal/placement" "reflect" "slices" "sort" @@ -24,24 +24,24 @@ import ( "github.com/nats-io/nats.go" "github.com/nats-io/nats.go/jetstream" + mmac "git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient" + mma "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshhandactsclient" occ "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/clients/operatorchannelclient" - mmac "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" - mma "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-sdk/go/seatdef" "mesh-delivery-assigner/internal/assign" + plc "mesh-delivery-assigner/internal/assignment" "mesh-delivery-assigner/internal/judge" - plc "mesh-delivery-assigner/internal/placement" ) // The seat mesh-module-assigner's holder in the assigner's process (novox/hq ADR 0567 §2-§8): each pass follows every open -// proposal one step, from what the world holds. A proposal the verbs wrote is said (placement-proposed); the nodes it +// proposal one step, from what the world holds. A proposal the verbs wrote is said (assignments-proposed); the nodes it // must hear from are found by composing every node with and without it, and those its key does not change get a reached -// candidate; their answers are judged (placement-judged) and, on a passing verdict, the operator is asked to approve it; -// on the warrant each changed key is written by compare-and-set at the revision it was laid over (placement-changed), +// candidate; their answers are judged (assignments-judged) and, on a passing verdict, the operator is asked to approve it; +// on the warrant each changed key is written by compare-and-set at the revision it was laid over (assignments-changed), // and the controller's assignments are kept in step through its verbs until the lease write. A key moved meanwhile -// lays the proposal again; a decline, an expiry or a withdrawal ends it (placement-withdrawn). Every event is said on the +// lays the proposal again; a decline, an expiry or a withdrawal ends it (assignments-withdrawn). Every event is said on the // assigner's own stream, so only this process says them. Nothing polls: the wake at an answer's bound is the one wake. // publisher says one of the seat's events on the assigner's stream, under a message id the bus keeps one of. @@ -58,7 +58,7 @@ func seatPublisher(ctx context.Context, js jetstream.JetStream, node string) pub } msg := nats.NewMsg(seatdef.EventSubject(mma.Seat, event)) msg.Header.Set("x-event-id", id) - msg.Header.Set("x-source", placement.Module) + msg.Header.Set("x-source", assignment.Module) msg.Header.Set("x-node", node) msg.Header.Set("x-time", time.Now().UTC().Format(time.RFC3339Nano)) msg.Header.Set("content-type", "application/json") @@ -69,7 +69,7 @@ func seatPublisher(ctx context.Context, js jetstream.JetStream, node string) pub } // seatActs keeps the seat's acts while this process runs: the warrant recorded in the hand-act log. The seat calls no verb of -// the controller: placement is the one source (ADR 0567 §8, issue 774). +// the controller: assignments is the one source (ADR 0567 §8, issue 774). type seatActs struct { bus caller wake func() @@ -82,7 +82,7 @@ func newSeatActs(bus caller, wake func(), logf func(string, ...any)) *seatActs { return &seatActs{bus: bus, wake: wake, logf: logf, rec: meshhandactsclient.Recorder{Ask: bus.Call}} } -// record records once, in the hand-act log, that placement was written on the operator's warrant (§5): the router's +// record records once, in the hand-act log, that assignments was written on the operator's warrant (§5): the router's // record of the teller's ask says who chose and how (ADR 0274). The log is the seat mesh-hand-acts' from its delivery, and // the controller's while that seat has no holder (ADR 0565 §11, meshhandactsclient.Recorder). func (a *seatActs) record(warrant string) { @@ -92,9 +92,9 @@ func (a *seatActs) record(warrant string) { ask, _, _ := strings.Cut(warrant, "@") go func() { if taken, err := a.rec.RecordWarrant(buckets.TellerModule, ask); err != nil { - a.logf("the placement's warrant %s is not recorded in the hand-act log: %v", ask, err) + a.logf("the assignments's warrant %s is not recorded in the hand-act log: %v", ask, err) } else { - a.logf("the placement's warrant %s is recorded in the log of %s", ask, taken.Log) + a.logf("the assignments's warrant %s is recorded in the log of %s", ask, taken.Log) } }() } @@ -147,10 +147,9 @@ func (p *passer) sayWaits(unbound []string) { p.said[c.Key], p.saidWaits = true, state } -// seatPlacements is every node's placement and the revision of its node key (its legacy key's for a node not yet -// converted), as the watch holds them: the revision a proposal is laid over (§2, research 072 row 37). -func (p *passer) seatPlacements() (map[string]mma.Placement, map[string]uint64) { - return p.w.placement.AllRevisions() +// seatAssignments is every node's assignments and the revision of its node key, as the watch holds them: the revision a proposal is laid over (§2, research 072 row 37). +func (p *passer) seatAssignments() (map[string]mma.Assignments, map[string]uint64) { + return p.w.assignmentKeys.AllRevisions() } // candidatesOf is a proposal's candidates by node, with their keys' revisions and the newest write. @@ -194,8 +193,8 @@ func (p *passer) answersTo(id string) map[string]*nodeengine.CandidateAnswer { return out } -// placementSeat takes every open proposal one step further. -func (p *passer) placementSeat(ctx context.Context) { +// assignmentsSeat takes every open proposal one step further. +func (p *passer) assignmentsSeat(ctx context.Context) { for _, b := range seatBuckets { if p.kvs[b] == nil { return @@ -204,8 +203,10 @@ func (p *passer) placementSeat(ctx context.Context) { if p.saidProposed == nil { p.saidProposed, p.intended = map[string]bool{}, map[string]map[string]map[string]bool{} } - p.convertPlacement(ctx) - current, revs := p.seatPlacements() + if !p.copyRetired(ctx) { + return // the old state is copied whole before the seat takes a step: a half-copied state would place nothing on a node + } + current, revs := p.seatAssignments() ids := make([]string, 0) raws := p.w.read(plc.ProposalsBucket) for id := range raws { @@ -251,7 +252,7 @@ func (p *passer) deletePulses(ctx context.Context, id string) { // ask raises (or raises again) a proposal's authorising ask and its bound, and answers the proposal asked. func (p *passer) ask(ctx context.Context, prop mma.Proposal, akey, brief string) (mma.Proposal, bool) { - a, err := assign.PlacementAsk(akey, prop, brief, assign.PlacementAsking(prop, p.clock()), p.clock()) + a, err := assign.AssignmentsAsk(akey, prop, brief, assign.AssignmentsAsking(prop, p.clock()), p.clock()) if err != nil { p.logf("the ask about the proposal %s is not written: %v", prop.ID, err) return prop, false @@ -270,9 +271,9 @@ func (p *passer) ask(ctx context.Context, prop mma.Proposal, akey, brief string) } // stepProposal takes one proposal one step further. -func (p *passer) stepProposal(ctx context.Context, prop mma.Proposal, rev uint64, current map[string]mma.Placement, revs map[string]uint64) { +func (p *passer) stepProposal(ctx context.Context, prop mma.Proposal, rev uint64, current map[string]mma.Assignments, revs map[string]uint64) { if !p.saidProposed[prop.ID] && prop.State == mmac.ProposalAnswering && prop.Verdict == nil { - p.sayEvent(mma.EventPlacementProposed, mma.PlacementEvent{Proposal: prop}, prop.ID) + p.sayEvent(mma.EventAssignmentsProposed, mma.AssignmentsEvent{Proposal: prop}, prop.ID) p.saidProposed[prop.ID] = true } if prop.Withdrawn != "" { @@ -281,14 +282,14 @@ func (p *passer) stepProposal(ctx context.Context, prop mma.Proposal, rev uint64 } if prop.Seed || prop.HandBack { // Adopt and hand-back are gone since the lease write (ADR 0547 §11, ADR 0567 §11): a proposal one left open ends. - p.endProposal(ctx, prop, "adopt and hand-back are gone since the lease write: placement is the mesh's alone") + p.endProposal(ctx, prop, "adopt and hand-back are gone since the lease write: assignments is the mesh's alone") return } if prop.Warrant != nil { p.addNode(ctx, prop, revs) return } - akey, _ := asks.PlacementAskKey(prop.ID) + akey, _ := asks.AssignmentsAskKey(prop.ID) again := prop.Again != nil && !prop.Again.IsZero() && (prop.Verdict == nil || prop.Again.After(prop.Verdict.At)) if prop.State == mmac.ProposalAsked { askPulse, _ := plc.PulseAskKey(prop.ID) @@ -349,7 +350,7 @@ func (p *passer) stepProposal(ctx context.Context, prop mma.Proposal, rev uint64 } } if p.writeProposal(ctx, prop, rev) { - p.sayEvent(mma.EventPlacementJudged, mma.PlacementEvent{Proposal: prop}, + p.sayEvent(mma.EventAssignmentsJudged, mma.AssignmentsEvent{Proposal: prop}, fmt.Sprintf("%s.judged.%d", prop.ID, v.At.UnixNano())) } } @@ -371,7 +372,7 @@ func moved(prop mma.Proposal, revs map[string]uint64) bool { } // intendedOf is, by proposal and the revisions it was laid over, what Intended computed, kept while this process runs. -func (p *passer) intendedOf(prop mma.Proposal, current map[string]mma.Placement, cands map[string]mma.CandidatesByProposalNodeValue) map[string]map[string]bool { +func (p *passer) intendedOf(prop mma.Proposal, current map[string]mma.Assignments, cands map[string]mma.CandidatesByProposalNodeValue) map[string]map[string]bool { key := fmt.Sprint(prop.ID, prop.Base) if got, ok := p.intended[key]; ok { return got @@ -381,14 +382,14 @@ func (p *passer) intendedOf(prop mma.Proposal, current map[string]mma.Placement, return got } -// compute is what the proposal changes on each node by its placement, and the nodes that answer it: every node with a -// placement or a record (judge.Intended, ADR 0593 §8). The holder composes nothing: each node says what its compositions +// compute is what the proposal changes on each node by its assignments, and the nodes that answer it: every node with a +// assignments or a record (judge.Intended, ADR 0593 §8). The holder composes nothing: each node says what its compositions // change in its answer. -func (p *passer) compute(prop mma.Proposal, current map[string]mma.Placement, cands map[string]mma.CandidatesByProposalNodeValue) (map[string]map[string]bool, []string) { - laid := map[string]*mma.Placement{} +func (p *passer) compute(prop mma.Proposal, current map[string]mma.Assignments, cands map[string]mma.CandidatesByProposalNodeValue) (map[string]map[string]bool, []string) { + laid := map[string]*mma.Assignments{} for _, node := range plc.Nodes(prop) { if c, ok := cands[node]; ok && !c.Reached { - laid[node] = c.Placement + laid[node] = c.Assignments } } nodes := map[string]bool{} @@ -403,7 +404,7 @@ func (p *passer) compute(prop mma.Proposal, current map[string]mma.Placement, ca // mustAnswer finds the nodes the proposal must hear from (§3), writes a reached candidate for each whose key it does not // change, and writes them into the proposal. -func (p *passer) mustAnswer(ctx context.Context, prop mma.Proposal, rev uint64, current map[string]mma.Placement, cands map[string]mma.CandidatesByProposalNodeValue) { +func (p *passer) mustAnswer(ctx context.Context, prop mma.Proposal, rev uint64, current map[string]mma.Assignments, cands map[string]mma.CandidatesByProposalNodeValue) { for _, node := range plc.Nodes(prop) { if _, ok := cands[node]; !ok { return // the verbs write the candidates before the proposal: wait to hear them @@ -421,7 +422,7 @@ func (p *passer) mustAnswer(ctx context.Context, prop mma.Proposal, rev uint64, } c := mma.CandidatesByProposalNodeValue{Proposal: prop.ID, Reached: true} if pl, ok := current[node]; ok { - c.Placement = &pl + c.Assignments = &pl } p.put(ctx, plc.CandidatesBucket, key, func() ([]byte, error) { return plc.EncodeCandidate(c) }) } @@ -436,7 +437,7 @@ func (p *passer) mustAnswer(ctx context.Context, prop mma.Proposal, rev uint64, // layAgain lays a proposal over the keys as they stand now (§5: a key moved): its candidates are written again, the // nodes that must answer found again, and it is judged and asked again; a proposal that no longer lays ends. -func (p *passer) layAgain(ctx context.Context, prop mma.Proposal, rev uint64, current map[string]mma.Placement, revs map[string]uint64, why string) { +func (p *passer) layAgain(ctx context.Context, prop mma.Proposal, rev uint64, current map[string]mma.Assignments, revs map[string]uint64, why string) { held, recorded := p.lays(ctx) again, laid, findings := assign.Propose(assign.ProposeIn{ID: prop.ID, By: prop.By, Why: prop.Why, At: prop.At, Changes: prop.Changes, Current: current, Revisions: revs, Held: held, Recorded: recorded}) @@ -448,7 +449,7 @@ func (p *passer) layAgain(ctx context.Context, prop mma.Proposal, rev uint64, cu p.deletePulses(ctx, prop.ID) for _, node := range plc.Nodes(again) { key, _ := plc.CandidateKey(prop.ID, node) - c := mma.CandidatesByProposalNodeValue{Proposal: prop.ID, Placement: laid[node]} + c := mma.CandidatesByProposalNodeValue{Proposal: prop.ID, Assignments: laid[node]} p.put(ctx, plc.CandidatesBucket, key, func() ([]byte, error) { return plc.EncodeCandidate(c) }) } again.Written, again.Words = ptr(p.clock().UTC()), why+": laid again over the keys as they stand, to be judged and asked again" @@ -469,9 +470,9 @@ func (p *passer) lays(ctx context.Context) (func(string) bool, func(string) bool } // warranted writes an approved proposal (§5): each changed key by compare-and-set at the revision it was laid over; then the warrant recorded, the candidates and the -// proposal deleted, and placement-changed said. -func (p *passer) warranted(ctx context.Context, prop mma.Proposal, rev uint64, akey string, current map[string]mma.Placement, revs map[string]uint64) { - if err := p.performs(akey, assign.PlacementAct(prop)); err != nil { +// proposal deleted, and assignments-changed said. +func (p *passer) warranted(ctx context.Context, prop mma.Proposal, rev uint64, akey string, current map[string]mma.Assignments, revs map[string]uint64) { + if err := p.performs(akey, assign.AssignmentsAct(prop)); err != nil { p.endProposal(ctx, prop, "its warrant does not bind these changes, so nothing is written: "+err.Error()) return } @@ -481,9 +482,9 @@ func (p *passer) warranted(ctx context.Context, prop mma.Proposal, rev uint64, a return } cands, _, _ := p.candidatesOf(prop.ID) - laid := map[string]*mma.Placement{} + laid := map[string]*mma.Assignments{} for _, node := range plc.Nodes(prop) { - laid[node] = cands[node].Placement + laid[node] = cands[node].Assignments } writes, err := assign.Writes(prop, laid) if err != nil { @@ -491,7 +492,7 @@ func (p *passer) warranted(ctx context.Context, prop mma.Proposal, rev uint64, a return } var changes []plc.Change - writtenLaid := map[string]*mma.Placement{} + writtenLaid := map[string]*mma.Assignments{} after := maps.Clone(current) afterRevs := maps.Clone(revs) for _, w := range writes { @@ -499,7 +500,7 @@ func (p *passer) warranted(ctx context.Context, prop mma.Proposal, rev uint64, a if err != nil { // Another writer came first (a hand-back): what is written stands and is followed through; the rest is laid // again over the keys as this write left them. - p.logf("placement %s is not written at revision %d (%v): the rest of the proposal %s is laid again", w.Key, w.Base, err, prop.ID) + p.logf("assignments %s is not written at revision %d (%v): the rest of the proposal %s is laid again", w.Key, w.Base, err, prop.ID) p.followWrite(prop, answer, changes) if rest, ok := assign.Rest(prop, writtenLaid); ok { p.layAgain(ctx, rest, rev, after, afterRevs, "a key moved while it was written, after "+strings.Join(sortedNodes(writtenLaid), ", ")+" were") @@ -509,7 +510,7 @@ func (p *passer) warranted(ctx context.Context, prop mma.Proposal, rev uint64, a return } before := current[w.Node] - now := mma.Placement{Node: w.Node} + now := mma.Assignments{Node: w.Node} if w.Value != nil { now = *w.Value after[w.Node] = *w.Value @@ -532,9 +533,9 @@ func (p *passer) warranted(ctx context.Context, prop mma.Proposal, rev uint64, a // addNode writes a joining node on the warrant of the enrolment that issued its token (ADR 0587 §7): the operator's Approve // was the issue's, so no node is waited on and no ask is raised. The key is created at the revision it was laid over (none: a // node with a key is no joining node); the enrolment recorded the warrant in the hand-act log when it was given, so this does -// not record it again; placement-changed carries the warrant. A key written meanwhile ends the proposal, saying so. +// not record it again; assignments-changed carries the warrant. A key written meanwhile ends the proposal, saying so. func (p *passer) addNode(ctx context.Context, prop mma.Proposal, revs map[string]uint64) { - current, _ := p.seatPlacements() + current, _ := p.seatAssignments() if moved(prop, revs) { p.endProposal(ctx, prop, "a key it was laid over was written since: the enrolment proposes the node again") return @@ -556,7 +557,7 @@ func (p *passer) addNode(ctx context.Context, prop mma.Proposal, revs map[string p.endProposal(ctx, prop, "the node's key was not written: "+err.Error()) return } - c := plc.ChangeOf(mma.Placement{Node: node}, value) + c := plc.ChangeOf(mma.Assignments{Node: node}, value) c.Before, c.After = uint64(prop.Base[node]), rev p.deleteCandidates(ctx, prop.ID) p.deletePulses(ctx, prop.ID) @@ -569,9 +570,9 @@ func (p *passer) addNode(ctx context.Context, prop mma.Proposal, revs map[string } // removeNode deletes the key of a joining node whose enrolment ended unconfirmed, on that enrolment's warrant (the operator's -// decision of 2026-11-02 01:05): no node answers and no ask is raised, as for its add-node; placement-changed carries the warrant. +// decision of 2026-11-02 01:05): no node answers and no ask is raised, as for its add-node; assignments-changed carries the warrant. // A node that runs modules or holds seats is no joining node and stays (LayChanges' finding ends the proposal). -func (p *passer) removeNode(ctx context.Context, prop mma.Proposal, current map[string]mma.Placement, findings []string) { +func (p *passer) removeNode(ctx context.Context, prop mma.Proposal, current map[string]mma.Assignments, findings []string) { node := prop.Changes[0].Node before, ok := current[node] if len(findings) > 0 || !ok { @@ -583,7 +584,7 @@ func (p *passer) removeNode(ctx context.Context, prop mma.Proposal, current map[ p.endProposal(ctx, prop, "the node's key was not deleted: "+err.Error()) return } - c := plc.ChangeOf(before, mma.Placement{Node: node}) + c := plc.ChangeOf(before, mma.Assignments{Node: node}) c.Before, c.After = uint64(prop.Base[node]), rev p.deleteCandidates(ctx, prop.ID) p.deletePulses(ctx, prop.ID) @@ -591,12 +592,12 @@ func (p *passer) removeNode(ctx context.Context, prop mma.Proposal, current map[ p.w.put(plc.ProposalsBucket, prop.ID, nil) w := prop.Warrant p.sayChanged(prop, mdc.Answer{Option: assign.OptionYes, Warrant: w.Warrant, - Words: fmt.Sprintf("the machine %s left placement: its enrolment (ask %s) ended before it confirmed", w.Node, w.Ask)}, + Words: fmt.Sprintf("the machine %s left assignments: its enrolment (ask %s) ended before it confirmed", w.Node, w.Ask)}, []plc.Change{c}) } -// followWrite follows the keys a warrant wrote: the warrant recorded and placement-changed said. No verb of the -// controller is called: the placement is the one source of where modules run (ADR 0567 §8, issue 774). +// followWrite follows the keys a warrant wrote: the warrant recorded and assignments-changed said. No verb of the +// controller is called: the assignments is the one source of where modules run (ADR 0567 §8, issue 774). func (p *passer) followWrite(prop mma.Proposal, answer mdc.Answer, changes []plc.Change) { if len(changes) == 0 { return @@ -605,7 +606,7 @@ func (p *passer) followWrite(prop mma.Proposal, answer mdc.Answer, changes []plc p.sayChanged(prop, answer, changes) } -func sortedNodes(m map[string]*mma.Placement) []string { +func sortedNodes(m map[string]*mma.Assignments) []string { out := make([]string, 0, len(m)) for n := range m { out = append(out, n) @@ -614,7 +615,7 @@ func sortedNodes(m map[string]*mma.Placement) []string { return out } -// sayChanged says placement-changed for the keys written. +// sayChanged says assignments-changed for the keys written. func (p *passer) sayChanged(prop mma.Proposal, answer mdc.Answer, changes []plc.Change) { if len(changes) == 0 { return @@ -626,14 +627,14 @@ func (p *passer) sayChanged(prop mma.Proposal, answer mdc.Answer, changes []plc. } } plc.SortChanges(changes) - p.sayEvent(mma.EventPlacementChanged, mma.PlacementEvent{Proposal: prop, Nodes: changeBodies(changes), - Warrant: &mma.PlacementEventWarrant{Warrant: answer.Warrant, Words: answer.Words, At: at}}, prop.ID+".changed") + p.sayEvent(mma.EventAssignmentsChanged, mma.AssignmentsEvent{Proposal: prop, Nodes: changeBodies(changes), + Warrant: &mma.AssignmentsEventWarrant{Warrant: answer.Warrant, Words: answer.Words, At: at}}, prop.ID+".changed") } -// endProposal ends a proposal: its ask, its candidates and its key deleted, and placement-withdrawn said carrying it +// endProposal ends a proposal: its ask, its candidates and its key deleted, and assignments-withdrawn said carrying it // whole with why it ended. func (p *passer) endProposal(ctx context.Context, prop mma.Proposal, why string) { - if akey, err := asks.PlacementAskKey(prop.ID); err == nil && p.w.has(buckets.Asks, akey) { + if akey, err := asks.AssignmentsAskKey(prop.ID); err == nil && p.w.has(buckets.Asks, akey) { if err := p.kvs[buckets.Asks].Delete(ctx, akey); err == nil { p.w.put(buckets.Asks, akey, nil) } @@ -646,7 +647,7 @@ func (p *passer) endProposal(ctx context.Context, prop mma.Proposal, why string) } p.w.put(plc.ProposalsBucket, prop.ID, nil) p.logf("the proposal %s ends: %s", prop.ID, why) - p.sayEvent(mma.EventPlacementWithdrawn, mma.PlacementEvent{Proposal: prop, Ended: why}, prop.ID+".withdrawn") + p.sayEvent(mma.EventAssignmentsWithdrawn, mma.AssignmentsEvent{Proposal: prop, Ended: why}, prop.ID+".withdrawn") } // deleteCandidates deletes every candidate of a proposal. @@ -686,7 +687,7 @@ func (p *passer) writeProposal(ctx context.Context, prop mma.Proposal, rev uint6 } // say says one of the seat's events, said in the journal when the bus refuses it. -func (p *passer) sayEvent(event string, body mma.PlacementEvent, id string) { +func (p *passer) sayEvent(event string, body mma.AssignmentsEvent, id string) { if p.publish == nil { return } @@ -695,30 +696,30 @@ func (p *passer) sayEvent(event string, body mma.PlacementEvent, id string) { } } -// writeNode writes one node's placement after (nil: none) as its keys (mesh-sdk mmac.Keys.Writes; research +// writeNode writes one node's assignments after (nil: none) as its keys (mesh-sdk mmac.Keys.Writes; research // 072 row 37): the module keys that change, each carrying the value it replaces, then the node key, the commit, then the // module keys no longer placed. It answers the node key's revision after (0: deleted). A write that fails up to the // commit ends it with nothing changed for any reader, the module keys written before it read at their previous values; // a delete after the commit that fails leaves a module key the node key does not name, which places nothing and is // deleted at the node's next write. Each key written is taken into the world at once, so the next write of the pass // reads it. -func (p *passer) writeNode(ctx context.Context, node string, after *mma.Placement) (uint64, error) { - writes, err := p.w.placement.Writes(node, after) +func (p *passer) writeNode(ctx context.Context, node string, after *mma.Assignments) (uint64, error) { + writes, err := p.w.assignmentKeys.Writes(node, after) if err != nil { return 0, err } - kv := p.kvs[plc.PlacementBucket] + kv := p.kvs[plc.AssignmentsBucket] committed, rev := false, uint64(0) for _, w := range writes { got, err := writeKey(ctx, kv, w) if err != nil { if committed { - p.logf("placement %s is not deleted after %s's write (%v): it places nothing, and goes at the node's next write", w.Key, node, err) + p.logf("assignments %s is not deleted after %s's write (%v): it places nothing, and goes at the node's next write", w.Key, node, err) continue } - return 0, fmt.Errorf("placement %s at revision %d: %w", w.Key, w.Base, err) + return 0, fmt.Errorf("assignments %s at revision %d: %w", w.Key, w.Base, err) } - p.w.putEntry(plc.PlacementBucket, w.Key, w.Value, got, p.clock()) + p.w.putEntry(plc.AssignmentsBucket, w.Key, w.Value, got, p.clock()) if w.Commit { committed, rev = true, got } @@ -726,7 +727,7 @@ func (p *passer) writeNode(ctx context.Context, node string, after *mma.Placemen return rev, nil } -// writeKey makes one write of a placement key by compare-and-set and answers the key's revision after (0: deleted). +// writeKey makes one write of an assignments key by compare-and-set and answers the key's revision after (0: deleted). func writeKey(ctx context.Context, kv jetstream.KeyValue, w mmac.KeyWrite) (uint64, error) { switch { case w.Value == nil: @@ -738,33 +739,6 @@ func writeKey(ctx context.Context, kv jetstream.KeyValue, w mmac.KeyWrite) (uint } } -// convertPlacement converts every node the state holds only in the form before, `placement.`, into its node key -// and module keys (research 072 row 37), once: each node converted is said, and a node whose conversion fails is said -// and tried again at the next pass. The legacy key is left for the readers not yet delivered; a node key wins over it -// for every reader that reads both, and it is deleted with the node key when the node's placement is. -func (p *passer) convertPlacement(ctx context.Context) { - if p.convertedAll { - return - } - migrations, err := p.w.placement.Migrations() - if err != nil { - p.logf("placement: some nodes in the form before cannot be converted, and stay read from it: %v", err) - } - failed := false - for _, node := range slices.Sorted(maps.Keys(migrations)) { - before, rev, _ := p.w.placement.Of(node) - got, err := p.writeNode(ctx, node, &before) - if err != nil { - failed = true - p.logf("placement: %s is not converted from placement.%s (revision %d) to its node and module keys, and is tried again at the next pass: %v", node, node, rev, err) - continue - } - p.logf("placement: %s converted from placement.%s (revision %d) to %s.node (revision %d) and %d module keys (research 072 row 37); placement.%s stays for readers not yet delivered", - node, node, rev, node, got, len(before.Modules), node) - } - p.convertedAll = !failed && err == nil -} - func ptr[T any](v T) *T { return &v } // changeBodies are the changes as the seat's event carries them. diff --git a/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/seat_tools.go b/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/seat_tools.go index 9cd6f87c..22342577 100644 --- a/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/seat_tools.go +++ b/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/seat_tools.go @@ -14,12 +14,12 @@ import ( "git.novox.be/novox/mesh-contracts/go/seats/nodeengine" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" - mmac "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" - mma "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" + mmac "git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient" + mma "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" stdio "git.novox.be/novox/mesh-sdk/go" "mesh-delivery-assigner/internal/assign" - plc "mesh-delivery-assigner/internal/placement" + plc "mesh-delivery-assigner/internal/assignment" ) // The seat mesh-module-assigner's seven verbs (novox/hq ADR 0567 §10), served through the tool runner beside the put tools: a @@ -59,18 +59,18 @@ func newSeatTools(st seatStore, ask func(string, any) (json.RawMessage, error)) return &seatTools{st: st, ask: ask, now: time.Now} } -// keyed is one placement key as a verb answers it. +// keyed is one assignments key as a verb answers it. type keyed struct { - Placement mma.Placement `json:"placement"` - Revision uint64 `json:"revision"` + Assignments mma.Assignments `json:"assignments"` + Revision uint64 `json:"revision"` } -// placements reads every node's placement and the revision of its node key (its legacy key's before it is converted), +// assignments reads every node's assignments and the revision of its node key (its legacy key's before it is converted), // assembled from its keys in either form (mmac.Keys; research 072 row 37). -func (s *seatTools) placements() (map[string]mma.Placement, map[string]uint64, error) { - keys, err := s.st.keys(mma.StatePlacement) +func (s *seatTools) assignments() (map[string]mma.Assignments, map[string]uint64, error) { + keys, err := s.st.keys(mma.StateAssignments) if err != nil { - return nil, nil, fmt.Errorf("placement cannot be listed: %w", err) + return nil, nil, fmt.Errorf("assignments cannot be listed: %w", err) } sort.Strings(keys) held := mmac.NewKeys() @@ -79,9 +79,9 @@ func (s *seatTools) placements() (map[string]mma.Placement, map[string]uint64, e if _, _, _, err := mmac.ParseKey(key); err != nil { continue } - raw, rev, found, err := s.st.get(mma.StatePlacement, key) + raw, rev, found, err := s.st.get(mma.StateAssignments, key) if err != nil { - return nil, nil, fmt.Errorf("placement key %s cannot be read: %w", key, err) + return nil, nil, fmt.Errorf("assignments key %s cannot be read: %w", key, err) } if !found { continue @@ -92,7 +92,7 @@ func (s *seatTools) placements() (map[string]mma.Placement, map[string]uint64, e } out, revs := held.AllRevisions() if len(bad) > 0 && len(out) == 0 { - return nil, nil, fmt.Errorf("placement cannot be read: %s", strings.Join(bad, "; ")) + return nil, nil, fmt.Errorf("assignments cannot be read: %s", strings.Join(bad, "; ")) } return out, revs, nil } @@ -145,14 +145,14 @@ func (s *seatTools) proposal(id string) (mma.Proposal, error) { // write writes a proposal laid by assign.Propose: each candidate, then the proposal, whose write the assigner's process // takes as the event to compose and judge it. -func (s *seatTools) write(p mma.Proposal, laid map[string]*mma.Placement) error { +func (s *seatTools) write(p mma.Proposal, laid map[string]*mma.Assignments) error { p.Written = ptr(s.now().UTC()) for _, node := range plc.Nodes(p) { key, err := plc.CandidateKey(p.ID, node) if err != nil { return err } - raw, err := plc.EncodeCandidate(mma.CandidatesByProposalNodeValue{Proposal: p.ID, Placement: laid[node]}) + raw, err := plc.EncodeCandidate(mma.CandidatesByProposalNodeValue{Proposal: p.ID, Assignments: laid[node]}) if err != nil { return err } @@ -215,7 +215,7 @@ func (s *seatTools) doPropose(args map[string]any, by string) (any, error) { return nil, errors.New("the seed is gone with adopt: propose takes place, take-off, pin, unpin, hold, unhold, decide and config") } } - current, revs, err := s.placements() + current, revs, err := s.assignments() if err != nil { return nil, err } @@ -282,7 +282,7 @@ func moduleOfCaller(by string, enrollers []string) string { } // doWithdraw is the verb withdraw (§2): the proposal is marked, and the assigner deletes it and its candidates and says -// placement-withdrawn carrying it whole. +// assignments-withdrawn carrying it whole. func (s *seatTools) doWithdraw(args map[string]any, by string) (any, error) { why := arg(args, "why") if why == "" { @@ -321,8 +321,8 @@ func (s *seatTools) doAgain(args map[string]any) (any, error) { return map[string]any{"proposal": p.ID, "again": p.Again, "follow": followProposal(p.ID)}, nil } -// placementAnswer is what the verb placement answers. -type placementAnswer struct { +// assignmentsAnswer is what the verb assignments answers. +type assignmentsAnswer struct { Keys map[string]keyed `json:"keys,omitempty"` Nodes []string `json:"nodes,omitempty"` Count int `json:"count"` @@ -340,18 +340,18 @@ type historyEntry struct { Deleted bool `json:"deleted,omitempty"` } -// doPlacement is the verb placement (§10, §6): one node's key, one module's nodes, or every key; with history, the +// doAssignments is the verb assignments (§10, §6): one node's key, one module's nodes, or every key; with history, the // changes in order from the revisions the state keeps. -func (s *seatTools) doPlacement(args map[string]any) (any, error) { +func (s *seatTools) doAssignments(args map[string]any) (any, error) { if arg(args, "seats") == "true" { return s.seats(arg(args, "node")) } - all, revs, err := s.placements() + all, revs, err := s.assignments() if err != nil { return nil, err } node, module := arg(args, "node"), arg(args, "module") - out := placementAnswer{Keys: map[string]keyed{}, Count: len(all), History: plc.PlacementHistory} + out := assignmentsAnswer{Keys: map[string]keyed{}, Count: len(all), History: plc.AssignmentsHistory} for n, p := range all { if node != "" && n != node { continue @@ -359,12 +359,12 @@ func (s *seatTools) doPlacement(args map[string]any) (any, error) { if module != "" && !strings.Contains(" "+strings.Join(mmac.Assigned(p), " ")+" ", " "+module+" ") { continue } - out.Keys[n] = keyed{Placement: p, Revision: revs[n]} + out.Keys[n] = keyed{Assignments: p, Revision: revs[n]} out.Nodes = append(out.Nodes, n) } sort.Strings(out.Nodes) if node != "" && len(out.Keys) == 0 && arg(args, "history") != "true" { - return nil, fmt.Errorf("%s has no placement key", node) + return nil, fmt.Errorf("%s has no assignments key", node) } if arg(args, "history") != "true" { return out, nil @@ -402,8 +402,8 @@ func (s *seatTools) doPlacement(args map[string]any) (any, error) { out.Changes = append(out.Changes, e) } } - out.Older = fmt.Sprintf("the state keeps %d revisions of each key; an older change is in its placement-changed event on "+ - "the seat's subject on the shared events stream, kept %d days", plc.PlacementHistory, plc.EventDays) + out.Older = fmt.Sprintf("the state keeps %d revisions of each key; an older change is in its assignments-changed event on "+ + "the seat's subject on the shared events stream, kept %d days", plc.AssignmentsHistory, plc.EventDays) return out, nil } @@ -483,11 +483,11 @@ func (s *seatTools) doProposals(args map[string]any) (any, error) { // only from a tool of that name), with the seat's own schemas. func (s *seatTools) tools() []stdio.Tool { run := map[string]func(map[string]any) (any, error){ - mma.VerbPlacement: s.doPlacement, - mma.VerbProposals: s.doProposals, - mma.VerbPropose: func(a map[string]any) (any, error) { return s.doPropose(a, stdio.CallerOf(a)) }, - mma.VerbWithdraw: func(a map[string]any) (any, error) { return s.doWithdraw(a, stdio.CallerOf(a)) }, - mma.VerbAgain: s.doAgain, + mma.VerbAssignments: s.doAssignments, + mma.VerbProposals: s.doProposals, + mma.VerbPropose: func(a map[string]any) (any, error) { return s.doPropose(a, stdio.CallerOf(a)) }, + mma.VerbWithdraw: func(a map[string]any) (any, error) { return s.doWithdraw(a, stdio.CallerOf(a)) }, + mma.VerbAgain: s.doAgain, } var out []stdio.Tool for _, v := range mma.Verbs() { @@ -496,7 +496,7 @@ func (s *seatTools) tools() []stdio.Tool { return out } -// seats is the verb placement with seats (ADR 0571 §4): who holds each seat and on which node, as the state seat-holders +// seats is the verb assignments with seats (ADR 0571 §4): who holds each seat and on which node, as the state seat-holders // holds it; with node, the keys of seats held on that node. func (s *seatTools) seats(node string) (any, error) { keys, err := s.st.keys(mma.StateSeatHolders) @@ -525,71 +525,51 @@ func (s *seatTools) seats(node string) (any, error) { return map[string]any{"seats": out, "holds": len(keys)}, nil } -// keyedRevision is one revision of a node's placement key, the key it is of beside it. +// keyedRevision is one revision of a node's assignments key, the key it is of beside it. type keyedRevision struct { stdio.StateRevision Key string } -// nodeHistory is the revisions of a node's placement, in the order the bus wrote them: its legacy key's -// `placement.` before it was converted, then its node key's `.node`, whose every revision is one write of -// the node (research 072 row 37: the node key is a write's commit, so its history is the node's placement history). +// nodeHistory is the revisions of a node's assignments, in the order the bus wrote them: its node key's `.node`, +// whose every revision is one write of the node (research 072 row 37: the node key is a write's commit, so its history is +// the node's assignments history). func (s *seatTools) nodeHistory(node string) ([]keyedRevision, error) { - legacy, err := mmac.LegacyKey(node) - if err != nil { - return nil, err - } nodeKey, err := mmac.NodeKey(node) if err != nil { return nil, err } + revisions, err := s.st.history(mma.StateAssignments, nodeKey) + if err != nil { + return nil, fmt.Errorf("the history of %s cannot be read: %w", nodeKey, err) + } var out []keyedRevision - for _, key := range []string{legacy, nodeKey} { - revisions, err := s.st.history(mma.StatePlacement, key) - if err != nil { - return nil, fmt.Errorf("the history of %s cannot be read: %w", key, err) - } - for _, r := range revisions { - out = append(out, keyedRevision{StateRevision: r, Key: key}) - } + for _, r := range revisions { + out = append(out, keyedRevision{StateRevision: r, Key: nodeKey}) } sort.SliceStable(out, func(i, j int) bool { return out[i].Revision < out[j].Revision }) return out, nil } -// sketch is a revision of a node's placement as far as its history says a change: each module's digest, and its pins +// sketch is a revision of a node's assignments as far as its history says a change: each module's digest, and its pins // and holdings, which change every module (plc.ChangeOf). type sketch struct { - modules []mma.PlacementByNodeNodeValueModulesItem + modules []mma.AssignmentsByNodeNodeValueModulesItem nodeWide string } -// sketchOf reads one revision of a node key or a legacy key; a value that does not read places nothing. +// sketchOf reads one revision of a node key; a value that does not read places nothing. func sketchOf(key string, value []byte) sketch { _, _, kind, err := mmac.ParseKey(key) + if err != nil || kind != mmac.NodeKind { + return sketch{} + } + n, err := mmac.DecodeNode(value) if err != nil { return sketch{} } - wide := func(pins map[string]mma.Provider, holds []mma.Holding) string { - raw, _ := json.Marshal([]any{pins, holds}) - return string(raw) - } - if kind == mmac.NodeKind { - n, err := mmac.DecodeNode(value) - if err != nil { - return sketch{} - } - return sketch{modules: n.Modules, nodeWide: wide(n.Pins, n.Holds)} - } - p, err := mmac.DecodePlacement(value) - if err != nil { - return sketch{} - } - out := sketch{nodeWide: wide(p.Pins, p.Holds)} - for _, m := range p.Modules { - out.modules = append(out.modules, mma.PlacementByNodeNodeValueModulesItem{Module: m.Module, Digest: mmac.Digest(m)}) - } - return out + raw, _ := json.Marshal([]any{n.Pins, n.Holds}) + return sketch{modules: n.Modules, nodeWide: string(raw)} } // changeFrom is what changed from before to s on node, as plc.ChangeOf says it. diff --git a/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/seat_tools_test.go b/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/seat_tools_test.go index 5eb037b5..1e6aeaac 100644 --- a/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/seat_tools_test.go +++ b/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/seat_tools_test.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "maps" "mesh-delivery-assigner/internal/buckets" "os" "reflect" @@ -19,16 +20,16 @@ import ( "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" "git.novox.be/novox/mesh-sdk/go/keys" + mmac "git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient" + mma "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" mcc "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient" - mmac "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" - mma "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient" stdio "git.novox.be/novox/mesh-sdk/go" "git.novox.be/novox/mesh-sdk/go/catalogue" "github.com/nats-io/nats.go/jetstream" "mesh-delivery-assigner/internal/assign" - plc "mesh-delivery-assigner/internal/placement" + plc "mesh-delivery-assigner/internal/assignment" ) func (s memStore) del(state, key string) error { @@ -45,13 +46,13 @@ func (s memStore) history(state, key string) ([]stdio.StateRevision, error) { } // ADR 0567 §2, §7, §10: the seat's five verbs over the states. propose lays a proposal and writes it with its candidates, -// or refuses naming every finding and writes nothing; withdraw and again mark it for the holder; placement and +// or refuses naming every finding and writes nothing; withdraw and again mark it for the holder; assignments and // proposals read every state the seat keeps, and history names the stream for what the state no longer keeps. func TestTheSeatsVerbs(t *testing.T) { - anchor := mma.Placement{V: keys.Version, Node: "anchor", Modules: []mma.PlacedModule{{Module: "web"}, {Module: "mesh-enrolment"}}, + anchor := mma.Assignments{V: keys.Version, Node: "anchor", Modules: []mma.PlacedModule{{Module: "web"}, {Module: "mesh-enrolment"}}, Holds: []mma.Holding{{Claim: "mesh-node-enroller", Scope: "mesh", Module: "mesh-enrolment"}}} st := memStore{} - for _, state := range []string{mma.StatePlacement, mma.StateProposals, mma.StateCandidates, modulesState, haveState} { + for _, state := range []string{mma.StateAssignments, mma.StateProposals, mma.StateCandidates, modulesState, haveState} { st[state] = &memKV{values: map[string][]byte{}} } for _, m := range []string{"web", "shell", "mesh-enrolment"} { @@ -68,8 +69,7 @@ func TestTheSeatsVerbs(t *testing.T) { }) s.now = func() time.Time { return time.Date(2026, 10, 28, 20, 0, 0, 0, time.UTC) } - raw, _ := mmac.EncodePlacement(anchor) - st[mma.StatePlacement].values["placement.anchor"] = raw + setAssignments(st[mma.StateAssignments], anchor) // withdraw and again mark the proposal; the holder ends or judges it. got, err := s.doPropose(map[string]any{"why": "hold", "changes": `[{"op":"hold","node":"anchor","module":"web","claim":"node-shell","scope":"node"}]`}, "op") @@ -106,19 +106,19 @@ func TestTheSeatsVerbs(t *testing.T) { t.Fatal(err) } move := got.(proposed).Proposal - if move.By != "operator@laptop" || move.Base["anchor"] != int64(len(raw)) || move.Base["laptop"] != 0 || len(st[mma.StateCandidates].values) != 2 { + if move.By != "operator@laptop" || move.Base["anchor"] != int64(len(st[mma.StateAssignments].values["anchor.node"])) || move.Base["laptop"] != 0 || len(st[mma.StateCandidates].values) != 2 { t.Fatalf("propose = %+v", move) } - // placement and proposals read every state the seat keeps (§10); history names the stream for what is older. + // assignments and proposals read every state the seat keeps (§10); history names the stream for what is older. reads := map[string]bool{} - pa, err := s.doPlacement(map[string]any{"module": "web", "history": "true"}) - if a := pa.(placementAnswer); err != nil || !slices.Equal(a.Nodes, []string{"anchor"}) || len(a.Changes) != 1 || !strings.Contains(a.Older, "shared events stream") { - t.Errorf("placement of web = %+v, %v", pa, err) + pa, err := s.doAssignments(map[string]any{"module": "web", "history": "true"}) + if a := pa.(assignmentsAnswer); err != nil || !slices.Equal(a.Nodes, []string{"anchor"}) || len(a.Changes) != 1 || !strings.Contains(a.Older, "shared events stream") { + t.Errorf("assignments of web = %+v, %v", pa, err) } else { - reads[mma.StatePlacement], reads["events"] = true, true + reads[mma.StateAssignments], reads["events"] = true, true } - if _, err := s.doPlacement(map[string]any{"node": "laptop"}); err == nil { + if _, err := s.doAssignments(map[string]any{"node": "laptop"}); err == nil { t.Error("a node with no key was answered") } ps, err := s.doProposals(map[string]any{"id": move.ID}) @@ -132,7 +132,7 @@ func TestTheSeatsVerbs(t *testing.T) { t.Fatal(err) } for _, state := range raw0.State { - if slices.Contains([]string{mma.StatePlacement, mma.StateProposals, mma.StateCandidates}, state.Name) && !reads[state.Name] { + if slices.Contains([]string{mma.StateAssignments, mma.StateProposals, mma.StateCandidates}, state.Name) && !reads[state.Name] { t.Errorf("no verb reads the seat's state %s", state.Name) } } @@ -142,7 +142,7 @@ func TestTheSeatsVerbs(t *testing.T) { } } - // Issue 774 (ADR 0567 §8): every kind of change is proposed, and there is no controller to hand placement back to. + // Issue 774 (ADR 0567 §8): every kind of change is proposed, and there is no controller to hand assignments back to. if _, err := s.doPropose(map[string]any{"why": "hold", "changes": `[{"op":"hold","node":"anchor","module":"web","claim":"node-shell","scope":"node"}]`}, "op"); err != nil { t.Errorf("a hold: %v", err) } @@ -168,11 +168,10 @@ func TestTheSeatsVerbs(t *testing.T) { if err := addNode("novox.mesh-enrolment", good); err != nil { t.Errorf("an add-node from mesh-enrolment: %v", err) } - // A remove-node (the operator's decision of 2026-11-02 01:05) takes a joining node that never confirmed off placement: from + // A remove-node (the operator's decision of 2026-11-02 01:05) takes a joining node that never confirmed off assignments: from // mesh-enrolment, on the warrant naming that node, of a node running nothing, it is proposed with the node's key to delete; // of a node running modules, or with another node's warrant, it is refused. - joiner, _ := mmac.EncodePlacement(mma.Placement{V: keys.Version, Node: "joiner", Decided: mma.NodeDecided{Address: "10.9.0.7"}, Modules: []mma.PlacedModule{}}) - st[mma.StatePlacement].values["placement.joiner"] = joiner + setAssignments(st[mma.StateAssignments], mma.Assignments{V: keys.Version, Node: "joiner", Decided: mma.NodeDecided{Address: "10.9.0.7"}, Modules: []mma.PlacedModule{}}) removeNode := func(node, warrant string) (any, error) { return s.doPropose(map[string]any{"why": "enrolment ended", "warrant": warrant, "changes": []any{map[string]any{"op": "remove-node", "node": node}}}, "novox.mesh-enrolment") @@ -198,9 +197,7 @@ func TestTheSeatsVerbs(t *testing.T) { t.Run("1955/L7 (ADR 0571 §6): rule 7 reads the node's own composition; an unplaced node is said", func(t *testing.T) { w := newWorld() fp := meshcatalogclient.SourcePrefix2 + strings.Repeat("2", 64) - pl, _ := mmac.EncodePlacement(mma.Placement{V: keys.Version, Node: "ace", Modules: []mma.PlacedModule{{Module: "web"}}}) - pkey, _ := mmac.LegacyKey("ace") - w.putEntry(plc.PlacementBucket, pkey, pl, 1, time.Now()) + putAssignments(w, mma.Assignments{V: keys.Version, Node: "ace", Modules: []mma.PlacedModule{{Module: "web"}}}) for node, digest := range map[string]string{"ace": "sha256:" + strings.Repeat("a", 64), "g14": ""} { hn, _ := nodeengineclient.EncodeHaveNode(nodeengine.HaveByNodeNodeValue{Composed: digest}) hk, _ := nodeengineclient.HaveNodeKey(node) @@ -220,25 +217,18 @@ func TestTheSeatsVerbs(t *testing.T) { if _, ok := findings[assign.UnplacedKey("g14")]; !ok || len(findings) != 1 { t.Fatalf("findings = %v", findings) } - pl2, _ := mmac.EncodePlacement(mma.Placement{V: keys.Version, Node: "g14", Modules: []mma.PlacedModule{{Module: "web"}}}) - pkey2, _ := mmac.LegacyKey("g14") - w.putEntry(plc.PlacementBucket, pkey2, pl2, 1, time.Now()) + putAssignments(w, mma.Assignments{V: keys.Version, Node: "g14", Modules: []mma.PlacedModule{{Module: "web"}}}) p.unplacedFindings() if len(findings) != 0 { t.Errorf("placed again: findings %v", findings) } }) - t.Run("1955/L7 (ADR 0571 §4): seat-holders is written from placement, only the keys a change moves, and answered by placement seats", func(t *testing.T) { + t.Run("1955/L7 (ADR 0571 §4): seat-holders is written from assignments, only the keys a change moves, and answered by assignments seats", func(t *testing.T) { writes := 0 kv := &countingKV{memKV: memKV{values: map[string][]byte{}}, writes: &writes} p := &passer{w: newWorld(), logf: t.Logf, kvs: kvSet{plc.SeatHoldersBucket: kv}} place := func(node string, holds ...mma.Holding) { - pl, err := mmac.EncodePlacement(mma.Placement{V: keys.Version, Node: node, Modules: []mma.PlacedModule{{Module: "web"}, {Module: "nats"}}, Holds: holds}) - if err != nil { - t.Fatal(err) - } - k, _ := mmac.LegacyKey(node) - p.w.putEntry(plc.PlacementBucket, k, pl, 1, time.Now()) + putAssignments(p.w, mma.Assignments{V: keys.Version, Node: node, Modules: []mma.PlacedModule{{Module: "web"}, {Module: "nats"}}, Holds: holds}) } place("ace", mma.Holding{Claim: "mesh-broker", Scope: "mesh", Module: "nats"}, mma.Holding{Claim: "node-shell", Scope: "node", Module: "web"}) place("g14", mma.Holding{Claim: "node-shell", Scope: "node", Module: "web"}) @@ -249,7 +239,7 @@ func TestTheSeatsVerbs(t *testing.T) { } p.seatHolders(ctx) if writes != 3 { - t.Errorf("an unchanged placement wrote %d keys", writes-3) + t.Errorf("an unchanged assignments wrote %d keys", writes-3) } place("g14") // g14 holds nothing now p.seatHolders(ctx) @@ -262,68 +252,122 @@ func TestTheSeatsVerbs(t *testing.T) { t.Errorf("seats on ace = %+v, %v", out, err) } }) - t.Run("research 072 row 37: the form before is converted once, a node is written as its keys, and history reads both", func(t *testing.T) { + t.Run("research 072 row 37: a node is written as its keys, and history reads the node key", func(t *testing.T) { ctx := context.Background() kv := &revKV{values: map[string][]byte{}, revs: map[string]uint64{}} - var logs []string - p := &passer{w: newWorld(), kvs: kvSet{plc.PlacementBucket: kv}, now: time.Now, - logf: func(f string, a ...any) { logs = append(logs, fmt.Sprintf(f, a...)) }} - put := func(key string, value []byte, rev uint64) { - p.w.putEntry(plc.PlacementBucket, key, value, rev, time.Now()) + p := &passer{w: newWorld(), kvs: kvSet{plc.AssignmentsBucket: kv}, now: time.Now, logf: t.Logf} + first := mma.Assignments{Node: "anchor", Modules: []mma.PlacedModule{{Module: "web"}}} + if _, err := p.writeNode(ctx, "anchor", &first); err != nil || strings.Join(kv.log, " ") != "create anchor.web create anchor.node" { + t.Fatalf("the first write = %v, %v", kv.log, err) } - was := mma.Placement{Node: "anchor", Modules: []mma.PlacedModule{{Module: "web"}}} - raw, _ := mmac.EncodePlacement(was) - put("placement.anchor", raw, kv.write("placement.anchor", raw)) - p.convertPlacement(ctx) - if strings.Join(kv.log, " ") != "create anchor.web create anchor.node" || !p.convertedAll || - !strings.Contains(strings.Join(logs, "\n"), "anchor converted from placement.anchor") { - t.Fatalf("the conversion wrote %v, said %v", kv.log, logs) - } - p.convertPlacement(ctx) - if len(kv.log) != 2 { - t.Errorf("a converted node is converted again: %v", kv.log) - } - // A write: the module keys, the node key (its commit), the mirror, then the module key no longer placed. - after := mma.Placement{Node: "anchor", Modules: []mma.PlacedModule{{Module: "shell", Ports: map[string]int64{"22": 2222}}}} + raw := kv.values["anchor.node"] + // A write: the module keys, the node key (its commit), then the module key no longer placed. + after := mma.Assignments{Node: "anchor", Modules: []mma.PlacedModule{{Module: "shell", Ports: map[string]int64{"22": 2222}}}} kv.log = nil rev, err := p.writeNode(ctx, "anchor", &after) - if err != nil || strings.Join(kv.log, " ") != "create anchor.shell update anchor.node update placement.anchor delete anchor.web" || rev != kv.revs["anchor.node"] { + if err != nil || strings.Join(kv.log, " ") != "create anchor.shell update anchor.node delete anchor.web" || rev != kv.revs["anchor.node"] { t.Fatalf("the write = %v, %d, %v", kv.log, rev, err) } - if got, _ := p.seatPlacements(); !mmac.SamePlacement(got["anchor"], after) { + if got, _ := p.seatAssignments(); !mmac.SameAssignments(got["anchor"], after) { t.Errorf("the write is not read back at once: %+v", got) } // A commit refused leaves every reader on what stood: the module key written before it carries the value it replaced. kv.refuse, kv.log = "anchor.node", nil - moved := mma.Placement{Node: "anchor", Modules: []mma.PlacedModule{{Module: "shell", Ports: map[string]int64{"22": 2223}}}} + moved := mma.Assignments{Node: "anchor", Modules: []mma.PlacedModule{{Module: "shell", Ports: map[string]int64{"22": 2223}}}} if _, err := p.writeNode(ctx, "anchor", &moved); err == nil || strings.Join(kv.log, " ") != "update anchor.shell" { t.Fatalf("a refused commit = %v, %v", kv.log, err) } - fresh, err := mmac.PlacementsOf(kv.values) - if err != nil || !mmac.SamePlacement(fresh["anchor"], after) { + fresh, err := mmac.AssignmentsOf(kv.values) + if err != nil || !mmac.SameAssignments(fresh["anchor"], after) { t.Errorf("a reader of the keys after a refused commit reads %+v, %v", fresh["anchor"], err) } kv.refuse = "" - // The verb's history reads the form before, then the node key. - st := memStore{mma.StatePlacement: &memKV{values: kv.values}} + // The verb's history reads the node key. + st := memStore{mma.StateAssignments: &memKV{values: kv.values}} hs := newSeatTools(st, nil) hs.st = historyStore{memStore: st, revisions: map[string][]stdio.StateRevision{ - "placement.anchor": {{Key: "placement.anchor", Value: raw, Revision: 1, Op: "put"}}, - "anchor.node": {{Key: "anchor.node", Value: kv.values["anchor.node"], Revision: 5, Op: "put"}, {Key: "anchor.node", Revision: 9, Op: "del"}}, + "anchor.node": {{Key: "anchor.node", Value: raw, Revision: 1, Op: "put"}, {Key: "anchor.node", Value: kv.values["anchor.node"], Revision: 5, Op: "put"}, {Key: "anchor.node", Revision: 9, Op: "del"}}, }} - pa, err := hs.doPlacement(map[string]any{"node": "anchor", "history": "true"}) - a, _ := pa.(placementAnswer) + pa, err := hs.doAssignments(map[string]any{"node": "anchor", "history": "true"}) + a, _ := pa.(assignmentsAnswer) if err != nil || len(a.Changes) != 3 || !slices.Equal(a.Changes[0].Change.Added, []string{"web"}) || !slices.Equal(a.Changes[1].Change.Added, []string{"shell"}) || !slices.Equal(a.Changes[1].Change.Removed, []string{"web"}) || !a.Changes[2].Deleted || !slices.Equal(a.Changes[2].Change.Removed, []string{"shell"}) { t.Errorf("history = %+v, %v", pa, err) } - // No placement: the mirror first, then the node key, then the module keys. + // No assignments: the node key, then the module keys. kv.log = nil - if _, err := p.writeNode(ctx, "anchor", nil); err != nil || strings.Join(kv.log, " ") != "delete placement.anchor delete anchor.node delete anchor.shell" { + if _, err := p.writeNode(ctx, "anchor", nil); err != nil || strings.Join(kv.log, " ") != "delete anchor.node delete anchor.shell" { t.Errorf("the delete = %v, %v", kv.log, err) } }) + t.Run("ADR 0641: the retired state is copied into assignments once, key by key, with no gap", func(t *testing.T) { + ctx := context.Background() + web, _ := mmac.AssignmentsValues(mma.Assignments{Node: "anchor", Modules: []mma.PlacedModule{{Module: "web"}}}) + shell, _ := mmac.AssignmentsValues(mma.Assignments{Node: "anchor", Modules: []mma.PlacedModule{{Module: "shell"}}}) + for _, c := range []struct { + name string + old, present map[string][]byte // the retired state, and what assignments holds already + wantLog string // the writes into assignments, in order + wantValues map[string][]byte // assignments afterwards + wantLeft []string // what the retired state still holds + said copiedSaid // the event; none when zero + }{ + {"an empty new state takes every key, the node key last, and the old form is dropped", + map[string][]byte{"anchor.node": web["anchor.node"], "anchor.web": web["anchor.web"], "placement.anchor": []byte(`{"whole":1}`)}, + nil, "create anchor.web create anchor.node", web, nil, copiedSaid{Copied: 2, Dropped: 1}}, + {"a key already in the new state is kept as it is", + map[string][]byte{"anchor.node": web["anchor.node"], "anchor.web": web["anchor.web"]}, + map[string][]byte{"anchor.node": shell["anchor.node"], "anchor.shell": shell["anchor.shell"]}, + "create anchor.web", map[string][]byte{"anchor.node": shell["anchor.node"], "anchor.shell": shell["anchor.shell"], "anchor.web": web["anchor.web"]}, nil, copiedSaid{Copied: 1, Present: 1}}, + {"a key that fits no form is left and counted", + map[string][]byte{"anchor.node": web["anchor.node"], "anchor.web": web["anchor.web"], "a.b.c": []byte("x")}, + nil, "create anchor.web create anchor.node", web, []string{"a.b.c"}, copiedSaid{Copied: 2, Skipped: 1}}, + {"an empty old state copies nothing and says nothing", nil, nil, "", nil, nil, copiedSaid{}}, + } { + t.Run(c.name, func(t *testing.T) { + next := &revKV{values: map[string][]byte{}, revs: map[string]uint64{}} + for k, v := range c.present { + next.write(k, v) + } + old := &retiredKV{values: map[string][]byte{}} + maps.Copy(old.values, c.old) + var logs []string + bus := &emitted{} + p := &passer{w: newWorld(), kvs: kvSet{retiredBucket: old, plc.AssignmentsBucket: next}, now: time.Now, seat: &seatActs{bus: bus}, + logf: func(f string, a ...any) { logs = append(logs, fmt.Sprintf(f, a...)) }} + if !p.copyRetired(ctx) || !p.copied { + t.Fatalf("the copy did not end: %v", logs) + } + if got := strings.Join(next.log, " "); got != c.wantLog { + t.Errorf("writes = %q, want %q", got, c.wantLog) + } + if c.wantValues != nil && !maps.EqualFunc(next.values, c.wantValues, slices.Equal[[]byte]) { + t.Errorf("assignments = %v, want %v", next.values, c.wantValues) + } + if left := slices.Sorted(maps.Keys(old.values)); !slices.Equal(left, c.wantLeft) && (len(left) != 0 || len(c.wantLeft) != 0) { + t.Errorf("the retired state still holds %v, want %v", left, c.wantLeft) + } + if c.said == (copiedSaid{}) { + if len(bus.events) != 0 || len(logs) != 0 { + t.Errorf("a copy of nothing said %v, %v", bus.events, logs) + } + } else if len(bus.events) != 1 || bus.events[0] != c.said || len(logs) != 1 { + t.Errorf("said %v (journal %v), want the event %+v once", bus.events, logs, c.said) + } + // A restart finds the retired state emptied: nothing comes back that assignments has dropped since. + before := len(next.log) + p.copied = false + if !p.copyRetired(ctx) || len(next.log) != before || len(bus.events) > 1 { + t.Errorf("a second copy wrote %v, said %v", next.log[before:], bus.events) + } + // The copied keys are in the world at once, so the seat's next step reads them. + if got := len(p.w.read(plc.AssignmentsBucket)); got != c.said.Copied { + t.Errorf("%d copied keys are in the world, want %d", got, c.said.Copied) + } + }) + } + }) t.Run("1954/D1: a failed provision is the assigner's finding until it is provided", func(t *testing.T) { findings := map[string][]byte{} p := &passer{w: newWorld(), logf: func(string, ...any) {}, kvs: kvSet{buckets.Provisions: nil}, findings: assign.Findings{ @@ -547,8 +591,11 @@ func (k *revKV) write(key string, value []byte) uint64 { } func (k *revKV) Create(_ context.Context, key string, value []byte, _ ...jetstream.KVCreateOpt) (uint64, error) { - if _, ok := k.values[key]; ok || key == k.refuse { - return 0, errors.New("key exists") + if _, ok := k.values[key]; ok { + return 0, jetstream.ErrKeyExists + } + if key == k.refuse { + return 0, errors.New("refused") } k.log = append(k.log, "create "+key) return k.write(key, value), nil @@ -581,3 +628,71 @@ type historyStore struct { func (s historyStore) history(_, key string) ([]stdio.StateRevision, error) { return s.revisions[key], nil } + +// putAssignments takes a node's assignments into the world as the keys of the state: a key per module, then the node key. +func putAssignments(w *world, a mma.Assignments) { + values, err := mmac.AssignmentsValues(a) + if err != nil { + panic(err) + } + for _, k := range slices.Sorted(maps.Keys(values)) { + w.putEntry(plc.AssignmentsBucket, k, values[k], 1, time.Now()) + } +} + +// setAssignments holds a node's assignments in a state as its keys: a key per module, and the node key. +func setAssignments(kv *memKV, a mma.Assignments) { + values, err := mmac.AssignmentsValues(a) + if err != nil { + panic(err) + } + maps.Copy(kv.values, values) +} + +// retiredKV is the retired state: its keys, its values, and deletes of them. +type retiredKV struct { + jetstream.KeyValue + values map[string][]byte +} + +func (k *retiredKV) Keys(context.Context, ...jetstream.WatchOpt) ([]string, error) { + if len(k.values) == 0 { + return nil, jetstream.ErrNoKeysFound + } + return slices.Sorted(maps.Keys(k.values)), nil +} + +func (k *retiredKV) Get(_ context.Context, key string) (jetstream.KeyValueEntry, error) { + v, ok := k.values[key] + if !ok { + return nil, jetstream.ErrKeyNotFound + } + return retiredEntry{key: key, value: v}, nil +} + +func (k *retiredKV) Delete(_ context.Context, key string, _ ...jetstream.KVDeleteOpt) error { + delete(k.values, key) + return nil +} + +type retiredEntry struct { + jetstream.KeyValueEntry + key string + value []byte +} + +func (e retiredEntry) Value() []byte { return e.value } +func (e retiredEntry) Operation() jetstream.KeyValueOp { return jetstream.KeyValuePut } + +// emitted is a bus that keeps the module events it was asked to say. +type emitted struct { + caller + events []copiedSaid +} + +func (e *emitted) Emit(eventType string, body any) error { + if eventType == CopiedEvent { + e.events = append(e.events, body.(copiedSaid)) + } + return nil +} diff --git a/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/views.go b/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/views.go index eebbdc07..12e2e9c2 100644 --- a/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/views.go +++ b/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/views.go @@ -3,16 +3,16 @@ package main import ( "context" "encoding/json" + "mesh-delivery-assigner/internal/assignment" "mesh-delivery-assigner/internal/buckets" - "mesh-delivery-assigner/internal/placement" "slices" "time" "github.com/nats-io/nats.go/jetstream" + mma "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" mcc "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient" "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshhandactsclient" - mma "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" stdio "git.novox.be/novox/mesh-sdk/go" "git.novox.be/novox/mesh-sdk/go/process" @@ -23,11 +23,11 @@ import ( "git.novox.be/novox/mesh-delivery/sdk/graph" "mesh-delivery-assigner/internal/assign" - plc "mesh-delivery-assigner/internal/placement" + plc "mesh-delivery-assigner/internal/assignment" ) // marksBucket is the assigner's state of what its views family said each state up to. -var marksBucket = placement.Module + "_" + views.MarksState +var marksBucket = assignment.Module + "_" + views.MarksState // viewedTool is one tool over one state of the assigner's. type viewedTool struct{ tool, state, words string } @@ -35,14 +35,14 @@ type viewedTool struct{ tool, state, words string } // firstViewTools are the assigner's first-view tools (ADR 0621 §1): each answers only its callers list // (views.CallersEnv), which the manifest writes. var firstViewTools = []viewedTool{ - {"placement", mma.StatePlacement, "where each module is placed: the seat mesh-module-assigner's placement, by module."}, + {"assignments", mma.StateAssignments, "where each module is placed: the seat mesh-module-assigner's assignments, by module."}, {"seat-holders", mma.StateSeatHolders, "who holds which seat on which node."}, {"wants", "wants", "what each node should run: want.., its fingerprint and the rule that gave it."}, } // readTools are the assigner's ordinary read tools, one per state its readers read (design 25 § 12.2). var readTools = []viewedTool{ - {"assigner_read_placement", mma.StatePlacement, "where each module is placed, by module."}, + {"assigner_read_assignments", mma.StateAssignments, "where each module is placed, by module."}, {"assigner_read_seat_holders", mma.StateSeatHolders, "who holds which seat on which node."}, {"assigner_read_wants", "wants", "what each node should run: want..."}, {"assigner_read_findings", mcc.FindingsState, "the assigner's findings, which the condition keeper raises and clears."}, @@ -52,7 +52,7 @@ var readTools = []viewedTool{ // viewedStates are the states the views family says, each by its bucket: the six its readers read. var viewedStates = map[string]string{ - mma.StatePlacement: plc.PlacementBucket, + mma.StateAssignments: plc.AssignmentsBucket, mma.StateSeatHolders: plc.SeatHoldersBucket, "wants": buckets.Wants, mcc.FindingsState: assign.FindingsBucket, @@ -66,13 +66,13 @@ func viewTools(getenv func(string) string, state func(string) views.Source, now marks := state(views.MarksState) var out []stdio.Tool for _, t := range firstViewTools { - out = append(out, views.FirstViewTool(placement.Module, t.tool, t.state, t.words, views.ParseCallers(getenv(views.CallersEnv(t.tool))), state(t.state), marks, now)) + out = append(out, views.FirstViewTool(assignment.Module, t.tool, t.state, t.words, views.ParseCallers(getenv(views.CallersEnv(t.tool))), state(t.state), marks, now)) } for _, t := range readTools { - out = append(out, views.ReadTool(placement.Module, t.tool, t.state, t.words, state(t.state), marks, now)) + out = append(out, views.ReadTool(assignment.Module, t.tool, t.state, t.words, state(t.state), marks, now)) } // Its own first views, of the indexer's manifests, modules and graph (DLV-F3r, ADR 0631 §2): read, and asked again by hand. - return append(out, views.StatusTools(placement.Module, "assigner_first_views", "assigner_first_view_again", stdio.State(views.StatusState), meshhandactsclient.Recorder{Ask: stdio.Ask}, now)...) + return append(out, views.StatusTools(assignment.Module, "assigner_first_views", "assigner_first_view_again", stdio.State(views.StatusState), meshhandactsclient.Recorder{Ask: stdio.Ask}, now)...) } func keptSource(name string) views.Source { return views.KeptSource{State: stdio.State(name)} } @@ -84,13 +84,13 @@ func startViews(ctx context.Context, js jetstream.JetStream, nc process.Subscrib if findings.Put == nil { return } - if err := viewFindings(findings).Put(views.TooLargeFinding(placement.Module, state, key, size, time.Now())); err != nil { + if err := viewFindings(findings).Put(views.TooLargeFinding(assignment.Module, state, key, size, time.Now())); err != nil { logf("views: the finding of %s.%s too large is not written: %v", state, key, err) } } process.AwaitBucket(ctx, js, nc, marksBucket, "no change is said on the views family, so every reader waits on its first view", logf).Ready(func(mk jetstream.KeyValue) { em := viewsEmitter(js, account, mk, findings, tooLarge, logf) - for _, state := range []string{"asks", "differences", mcc.FindingsState, mma.StatePlacement, mma.StateSeatHolders, "wants"} { + for _, state := range []string{"asks", "differences", mcc.FindingsState, mma.StateAssignments, mma.StateSeatHolders, "wants"} { state := state start := func(kv jetstream.KeyValue) { if err := em.Start(ctx, state, kv, errs); err != nil { @@ -109,7 +109,7 @@ func startViews(ctx context.Context, js jetstream.JetStream, nc process.Subscrib // viewsEmitter is the assigner's emitter of its views family: a change not said past views.Bound is its finding // view-unsaid, in its findings (review round 2); with no findings state it is said in the log only. func viewsEmitter(js jetstream.JetStream, account process.Account, mk jetstream.KeyValue, findings assign.Findings, tooLarge func(string, string, int), logf func(string, ...any)) views.Emitter { - em := views.Emitter{Owner: placement.Module, Subject: account.EventSubject, Publish: views.JetStreamPublish(js), Marks: mk, TooLarge: tooLarge, Logf: logf} + em := views.Emitter{Owner: assignment.Module, Subject: account.EventSubject, Publish: views.JetStreamPublish(js), Marks: mk, TooLarge: tooLarge, Logf: logf} if findings.Put != nil && findings.Delete != nil { em.Findings = viewFindings(findings) } @@ -122,7 +122,7 @@ func viewFindings(f assign.Findings) views.FindingWriter { } // firstViewsBucket is the assigner's own state of its first views (views.StatusState). -var firstViewsBucket = placement.Module + "_" + views.StatusState +var firstViewsBucket = assignment.Module + "_" + views.StatusState // indexerViews are the assigner's first views of the indexer's manifests, modules and module graph (ADR 0615 §1, // ADR 0621 §2, ADR 0631; DLV-F2r): asked at the start through the indexer's tools, then followed on indexer-views, asking once more on a gap; @@ -154,7 +154,7 @@ func (iv *indexerViews) start(ctx context.Context, w *world, wake func(), settle if iv.findings.Put == nil { return } - f := views.UnansweredFinding(placement.Module, s, time.Now()) + f := views.UnansweredFinding(assignment.Module, s, time.Now()) var err error if raised { err = viewFindings(iv.findings).Put(f) @@ -209,7 +209,7 @@ func (iv *indexerViews) start(ctx context.Context, w *world, wake func(), settle // notHeardWriter writes the assigner's finding of a family it does not hear into its findings (heard.Writer); with no // findings state it is said in the log alone. func notHeardWriter(findings assign.Findings, logf func(string, ...any)) heard.Writer { - w := heard.Writer{Observer: placement.Module, Logf: logf, Put: findings.Put} + w := heard.Writer{Observer: assignment.Module, Logf: logf, Put: findings.Put} if findings.Delete != nil { w.Delete = func(key string) error { if keys, err := findings.Keys(); err == nil && !slices.Contains(keys, key) { diff --git a/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/wants_tools.go b/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/wants_tools.go index 085adbdd..964b05fe 100644 --- a/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/wants_tools.go +++ b/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/wants_tools.go @@ -12,9 +12,9 @@ import ( "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" meshhandacts "git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshhandacts" - "git.novox.be/novox/mesh-contracts/placement/go/seats/meshwants" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" + "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshwants" mcc "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient" "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshhandactsclient" diff --git a/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/world.go b/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/world.go index ed34c4d9..7c60c464 100644 --- a/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/world.go +++ b/modules/mesh-delivery-assigner/cmd/mesh-delivery-assigner/world.go @@ -13,9 +13,9 @@ import ( "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" "git.novox.be/novox/mesh-contracts/go/seats/nodeengine" wdc "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshwatchdogsclient" - mwc "git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" mdc "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient" + mwc "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds" "git.novox.be/novox/mesh-delivery/sdk/trunk" @@ -23,12 +23,12 @@ import ( "github.com/nats-io/nats.go/jetstream" - mmac "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" - mma "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" + mmac "git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient" + mma "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-sdk/go/catalogue" "mesh-delivery-assigner/internal/assign" - plc "mesh-delivery-assigner/internal/placement" + plc "mesh-delivery-assigner/internal/assignment" ) // world is what the assigner watched, as it stands now: every value by bucket and key, and the manifests read by @@ -45,9 +45,9 @@ type world struct { meta map[string]entryMeta // trunkBuilt is, by module, its newest done trunk build in the builder's log (putBuilt). trunkBuilt map[string]builds.BuildLogEntry - // placement is the seat's state placement assembled node by node from its keys in either form (mesh-sdk + // assignments is the seat's state assignments assembled node by node from its keys in either form (mesh-sdk // mmac.Keys; research 072 row 37): the one reader of its keys in the assigner. - placement *mmac.Keys + assignmentKeys *mmac.Keys // sourceGroups is, by revision of the gate's source.main, the release (group) that moved it, from every revision its // history keeps (watched with history): a build's source revision names the release that carries it (ADR 0601 §1). sourceGroups map[uint64]string @@ -68,7 +68,7 @@ type parsed struct { func newWorld() *world { return &world{values: map[string]map[string][]byte{}, manifests: map[string]parsed{}, healthy: map[string][]string{}, changes: map[string]uint64{}, - meta: map[string]entryMeta{}, trunkBuilt: map[string]builds.BuildLogEntry{}, placement: mmac.NewKeys(), + meta: map[string]entryMeta{}, trunkBuilt: map[string]builds.BuildLogEntry{}, assignmentKeys: mmac.NewKeys(), sourceGroups: map[uint64]string{}} } @@ -82,8 +82,8 @@ func (w *world) putEntry(bucket, key string, value []byte, revision uint64, at t return } w.meta[bucket+"|"+key] = entryMeta{revision: revision, at: at} - if bucket == plc.PlacementBucket { - _, _ = w.placement.Put(key, value, revision, false) // put took it already; this is its revision + if bucket == plc.AssignmentsBucket { + _, _ = w.assignmentKeys.Put(key, value, revision, false) // put took it already; this is its revision } if bucket == buckets.Source && key == mdc.SourceMainKey { if s, err := mdc.DecodeSource(value); err == nil && s.Group != "" { @@ -142,16 +142,16 @@ func (w *world) put(bucket, key string, value []byte) { w.changes[bucket]++ // the bucket's generation (bucketGen), a delete included if value == nil { delete(w.values[bucket], key) - if bucket == plc.PlacementBucket { - _, _ = w.placement.Put(key, nil, 0, true) + if bucket == plc.AssignmentsBucket { + _, _ = w.assignmentKeys.Put(key, nil, 0, true) } return } w.values[bucket][key] = value w.changes[bucket+"|"+key]++ - if bucket == plc.PlacementBucket { + if bucket == plc.AssignmentsBucket { // A key or value that does not read places nothing new on its node; the indexer's read tool says it. - _, _ = w.placement.Put(key, value, 0, false) + _, _ = w.assignmentKeys.Put(key, value, 0, false) } if bucket == buckets.Have { if h, err := nodeengineclient.DecodeHave(value); err == nil && h.Fp != "" && h.Phase == nodeengineclient.PhaseRunning && h.Health == nodeengineclient.HealthOK { @@ -256,8 +256,8 @@ func (w *world) manifest(ctx context.Context, get getter, digest string) parsed return p } -// assigned is each node's modules the seat mesh-module-assigner's placement names, in name order (ADR 0549 §5, ADR 0567 §8): the -// placement is the one source of where modules run. The controller's assignments are read no more (issue 774). +// assigned is each node's modules the seat mesh-module-assigner's assignments names, in name order (ADR 0549 §5, ADR 0567 §8): the +// assignments is the one source of where modules run. The controller's assignments are read no more (issue 774). func (w *world) assigned() map[string][]string { placed, _ := w.placed() out := make(map[string][]string, len(placed)) @@ -267,23 +267,23 @@ func (w *world) assigned() map[string][]string { return out } -// placed is each node's modules in the seat's placement (ADR 0567 §1), and whether any placement key was read: a key +// placed is each node's modules in the seat's assignments (ADR 0567 §1), and whether any assignments key was read: a key // that does not read places nothing on its node and is said by the indexer's own read tool. func (w *world) placed() (map[string][]string, bool) { out := map[string][]string{} - for node, p := range w.placements() { + for node, p := range w.assignments() { out[node] = mmac.Assigned(p) } - return out, len(w.read(plc.PlacementBucket)) > 0 + return out, len(w.read(plc.AssignmentsBucket)) > 0 } -// placements is every node's placement, assembled from its keys in either form (mmac.Keys). -func (w *world) placements() map[string]mma.Placement { - out, _ := w.placement.AllRevisions() +// assignments is every node's assignments, assembled from its keys in either form (mmac.Keys). +func (w *world) assignments() map[string]mma.Assignments { + out, _ := w.assignmentKeys.AllRevisions() return out } -// placedNodes is every node the seat's placement names (ADR 0567 §1): those compose their own modules (ADR 0547 §2), +// placedNodes is every node the seat's assignments names (ADR 0567 §1): those compose their own modules (ADR 0547 §2), // so their wants name the build alone and the seventh rule does not hold them. func (w *world) placedNodes() map[string]bool { placed, _ := w.placed() @@ -309,8 +309,8 @@ func (w *world) trunk() map[string]meshcatalog.ModulesByModuleValue { return out } -// records is each node's record in its own have key (`.node`): the nodes a placement proposal is answered by, -// with those placement names (ADR 0593 §8). +// records is each node's record in its own have key (`.node`): the nodes an assignments proposal is answered by, +// with those assignments names (ADR 0593 §8). func (w *world) records() map[string]nodeengine.NodeRecord { out := map[string]nodeengine.NodeRecord{} for key, value := range w.read(buckets.Have) { @@ -542,7 +542,7 @@ func (w *world) ownComposed(node string) (assign.ComposedDigest, bool) { Words: "the node composes its own"}, true } -// unplacedNodes are the nodes whose engine has a have and that no placement names: they get no +// unplacedNodes are the nodes whose engine has a have and that no assignments names: they get no // want, since nothing composes them. func (w *world) unplacedNodes() []string { placed := w.placedNodes() diff --git a/modules/mesh-delivery-assigner/go.mod b/modules/mesh-delivery-assigner/go.mod index af74768b..d61fbae0 100644 --- a/modules/mesh-delivery-assigner/go.mod +++ b/modules/mesh-delivery-assigner/go.mod @@ -4,9 +4,9 @@ go 1.26.0 require ( git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient v0.0.0-00010101000000-000000000000 - git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner v0.0.0-00010101000000-000000000000 - git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient v0.0.0-00010101000000-000000000000 - git.novox.be/novox/mesh-contracts/placement/go/seats/meshwants v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-delivery/contracts/go/seats/meshwants v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshhandactsclient v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshhandacts v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-sdk/go v0.0.0-00010101000000-000000000000 @@ -65,7 +65,7 @@ replace git.novox.be/novox/mesh-delivery/modules/mesh-delivery-assigner/asks => require git.novox.be/novox/mesh-delivery/modules/mesh-delivery-view/viewstate v0.0.0-00010101000000-000000000000 replace git.novox.be/novox/mesh-delivery/modules/mesh-delivery-view/viewstate => ../mesh-delivery-view/viewstate -require git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient v0.0.0-00010101000000-000000000000 +require git.novox.be/novox/mesh-delivery/contracts/go/clients/meshwantsclient v0.0.0-00010101000000-000000000000 require git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliverycheckerclient v0.0.0-00010101000000-000000000000 replace git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliverycheckerclient => ../../contracts/go/clients/meshdeliverycheckerclient diff --git a/modules/mesh-delivery-assigner/internal/assign/asks.go b/modules/mesh-delivery-assigner/internal/assign/asks.go index 78d315a9..2587a5ae 100644 --- a/modules/mesh-delivery-assigner/internal/assign/asks.go +++ b/modules/mesh-delivery-assigner/internal/assign/asks.go @@ -2,14 +2,14 @@ package assign import ( "fmt" - "mesh-delivery-assigner/internal/placement" + "mesh-delivery-assigner/internal/assignment" "regexp" "strings" "time" - mwc "git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient" + mwc "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-assigner/asks" occ "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/clients/operatorchannelclient" @@ -69,7 +69,7 @@ func RestoreAsk(key, node, module, fp, words string, now time.Time) asks.Ask { // words, under the assigner's own name (ADR 0437 clause 9), keyed by the held fingerprint. needsYou is true for a module holding // data, whose restore ask waits for the operator. func HeldCondition(module, fp, node, words string, raised, needsYou bool) oc.Condition { - key := placement.Module + ".held." + module + "." + strings.TrimPrefix(fp, meshcatalogclient.SourcePrefix2) + key := assignment.Module + ".held." + module + "." + strings.TrimPrefix(fp, meshcatalogclient.SourcePrefix2) subject := oc.ConditionSubject{Scope: "module", ID: module, Machine: node} if !raised { return oc.Condition{Key: key, Subject: subject, State: occ.StateCleared} @@ -92,7 +92,7 @@ func HeldCondition(module, fp, node, words string, raised, needsYou bool) oc.Con // the node keeps its want. It names the node, the module, fp and the have's words, keyed by node and module, and is cleared // (raised false) once the node no longer fails alone. func NodeFailedCondition(node, module, fp string, f NodeFailure, raised bool) oc.Condition { - key := placement.Module + ".failed-alone." + node + "." + module + key := assignment.Module + ".failed-alone." + node + "." + module subject := oc.ConditionSubject{Scope: "module", ID: module, Machine: node} if !raised { return oc.Condition{Key: key, Subject: subject, State: occ.StateCleared, @@ -115,7 +115,7 @@ func NodeFailedCondition(node, module, fp string, f NodeFailure, raised bool) oc // naming the module, the node and the rule holding its first want back with the rule's words, under the assigner's own name keyed by // node and module, and cleared (raised false) when the node has a want for it again. func NeitherCondition(node, module string, h Holding, raised bool) oc.Condition { - key := placement.Module + ".neither." + node + "." + module + key := assignment.Module + ".neither." + node + "." + module subject := oc.ConditionSubject{Scope: "module", ID: module, Machine: node} if !raised { return oc.Condition{Key: key, Subject: subject, State: occ.StateCleared, @@ -237,7 +237,7 @@ func BackupAsk(key, node, module, fp, digest, words string, asking int, now time // BackupCondition is the condition raised when a backup before an apply fails and the module is held on the node (ADR 0539 §16), // naming the node, the module and the backup's words; cleared under the same key. func BackupCondition(node, module, fp, words string, raised bool) oc.Condition { - key := placement.Module + ".backup." + node + "." + module + "." + strings.TrimPrefix(fp, meshcatalogclient.SourcePrefix2) + key := assignment.Module + ".backup." + node + "." + module + "." + strings.TrimPrefix(fp, meshcatalogclient.SourcePrefix2) subject := oc.ConditionSubject{Scope: "module", ID: module, Machine: node} if !raised { return oc.Condition{Key: key, Subject: subject, State: occ.StateCleared} diff --git a/modules/mesh-delivery-assigner/internal/assign/asks_test.go b/modules/mesh-delivery-assigner/internal/assign/asks_test.go index 07bc607f..9624858b 100644 --- a/modules/mesh-delivery-assigner/internal/assign/asks_test.go +++ b/modules/mesh-delivery-assigner/internal/assign/asks_test.go @@ -2,16 +2,16 @@ package assign import ( "fmt" - "mesh-delivery-assigner/internal/placement" + "mesh-delivery-assigner/internal/assignment" "mesh-delivery-assigner/internal/puts" "slices" "strings" "testing" "time" - mwc "git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient" + mwc "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-assigner/asks" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-view/viewstate" @@ -148,7 +148,7 @@ func saidConditionsAreFindings(t *testing.T) { {HeldCondition("mail", fNew, "ace", "refused", true, true), LiftedCondition("mail", fNew), "module.mail." + strings.TrimPrefix(fNew, meshcatalogclient.SourcePrefix2) + ".held"}, {NodeFailedCondition("ace", "mail", fNew, NodeFailure{Words: "x", Others: []string{"g14"}}, true), NodeFailedCondition("ace", "mail", fNew, NodeFailure{}, false), "module.ace.mail.failed-alone"}, {NeitherCondition("ace", "mail", Holding{Rule: 3, Words: "w"}, true), NeitherCondition("ace", "mail", Holding{}, false), "module.ace.mail.neither"}, - {SeatWaitsCondition([]string{"placement"}), SeatWaitsCondition(nil), "module.mesh-delivery-assigner.seat-states"}, + {SeatWaitsCondition([]string{"assignments"}), SeatWaitsCondition(nil), "module.mesh-delivery-assigner.seat-states"}, {BackupCondition("ace", "mail", fNew, "disk full", true), BackupCondition("ace", "mail", fNew, "", false), "module.ace.mail." + strings.TrimPrefix(fNew, meshcatalogclient.SourcePrefix2) + ".backup"}, } { puts, deletes, said := map[string]mcseat.FindingsByKeyValue{}, []string{}, 0 @@ -162,7 +162,7 @@ func saidConditionsAreFindings(t *testing.T) { t.Fatalf("%s: %v", c.key, err) } got, ok := puts[c.key] - if !ok || got.Severity != c.raised.Severity || got.Headline != c.raised.Headline || got.Observer != placement.Module || + if !ok || got.Severity != c.raised.Severity || got.Headline != c.raised.Headline || got.Observer != assignment.Module || got.Subject.Machine != c.raised.Subject.Machine || said != 0 { t.Fatalf("%s: finding %+v (written %v), said %d", c.key, got, puts, said) } diff --git a/modules/mesh-delivery-assigner/internal/assign/placement.go b/modules/mesh-delivery-assigner/internal/assign/assignments.go similarity index 86% rename from modules/mesh-delivery-assigner/internal/assign/placement.go rename to modules/mesh-delivery-assigner/internal/assign/assignments.go index c2e328e1..6455b562 100644 --- a/modules/mesh-delivery-assigner/internal/assign/placement.go +++ b/modules/mesh-delivery-assigner/internal/assign/assignments.go @@ -1,7 +1,7 @@ package assign import ( - "mesh-delivery-assigner/internal/placement" + "mesh-delivery-assigner/internal/assignment" "strings" occ "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/clients/operatorchannelclient" @@ -11,8 +11,8 @@ import ( // SeatWaitsCondition is the assigner's word on the seat mesh-module-assigner while a state of the seat is not bound (ADR 0567 // §1): raised naming each, cleared when none is left. func SeatWaitsCondition(unbound []string) oc.Condition { - key := placement.Module + ".seat-states" - subject := oc.ConditionSubject{Scope: "module", ID: placement.Module} + key := assignment.Module + ".seat-states" + subject := oc.ConditionSubject{Scope: "module", ID: assignment.Module} if len(unbound) == 0 { return oc.Condition{Key: key, Subject: subject, State: occ.StateCleared, Resolved: "the seat mesh-module-assigner holds all its states."} } diff --git a/modules/mesh-delivery-assigner/internal/assign/findings.go b/modules/mesh-delivery-assigner/internal/assign/findings.go index aa6c610f..ca58dfc9 100644 --- a/modules/mesh-delivery-assigner/internal/assign/findings.go +++ b/modules/mesh-delivery-assigner/internal/assign/findings.go @@ -3,7 +3,7 @@ package assign import ( "errors" "fmt" - "mesh-delivery-assigner/internal/placement" + "mesh-delivery-assigner/internal/assignment" "strings" "time" @@ -21,13 +21,13 @@ import ( // assigner no longer says it straight (never both). // FindingsBucket is the assigner's findings. -var FindingsBucket = mcc.FindingsBucket(placement.Module) +var FindingsBucket = mcc.FindingsBucket(assignment.Module) // FindingOf is a said condition as a finding: its key `..`, from the condition's own key // `mesh-delivery-assigner..` (the subject's id when the key names none), its words unchanged. raised is false for // a clear, whose key is to be deleted; value is nil then. func FindingOf(c oc.Condition, at time.Time) (key string, value []byte, raised bool, err error) { - rest, ok := strings.CutPrefix(c.Key, placement.Module+".") + rest, ok := strings.CutPrefix(c.Key, assignment.Module+".") if !ok || rest == "" { return "", nil, false, fmt.Errorf("the condition %q is not the assigner's", c.Key) } @@ -56,7 +56,7 @@ func FindingOf(c oc.Condition, at time.Time) (key string, value []byte, raised b } f := mcc.Observation{Scope: scope, ID: id, Kind: kind, Machine: c.Subject.Machine, Severity: string(c.Severity), Summary: summary, Headline: c.Headline, Explanation: c.Explanation, Resolved: c.Resolved, Needs: c.Needs, Actions: actions, - Source: "assigner." + kind}.Finding(placement.Module, "", at) + Source: "assigner." + kind}.Finding(assignment.Module, "", at) value, err = mcc.EncodeFinding(f) return key, value, true, err } diff --git a/modules/mesh-delivery-assigner/internal/assign/policy.go b/modules/mesh-delivery-assigner/internal/assign/policy.go index fcc9be3d..5a20f309 100644 --- a/modules/mesh-delivery-assigner/internal/assign/policy.go +++ b/modules/mesh-delivery-assigner/internal/assign/policy.go @@ -3,8 +3,8 @@ package assign import ( "encoding/json" "fmt" + "mesh-delivery-assigner/internal/assignment" "mesh-delivery-assigner/internal/buckets" - "mesh-delivery-assigner/internal/placement" "sort" "strings" "time" @@ -20,7 +20,7 @@ import ( // catalogue.Upgrade: a person's choice over the module's word, its irreplaceable data and the default, the bus as any module (ADR 0601 §1). // PoliciesBucket is the assigner's state `policies`. -var PoliciesBucket = placement.Module + "_" + buckets.PoliciesState +var PoliciesBucket = assignment.Module + "_" + buckets.PoliciesState // DecodePolicies reads the policies state's values by key (the module); a value that cannot be read is left out, named. func DecodePolicies(values map[string][]byte) (map[string]catalogue.PersonPolicy, []string) { @@ -76,7 +76,7 @@ func ProvisionFailedFinding(key string, p meshprovisionerclient.Provision, at ti // encode is the observation as the assigner's finding at at, in the seat's layout. func encode(o mcc.Observation, at time.Time) ([]byte, error) { - return mcc.EncodeFinding(o.Finding(placement.Module, "", at)) + return mcc.EncodeFinding(o.Finding(assignment.Module, "", at)) } func also(provider, consumer string) []string { @@ -109,7 +109,7 @@ func ProvisionMissingFinding(key string, bound time.Duration, at time.Time) ([]b Resolved: provision + " for " + module + " on " + node + " has its key", Source: "assigner." + KindProvisionMissing}, at) } -// KindUnplaced is, from the lease write, a node that runs but no placement names (ADR 0571 §6). +// KindUnplaced is, from the lease write, a node that runs but no assignments names (ADR 0571 §6). const KindUnplaced = "unplaced" // UnplacedKey is its finding's key, `machine..unplaced`. @@ -117,12 +117,12 @@ func UnplacedKey(node string) string { return mcc.Key(mcc.ScopeMachine, node, KindUnplaced) } -// UnplacedFinding is the finding of a node no placement names since the lease write. +// UnplacedFinding is the finding of a node no assignments names since the lease write. func UnplacedFinding(node string, at time.Time) ([]byte, error) { - summary := fmt.Sprintf("%s runs, but no placement names it: since the controller handed its lease away nothing composes it, "+ + summary := fmt.Sprintf("%s runs, but no assignments names it: since the controller handed its lease away nothing composes it, "+ "so the assigner writes it no want", node) return encode(mcc.Observation{Scope: mcc.ScopeMachine, ID: node, Kind: KindUnplaced, Machine: node, Summary: summary, - Headline: clipHeadline(node + " has no placement"), Needs: "place it with mesh-module-assigner.propose.", + Headline: clipHeadline(node + " has no assignments"), Needs: "place it with mesh-module-assigner.propose.", Explanation: occ.NeedsYou + "place it with mesh-module-assigner.propose. " + summary + ".", Resolved: node + " is placed", Source: "assigner." + KindUnplaced}, at) } diff --git a/modules/mesh-delivery-assigner/internal/assign/put.go b/modules/mesh-delivery-assigner/internal/assign/put.go index 9b3693f1..da4c7aa5 100644 --- a/modules/mesh-delivery-assigner/internal/assign/put.go +++ b/modules/mesh-delivery-assigner/internal/assign/put.go @@ -4,8 +4,8 @@ import ( "fmt" "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" "git.novox.be/novox/mesh-contracts/go/seats/nodeengine" - mwc "git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient" + mwc "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-assigner/asks" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds" "mesh-delivery-assigner/internal/puts" @@ -75,7 +75,7 @@ func StepPut(w PutWorld) PutNext { } fp := short(p.FP) if !w.Assigned { - return say(puts.Refused, "%s is not assigned to %s: a put moves a module where it stands assigned (propose its placement first)", w.Module, w.Node) + return say(puts.Refused, "%s is not assigned to %s: a put moves a module where it stands assigned (propose its assignments first)", w.Module, w.Node) } switch w.Build { case builds.BuildDone: diff --git a/modules/mesh-delivery-assigner/internal/assign/seat.go b/modules/mesh-delivery-assigner/internal/assign/seat.go index d04ea4f7..7e7fde86 100644 --- a/modules/mesh-delivery-assigner/internal/assign/seat.go +++ b/modules/mesh-delivery-assigner/internal/assign/seat.go @@ -10,27 +10,27 @@ import ( "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-assigner/asks" + mmac "git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient" + mma "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" occ "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/clients/operatorchannelclient" oc "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel" - mmac "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" - mma "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" - "mesh-delivery-assigner/internal/placement" - plc "mesh-delivery-assigner/internal/placement" + "mesh-delivery-assigner/internal/assignment" + plc "mesh-delivery-assigner/internal/assignment" ) -// The seat mesh-module-assigner (novox/hq ADR 0567), held by the assigner beside its wants: placement is its state -// `placement`, changed only by a proposal (§2) the nodes it touches answer (§3), judged (§4) and written on the +// The seat mesh-module-assigner (novox/hq ADR 0567), held by the assigner beside its wants: assignments is its state +// `assignments`, changed only by a proposal (§2) the nodes it touches answer (§3), judged (§4) and written on the // operator's Approve at the revision it was laid over (§5). What here decides; the tools and the passes do. -// ProposeIn is what a proposal is laid over: its id, who and why, its changes; every node's current placement and the +// ProposeIn is what a proposal is laid over: its id, who and why, its changes; every node's current assignments and the // revision of its key; which modules the indexer holds and which nodes have a record; and, for the seed, the export's // digest. type ProposeIn struct { ID, By, Why string At time.Time Changes []mma.ProposalChange - Current map[string]mma.Placement + Current map[string]mma.Assignments Revisions map[string]uint64 Held func(module string) bool Recorded func(node string) bool @@ -39,11 +39,11 @@ type ProposeIn struct { } // Propose lays a proposal over the current keys (§2): the proposal (state answering, each changed node's base revision), -// the laid placement of every node whose key it changes, and every finding, which refuses it whole: a change that does not fit its node, and what -// CheckPlacements refuses of the result laid over every placement (two modules on one host port, a provider not placed, +// the laid assignments of every node whose key it changes, and every finding, which refuses it whole: a change that does not fit its node, and what +// CheckAssignments refuses of the result laid over every assignments (two modules on one host port, a provider not placed, // a seat held once held twice, two nodes with one address, two hubs, a module the indexer does not hold, a node with no // record). A key whose value the proposal leaves as it is changes nothing and is not laid. -func Propose(in ProposeIn) (mma.Proposal, map[string]*mma.Placement, []string) { +func Propose(in ProposeIn) (mma.Proposal, map[string]*mma.Assignments, []string) { p := mma.Proposal{ID: in.ID, By: strings.TrimSpace(in.By), Why: strings.TrimSpace(in.Why), At: in.At.UTC(), Changes: in.Changes, Warrant: in.Warrant, Base: map[string]int64{}, State: mmac.ProposalAnswering} var findings []string @@ -64,13 +64,13 @@ func Propose(in ProposeIn) (mma.Proposal, map[string]*mma.Placement, []string) { laid, lay := plc.LayChanges(in.Current, in.Changes) findings = append(findings, lay...) findings = append(findings, ControllerRefusals(in.Changes, laid)...) - all := map[string]mma.Placement{} + all := map[string]mma.Assignments{} for node, pl := range in.Current { all[node] = pl } - out := map[string]*mma.Placement{} + out := map[string]*mma.Assignments{} for node, pl := range laid { - if before, ok := in.Current[node]; ok && mmac.SamePlacement(before, pl) { + if before, ok := in.Current[node]; ok && mmac.SameAssignments(before, pl) { continue } pl := pl @@ -79,7 +79,7 @@ func Propose(in ProposeIn) (mma.Proposal, map[string]*mma.Placement, []string) { p.Base[node] = int64(in.Revisions[node]) } // A remove-node deletes the key of a joining node that never confirmed (the operator's decision of 2026-11-02 01:05): its - // candidate is no placement, and the checks across nodes read the mesh without it. LayChanges said why one cannot go. + // candidate is no assignments, and the checks across nodes read the mesh without it. LayChanges said why one cannot go. for _, node := range plc.RemovedNodes(p) { if _, ok := in.Current[node]; !ok { continue @@ -89,7 +89,7 @@ func Propose(in ProposeIn) (mma.Proposal, map[string]*mma.Placement, []string) { p.Base[node] = int64(in.Revisions[node]) } if len(findings) == 0 && len(out) == 0 { - findings = append(findings, "the changes leave every placement as it is") + findings = append(findings, "the changes leave every assignments as it is") } held, recorded := in.Held, in.Recorded if held == nil { @@ -110,7 +110,7 @@ func Propose(in ProposeIn) (mma.Proposal, map[string]*mma.Placement, []string) { was := recorded recorded = func(node string) bool { return joining[node] || was(node) } } - for _, r := range plc.CheckPlacements(all, held, recorded) { + for _, r := range plc.CheckAssignments(all, held, recorded) { findings = append(findings, r) } return p, out, findings @@ -119,14 +119,14 @@ func Propose(in ProposeIn) (mma.Proposal, map[string]*mma.Placement, []string) { // ControllerModule is the controller's module, which nothing places again (issue 1126). const ControllerModule = "mesh-controller" -// ControllerEnded is why a placement of the controller is refused: the parts that acted only once its lease was handed +// ControllerEnded is why assignments of the controller is refused: the parts that acted only once its lease was handed // (consumers, the user list, enrolment, dead letters) now act without reading the lease, so a controller placed again // would write beside them, silently. const ControllerEnded = "the controller ended by its unassignment on 2026-11-02; placing it again would make two writers (issue 1126)" // ControllerRefusals names every change, and every node as laid, that places the controller: by a place, by a seed's -// whole placement, or in what the changes lay. None when nothing does. -func ControllerRefusals(changes []mma.ProposalChange, laid map[string]mma.Placement) []string { +// whole assignments, or in what the changes lay. None when nothing does. +func ControllerRefusals(changes []mma.ProposalChange, laid map[string]mma.Assignments) []string { var out []string nodes := map[string]bool{} for i, c := range changes { @@ -148,16 +148,16 @@ func ControllerRefusals(changes []mma.ProposalChange, laid map[string]mma.Placem } // Write is one node a warrant writes (§5): the node, its node key `.node` (the commit of the node's keys, -// mesh-sdk mmac.Keys.Writes), the revision of the node's placement it is written at (0: it has none, so +// mesh-sdk mmac.Keys.Writes), the revision of the node's assignments it is written at (0: it has none, so // it is created) and its value (nil: it is deleted). type Write struct { Node, Key string Base uint64 - Value *mma.Placement + Value *mma.Assignments } // Writes is every key a proposal writes, by node order. -func Writes(p mma.Proposal, laid map[string]*mma.Placement) ([]Write, error) { +func Writes(p mma.Proposal, laid map[string]*mma.Assignments) ([]Write, error) { var out []Write for _, node := range plc.Nodes(p) { key, err := mmac.NodeKey(node) @@ -179,40 +179,40 @@ const ( // CallClearedCondition clears the condition of a refused controller call a previous process raised, for its pair (node and // module, or node and provision for a pin). func CallClearedCondition(kind, node, module string) oc.Condition { - return oc.Condition{Key: placement.Module + "." + kind + "." + node + "." + module, + return oc.Condition{Key: assignment.Module + "." + kind + "." + node + "." + module, Subject: oc.ConditionSubject{Scope: "module", ID: module, Machine: node}, State: occ.StateCleared, - Resolved: "the assigner calls no verb of the controller any more: placement is the one source."} + Resolved: "the assigner calls no verb of the controller any more: assignments is the one source."} } -// PlacementAct is the act an approved proposal's yes binds: writing exactly its changes at its base revisions. -func PlacementAct(p mma.Proposal) occ.Act { - return occ.Act{"placement": "write", "proposal": p.ID, "changes": plc.ChangesDigest(p)} +// AssignmentsAct is the act an approved proposal's yes binds: writing exactly its changes at its base revisions. +func AssignmentsAct(p mma.Proposal) occ.Act { + return occ.Act{"assignments": "write", "proposal": p.ID, "changes": plc.ChangesDigest(p)} } -// PlacementAsking is a proposal's asking number: each judging that raises its ask again (again, a key moved) is +// AssignmentsAsking is a proposal's asking number: each judging that raises its ask again (again, a key moved) is // another asking, under an id of its own. -func PlacementAsking(p mma.Proposal, at time.Time) int { +func AssignmentsAsking(p mma.Proposal, at time.Time) int { return int(at.Unix()) } -// PlacementAsk is the authorising ask about a proposal (§5): an Approve that binds its act, so only a channel that +// AssignmentsAsk is the authorising ask about a proposal (§5): an Approve that binds its act, so only a channel that // proves who answered takes it, with the brief of the verdict by node (judge.Brief) as its explanation. -func PlacementAsk(key string, p mma.Proposal, brief string, asking int, now time.Time) (asks.Ask, error) { - binds, err := occ.ActDigest(PlacementAct(p)) +func AssignmentsAsk(key string, p mma.Proposal, brief string, asking int, now time.Time) (asks.Ask, error) { + binds, err := occ.ActDigest(AssignmentsAct(p)) if err != nil { return asks.Ask{}, err } nodes := plc.Nodes(p) - headline := fmt.Sprintf("Change placement on %s?", strings.Join(nodes, ", ")) + headline := fmt.Sprintf("Change assignments on %s?", strings.Join(nodes, ", ")) if n := len([]rune(headline)); n > occ.HeadlineLength { - headline = fmt.Sprintf("Change placement on %d machines?", len(nodes)) + headline = fmt.Sprintf("Change assignments on %d machines?", len(nodes)) } return assignerAsk(asking, oc.Ask{ ID: meshdeliveryclient.AskingID(key, asking), Headline: headline, Explanation: brief, Options: []oc.Option{ - {ID: OptionYes, Label: "Approve", Does: "the placement of " + strings.Join(nodes, ", ") + " is written as proposed", + {ID: OptionYes, Label: "Approve", Does: "the assignments of " + strings.Join(nodes, ", ") + " is written as proposed", Level: occ.Approve, Binds: binds}, {ID: OptionNo, Label: "Decline", Does: "nothing is written, and the proposal ends", Level: occ.Acknowledge}, }, @@ -225,7 +225,7 @@ func PlacementAsk(key string, p mma.Proposal, brief string, asking int, now time // Rest is what a proposal leaves to write once the keys of written are: its changes of the other nodes, to be laid again // over the keys as the write left them (§5: a key moved while it was written). False when nothing is left. -func Rest(p mma.Proposal, written map[string]*mma.Placement) (mma.Proposal, bool) { +func Rest(p mma.Proposal, written map[string]*mma.Assignments) (mma.Proposal, bool) { rest := p rest.Changes, rest.Base = nil, map[string]int64{} for _, c := range p.Changes { diff --git a/modules/mesh-delivery-assigner/internal/assign/seat_test.go b/modules/mesh-delivery-assigner/internal/assign/seat_test.go index cf4ad446..a7724b9e 100644 --- a/modules/mesh-delivery-assigner/internal/assign/seat_test.go +++ b/modules/mesh-delivery-assigner/internal/assign/seat_test.go @@ -8,23 +8,23 @@ import ( "testing" "time" + mmac "git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient" + mma "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" occ "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/clients/operatorchannelclient" - mmac "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" - mma "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" - plc "mesh-delivery-assigner/internal/placement" + plc "mesh-delivery-assigner/internal/assignment" ) // ADR 0567 §2, §5, §7, §8: what the seat's holder decides of a proposal: every refusal named at once, a valid one laid // with the base revision of each key it changes, the writes at those revisions, the controller's calls that keep its // assignments in step, the authorising ask bound to the changes, and the export read as the controller answers it. -func TestThePlacementSeatDecides(t *testing.T) { - anchor := mma.Placement{V: keys.Version, Node: "anchor", Decided: mma.NodeDecided{Hub: true, Address: "10.0.0.1"}, +func TestTheAssignmentsSeatDecides(t *testing.T) { + anchor := mma.Assignments{V: keys.Version, Node: "anchor", Decided: mma.NodeDecided{Hub: true, Address: "10.0.0.1"}, Modules: []mma.PlacedModule{{Module: "store", Ports: map[string]int64{"5432": 5432}}, {Module: "web"}}} - laptop := mma.Placement{V: keys.Version, Node: "laptop", Decided: mma.NodeDecided{Address: "10.0.0.2"}, Modules: []mma.PlacedModule{{Module: "shell"}}} + laptop := mma.Assignments{V: keys.Version, Node: "laptop", Decided: mma.NodeDecided{Address: "10.0.0.2"}, Modules: []mma.PlacedModule{{Module: "shell"}}} in := func(changes ...mma.ProposalChange) ProposeIn { return ProposeIn{ID: "p-one", By: "operator@laptop", Why: "move web", At: time.Unix(1, 0), Changes: changes, - Current: map[string]mma.Placement{"anchor": anchor, "laptop": laptop}, Revisions: map[string]uint64{"anchor": 4, "laptop": 9}, + Current: map[string]mma.Assignments{"anchor": anchor, "laptop": laptop}, Revisions: map[string]uint64{"anchor": 4, "laptop": 9}, Held: func(m string) bool { return m != "ghost" }, Recorded: func(n string) bool { return n == "anchor" || n == "laptop" }} } @@ -43,14 +43,14 @@ func TestThePlacementSeatDecides(t *testing.T) { }{ "a module not held": {in(mma.ProposalChange{Op: mmac.OpPlace, Node: "laptop", Module: "ghost"}), "the indexer holds no module ghost"}, "a node with no record": {in(mma.ProposalChange{Op: mmac.OpPlace, Node: "stranger", Module: "web"}), "stranger is a node with no record"}, - "nothing changes": {in(mma.ProposalChange{Op: mmac.OpPlace, Node: "anchor", Module: "web"}), "leave every placement as it is"}, + "nothing changes": {in(mma.ProposalChange{Op: mmac.OpPlace, Node: "anchor", Module: "web"}), "leave every assignments as it is"}, "no why": {func() ProposeIn { i := in(move...); i.Why = " "; return i }(), "says why"}, "no caller": {func() ProposeIn { i := in(move...); i.By = ""; return i }(), "names no caller"}, "a seed by hand": {in(mma.ProposalChange{Op: mmac.OpSeed, Node: "laptop", Whole: &laptop}), "gone with adopt"}, // 1126: the controller ended by its unassignment; placed again it would be a second writer beside the parts that // act without its lease. "the controller placed again": {in(mma.ProposalChange{Op: mmac.OpPlace, Node: "laptop", Module: "mesh-controller"}), "ended by its unassignment"}, - "the controller seeded whole": {in(mma.ProposalChange{Op: mmac.OpSeed, Node: "anchor", Whole: &mma.Placement{V: keys.Version, Node: "anchor", + "the controller seeded whole": {in(mma.ProposalChange{Op: mmac.OpSeed, Node: "anchor", Whole: &mma.Assignments{V: keys.Version, Node: "anchor", Decided: anchor.Decided, Modules: []mma.PlacedModule{{Module: "mesh-controller"}}}}), "two writers (issue 1126)"}, "a pin of nobody placed": {in(mma.ProposalChange{Op: mmac.OpPin, Node: "laptop", Provision: "db", Provider: &mma.Provider{Node: "laptop", Module: "store"}}), "not placed there"}, } { @@ -59,7 +59,7 @@ func TestThePlacementSeatDecides(t *testing.T) { } } - // Every kind of change is taken: the placement is the one source, and the controller's verbs limit nothing (ADR 0567 §8). + // Every kind of change is taken: the assignments is the one source, and the controller's verbs limit nothing (ADR 0567 §8). for _, c := range []mma.ProposalChange{ {Op: mmac.OpConfig, Node: "anchor", Module: "web", Values: map[string]any{"puid": 1}}, {Op: mmac.OpPlace, Node: "laptop", Module: "web", Ports: map[string]int64{"80": 8080}, Bindings: map[string]mma.Provider{"db": {Node: "anchor", Module: "store"}}}, @@ -74,7 +74,7 @@ func TestThePlacementSeatDecides(t *testing.T) { } // A key moved while the warrant wrote: what is left to write is the other nodes' changes, to be laid and asked again. - if rest, ok := Rest(p, map[string]*mma.Placement{"anchor": laid["anchor"]}); !ok || len(rest.Changes) != 1 || rest.Changes[0].Node != "laptop" || + if rest, ok := Rest(p, map[string]*mma.Assignments{"anchor": laid["anchor"]}); !ok || len(rest.Changes) != 1 || rest.Changes[0].Node != "laptop" || len(rest.Base) != 1 || rest.State != mmac.ProposalAnswering || rest.Must != nil { t.Errorf("the rest = %+v, %v", rest, ok) } @@ -95,13 +95,13 @@ func TestThePlacementSeatDecides(t *testing.T) { if err != nil || len(writes) != 2 || writes[0].Key != "anchor.node" || writes[0].Base != 4 || writes[1].Base != 9 { t.Fatalf("writes = %+v, %v", writes, err) } - a, err := PlacementAsk("placement.p-one", p, "move web (proposed by operator@laptop)", 7, time.Unix(100, 0)) - if err != nil || occ.Check(mustAsk(t, a), time.Unix(100, 0)) != nil || mustAsk(t, a).Options[0].Level != occ.Approve || occ.Performs(mustAsk(t, a).Options[0], PlacementAct(p)) != nil { + a, err := AssignmentsAsk("assignments.p-one", p, "move web (proposed by operator@laptop)", 7, time.Unix(100, 0)) + if err != nil || occ.Check(mustAsk(t, a), time.Unix(100, 0)) != nil || mustAsk(t, a).Options[0].Level != occ.Approve || occ.Performs(mustAsk(t, a).Options[0], AssignmentsAct(p)) != nil { t.Fatalf("the ask = %+v, %v", a, err) } q := p q.Changes = append(slices.Clone(p.Changes), mma.ProposalChange{Op: mmac.OpUnpin, Node: "laptop", Provision: "x"}) - if occ.Performs(mustAsk(t, a).Options[0], PlacementAct(q)) == nil { + if occ.Performs(mustAsk(t, a).Options[0], AssignmentsAct(q)) == nil { t.Error("the ask's yes binds changes it was not asked about") } } diff --git a/modules/mesh-delivery-assigner/internal/assign/wants.go b/modules/mesh-delivery-assigner/internal/assign/wants.go index 34e018f8..650b2157 100644 --- a/modules/mesh-delivery-assigner/internal/assign/wants.go +++ b/modules/mesh-delivery-assigner/internal/assign/wants.go @@ -4,8 +4,8 @@ import ( "fmt" "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" "git.novox.be/novox/mesh-contracts/go/seats/nodeengine" - mwc "git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" + mwc "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/modules/mesh-grants/grantsstate" "maps" "mesh-delivery-assigner/internal/puts" @@ -90,9 +90,9 @@ type Module struct { // needs, ADR 0618 §1): a node's want is held only while that node's engine reads none of them (ADR 0624 §2). Nil: the // build names none, and holds nothing. FormNeeds []string - // Self says whether node composes the module itself: the catalogue's placement names the node (ADR 0547 §2). It is the one + // Self says whether node composes the module itself: the catalogue's assignments names the node (ADR 0547 §2). It is the one // source of a want's Self: every want the rules write or keep is derived from it (selfDerived), so a want written Self while - // the node was placed does not outlive the placement (issue 1126). Nil: no node does. + // the node was placed does not outlive the assignments (issue 1126). Nil: no node does. Self func(node string) bool } @@ -587,11 +587,11 @@ func Wants(m Module, n Nodes, wants map[string]mwc.Want, lastHealthy map[string] return out } -// selfDerived is next with its Self derived from the placement now (Module.Self), whichever rule made it, and whether it is +// selfDerived is next with its Self derived from the assignments now (Module.Self), whichever rule made it, and whether it is // written. A placed node's want names the build alone (ADR 0547 §2). An unplaced node's want that a rule kept from the want // written while the node was placed (held at F, held back by rule 3 or 5, a data module asked about) names the controller's // composition of the same fingerprint, asked as any move of a record module is (ADR 0539 §14): on 2026-10-27, after the -// placement seed's revert, g14's systemd-resolved (held) and forticlient (rule 5), and shanks's forticlient (rule 3), kept +// assignments seed's revert, g14's systemd-resolved (held) and forticlient (rule 5), and shanks's forticlient (rule 3), kept // their Self wants, which their node agents, no longer placed, could not compose (issue 1126). With no such composition, or // while its ask waits, the want written stays and the node's holding says why. func (m Module) selfDerived(node string, before mwc.Want, had bool, next mwc.Want, n Nodes, out *Outcome) (mwc.Want, bool) { diff --git a/modules/mesh-delivery-assigner/internal/assign/wants_test.go b/modules/mesh-delivery-assigner/internal/assign/wants_test.go index d0208d41..4707f6d7 100644 --- a/modules/mesh-delivery-assigner/internal/assign/wants_test.go +++ b/modules/mesh-delivery-assigner/internal/assign/wants_test.go @@ -3,8 +3,8 @@ package assign import ( "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" "git.novox.be/novox/mesh-contracts/go/seats/nodeengine" - mwc "git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" + mwc "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-assigner/asks" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds" "git.novox.be/novox/mesh-delivery/modules/mesh-grants/grantsstate" @@ -348,7 +348,7 @@ func TestEachRuleHoldsTheWantBack(t *testing.T) { } } - // 1126/AC-SW1, AC-SW2, AC-SW3: a want written Self while g14 was placed, kept by a rule after the placement is gone, names + // 1126/AC-SW1, AC-SW2, AC-SW3: a want written Self while g14 was placed, kept by a rule after the assignments is gone, names // the controller's composition of the fingerprint the rule keeps (on 2026-10-27 g14's systemd-resolved, held, and forticlient, // rule 5, kept their Self wants, which g14 could no longer compose); with no such composition, or while a record ask waits, it // stays and is said; a placed node's want is Self with no composition. diff --git a/modules/mesh-delivery-assigner/internal/placement/placement_test.go b/modules/mesh-delivery-assigner/internal/assignment/assignments_test.go similarity index 89% rename from modules/mesh-delivery-assigner/internal/placement/placement_test.go rename to modules/mesh-delivery-assigner/internal/assignment/assignments_test.go index acffae50..920c649e 100644 --- a/modules/mesh-delivery-assigner/internal/placement/placement_test.go +++ b/modules/mesh-delivery-assigner/internal/assignment/assignments_test.go @@ -1,4 +1,4 @@ -package placement +package assignment import ( "encoding/json" @@ -9,16 +9,16 @@ import ( "testing" "time" - client "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" - seat "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" + client "git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient" + seat "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-sdk/go/keys" ) -// ADR 0567 §2, §4: what a change says, the holder's check of every placement, a proposal's changes laid over the keys, +// ADR 0567 §2, §4: what a change says, the holder's check of every assignments, a proposal's changes laid over the keys, // its digests (held to the values the earlier holder's code made, in the field order it wrote), its refusals, and who holds // which seat. -func TestPlacement(t *testing.T) { - g14 := seat.Placement{Node: "g14", Revision: "abc", +func TestAssignments(t *testing.T) { + g14 := seat.Assignments{Node: "g14", Revision: "abc", Decided: seat.NodeDecided{Endpoint: "g14.example:51820", Site: "home", Address: "10.77.0.2", Account: "op", Taken: []string{"zsh"}, Tunnel: &seat.FoundTunnel{Interface: "wg0", Config: "c", Port: 51820, Mtu: 1420, PublicKey: "K", Peers: []seat.FoundTunnelPeer{{PublicKey: "P", Address: "10.0.0.3"}}}, PeerNames: map[string]string{"P": "pi"}}, @@ -29,13 +29,13 @@ func TestPlacement(t *testing.T) { Pins: map[string]seat.Provider{"shell": {Node: "g14", Module: "zsh"}}, Holds: []seat.Holding{{Claim: "node-shell", Scope: "node", Module: "zsh"}}, } - novox := seat.Placement{Node: "novox", Decided: seat.NodeDecided{Hub: true, Address: "10.77.0.1"}, Modules: []seat.PlacedModule{{Module: "postgres", Ports: map[string]int64{"5432": 5432}}}, + novox := seat.Assignments{Node: "novox", Decided: seat.NodeDecided{Hub: true, Address: "10.77.0.1"}, Modules: []seat.PlacedModule{{Module: "postgres", Ports: map[string]int64{"5432": 5432}}}, Holds: []seat.Holding{{Claim: "mesh-store", Scope: "mesh", Module: "postgres"}}} - current := map[string]seat.Placement{"g14": g14, "novox": novox} + current := map[string]seat.Assignments{"g14": g14, "novox": novox} const id = "p-20261028t183000-1a2b3c" // the enroller is whichever module holds the seat mesh-node-enroller: no module is named for it const enroller = "enroll-lab" - enrollers := EnrollersOf(map[string]seat.Placement{"ace": {Node: "ace", + enrollers := EnrollersOf(map[string]seat.Assignments{"ace": {Node: "ace", Holds: []seat.Holding{{Claim: "mesh-node-enroller", Scope: "mesh", Module: enroller}}}}) if !slices.Equal(enrollers, []string{enroller}) { t.Fatalf("the holders of the enroller seat = %v", enrollers) @@ -54,7 +54,7 @@ func TestPlacement(t *testing.T) { if got := ChangeOf(g14, pinned); !reflect.DeepEqual(got.Changed, []string{"app", "zsh"}) { t.Errorf("a pin change changes every module on the node: %+v", got) } - if got := ChangeOf(seat.Placement{}, novox); !reflect.DeepEqual(got.Added, []string{"postgres"}) || got.Node != "novox" { + if got := ChangeOf(seat.Assignments{}, novox); !reflect.DeepEqual(got.Added, []string{"postgres"}) || got.Node != "novox" { t.Errorf("a new node: %+v", got) } c := []Change{{Node: "novox"}, {Node: "g14", Before: 1, After: 2}} @@ -64,18 +64,18 @@ func TestPlacement(t *testing.T) { } }) - t.Run("the check of every placement", func(t *testing.T) { + t.Run("the check of every assignments", func(t *testing.T) { known := func(m string) bool { return m != "ghost" } enrolled := func(n string) bool { return n != "stranger" } - if r := CheckPlacements(current, known, enrolled); len(r) != 0 { - t.Fatalf("a sound placement is refused: %v", r) + if r := CheckAssignments(current, known, enrolled); len(r) != 0 { + t.Fatalf("a sound assignments is refused: %v", r) } // A replicated mesh seat (ADR 0223) is held on two nodes; a seat that is not replicated is refused there. for name, refused := range map[string]bool{"mesh-dns-resolver": false, "mesh-broker": true} { - a := seat.Placement{Node: "ace", Modules: []seat.PlacedModule{{Module: "dns"}}, Holds: []seat.Holding{{Claim: name, Scope: "mesh", Module: "dns"}}} + a := seat.Assignments{Node: "ace", Modules: []seat.PlacedModule{{Module: "dns"}}, Holds: []seat.Holding{{Claim: name, Scope: "mesh", Module: "dns"}}} b := a b.Node = "novox" - r := CheckPlacements(map[string]seat.Placement{"ace": a, "novox": b}, known, enrolled) + r := CheckAssignments(map[string]seat.Assignments{"ace": a, "novox": b}, known, enrolled) if (len(r) != 0) != refused { t.Errorf("%s on two nodes: refusals %v, want refused %v", name, r, refused) } @@ -89,7 +89,7 @@ func TestPlacement(t *testing.T) { twice := novox twice.Node = "stranger" twice.Decided.Address = "10.77.0.2" - r := CheckPlacements(map[string]seat.Placement{"g14": clash, "novox": novox, "stranger": twice}, known, enrolled) + r := CheckAssignments(map[string]seat.Assignments{"g14": clash, "novox": novox, "stranger": twice}, known, enrolled) for _, want := range []string{"holds no module ghost", "ghost and app are both published on host port 18080", "bound to ace/postgres", "pin of shell names g14/bash", "stranger is a node with no record", "mesh-store, held once for the mesh, is held on novox and stranger", @@ -113,7 +113,7 @@ func TestPlacement(t *testing.T) { t.Fatalf("laid %+v, %v", laid, findings) } if !reflect.DeepEqual(client.Assigned(current["g14"]), []string{"zsh", "app"}) { - t.Error("laying changed the current placement") + t.Error("laying changed the current assignments") } if fresh, f := LayChanges(current, []seat.ProposalChange{{Op: client.OpPlace, Node: "pi", Module: "zsh"}}); len(f) != 0 || fresh["pi"].Node != "pi" { t.Errorf("a node with no key: %+v, %v", fresh, f) @@ -125,7 +125,7 @@ func TestPlacement(t *testing.T) { "not placed": {seat.ProposalChange{Op: client.OpTakeOff, Node: "g14", Module: "nope"}, "not placed"}, "no such field": {seat.ProposalChange{Op: client.OpDecide, Node: "g14", Field: "colour", Value: "x"}, "no decided field"}, "wrong type": {seat.ProposalChange{Op: client.OpDecide, Node: "g14", Field: "hub", Value: "x"}, "does not take"}, - "unknown op": {seat.ProposalChange{Op: "move", Node: "g14"}, "no change of placement"}, + "unknown op": {seat.ProposalChange{Op: "move", Node: "g14"}, "no change of assignments"}, "pin of nobody": {seat.ProposalChange{Op: client.OpPin, Node: "g14", Provision: "x"}, "provider"}, "not held": {seat.ProposalChange{Op: client.OpUnhold, Node: "g14", Module: "app", Claim: "x"}, "holds no"}, "bad port": {seat.ProposalChange{Op: client.OpPlace, Node: "g14", Module: "x", Ports: map[string]int64{"a": 1}}, "port"}, @@ -190,8 +190,8 @@ func TestPlacement(t *testing.T) { t.Errorf("the digest of %s is %s, and the earlier holder made %s", name, c.got, c.want) } } - if ExportDigest(current) != ExportDigest(map[string]seat.Placement{"novox": novox, "g14": g14}) || ExportDigest(current) == ExportDigest(nil) { - t.Error("the export digest depends on order or not on the placements") + if ExportDigest(current) != ExportDigest(map[string]seat.Assignments{"novox": novox, "g14": g14}) || ExportDigest(current) == ExportDigest(nil) { + t.Error("the export digest depends on order or not on the assignments") } // 774/S42/AC1 (ADR 0587 §7): an add-node carries its enrolment's warrant naming its node and address, as its one // change, from the enroller alone; a warrant naming another node or address is refused. @@ -267,7 +267,7 @@ func TestPlacement(t *testing.T) { AddNodeRefusal(remove, enroller, enrollers) != "" || !slices.Equal(RemovedNodes(remove), []string{"lab"}) { t.Errorf("a remove-node: %v, %q", err, AddNodeRefusal(remove, "mesh-delivery-assigner", enrollers)) } - joined := map[string]seat.Placement{"lab": {V: keys.Version, Node: "lab", Decided: seat.NodeDecided{Address: "10.0.0.7"}, Modules: []seat.PlacedModule{}}} + joined := map[string]seat.Assignments{"lab": {V: keys.Version, Node: "lab", Decided: seat.NodeDecided{Address: "10.0.0.7"}, Modules: []seat.PlacedModule{}}} if laid, f := LayChanges(joined, remove.Changes); len(f) != 0 || len(laid) != 0 { t.Errorf("a joining node removed: %v, %v", laid, f) } @@ -301,9 +301,9 @@ func TestPlacement(t *testing.T) { } }) - // ADR 0571 clause 4: who holds which seat, from placement; a node seat by its node. + // ADR 0571 clause 4: who holds which seat, from assignments; a node seat by its node. t.Run("who holds which seat", func(t *testing.T) { - held := SeatHoldersOf(map[string]seat.Placement{ + held := SeatHoldersOf(map[string]seat.Assignments{ "ace": {Node: "ace", Holds: []seat.Holding{{Claim: "mesh-dns-resolver", Scope: "mesh", Module: "dnsmasq"}, {Claim: "node-files", Scope: "node", Module: "mesh-files"}}}, "novox": {Node: "novox", Holds: []seat.Holding{{Claim: "mesh-dns-resolver", Scope: "mesh", Module: "dnsmasq"}, {Claim: "mesh-broker", Scope: "mesh", Module: "nats"}, {Claim: "photo-gallery", Scope: "mesh", Module: "photos"}}}, diff --git a/modules/mesh-delivery-assigner/internal/placement/candidate.go b/modules/mesh-delivery-assigner/internal/assignment/candidate.go similarity index 76% rename from modules/mesh-delivery-assigner/internal/placement/candidate.go rename to modules/mesh-delivery-assigner/internal/assignment/candidate.go index 33b0a48e..33e5f03c 100644 --- a/modules/mesh-delivery-assigner/internal/placement/candidate.go +++ b/modules/mesh-delivery-assigner/internal/assignment/candidate.go @@ -1,17 +1,17 @@ -package placement +package assignment import ( "encoding/json" "fmt" "strings" - client "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" - seat "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" + client "git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient" + seat "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-sdk/go/keys" ) -// Placement candidates (novox/hq ADR 0567 §3): for each node a proposal must hear from, the holder writes one key -// `.` in its state `candidates`: the node's placement with the proposal laid over it for a node whose key +// Assignments candidates (novox/hq ADR 0567 §3): for each node a proposal must hear from, the holder writes one key +// `.` in its state `candidates`: the node's assignments with the proposal laid over it for a node whose key // it changes, or the node's current key for one whose composition it changes only through another node (Reached). The // layouts are the seat's contract's (CandidatesByProposalNodeValue); what refuses a candidate is here. @@ -31,7 +31,7 @@ func CandidateKey(proposal, node string) (string, error) { func ParseCandidateKey(key string) (proposal, node string, err error) { proposal, node, ok := strings.Cut(key, ".") if !ok || keys.CheckName("node", node) != nil || client.CheckProposalID(proposal) != nil { - return "", "", fmt.Errorf("placement: %q is not a placement candidate key, .", key) + return "", "", fmt.Errorf("assignments: %q is not an assignments candidate key, .", key) } return proposal, node, nil } @@ -40,8 +40,8 @@ func checkCandidate(c seat.CandidatesByProposalNodeValue) error { if err := client.CheckProposalID(c.Proposal); err != nil { return err } - if c.Placement != nil { - return client.CheckPlacement(*c.Placement) + if c.Assignments != nil { + return client.CheckAssignments(*c.Assignments) } return nil } @@ -52,15 +52,15 @@ func EncodeCandidate(c seat.CandidatesByProposalNodeValue) ([]byte, error) { return nil, err } c.V = keys.Version - if c.Placement != nil { - p := *c.Placement + if c.Assignments != nil { + p := *c.Assignments p.V = keys.Version - c.Placement = &p + c.Assignments = &p } return json.Marshal(c) } -// DecodeCandidate reads a candidate; it refuses an unknown version, a proposal id that is not one, and a placement the +// DecodeCandidate reads a candidate; it refuses an unknown version, a proposal id that is not one, and assignments the // client refuses. func DecodeCandidate(data []byte) (seat.CandidatesByProposalNodeValue, error) { if err := keys.CheckVersion(data); err != nil { @@ -68,7 +68,7 @@ func DecodeCandidate(data []byte) (seat.CandidatesByProposalNodeValue, error) { } c, err := seat.DecodeCandidatesByProposalNodeValue(data) if err != nil { - return seat.CandidatesByProposalNodeValue{}, fmt.Errorf("placement: a candidate does not fit its shape: %w", err) + return seat.CandidatesByProposalNodeValue{}, fmt.Errorf("assignments: a candidate does not fit its shape: %w", err) } if err := checkCandidate(c); err != nil { return seat.CandidatesByProposalNodeValue{}, err diff --git a/modules/mesh-delivery-assigner/internal/placement/check.go b/modules/mesh-delivery-assigner/internal/assignment/check.go similarity index 82% rename from modules/mesh-delivery-assigner/internal/placement/check.go rename to modules/mesh-delivery-assigner/internal/assignment/check.go index fd690963..a3151d9f 100644 --- a/modules/mesh-delivery-assigner/internal/placement/check.go +++ b/modules/mesh-delivery-assigner/internal/assignment/check.go @@ -1,9 +1,9 @@ -// Package placement is the assigner's own check of a placement and of a proposal that changes it (novox/hq ADR 0567 §2, +// Package assignments is the assigner's own check of assignments and of a proposal that changes it (novox/hq ADR 0567 §2, // ADR 0634 §1 on where a seat's code goes): what the holder of the seat mesh-module-assigner decides before it writes // anything. The layouts it works on are the seat's contract's (package meshmoduleassigner); what every reader of the -// placement shares (the keys assembled into a node's placement, the node key's digest, who holds a seat) is the seat's +// assignments shares (the keys assembled into a node's assignments, the node key's digest, who holds a seat) is the seat's // client's (package meshmoduleassignerclient). Nothing here is read by another module. -package placement +package assignment import ( "encoding/json" @@ -11,8 +11,8 @@ import ( "sort" "strings" - client "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" - seat "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" + client "git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient" + seat "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-sdk/go/catalogue" "git.novox.be/novox/mesh-sdk/go/keys" ) @@ -25,19 +25,19 @@ var KeptApart = []struct{ A, B, Record string }{ {A: "mesh-watcher", B: "mesh-self-check", Record: "ADR 0227 §6, ADR 0564 clause 6"}, } -// CheckPlacements is the holder's check of a proposal laid over every placement (ADR 0567 §2), over the placements by +// CheckAssignments is the holder's check of a proposal laid over every assignments (ADR 0567 §2), over the assignments by // node, the modules that are known and the nodes with a record. It returns every refusal, in node order, never only the -// first: a placement naming a node not enrolled or a module that is not known (held by the indexer, or provided by the +// first: assignments naming a node not enrolled or a module that is not known (held by the indexer, or provided by the // mesh itself: the caller's `known` says both); two modules of one node on the same host port; a pin, binding or holding // naming a provider that is not placed where it says; a seat of scope mesh held twice, unless the seat is replicated // (catalogue.Seat's Replicated, ADR 0223), which is held once per node; two nodes with one address on the private // network; more than one hub; two modules KeptApart on one node. A host port a manifest declares without a number and a // requirement left unbound are the composition's to find (its ForeseenPorts and Unbound). -func CheckPlacements(placements map[string]seat.Placement, known func(module string) bool, enrolled func(node string) bool) []string { +func CheckAssignments(assignments map[string]seat.Assignments, known func(module string) bool, enrolled func(node string) bool) []string { var refusals []string - nodes := sortedKeys(placements) + nodes := sortedKeys(assignments) placed := func(p seat.Provider) bool { - other, ok := placements[p.Node] + other, ok := assignments[p.Node] if !ok { return false } @@ -52,9 +52,9 @@ func CheckPlacements(placements map[string]seat.Placement, known func(module str addresses := map[string]string{} var hubs []string for _, node := range nodes { - p := placements[node] + p := assignments[node] if p.Node != node { - refusals = append(refusals, fmt.Sprintf("the placement of %s names the node %q", node, p.Node)) + refusals = append(refusals, fmt.Sprintf("the assignments of %s names the node %q", node, p.Node)) } if a := strings.TrimSpace(p.Decided.Address); a != "" { if other, twice := addresses[a]; twice { @@ -122,8 +122,8 @@ func CheckPlacements(placements map[string]seat.Placement, known func(module str return refusals } -// Change is what `placement-changed` says of one node (ADR 0567 §6): the node, the revision of its key before (0: it had -// none) and after (0: the key was deleted), and its modules added, removed or changed against the placement before. +// Change is what `assignments-changed` says of one node (ADR 0567 §6): the node, the revision of its key before (0: it had +// none) and after (0: the key was deleted), and its modules added, removed or changed against the assignments before. type Change struct { Node string `json:"node"` Before uint64 `json:"before"` @@ -141,10 +141,10 @@ func (c Change) Body() map[string]any { return out } -// ChangeOf is what changed from before to after on one node; before is the zero Placement for a node new to placement. +// ChangeOf is what changed from before to after on one node; before is the zero Assignments for a node new to assignments. // A change of the node's pins or holdings counts as a change of every module placed on it both times, since the // composition of each may read them. -func ChangeOf(before, after seat.Placement) Change { +func ChangeOf(before, after seat.Assignments) Change { c := Change{Node: after.Node} if c.Node == "" { c.Node = before.Node @@ -176,7 +176,7 @@ func ChangeOf(before, after seat.Placement) Change { return c } -// SortChanges orders a placement-changed event's nodes by node. +// SortChanges orders assignments-changed event's nodes by node. func SortChanges(c []Change) { sort.Slice(c, func(i, j int) bool { return c[i].Node < c[j].Node }) } @@ -187,14 +187,14 @@ func jsonEqual(a, b any) bool { return string(ja) == string(jb) } -// SeatHoldersOf is the state composed from every node's placement (ADR 0567 §1): each holding by the seat it claims, +// SeatHoldersOf is the state composed from every node's assignments (ADR 0567 §1): each holding by the seat it claims, // keyed as the client's SeatHoldersKey says, with the module holding it and its node. A claim naming a seat's former name // is keyed by the seat's name (ADR 0122); one naming no seat is kept, marked Outside. Deterministic, so a writer writes // only a key that differs. -func SeatHoldersOf(placements map[string]seat.Placement) map[string]seat.SeatHolders { +func SeatHoldersOf(assignments map[string]seat.Assignments) map[string]seat.SeatHolders { out := map[string]seat.SeatHolders{} - for _, n := range sortedKeys(placements) { - for _, h := range placements[n].Holds { + for _, n := range sortedKeys(assignments) { + for _, h := range assignments[n].Holds { node := "" if h.Scope == "node" { node = n diff --git a/modules/mesh-delivery-assigner/internal/placement/digest.go b/modules/mesh-delivery-assigner/internal/assignment/digest.go similarity index 90% rename from modules/mesh-delivery-assigner/internal/placement/digest.go rename to modules/mesh-delivery-assigner/internal/assignment/digest.go index 9812a627..b22e05c6 100644 --- a/modules/mesh-delivery-assigner/internal/placement/digest.go +++ b/modules/mesh-delivery-assigner/internal/assignment/digest.go @@ -1,4 +1,4 @@ -package placement +package assignment import ( "crypto/sha256" @@ -6,7 +6,7 @@ import ( "encoding/json" "fmt" - seat "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" + seat "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-sdk/go/keys" ) @@ -72,7 +72,7 @@ type tunnelPeerForm struct { Address string `json:"address,omitempty"` } -type placementForm struct { +type assignmentsForm struct { V int `json:"v"` Node string `json:"node"` Revision string `json:"revision,omitempty"` @@ -107,7 +107,7 @@ type changeForm struct { Layer map[string]any `json:"layer,omitempty"` MeshValues map[string]any `json:"mesh-wide-values,omitempty"` Values map[string]any `json:"values,omitempty"` - Whole *placementForm `json:"whole,omitempty"` + Whole *assignmentsForm `json:"whole,omitempty"` Decided *decidedForm `json:"decided,omitempty"` Keys *joiningForm `json:"keys,omitempty"` } @@ -142,8 +142,8 @@ func decidedOf(d seat.NodeDecided) decidedForm { Adopted: d.Adopted, Taken: d.Taken, Tunnel: tunnelOf(d.Tunnel), PeerNames: d.PeerNames} } -func placementOf(p seat.Placement) placementForm { - out := placementForm{V: int(p.V), Node: p.Node, Revision: p.Revision, Decided: decidedOf(p.Decided), +func assignmentsOf(p seat.Assignments) assignmentsForm { + out := assignmentsForm{V: int(p.V), Node: p.Node, Revision: p.Revision, Decided: decidedOf(p.Decided), Pins: providersOf(p.Pins)} if p.Modules != nil { out.Modules = []placedForm{} @@ -170,7 +170,7 @@ func changeOf(c seat.ProposalChange) changeForm { out.Value, _ = json.Marshal(c.Value) } if c.Whole != nil { - w := placementOf(*c.Whole) + w := assignmentsOf(*c.Whole) out.Whole = &w } if c.Decided != nil { @@ -183,14 +183,14 @@ func changeOf(c seat.ProposalChange) changeForm { return out } -// ExportDigest is the digest of an export's answer, every node's placement, as the seed proposes it: the same -// placements give the same digest, whatever order they came in. -func ExportDigest(placements map[string]seat.Placement) string { +// ExportDigest is the digest of an export's answer, every node's assignments, as the seed proposes it: the same +// assignments give the same digest, whatever order they came in. +func ExportDigest(assignments map[string]seat.Assignments) string { h := sha256.New() - for _, node := range sortedKeys(placements) { - p := placements[node] - p.V = keys.Version // the export was made with the placement encoded at the keys' version - raw, _ := json.Marshal(placementOf(p)) + for _, node := range sortedKeys(assignments) { + p := assignments[node] + p.V = keys.Version // the export was made with the assignments encoded at the keys' version + raw, _ := json.Marshal(assignmentsOf(p)) fmt.Fprintf(h, "%s\n%s\n", node, raw) } return "sha256:" + hex.EncodeToString(h.Sum(nil)) diff --git a/modules/mesh-delivery-assigner/internal/placement/main_test.go b/modules/mesh-delivery-assigner/internal/assignment/main_test.go similarity index 95% rename from modules/mesh-delivery-assigner/internal/placement/main_test.go rename to modules/mesh-delivery-assigner/internal/assignment/main_test.go index 044496f6..da545265 100644 --- a/modules/mesh-delivery-assigner/internal/placement/main_test.go +++ b/modules/mesh-delivery-assigner/internal/assignment/main_test.go @@ -1,4 +1,4 @@ -package placement +package assignment import ( "os" diff --git a/modules/mesh-delivery-assigner/internal/placement/proposal.go b/modules/mesh-delivery-assigner/internal/assignment/proposal.go similarity index 76% rename from modules/mesh-delivery-assigner/internal/placement/proposal.go rename to modules/mesh-delivery-assigner/internal/assignment/proposal.go index ecc089c1..b66cf164 100644 --- a/modules/mesh-delivery-assigner/internal/placement/proposal.go +++ b/modules/mesh-delivery-assigner/internal/assignment/proposal.go @@ -1,4 +1,4 @@ -package placement +package assignment import ( "crypto/rand" @@ -9,13 +9,13 @@ import ( "strings" "time" + client "git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient" + seat "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" enroller "git.novox.be/novox/mesh-contracts/identity-and-access/go/seats/meshnodeenroller" - client "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" - seat "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-sdk/go/keys" ) -// A placement changes only by a proposal (novox/hq ADR 0567 §2): `propose` takes why and a list of changes; the holder +// Assignments changes only by a proposal (novox/hq ADR 0567 §2): `propose` takes why and a list of changes; the holder // lays them over the current keys, names the revision of each key it laid them over, and either refuses at once with every // finding or writes the proposal under its id in its state `proposals` and a candidate for each node it must hear from. @@ -50,7 +50,7 @@ func CheckChange(c seat.ProposalChange) error { } needs := func(what string, ok bool) error { if !ok { - return fmt.Errorf("placement: a %s change of %s names no %s", c.Op, c.Node, what) + return fmt.Errorf("assignments: a %s change of %s names no %s", c.Op, c.Node, what) } return nil } @@ -67,50 +67,50 @@ func CheckChange(c seat.ProposalChange) error { return needs("field or value", c.Field != "" && c.Value != nil) case client.OpSeed: if c.Whole == nil || c.Whole.Node != c.Node { - return fmt.Errorf("placement: the seed of %s names no placement of that node", c.Node) + return fmt.Errorf("assignments: the seed of %s names no assignments of that node", c.Node) } - return client.CheckPlacement(*c.Whole) + return client.CheckAssignments(*c.Whole) case client.OpAddNode: if c.Decided == nil || c.Decided.Address == "" || c.Keys == nil || c.Keys.Overlay == "" { - return fmt.Errorf("placement: the add-node of %s names no address or no tunnel key", c.Node) + return fmt.Errorf("assignments: the add-node of %s names no address or no tunnel key", c.Node) } // The identity keys come at redeem, whole or not at all: an identity with no sealing key, or the reverse. if (c.Keys.Identity == "") != (c.Keys.Sealing == "") || (c.Keys.Identity == "" && c.Keys.Serving != "") { - return fmt.Errorf("placement: the add-node of %s names part of its identity keys; they come whole at redeem", c.Node) + return fmt.Errorf("assignments: the add-node of %s names part of its identity keys; they come whole at redeem", c.Node) } if c.Decided.Hub { - return fmt.Errorf("placement: the add-node of %s makes it the hub, which a joining node never is", c.Node) + return fmt.Errorf("assignments: the add-node of %s makes it the hub, which a joining node never is", c.Node) } if c.Module != "" || len(c.Ports)+len(c.Bindings) > 0 || c.Whole != nil { - return fmt.Errorf("placement: the add-node of %s places a module; a module is placed by a proposal of its own", c.Node) + return fmt.Errorf("assignments: the add-node of %s places a module; a module is placed by a proposal of its own", c.Node) } return nil case client.OpRemoveNode: if c.Module != "" || c.Decided != nil || c.Keys != nil || c.Whole != nil { - return fmt.Errorf("placement: the remove-node of %s names more than its node", c.Node) + return fmt.Errorf("assignments: the remove-node of %s names more than its node", c.Node) } return nil } - return fmt.Errorf("placement: %q is no change of placement: place, take-off, pin, unpin, hold, unhold, decide, config, add-node or remove-node", c.Op) + return fmt.Errorf("assignments: %q is no change of assignments: place, take-off, pin, unpin, hold, unhold, decide, config, add-node or remove-node", c.Op) } -// LayChanges is the placements of the nodes the changes name with the changes laid over current, in the order given; -// current is every node's placement now (absent: the node has no key). It returns, by node, the laid placement, and +// LayChanges is the assignments of the nodes the changes name with the changes laid over current, in the order given; +// current is every node's assignments now (absent: the node has no key). It returns, by node, the laid assignments, and // every finding: a change that does not fit its node (a module taken off that is not placed, configured or holding -// there; a field NodeDecided does not have) or a placement the client refuses. What CheckPlacements refuses across nodes +// there; a field NodeDecided does not have) or assignments the client refuses. What CheckAssignments refuses across nodes // is the caller's to ask of the result laid over current. -func LayChanges(current map[string]seat.Placement, changes []seat.ProposalChange) (map[string]seat.Placement, []string) { - laid := map[string]seat.Placement{} +func LayChanges(current map[string]seat.Assignments, changes []seat.ProposalChange) (map[string]seat.Assignments, []string) { + laid := map[string]seat.Assignments{} var findings []string - of := func(node string) seat.Placement { + of := func(node string) seat.Assignments { if p, ok := laid[node]; ok { return p } p, ok := current[node] if !ok { - return seat.Placement{V: keys.Version, Node: node, Modules: []seat.PlacedModule{}} + return seat.Assignments{V: keys.Version, Node: node, Modules: []seat.PlacedModule{}} } - return clonePlacement(p) + return cloneAssignments(p) } for i, c := range changes { if err := CheckChange(c); err != nil { @@ -129,14 +129,14 @@ func LayChanges(current map[string]seat.Placement, changes []seat.ProposalChange } switch c.Op { case client.OpSeed: - p = clonePlacement(*c.Whole) + p = cloneAssignments(*c.Whole) case client.OpAddNode: // A joining node has no key yet (ADR 0587 §7): one that has is no joining node. if _, has := current[c.Node]; has { findings = append(findings, fmt.Sprintf("change %d: %s is placed already, so it does not join", i+1, c.Node)) continue } - p = seat.Placement{V: keys.Version, Node: c.Node, Decided: *c.Decided, Modules: []seat.PlacedModule{}} + p = seat.Assignments{V: keys.Version, Node: c.Node, Decided: *c.Decided, Modules: []seat.PlacedModule{}} case client.OpRemoveNode: // Only a joining node never confirmed goes this way: one with no module and no holding (a node that runs // anything is taken off module by module, by a proposal the operator approves). @@ -227,7 +227,7 @@ func LayChanges(current map[string]seat.Placement, changes []seat.ProposalChange laid[c.Node] = p } for _, node := range sortedKeys(laid) { - if err := client.CheckPlacement(laid[node]); err != nil { + if err := client.CheckAssignments(laid[node]); err != nil { findings = append(findings, err.Error()) } } @@ -275,9 +275,9 @@ var decidedFields = func() map[string]bool { return out }() -func clonePlacement(p seat.Placement) seat.Placement { +func cloneAssignments(p seat.Assignments) seat.Assignments { raw, _ := json.Marshal(p) - var out seat.Placement + var out seat.Assignments _ = json.Unmarshal(raw, &out) if out.Modules == nil { out.Modules = []seat.PlacedModule{} @@ -301,33 +301,33 @@ func CheckProposal(p seat.Proposal) error { return err } if strings.TrimSpace(p.By) == "" || strings.TrimSpace(p.Why) == "" { - return fmt.Errorf("placement: the proposal %s names no proposer or no why", p.ID) + return fmt.Errorf("assignments: the proposal %s names no proposer or no why", p.ID) } if len(p.Base) == 0 || (len(p.Changes) == 0) != p.HandBack || (p.HandBack && p.Seed) { - return fmt.Errorf("placement: the proposal %s changes nothing, or is a hand-back with changes", p.ID) + return fmt.Errorf("assignments: the proposal %s changes nothing, or is a hand-back with changes", p.ID) } for i, c := range p.Changes { if err := CheckChange(c); err != nil { - return fmt.Errorf("placement: the proposal %s, change %d: %w", p.ID, i+1, err) + return fmt.Errorf("assignments: the proposal %s, change %d: %w", p.ID, i+1, err) } if (c.Op == client.OpSeed) != p.Seed { - return fmt.Errorf("placement: the proposal %s mixes the seed with other changes", p.ID) + return fmt.Errorf("assignments: the proposal %s mixes the seed with other changes", p.ID) } if _, ok := p.Base[c.Node]; !ok { - return fmt.Errorf("placement: the proposal %s changes %s and names no base revision of its key", p.ID, c.Node) + return fmt.Errorf("assignments: the proposal %s changes %s and names no base revision of its key", p.ID, c.Node) } } switch p.State { case client.ProposalAnswering, client.ProposalFailed: case client.ProposalAsked: if p.Ask == "" || p.Verdict == nil || p.Verdict.State != VerdictPass { - return fmt.Errorf("placement: the proposal %s is asked with no ask or no passing verdict", p.ID) + return fmt.Errorf("assignments: the proposal %s is asked with no ask or no passing verdict", p.ID) } default: - return fmt.Errorf("placement: the proposal %s is in no state %q", p.ID, p.State) + return fmt.Errorf("assignments: the proposal %s is in no state %q", p.ID, p.State) } if p.Seed && p.ExportDigest == "" { - return fmt.Errorf("placement: the seed %s names no export digest", p.ID) + return fmt.Errorf("assignments: the seed %s names no export digest", p.ID) } return checkAddNode(p) } @@ -344,25 +344,25 @@ func checkAddNode(p seat.Proposal) error { } if adds == 0 { if p.Warrant != nil { - return fmt.Errorf("placement: the proposal %s carries an enrolment's warrant and adds no node", p.ID) + return fmt.Errorf("assignments: the proposal %s carries an enrolment's warrant and adds no node", p.ID) } return nil } if adds != 1 || len(p.Changes) != 1 || p.HandBack || p.Seed { - return fmt.Errorf("placement: the proposal %s adds a node beside other changes; an add-node is a proposal's one change", p.ID) + return fmt.Errorf("assignments: the proposal %s adds a node beside other changes; an add-node is a proposal's one change", p.ID) } c, w := p.Changes[0], p.Warrant if w == nil || w.Ask == "" || w.Warrant == "" { - return fmt.Errorf("placement: the add-node of %s carries no warrant of its enrolment", c.Node) + return fmt.Errorf("assignments: the add-node of %s carries no warrant of its enrolment", c.Node) } if c.Op == client.OpRemoveNode { if w.Node != c.Node { - return fmt.Errorf("placement: the remove-node of %s carries the warrant of %s", c.Node, w.Node) + return fmt.Errorf("assignments: the remove-node of %s carries the warrant of %s", c.Node, w.Node) } return nil } if w.Node != c.Node || w.Address != c.Decided.Address { - return fmt.Errorf("placement: the add-node of %s at %s carries the warrant of %s at %s", c.Node, c.Decided.Address, w.Node, w.Address) + return fmt.Errorf("assignments: the add-node of %s at %s carries the warrant of %s at %s", c.Node, c.Decided.Address, w.Node, w.Address) } return nil } @@ -383,10 +383,10 @@ func AddNodeRefusal(p seat.Proposal, caller string, enrollers []string) string { return "" } -// EnrollersOf is the modules that hold the seat mesh-node-enroller on the placements given, sorted: the state seat-holders +// EnrollersOf is the modules that hold the seat mesh-node-enroller on the assignments given, sorted: the state seat-holders // composed from them (SeatHoldersOf), read by the client's HoldersOf. -func EnrollersOf(placements map[string]seat.Placement) []string { - holders, _ := client.HoldersOf(rawHolders(SeatHoldersOf(placements)), enroller.Seat, "") +func EnrollersOf(assignments map[string]seat.Assignments) []string { + holders, _ := client.HoldersOf(rawHolders(SeatHoldersOf(assignments)), enroller.Seat, "") var out []string for _, h := range holders { if !slices.Contains(out, h.Module) { @@ -422,7 +422,7 @@ func DecodeProposal(data []byte) (seat.Proposal, error) { } p, err := seat.DecodeProposalsByProposalValue(data) if err != nil { - return seat.Proposal{}, fmt.Errorf("placement: a proposal does not fit its shape: %w", err) + return seat.Proposal{}, fmt.Errorf("assignments: a proposal does not fit its shape: %w", err) } if err := CheckProposal(p); err != nil { return seat.Proposal{}, err diff --git a/modules/mesh-delivery-assigner/internal/placement/state.go b/modules/mesh-delivery-assigner/internal/assignment/state.go similarity index 72% rename from modules/mesh-delivery-assigner/internal/placement/state.go rename to modules/mesh-delivery-assigner/internal/assignment/state.go index b7578ffa..36b44250 100644 --- a/modules/mesh-delivery-assigner/internal/placement/state.go +++ b/modules/mesh-delivery-assigner/internal/assignment/state.go @@ -1,6 +1,6 @@ -package placement +package assignment -import seat "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" +import seat "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" // Module is the module that holds the seat mesh-module-assigner: the states of the seat are named after it, as every // module state is (a bucket is `_`), and its manifest declares them. @@ -8,7 +8,7 @@ const Module = "mesh-delivery-assigner" // The buckets of the seat's states, which only the holder writes (ADR 0567 §1). const ( - PlacementBucket = Module + "_" + seat.StatePlacement + AssignmentsBucket = Module + "_" + seat.StateAssignments ProposalsBucket = Module + "_" + seat.StateProposals CandidatesBucket = Module + "_" + seat.StateCandidates PulsesBucket = Module + "_" + seat.StatePulses @@ -16,8 +16,8 @@ const ( ) const ( - // PlacementHistory is the revisions the placement bucket keeps per key: the bus's most (§6). - PlacementHistory = 64 + // AssignmentsHistory is the revisions the assignments bucket keeps per key: the bus's most (§6). + AssignmentsHistory = 64 // EventDays is how long the seat's events are kept: they ride the shared events stream on the seat's own subject // (the seat's family, 7 days). EventDays = 7 diff --git a/modules/mesh-delivery-assigner/internal/buckets/buckets.go b/modules/mesh-delivery-assigner/internal/buckets/buckets.go index e96a59d6..5e56fd5e 100644 --- a/modules/mesh-delivery-assigner/internal/buckets/buckets.go +++ b/modules/mesh-delivery-assigner/internal/buckets/buckets.go @@ -7,13 +7,13 @@ package buckets import ( "git.novox.be/novox/mesh-contracts/go/seats/nodeengine" watchdogs "git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshwatchdogs" - wants "git.novox.be/novox/mesh-contracts/placement/go/seats/meshwants" catalog "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" delivery "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdelivery" + wants "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshwants" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds" "git.novox.be/novox/mesh-delivery/modules/mesh-grants/grantsstate" - plc "mesh-delivery-assigner/internal/placement" + plc "mesh-delivery-assigner/internal/assignment" ) // The states the assigner keeps: the wants seat's, and its own asks, puts and policies. diff --git a/modules/mesh-delivery-assigner/internal/judge/judge.go b/modules/mesh-delivery-assigner/internal/judge/judge.go index f6053d22..2b1d7b55 100644 --- a/modules/mesh-delivery-assigner/internal/judge/judge.go +++ b/modules/mesh-delivery-assigner/internal/judge/judge.go @@ -1,5 +1,5 @@ // Package judge is how the seat mesh-module-assigner's holder judges a proposal (novox/hq ADR 0567 §3, §4, as ADR 0593 §8 -// changes them): what it changes on each node by its placement (Intended), every node answering it, and the verdict of +// changes them): what it changes on each node by its assignments (Intended), every node answering it, and the verdict of // their answers (JudgeAnswers). The assigner's own (ADR 0593 §1), moved back from mesh-sdk's compose/judge: the holder // composes nothing, since every composition is its node's engine's, and a node answers through the candidates path // with what it composed. It holds no bus client and no timer: the holder gives it what it read. @@ -17,24 +17,24 @@ import ( "time" nec "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" - mma "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" + mma "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" - plc "mesh-delivery-assigner/internal/placement" + plc "mesh-delivery-assigner/internal/assignment" ) // AnswerBound is how long the holder waits for a node's answer after its candidate was written: the time to live of the -// node's pulse key (placement.PulseAnswerKey), whose expiry names it as giving no answer, never passed. The same answer +// node's pulse key (assignment.PulseAnswerKey), whose expiry names it as giving no answer, never passed. The same answer // bound a check gives a node (ADR 0593 §8, §7: 60 seconds, nec.AnswerBound). A variable so a test on the bus // can shorten it. var AnswerBound = nec.AnswerBound // Intended is, by node, the modules whose placed entry the proposal changes there, and the nodes that answer it: every -// node (ADR 0593 §8, changing ADR 0567 §4's list of the nodes that must answer), sorted. current is the placement as it -// stands, laid the proposal's placements by node (nil removes the node's key) and nodes every node with a record. A +// node (ADR 0593 §8, changing ADR 0567 §4's list of the nodes that must answer), sorted. current is the assignments as it +// stands, laid the proposal's assignments by node (nil removes the node's key) and nodes every node with a record. A // node's set holds the modules the proposal adds, removes or whose placed entry differs on that node; a node with no // key now gets an empty set (§7: the seed is answered as the same). What a node's compositions change beyond its own -// placement it says itself in its answer, marked in the brief as not computed. -func Intended(current map[string]mma.Placement, laid map[string]*mma.Placement, nodes []string) (intended map[string]map[string]bool, must []string) { +// assignments it says itself in its answer, marked in the brief as not computed. +func Intended(current map[string]mma.Assignments, laid map[string]*mma.Assignments, nodes []string) (intended map[string]map[string]bool, must []string) { intended = map[string]map[string]bool{} all := map[string]bool{} for _, n := range nodes { @@ -50,7 +50,7 @@ func Intended(current map[string]mma.Placement, laid map[string]*mma.Placement, before, placed := current[node] after, changesKey := laid[node] if !changesKey || !placed { - continue // untouched, or new to placement: every answer must be the same + continue // untouched, or new to assignments: every answer must be the same } var afterModules []mma.PlacedModule if after != nil { diff --git a/modules/mesh-delivery-assigner/internal/judge/judge_test.go b/modules/mesh-delivery-assigner/internal/judge/judge_test.go index 5c077346..786b5d5d 100644 --- a/modules/mesh-delivery-assigner/internal/judge/judge_test.go +++ b/modules/mesh-delivery-assigner/internal/judge/judge_test.go @@ -8,20 +8,20 @@ import ( "testing" "time" - mma "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" + mma "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" - plc "mesh-delivery-assigner/internal/placement" + plc "mesh-delivery-assigner/internal/assignment" ) -// ADR 0567 §3, §4 (rows moved from the assigner's check of a pull request's placement, ADR 0559 §5): what a proposal +// ADR 0567 §3, §4 (rows moved from the assigner's check of a pull request's assignments, ADR 0559 §5): what a proposal // changes by node, and the verdict of the answers: differences shown and failing nothing, a refusal and a silent node // failing, a later answer judging again, the seed failing on any difference. func TestJudge(t *testing.T) { - current := map[string]mma.Placement{"anchor": {Node: "anchor", Modules: []mma.PlacedModule{{Module: "mail"}}}, "server": {Node: "server"}} - laid := map[string]*mma.Placement{ + current := map[string]mma.Assignments{"anchor": {Node: "anchor", Modules: []mma.PlacedModule{{Module: "mail"}}}, "server": {Node: "server"}} + laid := map[string]*mma.Assignments{ "anchor": {Node: "anchor", Modules: []mma.PlacedModule{{Module: "mail", Ports: map[string]int64{"25": 2525}}, {Module: "web"}}}, "laptop": {Node: "laptop", Modules: []mma.PlacedModule{{Module: "web"}}}} - // ADR 0593 §8: every node answers; what the placement changes is read from the placement alone. + // ADR 0593 §8: every node answers; what the assignments changes is read from the assignments alone. got, must := Intended(current, laid, []string{"anchor", "laptop", "server"}) if !got["anchor"]["mail"] || !got["anchor"]["web"] || len(got["laptop"]) != 0 || len(got["server"]) != 0 || !slices.Equal(must, []string{"anchor", "laptop", "server"}) { diff --git a/modules/mesh-delivery-assigner/manifest_test.go b/modules/mesh-delivery-assigner/manifest_test.go index 6fd398e1..d48e2112 100644 --- a/modules/mesh-delivery-assigner/manifest_test.go +++ b/modules/mesh-delivery-assigner/manifest_test.go @@ -1,8 +1,8 @@ package manifest import ( - "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" - "git.novox.be/novox/mesh-contracts/placement/go/seats/meshwants" + "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" + "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshwants" "os" "reflect" "slices" @@ -63,7 +63,7 @@ func TestManifest(t *testing.T) { states[s.Name] = s.History } if want := map[string]int{"asks": 5, "differences": 64, "puts": 20, "wants": 20, - "placement": 64, "proposals": 5, "candidates": 5, "pulses": 0, "findings": 5, "policies": 20, "seat-holders": 5, views.MarksState: 1, views.StatusState: 1, + "assignments": 64, "placement": 64, "proposals": 5, "candidates": 5, "pulses": 0, "findings": 5, "policies": 20, "seat-holders": 5, views.MarksState: 1, views.StatusState: 1, "engine-reads": 1}; !reflect.DeepEqual(states, want) { t.Errorf("state = %v, want %v", states, want) } @@ -81,20 +81,20 @@ func TestManifest(t *testing.T) { // ADR 0546: it serves its own tools (put, release, puts) from its bundle through the tool runner (the artifact loads the // module), says each put's step as the event put, and calls the hand-act seat and the bus node's backup holder. // ADR 0567 §1, §6-§10: it holds the seat mesh-module-assigner, serving its five verbs (adopt and hand-back went with the - // controller, ADR 0590); keeps the seat's events on its stream placement for 30 days; calls the hand-act log; and reads + // controller, ADR 0590); keeps the seat's events on its stream assignments for 30 days; calls the hand-act log; and reads // mesh-config's resolved values to compose every node a proposal touches. // ADR 0565 §7, §9 (issue 1953 D4): and the seat mesh-wants, serving upgrade and bus. if len(m.Claims) != 2 || m.Claims[0].Name != meshmoduleassigner.Seat || m.Claims[0].At() != catalogue.ScopeMesh || - !slices.Equal(m.Claims[0].Serves, []string{"placement", "proposals", "propose", "withdraw", "again"}) || + !slices.Equal(m.Claims[0].Serves, []string{"assignments", "proposals", "propose", "withdraw", "again"}) || m.Claims[1].Name != meshwants.Seat || m.Claims[1].At() != catalogue.ScopeMesh || !slices.Equal(m.Claims[1].Serves, []string{"upgrade", "bus"}) { t.Errorf("the assigner claims %+v, want mesh-module-assigner with its five verbs and mesh-wants with its two", m.Claims) } // Whether the seats' definitions let it hold them is the manifest validator's (the judge, at the seat list). - // ADR 0621 §1: the first-view tools placement, seat-holders and wants and design 25 § 12.2's six read tools, of the + // ADR 0621 §1: the first-view tools assignments, seat-holders and wants and design 25 § 12.2's six read tools, of the // program's bundle, which runs in a runtime of its own on the assigner's account; ADR 0618 §3: and assigner_engine_reads, // which lists and asks again what each node's engine reads. - if !slices.Equal(m.Tools, []string{"assigner_put", "assigner_release", "assigner_puts", "placement", "seat-holders", "wants", - "assigner_read_placement", "assigner_read_seat_holders", "assigner_read_wants", "assigner_read_findings", "assigner_read_asks", "assigner_read_differences", + if !slices.Equal(m.Tools, []string{"assigner_put", "assigner_release", "assigner_puts", "assignments", "seat-holders", "wants", + "assigner_read_assignments", "assigner_read_seat_holders", "assigner_read_wants", "assigner_read_findings", "assigner_read_asks", "assigner_read_differences", "assigner_first_views", "assigner_first_view_again", "assigner_engine_reads"}) { t.Errorf("the assigner serves tools %v, want its three put tools, three first views, six read tools and assigner_engine_reads", m.Tools) } @@ -102,7 +102,7 @@ func TestManifest(t *testing.T) { t.Errorf("runs-as = %q: the bundle runs on the assigner's own account (ADR 0621 §1)", m.RunsAs) } // ADR 0621 §2: each first-view tool answers exactly its readers, never the assigner. - callers := map[string]string{"placement": "mesh-grants,node-tools,mesh-delivery-indexer", "seat-holders": "node-tools", "wants": "mesh-grants"} + callers := map[string]string{"assignments": "mesh-grants,node-tools,mesh-delivery-indexer", "seat-holders": "node-tools", "wants": "mesh-grants"} env := m.Build.Artifacts[0].Env for tool, list := range callers { if env[views.CallersEnv(tool)] != list || views.ParseCallers(env[views.CallersEnv(tool)]).Allows("n1."+m.Module) { @@ -130,7 +130,7 @@ func TestManifest(t *testing.T) { "seat:node-engine.manifest-reads"}; !slices.Equal(slices.Sorted(slices.Values(m.Invokes)), want) { // ADR 0618 §3 t.Errorf("invokes = %v, want %v", m.Invokes, want) } - if want := []string{"put", "views.asks", "views.differences", "views.findings", "views.placement", "views.seat-holders", "views.wants"}; !slices.Equal(m.Emits, want) { - t.Errorf("emits = %v, want put and its six views", m.Emits) + if want := []string{"assignments-copied", "put", "views.asks", "views.differences", "views.findings", "views.assignments", "views.seat-holders", "views.wants"}; !slices.Equal(m.Emits, want) { + t.Errorf("emits = %v, want the copy, put and its six views", m.Emits) } } diff --git a/modules/mesh-delivery-assigner/module.json b/modules/mesh-delivery-assigner/module.json index 7cf94b94..bc67d4a7 100644 --- a/modules/mesh-delivery-assigner/module.json +++ b/modules/mesh-delivery-assigner/module.json @@ -8,7 +8,7 @@ "name": "mesh-module-assigner", "scope": "mesh", "serves": [ - "placement", + "assignments", "proposals", "propose", "withdraw", @@ -56,11 +56,12 @@ "seat:node-engine.manifest-reads" ], "emits": [ + "assignments-copied", "put", "views.asks", "views.differences", "views.findings", - "views.placement", + "views.assignments", "views.seat-holders", "views.wants" ], @@ -68,10 +69,10 @@ "assigner_put", "assigner_release", "assigner_puts", - "placement", + "assignments", "seat-holders", "wants", - "assigner_read_placement", + "assigner_read_assignments", "assigner_read_seat_holders", "assigner_read_wants", "assigner_read_findings", @@ -82,6 +83,7 @@ "assigner_engine_reads" ], "state": [ + { "name": "assignments", "history": 64 }, { "name": "placement", "history": 64 }, { "name": "proposals", "history": 5 }, { "name": "candidates", "history": 5 }, @@ -147,7 +149,7 @@ "mesh-delivery-assigner" ], "env": { - "MESH_VIEW_CALLERS_PLACEMENT": "mesh-grants,node-tools,mesh-delivery-indexer", + "MESH_VIEW_CALLERS_ASSIGNMENTS": "mesh-grants,node-tools,mesh-delivery-indexer", "MESH_VIEW_CALLERS_SEAT_HOLDERS": "node-tools", "MESH_VIEW_CALLERS_WANTS": "mesh-grants" } diff --git a/modules/mesh-delivery-assigner/oldseat_test.go b/modules/mesh-delivery-assigner/oldseat_test.go index d09d47ef..fb24bf03 100644 --- a/modules/mesh-delivery-assigner/oldseat_test.go +++ b/modules/mesh-delivery-assigner/oldseat_test.go @@ -11,7 +11,7 @@ import ( // The seat the assigner holds is mesh-module-assigner (novox/hq ADR 0634 §9): no message, tool text or comment of the module // says the name it had before. func TestNoTextSaysTheOldSeatName(t *testing.T) { - old := "mesh-" + "placement" + old := "mesh-" + "assignments" err := filepath.WalkDir(".", func(path string, d fs.DirEntry, err error) error { if err != nil || d.IsDir() || !strings.HasSuffix(path, ".go") || path == "oldseat_test.go" { return err diff --git a/modules/mesh-delivery-builder/buildrun/go.mod b/modules/mesh-delivery-builder/buildrun/go.mod index 2ceabef1..29f44482 100644 --- a/modules/mesh-delivery-builder/buildrun/go.mod +++ b/modules/mesh-delivery-builder/buildrun/go.mod @@ -5,8 +5,8 @@ go 1.26.0 require ( git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshwatchdogs v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient v0.0.0-00010101000000-000000000000 - git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient v0.0.0-00010101000000-000000000000 - git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-delivery/sdk v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-sdk/go v0.0.0-00010101000000-000000000000 github.com/nats-io/nats.go v1.54.0 diff --git a/modules/mesh-delivery-builder/buildrun/names.go b/modules/mesh-delivery-builder/buildrun/names.go index 79def2fb..50ab553c 100644 --- a/modules/mesh-delivery-builder/buildrun/names.go +++ b/modules/mesh-delivery-builder/buildrun/names.go @@ -4,9 +4,9 @@ package buildrun // builder checks them against it) and from the build seats' states: a bucket is `_`, and no contract // names a holder (novox/hq ADR 0625 §4, ADR 0634 §1). const ( - // PlacementBucket is the assigner's state placement, which the toolchain gate reads for the placing of a build module. - AssignerModule = "mesh-delivery-assigner" - PlacementBucket = AssignerModule + "_placement" + // AssignmentsBucket is the assigner's state assignments, which the toolchain gate reads for the placing of a build module. + AssignerModule = "mesh-delivery-assigner" + AssignmentsBucket = AssignerModule + "_assignments" ) // StepsBucket is the bucket of a build module's state steps. diff --git a/modules/mesh-delivery-builder/buildrun/standing.go b/modules/mesh-delivery-builder/buildrun/standing.go index adc4adb0..e921c781 100644 --- a/modules/mesh-delivery-builder/buildrun/standing.go +++ b/modules/mesh-delivery-builder/buildrun/standing.go @@ -12,7 +12,7 @@ import ( "github.com/nats-io/nats.go/jetstream" - "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" + "git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient" "git.novox.be/novox/mesh-delivery/sdk/buildstep" "git.novox.be/novox/mesh-sdk/go/process" ) @@ -23,12 +23,12 @@ import ( // ace.typescript at v26.12.0, and every Node fingerprint said "builders disagree"). // // ToolchainGate stands between the toolchains buckets and what takes their entries in (the indexer's PutToolchain, a -// ToolchainSet): an entry is passed on only while the placement (the seat mesh-placement's state `placement`, held by -// the assigner, PlacementBucket, read by meshmoduleassignerclient.Placed; ADR 0567, ADR 0549 §3) places its build module on -// its node, and only once it was written since that placement; it is passed on as deleted otherwise, said once, and -// passed on when its node writes it again. A report written before the placement is one an earlier run of the module +// ToolchainSet): an entry is passed on only while the assignments (the seat mesh-module-assigner's state `assignments`, held by +// the assigner, AssignmentsBucket, read by meshmoduleassignerclient.Placed; ADR 0567, ADR 0549 §3) places its build module on +// its node, and only once it was written since that assignments; it is passed on as deleted otherwise, said once, and +// passed on when its node writes it again. A report written before the assignments is one an earlier run of the module // left (issue 1940, 2026-10-25 20:56: mesh-build-node put on ace again brought ace.typescript v26.12.0 of 2026-10-23 -// back into the agreement before the module ran there). Until the placement is settled, and while no node's placement +// back into the agreement before the module ran there). Until the assignments is settled, and while no node's assignments // is held (before the assigner's adopt writes the first one, or where the state cannot be read), every entry is passed // on. Builders that run and report different versions still disagree: that refusal is the guard of novox/hq ADR 0465, // and it stays. @@ -41,11 +41,11 @@ type ToolchainGate struct { forward func(module, key string, value []byte, deleted bool) bool // answers whether it changed what it holds logf func(format string, a ...any) placed *meshmoduleassignerclient.Placed - known bool // the placement is settled + known bool // the assignments is settled raw map[string]map[string][]byte // module -> toolchains key -> value, as the bucket holds it left map[string]string // / -> why it was said left out, so each reason is said once - // OnPlaced is told, outside the gate's lock, once the placement's history is read and after each revision of it - // FollowPlacement hears from then on (the counted nodes, Nodes, may have changed). Nil tells nobody. + // OnPlaced is told, outside the gate's lock, once the assignments's history is read and after each revision of it + // FollowAssignments hears from then on (the counted nodes, Nodes, may have changed). Nil tells nobody. OnPlaced func() } @@ -75,28 +75,28 @@ func (g *ToolchainGate) Toolchain(module, key string, value []byte, deleted bool return g.pass(module, key) } -// Placement takes one revision of the seat mesh-placement's placement in (at is when the bus wrote it) and answers whether what it +// Assignments takes one revision of the seat mesh-module-assigner's assignments in (at is when the bus wrote it) and answers whether what it // passed on changed. A value that does not read is said, and places nothing on its node. -func (g *ToolchainGate) Placement(key string, value []byte, deleted bool, at time.Time) bool { +func (g *ToolchainGate) Assignments(key string, value []byte, deleted bool, at time.Time) bool { g.mu.Lock() defer g.mu.Unlock() was := g.placed.Seeded() node, changed, err := g.placed.Put(key, value, deleted, at) if err != nil { - g.logf("%s: %v (issue 1940)", PlacementBucket, err) + g.logf("%s: %v (issue 1940)", AssignmentsBucket, err) } if !g.known { return false } if was != g.placed.Seeded() { - // The first placement came, or the last went: every report's standing changed. + // The first assignments came, or the last went: every report's standing changed. return g.all() } return g.again(node, changed) } -// Nodes are the nodes the placement places any module on, sorted: the nodes the mesh counts (a node leaves the count -// when the placement no longer names it; ADR 0624 §1). None until the placement is read. +// Nodes are the nodes the assignments places any module on, sorted: the nodes the mesh counts (a node leaves the count +// when the assignments no longer names it; ADR 0624 §1). None until the assignments is read. func (g *ToolchainGate) Nodes() []string { var out []string for node, modules := range g.placed.ByNode() { @@ -108,8 +108,8 @@ func (g *ToolchainGate) Nodes() []string { return out } -// Settled says the placement's history is read: from now on only the reports of placed nodes pass, once a node's -// placement is held. It answers whether what it passed on changed. +// Settled says the assignments's history is read: from now on only the reports of placed nodes pass, once a node's +// assignments is held. It answers whether what it passed on changed. func (g *ToolchainGate) Settled() bool { g.mu.Lock() defer g.mu.Unlock() @@ -144,7 +144,7 @@ func (g *ToolchainGate) again(node string, modules []string) bool { return changed } -// pass hands one key on as it stands: its value when its node is placed and wrote it since (or the placement is not +// pass hands one key on as it stands: its value when its node is placed and wrote it since (or the assignments is not // known), deleted otherwise. The caller holds mu. func (g *ToolchainGate) pass(module, key string) bool { value, held := g.raw[module][key] @@ -183,24 +183,24 @@ func (g *ToolchainGate) leftOut(module, node string, value []byte) string { return "" } -// FollowPlacement reads the seat mesh-placement's placement with its history into gate, says it settled, and follows it; changed +// FollowAssignments reads the seat mesh-module-assigner's assignments with its history into gate, says it settled, and follows it; changed // is told whenever what the gate passes on changed. Where the state cannot be read or watched (not there, or this part // may not read it) it says so and the gate passes every report on: not fatal, since an indexer, builder or checker that // stops for it stops every delivery (issue 1945). An error of the watch after that ends on the channel. -func FollowPlacement(ctx context.Context, js process.BucketOpener, gate *ToolchainGate, changed func(context.Context), logf func(string, ...any)) (<-chan error, error) { +func FollowAssignments(ctx context.Context, js process.BucketOpener, gate *ToolchainGate, changed func(context.Context), logf func(string, ...any)) (<-chan error, error) { errs := make(chan error, 1) - kv, err := js.KeyValue(ctx, PlacementBucket) + kv, err := js.KeyValue(ctx, AssignmentsBucket) if err != nil { - logf("the state %s cannot be read, so every node's toolchain report counts, a build node's the catalogue does not place too (issue 1940): %v", PlacementBucket, err) + logf("the state %s cannot be read, so every node's toolchain report counts, a build node's the catalogue does not place too (issue 1940): %v", AssignmentsBucket, err) return errs, nil } w, err := kv.WatchAll(ctx, jetstream.IncludeHistory()) if err != nil { - logf("the state %s cannot be watched, so every node's toolchain report counts, a build node's the catalogue does not place too (issue 1940): %v", PlacementBucket, err) + logf("the state %s cannot be watched, so every node's toolchain report counts, a build node's the catalogue does not place too (issue 1940): %v", AssignmentsBucket, err) return errs, nil } put := func(e jetstream.KeyValueEntry) bool { - return gate.Placement(e.Key(), e.Value(), e.Operation() != jetstream.KeyValuePut, e.Created()) + return gate.Assignments(e.Key(), e.Value(), e.Operation() != jetstream.KeyValuePut, e.Created()) } for settled := false; !settled; { select { @@ -209,7 +209,7 @@ func FollowPlacement(ctx context.Context, js process.BucketOpener, gate *Toolcha return errs, ctx.Err() case e, ok := <-w.Updates(): if !ok { - return errs, errors.New("the watch of " + PlacementBucket + " ended before it settled") + return errs, errors.New("the watch of " + AssignmentsBucket + " ended before it settled") } if e == nil { settled = true @@ -233,7 +233,7 @@ func FollowPlacement(ctx context.Context, js process.BucketOpener, gate *Toolcha return case e, ok := <-w.Updates(): if !ok { - errs <- errors.New("the watch of " + PlacementBucket + " ended") + errs <- errors.New("the watch of " + AssignmentsBucket + " ended") return } if e != nil && put(e) { diff --git a/modules/mesh-delivery-builder/buildrun/standing_test.go b/modules/mesh-delivery-builder/buildrun/standing_test.go index 0ac7afeb..506d0da2 100644 --- a/modules/mesh-delivery-builder/buildrun/standing_test.go +++ b/modules/mesh-delivery-builder/buildrun/standing_test.go @@ -12,8 +12,8 @@ import ( "github.com/nats-io/nats.go/jetstream" - "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" - "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" + "git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient" + "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-delivery/sdk/buildstep" ) @@ -27,14 +27,14 @@ func gateReport(t *testing.T, node, version string, at time.Time) (string, []byt return key, value } -// placing is a value of the state `placement` placing modules on node. +// placing is a value of the state `assignments` placing modules on node. func placing(t *testing.T, node string, modules ...string) (string, []byte) { t.Helper() - p := meshmoduleassigner.Placement{Node: node} + p := meshmoduleassigner.Assignments{Node: node} for _, m := range modules { p.Modules = append(p.Modules, meshmoduleassigner.PlacedModule{Module: m}) } - values, err := meshmoduleassignerclient.PlacementValues(p) + values, err := meshmoduleassignerclient.AssignmentsValues(p) if err != nil { t.Fatal(err) } @@ -42,29 +42,14 @@ func placing(t *testing.T, node string, modules ...string) (string, []byte) { return key, values[key] } -// placingLegacy is placing in the form before, `placement.` (research 072 row 37). -func placingLegacy(t *testing.T, node string, modules ...string) (string, []byte) { - t.Helper() - p := meshmoduleassigner.Placement{Node: node} - for _, m := range modules { - p.Modules = append(p.Modules, meshmoduleassigner.PlacedModule{Module: m}) - } - key, _ := meshmoduleassignerclient.LegacyKey(node) - value, err := meshmoduleassignerclient.EncodePlacement(p) - if err != nil { - t.Fatal(err) - } - return key, value -} - func agreedOf(set *ToolchainSet) (string, string) { tc, none := buildstep.AgreedToolchain(set.Entries("typescript")) return tc.Version, none } -// Issue 1940, the gate over the catalogue's placement (issue 774, ADR 0549 §3): a build node's report counts only while -// the placement places its build module on its node, and only once written since; before the placement is settled, and -// while no node's placement is held, every report counts. The live cases: mesh-build-node taken off ace left +// Issue 1940, the gate over the catalogue's assignments (issue 774, ADR 0549 §3): a build node's report counts only while +// the assignments places its build module on its node, and only once written since; before the assignments is settled, and +// while no node's assignments is held, every report counts. The live cases: mesh-build-node taken off ace left // ace.typescript at v26.12.0 against v26.12.1 on the three others; put on ace again, its report of 2026-10-23 06:29 came // back before the module ran there. func TestToolchainGate(t *testing.T) { @@ -78,7 +63,7 @@ func TestToolchainGate(t *testing.T) { for _, node := range three { if seeded { key, value := placing(t, node, NodeBuildModule) - gate.Placement(key, value, false, old.Add(-time.Hour)) + gate.Assignments(key, value, false, old.Add(-time.Hour)) } key, value := gateReport(t, node, "v26.12.1", placed.Add(time.Minute)) gate.Toolchain(NodeBuildModule, key, value, false) @@ -91,10 +76,10 @@ func TestToolchainGate(t *testing.T) { key, value := gateReport(t, "ace", "v26.12.0", old) gate.Toolchain(NodeBuildModule, key, value, false) if a := buildstep.ToolchainAgreement(set.Entries("typescript")); len(a.SittingOut) != 1 || a.SittingOut[0].Node != "ace" { // ADR 0584: the majority is agreed, ace's report counts as the one that sits out - t.Fatalf("before the placement is settled every report counts; got %+v", a) + t.Fatalf("before the assignments is settled every report counts; got %+v", a) } k, v := placing(t, "ace", GoBuildModule) // another build module on ace: no matter - gate.Placement(k, v, false, old) + gate.Assignments(k, v, false, old) if !gate.Settled() { t.Fatal("settling left ace's report out, so what was passed on changed") } @@ -109,13 +94,13 @@ func TestToolchainGate(t *testing.T) { } // Placed on ace with a report written since: it counts, and the builders really disagree. k, v = placing(t, "ace", GoBuildModule, NodeBuildModule) - gate.Placement(k, v, false, old.Add(-time.Minute)) + gate.Assignments(k, v, false, old.Add(-time.Minute)) if a := buildstep.ToolchainAgreement(set.Entries("typescript")); a.Toolchain.Version != "v26.12.1" || len(a.SittingOut) != 1 || a.SittingOut[0].Version != "v26.12.0" { t.Fatalf("builders that run different versions: the majority is agreed and ace sits out; got %+v", a) } // Taken off ace again (its key deleted): its report goes again. - if !gate.Placement(k, nil, true, placed) { - t.Fatal("taking ace's placement away passed nothing on") + if !gate.Assignments(k, nil, true, placed) { + t.Fatal("taking ace's assignments away passed nothing on") } if v, none := agreedOf(set); v != "v26.12.1" || none != "" { t.Fatalf("got %q, %q", v, none) @@ -132,26 +117,26 @@ func TestToolchainGate(t *testing.T) { at time.Time // when ace wrote its report counts bool }{ - {"a report left before the placement", old, false}, - {"a report written since the placement", placed.Add(30 * time.Second), true}, + {"a report left before the assignments", old, false}, + {"a report written since the assignments", placed.Add(30 * time.Second), true}, } { t.Run(c.name, func(t *testing.T) { gate, set, said := start(t, true) k, v := placing(t, "ace") // ace's key, first heard placing nothing - gate.Placement(k, v, false, old.Add(-time.Hour)) + gate.Assignments(k, v, false, old.Add(-time.Hour)) gate.Settled() k, v = placing(t, "ace", NodeBuildModule) - if gate.Placement(k, v, false, placed) { - t.Fatal("a placement with no report passed something on") + if gate.Assignments(k, v, false, placed) { + t.Fatal("assignments with no report passed something on") } key, value := gateReport(t, "ace", "v26.12.0", c.at) if got := gate.Toolchain(NodeBuildModule, key, value, false); got != c.counts { t.Fatalf("ace's report passed on: %v, want %v", got, c.counts) } - // ace's placement written again with another module beside: its standing does not move, nothing is said again. + // ace's assignments written again with another module beside: its standing does not move, nothing is said again. k, v = placing(t, "ace", GoBuildModule, NodeBuildModule) - if gate.Placement(k, v, false, placed.Add(time.Minute)) { - t.Fatal("the same placement of mesh-build-node changed what was passed on") + if gate.Assignments(k, v, false, placed.Add(time.Minute)) { + t.Fatal("the same assignments of mesh-build-node changed what was passed on") } if c.counts { return @@ -169,47 +154,38 @@ func TestToolchainGate(t *testing.T) { }) } - t.Run("no placement held: every report counts", func(t *testing.T) { + t.Run("no assignments held: every report counts", func(t *testing.T) { gate, set, _ := start(t, false) key, value := gateReport(t, "ace", "v26.12.0", old) gate.Toolchain(NodeBuildModule, key, value, false) gate.Settled() if n := len(set.Entries("typescript")); n != 4 { - t.Fatalf("before the first placement file every report counts; got %d", n) + t.Fatalf("before the first assignments file every report counts; got %d", n) } k, v := placing(t, "g14", NodeBuildModule) - if !gate.Placement(k, v, false, old.Add(-time.Hour)) || len(set.Entries("typescript")) != 1 { - t.Fatalf("the first placement leaves every node it does not place out; got %d", len(set.Entries("typescript"))) + if !gate.Assignments(k, v, false, old.Add(-time.Hour)) || len(set.Entries("typescript")) != 1 { + t.Fatalf("the first assignments leaves every node it does not place out; got %d", len(set.Entries("typescript"))) } - if !gate.Placement(k, nil, true, placed) || len(set.Entries("typescript")) != 4 { - t.Fatal("the last placement gone: every report counts again") + if !gate.Assignments(k, nil, true, placed) || len(set.Entries("typescript")) != 4 { + t.Fatal("the last assignments gone: every report counts again") } }) t.Run("the history: a module in the first revision heard of a key stands since before it", func(t *testing.T) { p := meshmoduleassignerclient.NewPlaced() - // ace in the form before, then converted: its module stands since before, the legacy key read no more. - k, v := placingLegacy(t, "ace", NodeBuildModule) + // ace's node key is the first revision heard of it: its module stands since before. + k, v := placing(t, "ace", NodeBuildModule) p.Put(k, v, false, old) - k, v = placing(t, "ace", NodeBuildModule) - if _, changed, err := p.Put(k, v, false, placed); len(changed) != 0 || err != nil { - t.Errorf("the conversion of ace's key changed %v, %v", changed, err) - } if _, changed, _ := p.Put("ace."+NodeBuildModule, []byte("{}"), false, placed); len(changed) != 0 { t.Errorf("a module key says which modules stand: %v", changed) } - if k, v := placingLegacy(t, "ace"); true { - if _, changed, _ := p.Put(k, v, false, placed); len(changed) != 0 { - t.Errorf("the legacy key is read after the node key: %v", changed) - } - } k2, v2 := placing(t, "g14") p.Put(k2, v2, false, old) k2, v2 = placing(t, "g14", NodeBuildModule) if _, changed, _ := p.Put(k2, v2, false, placed); strings.Join(changed, ",") != NodeBuildModule { t.Errorf("a module placed says changed %v", changed) } - if _, _, err := p.Put("placement.novox", []byte("not json"), false, old); err == nil { + if _, _, err := p.Put("novox.node", []byte("not json"), false, old); err == nil { t.Error("a value that does not read was not said") } if s, ok := p.Since("ace", NodeBuildModule); !ok || !s.IsZero() { @@ -223,14 +199,14 @@ func TestToolchainGate(t *testing.T) { } }) - t.Run("a placement the bus refuses is said, and the part goes on", func(t *testing.T) { + t.Run("assignments the bus refuses is said, and the part goes on", func(t *testing.T) { var logs bytes.Buffer logger := log.New(&logs, "", 0) - refused := errors.New("nats: permissions violation for publish to $JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_placement") + refused := errors.New("nats: permissions violation for publish to $JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_assignments") gate, _, _ := start(t, false) for _, bus := range []refusingBus{{err: refused}, {refuseOpen: true, err: refused}} { - if _, err := FollowPlacement(context.Background(), bus, gate, func(context.Context) {}, logger.Printf); err != nil { - t.Errorf("a refused placement stopped the start: %v", err) + if _, err := FollowAssignments(context.Background(), bus, gate, func(context.Context) {}, logger.Printf); err != nil { + t.Errorf("a refused assignments stopped the start: %v", err) } } if !strings.Contains(logs.String(), "cannot be watched") || !strings.Contains(logs.String(), "cannot be read") { diff --git a/modules/mesh-delivery-builder/cmd/mesh-delivery-builder/integration_test.go b/modules/mesh-delivery-builder/cmd/mesh-delivery-builder/integration_test.go index c25a9159..a1f9db6a 100644 --- a/modules/mesh-delivery-builder/cmd/mesh-delivery-builder/integration_test.go +++ b/modules/mesh-delivery-builder/cmd/mesh-delivery-builder/integration_test.go @@ -303,7 +303,7 @@ func startBedWith(t *testing.T, closed bool) *bed { viewstest.Serve(t, ctx, js, ctl, indexerModule, st, indexerModule+".indexer_read_"+st, readers...) } b.packages = viewstest.Serve(t, ctx, js, ctl, indexerModule, meshcatalog.StatePackages, indexerModule+".indexer_read_packages", readers...) - viewstest.Serve(t, ctx, js, ctl, assignerModule, "placement", assignerModule+".assigner_read_placement", readers...) + viewstest.Serve(t, ctx, js, ctl, assignerModule, "assignments", assignerModule+".assigner_read_assignments", readers...) b.builds = mk(builds.BuilderBuildsBucket, builds.BuilderBuildsHistory) b.instances = mk(instancesBucket, 1) // The tag asks and their bounds (design 51 *Tagged versions*): the bounds declared key-ttl, so an ended bound leaves the diff --git a/modules/mesh-delivery-builder/cmd/mesh-delivery-builder/main.go b/modules/mesh-delivery-builder/cmd/mesh-delivery-builder/main.go index 594e471b..ff15f021 100644 --- a/modules/mesh-delivery-builder/cmd/mesh-delivery-builder/main.go +++ b/modules/mesh-delivery-builder/cmd/mesh-delivery-builder/main.go @@ -72,7 +72,7 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro if err != nil { return err } - // The indexer's modules, manifests, candidates and packages and the assigner's placement are their owners' states, read by the + // The indexer's modules, manifests, candidates and packages and the assigner's assignments are their owners' states, read by the // first view of their read tools and then their views families, never their buckets (ADR 0615 §1, DLV-M1). An owner that // is down never holds the start: each is read on a goroutine once its first view is answered, and until then the // unanswered first view is the builder's finding. @@ -105,7 +105,7 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro if err != nil { return err } - placement, err := reader.Follow(assignerModule, "placement", assignerModule+".assigner_read_placement") + assignments, err := reader.Follow(assignerModule, "assignments", assignerModule+".assigner_read_assignments") if err != nil { return err } @@ -258,11 +258,11 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro // The toolchains first, so a build that starts from the first modules value finds the nodes. watch := newToolchainWatch(ctx, js, tools, b.NodesChanged, logger) // Only the toolchain reports of nodes the catalogue places the build module on count (issue 1940, ADR 0549 §3): the - // placement is settled first, so the toolchains settle through the gate already knowing it. + // assignments is settled first, so the toolchains settle through the gate already knowing it. gate := buildrun.NewToolchainGate([]string{buildrun.GoBuildModule, buildrun.NodeBuildModule}, tools.Put, logger.Printf) watch.put = gate.Toolchain // What each counted node's engine reads (ADR 0618 §2, ADR 0624 §1): asked through the node-engine seat's verb as the - // builder's own account, for every node the placement names, and kept in the state engine-reads once it is bound. + // builder's own account, for every node the assignments names, and kept in the state engine-reads once it is bound. reads := newEngineReads(calls.CallOn, gate, logger.Printf) // The old reference form's window over them (DLV-B9), kept in the state old-form once it is bound; builder_old_form // reads it and drops a retired node by hand. @@ -288,20 +288,20 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro }) gate.OnPlaced = reads.placed b.Forms = reads.built - // Until the placement's first view is answered the gate passes every report on, as before the placement is settled. - placementErrs := make(chan error, 1) + // Until the assignments's first view is answered the gate passes every report on, as before the assignments is settled. + assignmentsErrs := make(chan error, 1) go func() { select { case <-ctx.Done(): return - case <-placement.Follower().Ready(): + case <-assignments.Follower().Ready(): } - pe, err := buildrun.FollowPlacement(ctx, reader, gate, b.NodesChanged, logger.Printf) + pe, err := buildrun.FollowAssignments(ctx, reader, gate, b.NodesChanged, logger.Printf) if err != nil { - placementErrs <- err + assignmentsErrs <- err return } - placementErrs <- <-pe + assignmentsErrs <- <-pe }() if err := watch.start(); err != nil { return err @@ -456,7 +456,7 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro return nil case err := <-errs: return err - case err := <-placementErrs: + case err := <-assignmentsErrs: return err case err := <-watch.errs: return err diff --git a/modules/mesh-delivery-builder/cmd/mesh-delivery-builder/names.go b/modules/mesh-delivery-builder/cmd/mesh-delivery-builder/names.go index 62852567..b52510ea 100644 --- a/modules/mesh-delivery-builder/cmd/mesh-delivery-builder/names.go +++ b/modules/mesh-delivery-builder/cmd/mesh-delivery-builder/names.go @@ -16,7 +16,7 @@ const ( // indexerModule is the holder of the states modules, manifests, candidates and packages the builder follows. indexerModule = "mesh-delivery-indexer" - // assignerModule is the holder of the state placement. + // assignerModule is the holder of the state assignments. assignerModule = buildrun.AssignerModule // sourceBucket is the gate's state source (reads: mesh-delivery-gate.source), the heads a candidate's base names. diff --git a/modules/mesh-delivery-builder/cmd/mesh-delivery-builder/names_test.go b/modules/mesh-delivery-builder/cmd/mesh-delivery-builder/names_test.go index 544f3090..6b3b86c3 100644 --- a/modules/mesh-delivery-builder/cmd/mesh-delivery-builder/names_test.go +++ b/modules/mesh-delivery-builder/cmd/mesh-delivery-builder/names_test.go @@ -33,7 +33,7 @@ func TestTheNamesAreTheManifests(t *testing.T) { } } for _, view := range []string{indexerModule + ".views.modules", indexerModule + ".views.manifests", indexerModule + ".views.candidates", - indexerModule + ".views.packages", assignerModule + ".views.placement"} { + indexerModule + ".views.packages", assignerModule + ".views.assignments"} { if !slices.Contains(m.Consumes, view) { t.Errorf("the builder follows %s, its manifest consumes %v", view, m.Consumes) } @@ -41,7 +41,7 @@ func TestTheNamesAreTheManifests(t *testing.T) { for want, got := range map[string]string{ "mesh-delivery-gate_source": sourceBucket, "mesh-build-go_steps": buildrun.StepsBucket("mesh-build-go"), "mesh-build-node_toolchains": buildrun.ToolchainsBucket("mesh-build-node"), "mesh-delivery-indexer_packages": packagesBucket, - "mesh-delivery-assigner_placement": buildrun.PlacementBucket, + "mesh-delivery-assigner_assignments": buildrun.AssignmentsBucket, } { if want != got { t.Errorf("a bucket is %q, want %q", got, want) diff --git a/modules/mesh-delivery-builder/cmd/mesh-delivery-builder/oldform.go b/modules/mesh-delivery-builder/cmd/mesh-delivery-builder/oldform.go index 205f95e8..c77e8dc9 100644 --- a/modules/mesh-delivery-builder/cmd/mesh-delivery-builder/oldform.go +++ b/modules/mesh-delivery-builder/cmd/mesh-delivery-builder/oldform.go @@ -21,7 +21,7 @@ import ( ) // oldForm keeps the old reference form's window (design 51 *Every build keeps the old reference form*, DLV-B9) in the -// state old-form: stepped on every change of what the counted nodes state, of the placement, on a hand drop and at each +// state old-form: stepped on every change of what the counted nodes state, of the assignments, on a hand drop and at each // build key written, never on a timer. A node holding it seven days is the finding old-form-held; its close is said once // as version.old-form-closed and in the builder's log. type oldForm struct { @@ -84,7 +84,7 @@ func (o *oldForm) dropped() map[string]bool { } // step moves the window to what counted states. -// named is every node the read named (the placement's nodes before any is dropped), counted the statements of those +// named is every node the read named (the assignments's nodes before any is dropped), counted the statements of those // still counted: a read naming no node judges nothing (window.StepWindow), so it never closes the window. func (o *oldForm) step(ctx context.Context, named []string, counted map[string][]string) { o.mu.Lock() @@ -106,7 +106,7 @@ func (o *oldForm) step(ctx context.Context, named []string, counted map[string][ n, next.Since[n].Format(time.RFC3339)), headline: "A node holds the old reference form", explanation: "The builder writes an older reference form into every build key while a counted node's engine reads none of the newest (ADR 0624 §1); this node has held it seven days.", - resolved: "The node's engine states the newest form, the node leaves the placement, or it is dropped as retired.", + resolved: "The node's engine states the newest form, the node leaves the assignments, or it is dropped as retired.", needs: "builder_old_form shows the window; a retired node is dropped there by hand; builder_engine_reads asks the node again."}) } o.overdue = overdue diff --git a/modules/mesh-delivery-builder/cmd/mesh-delivery-builder/reads.go b/modules/mesh-delivery-builder/cmd/mesh-delivery-builder/reads.go index 2cad8e48..4475e766 100644 --- a/modules/mesh-delivery-builder/cmd/mesh-delivery-builder/reads.go +++ b/modules/mesh-delivery-builder/cmd/mesh-delivery-builder/reads.go @@ -10,14 +10,14 @@ import ( "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds" "git.novox.be/novox/mesh-sdk/go/process" - "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/buildrun" "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient/enginereads" + "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/buildrun" ) // EngineReadsState is the builder's state of what each counted node's engine states it reads (novox/hq ADR 0618 §2, ADR // 0624 §1): one key ``, written from the node-engine seat's verb manifest-reads at the builder's start, on the // seat's event manifest-reads-changed for that node, and when a person asks again (builder_engine_reads); a node that -// is away keeps its last statement, and its key is removed when the placement no longer names the node. A counted node +// is away keeps its last statement, and its key is removed when the assignments no longer names the node. A counted node // with no key reads as stating the baseline: its first forms. No node's record is read in its place. const EngineReadsState = "engine-reads" @@ -43,7 +43,7 @@ func newEngineReads(call enginereads.Caller, gate *buildrun.ToolchainGate, logf } // stepWindow moves the old form's window to what the counted nodes state now (design 51 *Every build keeps the old -// reference form*): on a statement's change, the placement's and a hand drop, and at each build key written. +// reference form*): on a statement's change, the assignments's and a hand drop, and at each build key written. func (r *engineReads) stepWindow() { if r.window == nil { return @@ -56,7 +56,7 @@ func (r *engineReads) stepWindow() { r.window.step(ctx, r.named(), counted) } -// named are the nodes the placement names, none dropped: what the window's step judges a read by. +// named are the nodes the assignments names, none dropped: what the window's step judges a read by. func (r *engineReads) named() []string { r.mu.Lock() defer r.mu.Unlock() @@ -86,7 +86,7 @@ func (r *engineReads) bind(ctx context.Context, kv jetstream.KeyValue, errs chan } } -// placed takes the placement in: a node it names newly is asked, one it no longer names is forgotten, key and all. The +// placed takes the assignments in: a node it names newly is asked, one it no longer names is forgotten, key and all. The // first call asks every counted node (the builder's start). func (r *engineReads) placed() { nodes := r.gate.Nodes() @@ -96,7 +96,7 @@ func (r *engineReads) placed() { r.mu.Unlock() for _, node := range nodes { if first || !slices.Contains(before, node) { - r.keeper.AskNode(node, "counted: the placement names it") + r.keeper.AskNode(node, "counted: the assignments names it") } } for _, node := range before { diff --git a/modules/mesh-delivery-builder/cmd/mesh-delivery-builder/refused_test.go b/modules/mesh-delivery-builder/cmd/mesh-delivery-builder/refused_test.go index 8c3ff000..f4d30f13 100644 --- a/modules/mesh-delivery-builder/cmd/mesh-delivery-builder/refused_test.go +++ b/modules/mesh-delivery-builder/cmd/mesh-delivery-builder/refused_test.go @@ -20,15 +20,15 @@ import ( "github.com/nats-io/nats.go" "github.com/nats-io/nats.go/jetstream" + "git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient" + "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient/enginereads" + "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-contracts/go/seats/nodeengine" - "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" - "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" sweep "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshartifactsweep" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/buildrun" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds/forms" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-forge-bridge/forgemsg" - "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient/enginereads" "mesh-delivery-builder/internal/build" "mesh-delivery-builder/internal/versions" @@ -36,7 +36,7 @@ import ( // 1945 (a part exits at start for a bucket it works without): a bus that refuses every toolchains bucket leaves the // watch unstarted and the part running, said; a refused bucket it cannot work without is an error naming the bucket and -// its grant. The gate over the seat's placement, and its refusal, are mesh-sdk's (buildrun.ToolchainGate, +// its grant. The gate over the seat's assignments, and its refusal, are mesh-sdk's (buildrun.ToolchainGate, // issue 774). func TestARefusedBucketIsSaidAndThePartGoesOn(t *testing.T) { t.Run("a floor's wake is heard on the events consumer (design 51 The floor)", aFloorsWakeIsHeard) @@ -69,28 +69,28 @@ func TestARefusedBucketIsSaidAndThePartGoesOn(t *testing.T) { t.Run("ADR 0624: the counted nodes' engine reads", theCountedNodesEngineReads) } -// theCountedNodesEngineReads: the builder asks the node-engine seat's verb of every node the placement names; a node away +// theCountedNodesEngineReads: the builder asks the node-engine seat's verb of every node the assignments names; a node away // and never asked has no statement and reads as the baseline's first forms, with no node record read in its place; an -// away node keeps its last statement; a node the placement no longer names leaves the count, its key removed; and +// away node keeps its last statement; a node the assignments no longer names leaves the count, its key removed; and // builder_engine_reads drops one node's key. func theCountedNodesEngineReads(t *testing.T) { gate := buildrun.NewToolchainGate(nil, func(string, string, []byte, bool) bool { return false }, t.Logf) place := func(nodes ...string) { for _, node := range []string{"n1", "n2"} { - pl := meshmoduleassigner.Placement{Node: node} + pl := meshmoduleassigner.Assignments{Node: node} if slices.Contains(nodes, node) { pl.Modules = []meshmoduleassigner.PlacedModule{{Module: "web"}} } - values, err := meshmoduleassignerclient.PlacementValues(pl) + values, err := meshmoduleassignerclient.AssignmentsValues(pl) if err != nil { t.Fatal(err) } for key, raw := range values { - gate.Placement(key, raw, false, time.Now()) + gate.Assignments(key, raw, false, time.Now()) } if !slices.Contains(nodes, node) { key, _ := meshmoduleassignerclient.ModuleKey(node, "web") - gate.Placement(key, nil, true, time.Now()) + gate.Assignments(key, nil, true, time.Now()) } } } @@ -125,7 +125,7 @@ func theCountedNodesEngineReads(t *testing.T) { if _, counted = r.forms(); !slices.Equal(counted["n2"], []string{"form:new"}) || r.keeper.Statements()["n2"].Answered { t.Errorf("away again, n2 reads %v (%+v)", counted["n2"], r.keeper.Statements()["n2"]) } - // n2 retired: it leaves the placement, and its key goes. + // n2 retired: it leaves the assignments, and its key goes. place("n1") r.placed() if _, counted = r.forms(); len(counted) != 1 || kept["n2"] != nil { diff --git a/modules/mesh-delivery-builder/go.mod b/modules/mesh-delivery-builder/go.mod index 722b5b2e..9e85230d 100644 --- a/modules/mesh-delivery-builder/go.mod +++ b/modules/mesh-delivery-builder/go.mod @@ -10,8 +10,8 @@ require ( git.novox.be/novox/mesh-delivery/contracts/go/seats/npmpackageregistry v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-delivery/contracts/go/seats/ociartifactregistry v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient v0.0.0-00010101000000-000000000000 - git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient v0.0.0-00010101000000-000000000000 - git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdelivery v0.0.0-00010101000000-000000000000 diff --git a/modules/mesh-delivery-builder/internal/build/run.go b/modules/mesh-delivery-builder/internal/build/run.go index b40c5abf..fa76ab4e 100644 --- a/modules/mesh-delivery-builder/internal/build/run.go +++ b/modules/mesh-delivery-builder/internal/build/run.go @@ -18,8 +18,8 @@ import ( "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/buildrun" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds" - sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds/forms" + sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep" repotrunk "git.novox.be/novox/mesh-delivery/sdk/trunk" "git.novox.be/novox/mesh-sdk/go/catalogue" @@ -92,7 +92,7 @@ type Builder struct { // Tags answers a check's build's tag from the kept tag asks; nil numbers no check's build. Tags TagOf // Forms answers what each node's engine states it reads (ADR 0618 §2, ADR 0624 §1): present, the nodes whose engine - // answered when last asked, and counted, every node the placement names, present or away, a node with no statement + // answered when last asked, and counted, every node the assignments names, present or away, a node with no statement // stating nothing (the baseline's first forms). A done key names each artifact in the forms these keep, the oldest in // its single reference, and its needs. Nil: no node states anything, and the first forms are kept. Forms func() (present, counted map[string][]string) diff --git a/modules/mesh-delivery-builder/internal/versions/oldform.go b/modules/mesh-delivery-builder/internal/versions/oldform.go index bbf53c50..6f435865 100644 --- a/modules/mesh-delivery-builder/internal/versions/oldform.go +++ b/modules/mesh-delivery-builder/internal/versions/oldform.go @@ -19,7 +19,7 @@ import ( // builder's state engine-reads; this is only the window over them: open while a counted node-engine, present or away, // reads of some artifact kind none of the newest form the builder knows for it, so an older form stays in new build keys // for it; closed, said once with `old-form-closed` and a log line, when none does. It moves on what moves a statement -// (the node-engine seat's verb `manifest-reads` and its event `manifest-reads-changed`), the placement, a hand drop and +// (the node-engine seat's verb `manifest-reads` and its event `manifest-reads-changed`), the assignments, a hand drop and // each build key written; never on a timer. // OldFormState is the builder's state of the window: one key, OldFormKey, an OldFormWindow. builder_old_form reads it. @@ -94,7 +94,7 @@ const FindingAfter = 7 * 24 * time.Hour // Step moves the window by mesh-sdk's one window rule (window.StepWindow): named is every counted node the read named, // those dropped by hand included, and holding those of them holding the window (Holding). A read naming no node (no -// placement read yet, an empty node_haves) judges nothing, so it never closes the window. It answers the new window, +// assignments read yet, an empty node_haves) judges nothing, so it never closes the window. It answers the new window, // whether it closed in this step (so the builder says `old-form-closed` and its log line once), and the nodes holding it // past FindingAfter (the finding, cleared for every other node). func (w OldFormWindow) Step(named, holding []string, now time.Time) (next OldFormWindow, closed bool, overdue []string) { @@ -146,7 +146,7 @@ const OldFormDescription = "The old reference form's window (design 51 *Every bu "the counted nodes, present or away, whose node-engine reads an older reference form alone and since when (a node holding it seven days " + "is the builder's finding " + OldFormHeld + "), when it last closed and who held it then, and the retired nodes dropped from the count by " + "hand. With drop (a node) and why, drops that retired node from the count by hand, a hand act the builder answers; a node still placed " + - "leaves the count by itself only when the placement no longer names it. builder_engine_reads shows what each node's engine states." + "leaves the count by itself only when the assignments no longer names it. builder_engine_reads shows what each node's engine states." // AnswerOldForm is builder_old_form's read: the window as the state holds it. func AnswerOldForm(r Reader) (OldFormWindow, error) { diff --git a/modules/mesh-delivery-builder/internal/versions/versions_test.go b/modules/mesh-delivery-builder/internal/versions/versions_test.go index 8e0c432c..d94a2fa6 100644 --- a/modules/mesh-delivery-builder/internal/versions/versions_test.go +++ b/modules/mesh-delivery-builder/internal/versions/versions_test.go @@ -365,7 +365,7 @@ func TestTheBuildersVersions(t *testing.T) { t.Error("a drop with no why is taken") } // Security-upgrade F3 (review round 1), by mesh-sdk's window.StepWindow: a read naming no node (an empty - // node_haves, the placement not read yet) never closes the window, and its holder's seven days still count. + // node_haves, the assignments not read yet) never closes the window, and its holder's seven days still count. if held, shut, late := w.Step(nil, nil, t0.Add(FindingAfter+time.Minute)); shut || !held.Open || !reflect.DeepEqual(late, []string{"c"}) { t.Fatalf("a read naming no node: %+v closed=%v overdue %v, want the window open and c overdue", held, shut, late) } diff --git a/modules/mesh-delivery-builder/manifest_test.go b/modules/mesh-delivery-builder/manifest_test.go index bbf16c6f..2829d711 100644 --- a/modules/mesh-delivery-builder/manifest_test.go +++ b/modules/mesh-delivery-builder/manifest_test.go @@ -102,7 +102,7 @@ func TestManifestDeclarations(t *testing.T) { // 1607/AC1 // 1887/A4c: the builder chooses no node, so it reads neither presence nor have, invokes no build module's tool and // holds no capacity; it puts asks on the three build seats and reads their steps (ADR 0525 §1, §4, §5). if want := []string{"mesh-build-go.steps", "mesh-build-go.toolchains", "mesh-build-image.steps", "mesh-build-node.steps", "mesh-build-node.toolchains", - "mesh-delivery-assigner.placement", "mesh-delivery-gate.source"}; !reflect.DeepEqual(sorted(m.Reads), want) { + "mesh-delivery-assigner.assignments", "mesh-delivery-gate.source"}; !reflect.DeepEqual(sorted(m.Reads), want) { t.Errorf("reads = %v, want %v", m.Reads, want) } if want := []string{"node-build-go", "node-build-image", "node-build-node"}; !reflect.DeepEqual(sorted(m.Uses), want) { @@ -111,18 +111,18 @@ func TestManifestDeclarations(t *testing.T) { // 1607/AC1 // 1937/AC3: the builder asks the controller whether the artifact store still holds a done key's blobs (held), and no // other tool; it holds no capacity. // 1955/L7 (ADR 0571 §2): the seat mesh-artifact-sweep's held, the controller's `held` is no longer asked (issue 1955). - // DLV-M1 (ADR 0615 §1): the indexer's modules, manifests and candidates and the assigner's placement are asked by their + // DLV-M1 (ADR 0615 §1): the indexer's modules, manifests and candidates and the assigner's assignments are asked by their // read tools and followed by their views families, never read; so are the indexer's packages, whose names a package is // numbered under (design 32). for _, r := range m.Reads { - // The toolchain gate follows the assigner's placement through meshmoduleassignerclient (buildrun.PlacementBucket), so + // The toolchain gate follows the assigner's assignments through meshmoduleassignerclient (buildrun.AssignmentsBucket), so // it declares that one read, as the console and route-proxy do; everything else of theirs is asked by tools. - if strings.HasPrefix(r, "mesh-delivery-indexer.") || (strings.HasPrefix(r, "mesh-delivery-assigner.") && r != "mesh-delivery-assigner.placement") { + if strings.HasPrefix(r, "mesh-delivery-indexer.") || (strings.HasPrefix(r, "mesh-delivery-assigner.") && r != "mesh-delivery-assigner.assignments") { t.Errorf("reads %s: the indexer's and the assigner's state is read by their tools and views families alone", r) } } // Design 51 *The floor*: the floor's three sources, the registry seats' versions, bridge_tags and indexer_requirements. - if want := []string{"mesh-delivery-assigner.assigner_read_placement", "mesh-delivery-forge-bridge.bridge_tags", "mesh-delivery-indexer.indexer_read_candidates", "mesh-delivery-indexer.indexer_read_manifests", + if want := []string{"mesh-delivery-assigner.assigner_read_assignments", "mesh-delivery-forge-bridge.bridge_tags", "mesh-delivery-indexer.indexer_read_candidates", "mesh-delivery-indexer.indexer_read_manifests", "mesh-delivery-indexer.indexer_read_modules", "mesh-delivery-indexer.indexer_read_packages", "mesh-delivery-indexer.indexer_requirements", "seat:container-image-registry.remove", "seat:container-image-registry.versions", "seat:go-package-registry.remove", "seat:go-package-registry.versions", "seat:mesh-artifact-sweep.held", "seat:mesh-hand-acts.hand-act", "seat:node-engine.manifest-reads", "seat:npm-package-registry.remove", "seat:npm-package-registry.versions", "seat:oci-artifact-registry.remove", "seat:oci-artifact-registry.versions"}; !reflect.DeepEqual(sorted(m.Invokes), want) || len(m.Config) != 0 { @@ -137,7 +137,7 @@ func TestManifestDeclarations(t *testing.T) { // 1607/AC1 if want := []string{"container-image-registry.kept", "container-image-registry.published", "container-image-registry.refused", "container-image-registry.removed", "go-package-registry.kept", "go-package-registry.published", "go-package-registry.refused", "go-package-registry.removed", "mesh-artifact-sweep.artifacts-gone", "mesh-build-go.toolchain.reported", "mesh-build-node.toolchain.reported", "mesh-catalog.manifest-accepted", - "mesh-conditions.condition-cleared", "mesh-delivery-assigner.views.placement", "mesh-delivery-checker.tag-wanted", "mesh-delivery-forge-bridge.forge.**", + "mesh-conditions.condition-cleared", "mesh-delivery-assigner.views.assignments", "mesh-delivery-checker.tag-wanted", "mesh-delivery-forge-bridge.forge.**", "mesh-delivery-indexer.views.candidates", "mesh-delivery-indexer.views.manifests", "mesh-delivery-indexer.views.modules", "mesh-delivery-indexer.views.packages", "node-engine.manifest-reads-changed", "npm-package-registry.kept", "npm-package-registry.published", "npm-package-registry.refused", "npm-package-registry.removed", diff --git a/modules/mesh-delivery-builder/module.json b/modules/mesh-delivery-builder/module.json index b4c66db8..af712bce 100644 --- a/modules/mesh-delivery-builder/module.json +++ b/modules/mesh-delivery-builder/module.json @@ -52,7 +52,7 @@ "oci-artifact-registry.published", "oci-artifact-registry.refused", "oci-artifact-registry.removed", - "mesh-delivery-assigner.views.placement", + "mesh-delivery-assigner.views.assignments", "mesh-delivery-indexer.views.candidates", "mesh-delivery-indexer.views.manifests", "mesh-delivery-indexer.views.modules", @@ -72,7 +72,7 @@ "seat:npm-package-registry.versions", "seat:oci-artifact-registry.remove", "seat:oci-artifact-registry.versions", - "mesh-delivery-assigner.assigner_read_placement", + "mesh-delivery-assigner.assigner_read_assignments", "mesh-delivery-indexer.indexer_read_candidates", "mesh-delivery-indexer.indexer_read_manifests", "mesh-delivery-indexer.indexer_read_modules", @@ -103,7 +103,7 @@ "mesh-build-image.steps", "mesh-build-node.steps", "mesh-build-node.toolchains", - "mesh-delivery-assigner.placement", + "mesh-delivery-assigner.assignments", "mesh-delivery-gate.source" ], "uses": [ diff --git a/modules/mesh-delivery-checker/cmd/mesh-delivery-checker/engines.go b/modules/mesh-delivery-checker/cmd/mesh-delivery-checker/engines.go index 48697172..60c98982 100644 --- a/modules/mesh-delivery-checker/cmd/mesh-delivery-checker/engines.go +++ b/modules/mesh-delivery-checker/cmd/mesh-delivery-checker/engines.go @@ -15,9 +15,9 @@ import ( "mesh-delivery-checker/internal/check" + "git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient" nec "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" ne "git.novox.be/novox/mesh-contracts/go/seats/nodeengine" - "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/buildrun" @@ -58,7 +58,7 @@ func (e engineAsks) ComposeCandidate(ctx context.Context, node string, ask ne.Co } } -// placedNodes names every node the seat mesh-placement places now, read from its state when a check composes (no +// placedNodes names every node the seat mesh-module-assigner places now, read from its state when a check composes (no // watch: a check reads it once). A state that cannot be read is an error, so the check says its composition was not // made rather than passing with no node asked (review finding 4). type placedNodes struct { @@ -70,14 +70,14 @@ func (p placedNodes) Placed() ([]string, error) { defer cancel() kv, err := p.kv(ctx) if err != nil { - return nil, fmt.Errorf("the state %s cannot be read: %w", buildrun.PlacementBucket, err) + return nil, fmt.Errorf("the state %s cannot be read: %w", buildrun.AssignmentsBucket, err) } lister, err := kv.ListKeys(ctx) if errors.Is(err, jetstream.ErrNoKeysFound) { - return nil, nil // a placement that places nothing + return nil, nil // assignments that places nothing } if err != nil { - return nil, fmt.Errorf("the state %s cannot be listed: %w", buildrun.PlacementBucket, err) + return nil, fmt.Errorf("the state %s cannot be listed: %w", buildrun.AssignmentsBucket, err) } seen := map[string]bool{} for key := range lister.Keys() { diff --git a/modules/mesh-delivery-checker/cmd/mesh-delivery-checker/integration_test.go b/modules/mesh-delivery-checker/cmd/mesh-delivery-checker/integration_test.go index c571a2a6..d220de5d 100644 --- a/modules/mesh-delivery-checker/cmd/mesh-delivery-checker/integration_test.go +++ b/modules/mesh-delivery-checker/cmd/mesh-delivery-checker/integration_test.go @@ -25,13 +25,13 @@ import ( "mesh-delivery-checker/internal/check" gobuild "git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep" + "git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient" "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" nec "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" + "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-contracts/go/seats/nodeengine" ne "git.novox.be/novox/mesh-contracts/go/seats/nodeengine" "git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshwatchdogs" - "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" - "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliverycheckerclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient" @@ -323,13 +323,13 @@ func (b *bed) said(t *testing.T, words string) { // the seat verb's caller-named and plain subjects); the others never answer. It answers what it was asked. func (b *bed) engine(t *testing.T, nodes ...string) func() []ne.ComposeCandidateInput { t.Helper() - kv, err := b.js.CreateKeyValue(b.ctx, jetstream.KeyValueConfig{Bucket: buildrun.PlacementBucket, History: 1}) + kv, err := b.js.CreateKeyValue(b.ctx, jetstream.KeyValueConfig{Bucket: buildrun.AssignmentsBucket, History: 1}) if err != nil { t.Fatal(err) } for _, n := range nodes { pk, _ := meshmoduleassignerclient.NodeKey(n) - raw, _ := meshmoduleassignerclient.EncodeNode(meshmoduleassigner.PlacementByNodeNodeValue{Node: n}) + raw, _ := meshmoduleassignerclient.EncodeNode(meshmoduleassigner.AssignmentsByNodeNodeValue{Node: n}) if _, err := kv.Put(b.ctx, pk, raw); err != nil { t.Fatal(err) } @@ -466,7 +466,7 @@ func startBed(t *testing.T) *bed { for _, st := range []string{candidatesState, modulesState, manifestsState, meshcatalog.StatePackages, check.IndexerPredefinedState, seatDefinitionsState} { viewstest.Serve(t, ctx, js, ctl, check.IndexerModule, st, check.IndexerModule+".indexer_read_"+strings.ReplaceAll(st, "-", "_"), "n1_"+check.CheckerModule) } - viewstest.Serve(t, ctx, js, ctl, buildrun.AssignerModule, meshmoduleassigner.StatePlacement, buildrun.AssignerModule+".assigner_read_placement", "n1_"+check.CheckerModule) + viewstest.Serve(t, ctx, js, ctl, buildrun.AssignerModule, meshmoduleassigner.StateAssignments, buildrun.AssignerModule+".assigner_read_assignments", "n1_"+check.CheckerModule) // 1929/AC1: the indexer's value of the repository's module, with no TypeScript manifest, so the Go build module alone // judges the repository (an unknown repository is judged by both, issue 1887 A12). known, err := meshcatalogclient.EncodeModule(meshcatalog.ModulesByModuleValue{Repository: repo, Commit: trunk, Path: "modules/m", None2: "the fingerprint is computed with its chain"}) diff --git a/modules/mesh-delivery-checker/cmd/mesh-delivery-checker/main.go b/modules/mesh-delivery-checker/cmd/mesh-delivery-checker/main.go index 3806a96c..e482e84c 100644 --- a/modules/mesh-delivery-checker/cmd/mesh-delivery-checker/main.go +++ b/modules/mesh-delivery-checker/cmd/mesh-delivery-checker/main.go @@ -25,7 +25,7 @@ import ( "mesh-delivery-checker/internal/check" - "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" + "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/buildrun" @@ -145,7 +145,7 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro logger.Printf("what the checker keeps could not be said on the bus, so readers of owed, previews and verdicts are not granted until it is: %v", err) } // The candidates it checks, the modules and manifests that say which build modules judge a repository, the published - // packages, the predefined keys and the placement are their owners' states (the indexer's, the assigner's), read by the + // packages, the predefined keys and the assignments are their owners' states (the indexer's, the assigner's), read by the // first view of their read tools and then their views families, never their buckets (ADR 0615 §1, ADR 0596, DLV-M2). // An owner that is down never holds the start: the checks start once every first view is answered, and until then the // unanswered first view is the checker's finding. @@ -168,7 +168,7 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro manifests := followOwner(check.IndexerModule, manifestsState, "indexer_read_manifests", indexerViews) packages := followOwner(check.IndexerModule, meshcatalog.StatePackages, "indexer_read_packages", indexerViews) predefined := followOwner(check.IndexerModule, check.IndexerPredefinedState, "indexer_read_predefined", indexerViews) - placement := followOwner(buildrun.AssignerModule, meshmoduleassigner.StatePlacement, "assigner_read_placement", assignerViews) + assignments := followOwner(buildrun.AssignerModule, meshmoduleassigner.StateAssignments, "assigner_read_assignments", assignerViews) // The seventh: the indexer's seat definitions, so a pull request whose head's definition does not parse fails. seatDefs := followOwner(check.IndexerModule, seatDefinitionsState, "indexer_read_seat_definitions", indexerViews) if followed != nil { @@ -246,7 +246,7 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro verdicts := &manifestVerdicts{} compositions := check.EngineCompositions{ Asker: engineAsks{call: caller.CallOn}, - Placed: placedNodes{kv: func(context.Context) (jetstream.KeyValue, error) { return placement, nil }}.Placed, + Placed: placedNodes{kv: func(context.Context) (jetstream.KeyValue, error) { return assignments, nil }}.Placed, Bound: answerBound, Sandbox: seatSandbox{asker: buildrun.Asker{Publisher: asks, Waiter: waiter, Bound: stepBound}}, Verdicts: verdicts.all, @@ -303,23 +303,23 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro // The toolchains first, so a check that starts from the first candidate finds the nodes. watch := newToolchainWatch(ctx, js, tools, ch.NodesChanged, logger) // Only the toolchain reports of nodes the catalogue places the build module on count (issue 1940, ADR 0549 §3): the - // placement is settled first, so the toolchains settle through the gate already knowing it. + // assignments is settled first, so the toolchains settle through the gate already knowing it. gate := buildrun.NewToolchainGate([]string{buildrun.GoBuildModule, buildrun.NodeBuildModule}, tools.Put, logger.Printf) watch.put = gate.Toolchain - // Until the placement's first view is answered the gate passes every report on, as before the placement is settled. - placementErrs := make(chan error, 1) + // Until the assignments's first view is answered the gate passes every report on, as before the assignments is settled. + assignmentsErrs := make(chan error, 1) go func() { select { case <-ctx.Done(): return - case <-placement.Follower().Ready(): + case <-assignments.Follower().Ready(): } - pe, err := buildrun.FollowPlacement(ctx, reader, gate, ch.NodesChanged, logger.Printf) + pe, err := buildrun.FollowAssignments(ctx, reader, gate, ch.NodesChanged, logger.Printf) if err != nil { - placementErrs <- err + assignmentsErrs <- err return } - placementErrs <- <-pe + assignmentsErrs <- <-pe }() if err := watch.start(); err != nil { return err @@ -481,7 +481,7 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro defer hub.Stop() // The checks, the tries and the hand asks, once every first view is answered (the lead's ruling for DLV-M2): a check // judged without the candidates, the modules, the manifests, the published packages, the predefined keys or the - // placement would be another check. + // assignments would be another check. go func() { select { case <-ctx.Done(): @@ -500,7 +500,7 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro return nil case err := <-errs: return err - case err := <-placementErrs: + case err := <-assignmentsErrs: return err case err := <-watch.errs: return err diff --git a/modules/mesh-delivery-checker/cmd/mesh-delivery-checker/refused_test.go b/modules/mesh-delivery-checker/cmd/mesh-delivery-checker/refused_test.go index 9ba279c6..9b980baa 100644 --- a/modules/mesh-delivery-checker/cmd/mesh-delivery-checker/refused_test.go +++ b/modules/mesh-delivery-checker/cmd/mesh-delivery-checker/refused_test.go @@ -17,7 +17,7 @@ import ( // 1945 (a part exits at start for a bucket it works without): a bus that refuses every toolchains bucket leaves the // watch unstarted and the part running, said; a refused bucket it cannot work without is an error naming the bucket and -// its grant. The gate over the seat's placement, and its refusal, are mesh-sdk's (buildrun.ToolchainGate, +// its grant. The gate over the seat's assignments, and its refusal, are mesh-sdk's (buildrun.ToolchainGate, // issue 774). func TestARefusedBucketIsSaidAndThePartGoesOn(t *testing.T) { set := &buildrun.ToolchainSet{} diff --git a/modules/mesh-delivery-checker/go.mod b/modules/mesh-delivery-checker/go.mod index 86dfd03e..b1c8eb63 100644 --- a/modules/mesh-delivery-checker/go.mod +++ b/modules/mesh-delivery-checker/go.mod @@ -5,9 +5,9 @@ go 1.26.0 require ( git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshwatchdogs v0.0.0-00010101000000-000000000000 - git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient v0.0.0-00010101000000-000000000000 - git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner v0.0.0-00010101000000-000000000000 - git.novox.be/novox/mesh-contracts/placement/go/seats/meshwants v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-delivery/contracts/go/seats/meshwants v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliverycheckerclient v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient v0.0.0-00010101000000-000000000000 diff --git a/modules/mesh-delivery-checker/internal/check/composition.go b/modules/mesh-delivery-checker/internal/check/composition.go index 9acc7e50..3baf5fa0 100644 --- a/modules/mesh-delivery-checker/internal/check/composition.go +++ b/modules/mesh-delivery-checker/internal/check/composition.go @@ -3,9 +3,9 @@ package check import ( "strings" - "git.novox.be/novox/mesh-contracts/placement/go/seats/meshwants" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliverycheckerclient" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdeliverychecker" + "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshwants" ) // withComposition is v with the check's composition taken in: a failed one fails the check, naming its machine. diff --git a/modules/mesh-delivery-checker/internal/check/composition_test.go b/modules/mesh-delivery-checker/internal/check/composition_test.go index 45c9b3e1..6dd2f466 100644 --- a/modules/mesh-delivery-checker/internal/check/composition_test.go +++ b/modules/mesh-delivery-checker/internal/check/composition_test.go @@ -8,10 +8,10 @@ import ( "strings" "testing" - "git.novox.be/novox/mesh-contracts/placement/go/seats/meshwants" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliverycheckerclient" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdeliverychecker" + "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshwants" buildsrec "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds" "git.novox.be/novox/mesh-sdk/go/catalogue" ) diff --git a/modules/mesh-delivery-checker/internal/check/engines.go b/modules/mesh-delivery-checker/internal/check/engines.go index 8384cf89..22c13d91 100644 --- a/modules/mesh-delivery-checker/internal/check/engines.go +++ b/modules/mesh-delivery-checker/internal/check/engines.go @@ -12,10 +12,10 @@ import ( "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" nec "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" ne "git.novox.be/novox/mesh-contracts/go/seats/nodeengine" - "git.novox.be/novox/mesh-contracts/placement/go/seats/meshwants" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliverycheckerclient" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" + "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshwants" "git.novox.be/novox/mesh-delivery/sdk/buildstep" "git.novox.be/novox/mesh-delivery/sdk/composer" ) @@ -46,7 +46,7 @@ type Sandbox interface { // EngineCompositions is the checker's composition of a check (Examiner.Compositions). type EngineCompositions struct { Asker EngineAsker - // Placed names every node in placement now (the seat mesh-placement's state), or why it cannot be read: then no node + // Placed names every node in assignments now (the seat mesh-module-assigner's state), or why it cannot be read: then no node // can be asked, and the composition is not made (an error), never passed with nobody asked. Placed func() ([]string, error) // Bound is the answer bound (the setting compose-answer-bound); zero is nec.AnswerBound. @@ -80,7 +80,7 @@ func (e EngineCompositions) Compose(ctx context.Context, c *Case) (meshwants.Com var nodes []string if e.Placed != nil { if nodes, err = e.Placed(); err != nil { - return meshwants.CompositionsByGroupHeadsValue{}, fmt.Errorf("the placement cannot be read, so no node can be asked: %w", err) + return meshwants.CompositionsByGroupHeadsValue{}, fmt.Errorf("the assignments cannot be read, so no node can be asked: %w", err) } } bound := e.Bound diff --git a/modules/mesh-delivery-checker/internal/check/engines_test.go b/modules/mesh-delivery-checker/internal/check/engines_test.go index d157f802..ce6075d8 100644 --- a/modules/mesh-delivery-checker/internal/check/engines_test.go +++ b/modules/mesh-delivery-checker/internal/check/engines_test.go @@ -12,10 +12,10 @@ import ( "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" nec "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" ne "git.novox.be/novox/mesh-contracts/go/seats/nodeengine" - "git.novox.be/novox/mesh-contracts/placement/go/seats/meshwants" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliverycheckerclient" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdeliverychecker" + "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshwants" "git.novox.be/novox/mesh-delivery/sdk/buildstep" "git.novox.be/novox/mesh-delivery/sdk/composer" ) @@ -204,11 +204,11 @@ func TestTheCompositionIsMadeFromTheEnginesAnswers(t *testing.T) { } }) - // Review finding 4: a placement that cannot be read is no mesh with no node: the composition is not made, said. - t.Run("an unreadable placement makes no composition", func(t *testing.T) { + // Review finding 4: assignments that cannot be read is no mesh with no node: the composition is not made, said. + t.Run("an unreadable assignments makes no composition", func(t *testing.T) { ec := EngineCompositions{Asker: &fakeEngines{}, Placed: func() ([]string, error) { return nil, errors.New("no such bucket") }} if _, err := ec.Compose(context.Background(), cs); err == nil || !strings.Contains(err.Error(), "no such bucket") { - t.Errorf("an unreadable placement: %v", err) + t.Errorf("an unreadable assignments: %v", err) } b := newBench(t) b.ex.Compositions = ec diff --git a/modules/mesh-delivery-checker/internal/check/every_failure_test.go b/modules/mesh-delivery-checker/internal/check/every_failure_test.go index 67638802..88172630 100644 --- a/modules/mesh-delivery-checker/internal/check/every_failure_test.go +++ b/modules/mesh-delivery-checker/internal/check/every_failure_test.go @@ -6,9 +6,9 @@ import ( "strings" "testing" - "git.novox.be/novox/mesh-contracts/placement/go/seats/meshwants" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliverycheckerclient" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdeliverychecker" + "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshwants" "git.novox.be/novox/mesh-delivery/sdk/buildstep" ) diff --git a/modules/mesh-delivery-checker/internal/check/examiner_test.go b/modules/mesh-delivery-checker/internal/check/examiner_test.go index 23448cad..7657a503 100644 --- a/modules/mesh-delivery-checker/internal/check/examiner_test.go +++ b/modules/mesh-delivery-checker/internal/check/examiner_test.go @@ -20,12 +20,12 @@ import ( gobuild "git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep" "git.novox.be/novox/mesh-contracts/go/seats/meshconfig" - "git.novox.be/novox/mesh-contracts/placement/go/seats/meshwants" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliverycheckerclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdelivery" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdeliverychecker" + "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshwants" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/buildrun" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds" "git.novox.be/novox/mesh-delivery/sdk/buildstep" diff --git a/modules/mesh-delivery-checker/internal/check/judge.go b/modules/mesh-delivery-checker/internal/check/judge.go index 8f0aa3ec..2eb290a2 100644 --- a/modules/mesh-delivery-checker/internal/check/judge.go +++ b/modules/mesh-delivery-checker/internal/check/judge.go @@ -10,10 +10,10 @@ import ( "strings" "time" - "git.novox.be/novox/mesh-contracts/placement/go/seats/meshwants" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliverycheckerclient" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdeliverychecker" + "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshwants" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/buildrun" "git.novox.be/novox/mesh-delivery/sdk/buildstep" ) diff --git a/modules/mesh-delivery-checker/internal/check/try_test.go b/modules/mesh-delivery-checker/internal/check/try_test.go index f46e3893..6e95f3e3 100644 --- a/modules/mesh-delivery-checker/internal/check/try_test.go +++ b/modules/mesh-delivery-checker/internal/check/try_test.go @@ -11,10 +11,10 @@ import ( "time" "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" - "git.novox.be/novox/mesh-contracts/placement/go/seats/meshwants" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliverycheckerclient" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" + "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshwants" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-checker/checkermsg" "git.novox.be/novox/mesh-delivery/sdk/buildstep" ) diff --git a/modules/mesh-delivery-checker/manifest_test.go b/modules/mesh-delivery-checker/manifest_test.go index ab9dd7f8..39b6911b 100644 --- a/modules/mesh-delivery-checker/manifest_test.go +++ b/modules/mesh-delivery-checker/manifest_test.go @@ -115,7 +115,7 @@ func TestManifestDeclarations(t *testing.T) { // 1678/AC1 t.Errorf("reads = %v, want %v", m.Reads, want) } // DLV-M2 (ADR 0615 §1, ADR 0596): the indexer's candidates, modules, manifests, packages and predefined keys and the - // assigner's placement are asked by their read tools and followed by their views families, never read. + // assigner's assignments are asked by their read tools and followed by their views families, never read. for _, r := range m.Reads { if strings.HasPrefix(r, "mesh-delivery-indexer.") || strings.HasPrefix(r, "mesh-delivery-assigner.") { t.Errorf("reads %s: the indexer's and the assigner's state is read by their tools and views families alone", r) @@ -126,7 +126,7 @@ func TestManifestDeclarations(t *testing.T) { // 1678/AC1 // The forge's reads for a mesh-sdk pin (ADR 0484 clause 9), and of the node seat node-engine its one verb // compose-candidate, asked of every placed node (ADR 0593 §7), never the seat's other verbs. if want := []string{"gitea.gitea_commits", "gitea.gitea_compare", "gitea.gitea_get_file", "gitea.gitea_list_branches", - "mesh-delivery-assigner.assigner_read_placement", "mesh-delivery-indexer.indexer_read_candidates", "mesh-delivery-indexer.indexer_read_manifests", + "mesh-delivery-assigner.assigner_read_assignments", "mesh-delivery-indexer.indexer_read_candidates", "mesh-delivery-indexer.indexer_read_manifests", "mesh-delivery-indexer.indexer_read_modules", "mesh-delivery-indexer.indexer_read_packages", "mesh-delivery-indexer.indexer_read_predefined", "mesh-delivery-indexer.indexer_read_seat_definitions", "seat:mesh-catalog.seat-definitions", "seat:mesh-config.renames", "seat:mesh-hand-acts.hand-act", "seat:node-engine.compose-candidate"}; !reflect.DeepEqual(sorted(m.Invokes), want) { @@ -138,7 +138,7 @@ func TestManifestDeclarations(t *testing.T) { // 1678/AC1 // The checker is woken by a build module's report of a toolchain, as the indexer is: the bucket may not exist when it starts. // And a group's check by the package registry seats' published of its tagged versions (design 51 *Tagged versions*). if want := []string{"go-package-registry.published", "mesh-build-go.toolchain.reported", "mesh-build-node.toolchain.reported", - "mesh-catalog.seat-definition-changed", "mesh-catalog.seat-definition-removed", "mesh-delivery-assigner.views.placement", "mesh-delivery-indexer.views.candidates", "mesh-delivery-indexer.views.manifests", "mesh-delivery-indexer.views.modules", + "mesh-catalog.seat-definition-changed", "mesh-catalog.seat-definition-removed", "mesh-delivery-assigner.views.assignments", "mesh-delivery-indexer.views.candidates", "mesh-delivery-indexer.views.manifests", "mesh-delivery-indexer.views.modules", "mesh-delivery-indexer.views.packages", "mesh-delivery-indexer.views.predefined", "mesh-delivery-indexer.views.seat-definitions", "npm-package-registry.published"}; !reflect.DeepEqual(sorted(m.Consumes), want) { t.Errorf("consumes = %v, want %v", m.Consumes, want) } diff --git a/modules/mesh-delivery-checker/module.json b/modules/mesh-delivery-checker/module.json index bb8440fb..9fb77e6c 100644 --- a/modules/mesh-delivery-checker/module.json +++ b/modules/mesh-delivery-checker/module.json @@ -19,7 +19,7 @@ "mesh-catalog.seat-definition-removed", "mesh-build-go.toolchain.reported", "mesh-build-node.toolchain.reported", - "mesh-delivery-assigner.views.placement", + "mesh-delivery-assigner.views.assignments", "mesh-delivery-indexer.views.candidates", "mesh-delivery-indexer.views.manifests", "mesh-delivery-indexer.views.modules", @@ -77,7 +77,7 @@ "gitea.gitea_compare", "gitea.gitea_get_file", "gitea.gitea_list_branches", - "mesh-delivery-assigner.assigner_read_placement", + "mesh-delivery-assigner.assigner_read_assignments", "mesh-delivery-indexer.indexer_read_candidates", "mesh-delivery-indexer.indexer_read_manifests", "mesh-delivery-indexer.indexer_read_modules", diff --git a/modules/mesh-delivery-forge-bridge/forgemsg/forge.go b/modules/mesh-delivery-forge-bridge/forgemsg/forge.go index 21e83f3f..822de445 100644 --- a/modules/mesh-delivery-forge-bridge/forgemsg/forge.go +++ b/modules/mesh-delivery-forge-bridge/forgemsg/forge.go @@ -143,11 +143,11 @@ type Forge struct { Act string `json:"act,omitempty"` Words string `json:"words,omitempty"` Open []OpenPull `json:"open,omitempty"` - // RetiredPlacements is the placement files a bridge before ADR 0567 carried (ADR 0549 §2, ADR 0559 §2): placement is - // the seat mesh-placement's state now and no repository holds it (ADR 0567 §11), so a message carrying them is + // RetiredAssignments is the assignments files a bridge before ADR 0567 carried (ADR 0549 §2, ADR 0559 §2): assignments is + // the seat mesh-module-assigner's state now and no repository holds it (ADR 0567 §11), so a message carrying them is // refused, loudly, rather than read with them dropped. - RetiredPlacements json.RawMessage `json:"placements,omitempty"` - RetiredRead bool `json:"placements-read,omitempty"` + RetiredAssignments json.RawMessage `json:"assignments,omitempty"` + RetiredRead bool `json:"assignments-read,omitempty"` // Predefined is the catalogue's predefined.json, exact, at the merge commit of a merged message (novox/hq ADR 0565 // §2): what the indexer keeps as its state `predefined`. PredefinedRead says the bridge read the merge and it holds // no predefined.json. Carried on a merged message only; an older bridge sends neither, and the indexer keeps what it @@ -226,9 +226,9 @@ func (f Forge) check() error { if len(f.Open) > 0 { return fmt.Errorf("delivery: only an open message names open pull requests") } - if len(f.RetiredPlacements) > 0 || f.RetiredRead { - return fmt.Errorf("delivery: a forge message carries placement files, which no repository holds since ADR 0567: " + - "placement is the seat mesh-placement's state") + if len(f.RetiredAssignments) > 0 || f.RetiredRead { + return fmt.Errorf("delivery: a forge message carries assignments files, which no repository holds since ADR 0567: " + + "assignments is the seat mesh-module-assigner's state") } if err := f.checkPredefined(); err != nil { return err diff --git a/modules/mesh-delivery-forge-bridge/forgemsg/forge_test.go b/modules/mesh-delivery-forge-bridge/forgemsg/forge_test.go index 9f2262cd..a94f4779 100644 --- a/modules/mesh-delivery-forge-bridge/forgemsg/forge_test.go +++ b/modules/mesh-delivery-forge-bridge/forgemsg/forge_test.go @@ -148,8 +148,8 @@ func TestForge(t *testing.T) { {"empty lock hash", `"a"`, in(func(f *Forge) { f.Locks["a"]["go.sum"] = "" })}, {"named paths outside trees", `"other"`, in(func(f *Forge) { f.Paths = map[string]map[string]string{"other": {"src": "o"}} })}, {"a named path with no object", `"src"`, in(func(f *Forge) { f.Paths = map[string]map[string]string{"a": {"src": ""}} })}, - {"placement files", "mesh-placement", in(func(f *Forge) { f.RetiredPlacements = []byte(`{"anchor":"{}"}`) })}, - {"placements read", "mesh-placement", in(func(f *Forge) { f.RetiredRead = true })}, + {"assignments files", "mesh-module-assigner", in(func(f *Forge) { f.RetiredAssignments = []byte(`{"anchor":"{}"}`) })}, + {"assignments read", "mesh-module-assigner", in(func(f *Forge) { f.RetiredRead = true })}, {"predefined not an object", "one JSON object", in(func(f *Forge) { f.Predefined = []byte(`["domain"]`) })}, {"predefined both", "says the merge holds none", in(func(f *Forge) { f.Predefined = []byte(`{}`); f.PredefinedRead = true })}, {"pull with the predefined list", "only a merged message", Forge{Kind: ForgePull, Repository: "novox/mesh-sdk", Number: 1, Head: "h", Action: PullOpened, PredefinedRead: true}}, diff --git a/modules/mesh-delivery-forge-bridge/internal/bridge/files_test.go b/modules/mesh-delivery-forge-bridge/internal/bridge/files_test.go index 1d2f6572..aab23cd9 100644 --- a/modules/mesh-delivery-forge-bridge/internal/bridge/files_test.go +++ b/modules/mesh-delivery-forge-bridge/internal/bridge/files_test.go @@ -84,7 +84,7 @@ func TestMergedCarriesManifestsAndLocks(t *testing.T) { // 1448/AC1 if len(f.Packages) != 0 || strings.Contains(string(msg.Data), `"packages"`) { t.Errorf("the merged message carries packages: %v", f.Packages) } - // ADR 0567: placement is the seat mesh-placement's: a merge of the catalogue carries no placement file and asks the + // ADR 0567: assignments is the seat mesh-module-assigner's: a merge of the catalogue carries no assignments file and asks the // forge for none. forge.calls = nil msg, err = Convert(mergedSubject, mergedBody(map[string]any{"repo": "mesh-catalog"}), treesOf(&calls, "r0", map[string]string{"modules/a": "t1"}, nil), forge.get) @@ -94,11 +94,11 @@ func TestMergedCarriesManifestsAndLocks(t *testing.T) { // 1448/AC1 if f, err = forgemsg.DecodeForge(msg.Data); err != nil { t.Fatal(err) } - if len(f.RetiredPlacements) > 0 || f.RetiredRead || strings.Contains(string(msg.Data), `"placements`) { - t.Errorf("a merge of the catalogue carries placement: %s", msg.Data) + if len(f.RetiredAssignments) > 0 || f.RetiredRead || strings.Contains(string(msg.Data), `"assignments`) { + t.Errorf("a merge of the catalogue carries assignments: %s", msg.Data) } for _, c := range forge.calls { - if strings.HasPrefix(c.path, "placement") { + if strings.HasPrefix(c.path, "assignments") { t.Errorf("the forge was asked for %s", c.path) } } diff --git a/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer-tools/holds.go b/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer-tools/holds.go index 50e069f3..8a2080d7 100644 --- a/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer-tools/holds.go +++ b/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer-tools/holds.go @@ -25,7 +25,7 @@ func holdsTool(read stdio.KeptState, ask asker, now func() time.Time, acts meshh ") which not every counted node-engine states, present or away (ADR 0624 §1). Per module: what it needs, which nodes " + "lack what (or why no node is counted yet), the manifest the modules state keeps and the one held, the merge that " + "brought it and since when. The hold moves by itself once every counted node-engine states it (on manifest-reads-changed, " + - "the placement's change or a merge). With module and ask_again, the engines of the nodes that lack it are asked again " + + "the assignments's change or a merge). With module and ask_again, the engines of the nodes that lack it are asked again " + "and the hold is weighed now; with module, release and why, it moves now by your word whatever the engines state (a hand " + "act, recorded through mesh-hand-acts, which names the nodes it strands: each node whose engine lacks the reading, an " + "away node from its return, reads the new entry without it and an engine before form:seat-reach finds no store). A hold " + diff --git a/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer-tools/views.go b/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer-tools/views.go index 00fe26e6..3b287093 100644 --- a/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer-tools/views.go +++ b/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer-tools/views.go @@ -29,7 +29,7 @@ func viewTools(getenv func(string) string, state func(string) views.Source, now for _, t := range index.ReadTools { out = append(out, views.ReadTool(index.Module, t.Tool, t.State, t.Words, state(t.State), marks, now)) } - // Its own first views, of the assigner's placement (DLV-F3r): read, and asked again by hand. + // Its own first views, of the assigner's assignments (DLV-F3r): read, and asked again by hand. return append(out, views.StatusTools(index.Module, "indexer_first_views", "indexer_first_view_again", stdio.State(views.StatusState), meshhandactsclient.Recorder{Ask: stdio.Ask}, now)...) } diff --git a/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/integration_test.go b/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/integration_test.go index 5aff24c4..a9e3c7c8 100644 --- a/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/integration_test.go +++ b/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/integration_test.go @@ -24,8 +24,8 @@ import ( "github.com/nats-io/nats.go" "github.com/nats-io/nats.go/jetstream" - "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" - "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" + "git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient" + "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" @@ -559,7 +559,7 @@ func startIndexerRun(t *testing.T, put func(context.Context, buildLog), before . b(ctx, js) } - serveAssignerPlacement(t, ctx, js, ctl) + serveAssignerAssignments(t, ctx, js, ctl) broker := filepath.Join(t.TempDir(), "broker") cred, _ := json.Marshal(process.Account{URL: srv.ClientURL(), Node: "n1", Module: "mesh-delivery-indexer", User: "n1.mesh-delivery-indexer", Password: "pw"}) @@ -978,33 +978,33 @@ func TestIndexerJudgesAnUnreadableVerdictAgainAtStart(t *testing.T) { // 1923/AC // TestIndexerCountsOnlyThePlacedBuildNodesToolchains (issue 1940, issue 774): the toolchains bucket holds the report of // n2, where the catalogue no longer places mesh-build-go, at another version than n1's, as ace's v26.12.0 stayed beside // the v26.12.1 of the nodes mesh-build-node runs on. The indexer gives the merged module a fingerprint from n1's -// toolchain alone; when the placement puts mesh-build-go on n2 again, its report written since counts and the builders +// toolchain alone; when the assignments puts mesh-build-go on n2 again, its report written since counts and the builders // disagree (the guard stays); when it is taken off again, the fingerprint comes back. No restart, no timer: the -// placement's watch (ADR 0549 §3), not the controller's assignments. +// assignments's watch (ADR 0549 §3), not the controller's assignments. func TestIndexerCountsOnlyThePlacedBuildNodesToolchains(t *testing.T) { manifest, err := os.ReadFile("../../internal/index/testdata/catalogue-a67f0196-avahi.module.json") if err != nil { t.Fatal(err) } const goSum = "3b5028f0c1e9bde04dd766abd63eb5931bc516bae695cf46f00abcd8510b0381" - var placement, toolchains jetstream.KeyValue + var assignments, toolchains jetstream.KeyValue place := func(ctx context.Context, t *testing.T, node string, modules ...string) { - p := meshmoduleassigner.Placement{Node: node} + p := meshmoduleassigner.Assignments{Node: node} for _, m := range modules { p.Modules = append(p.Modules, meshmoduleassigner.PlacedModule{Module: m}) } // Its node key, the commit that names the modules placed (research 072 row 37). - values, err := meshmoduleassignerclient.PlacementValues(p) + values, err := meshmoduleassignerclient.AssignmentsValues(p) if err != nil { t.Fatal(err) } - key := meshmoduleassigner.PlacementByNodeNodeKey(node) - if _, err := placement.Put(ctx, key, values[key]); err != nil { + key := meshmoduleassigner.AssignmentsByNodeNodeKey(node) + if _, err := assignments.Put(ctx, key, values[key]); err != nil { t.Fatal(err) } } // DLV-F5r (ADR 0621 §3): the assigner's runtime starts together with the indexer and answers its first view only after - // the indexer asked; within the bound the indexer follows the placement it answers (only n1's toolchain counts). + // the indexer asked; within the bound the indexer follows the assignments it answers (only n1's toolchain counts). up := make(chan struct{}) assignerLate = up t.Cleanup(func() { assignerLate = nil }) @@ -1024,8 +1024,8 @@ func TestIndexerCountsOnlyThePlacedBuildNodesToolchains(t *testing.T) { t.Fatal(err) } } - // The seat mesh-placement's placement, as the assigner writes it (ADR 0567): n1 places mesh-build-go, n2 nothing. - if placement, err = js.CreateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: index.PlacementBucket, History: index.PlacementHistory}); err != nil { + // The seat mesh-module-assigner's assignments, as the assigner writes it (ADR 0567): n1 places mesh-build-go, n2 nothing. + if assignments, err = js.CreateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: index.AssignmentsBucket, History: index.AssignmentsHistory}); err != nil { t.Fatal(err) } place(ctx, t, "n1", "mesh-build-go") @@ -1072,13 +1072,13 @@ func TestIndexerCountsOnlyThePlacedBuildNodesToolchains(t *testing.T) { if _, err := toolchains.Put(ctx, key, value); err != nil { t.Fatal(err) } - // n2's report, written since its placement, counts: the builders disagree. A fingerprint already computed is kept + // n2's report, written since its assignments, counts: the builders disagree. A fingerprint already computed is kept // (issue 1940), and only a new one needs the agreement: a merge moving avahi gets none until they agree. time.Sleep(500 * time.Millisecond) if e, err := modules.Get(ctx, "avahi"); err != nil { t.Fatal(err) } else if m, err := meshcatalogclient.DecodeModule(e.Value()); err != nil || m.Fp2 != held.Fp2 { - t.Fatalf("after n2's placement avahi has fp2 %q, none2 %q: want its fingerprint %q kept", m.Fp2, m.None2, held.Fp2) + t.Fatalf("after n2's assignments avahi has fp2 %q, none2 %q: want its fingerprint %q kept", m.Fp2, m.None2, held.Fp2) } moved := strings.Replace(string(data), `"newtree"`, `"movedtree"`, 1) if moved == string(data) { @@ -1091,11 +1091,11 @@ func TestIndexerCountsOnlyThePlacedBuildNodesToolchains(t *testing.T) { return m.Fp2 == "" && strings.HasPrefix(m.None2, "builders disagree: go1.27.2 recipe 1 on n2") } if m := read(disagree); !disagree(m) { - t.Fatalf("within 20 s of a merge after n2's placement avahi has fp2 %q, none2 %q: want the builders to disagree", m.Fp2, m.None2) + t.Fatalf("within 20 s of a merge after n2's assignments avahi has fp2 %q, none2 %q: want the builders to disagree", m.Fp2, m.None2) } place(ctx, t, "n2") if m := read(agreed); !agreed(m) { - t.Fatalf("within 20 s of n2's placement taken away avahi has fp2 %q, none2 %q", m.Fp2, m.None2) + t.Fatalf("within 20 s of n2's assignments taken away avahi has fp2 %q, none2 %q", m.Fp2, m.None2) } } @@ -1103,14 +1103,14 @@ func TestIndexerCountsOnlyThePlacedBuildNodesToolchains(t *testing.T) { // assigner's runtime started together with the indexer's, answering after the indexer asked (DLV-F5r). var assignerLate chan struct{} -// serveAssignerPlacement is the assigner as the owner of its placement (ADR 0615, ADR 0621), with the real owner's side: -// its first-view tool placement answered from the bucket at its mark (the bucket made empty where a test made none), +// serveAssignerAssignments is the assigner as the owner of its assignments (ADR 0615, ADR 0621), with the real owner's side: +// its first-view tool assignments answered from the bucket at its mark (the bucket made empty where a test made none), // and its views family said by the views emitter onto ASSIGNER_VIEWS, which the indexer's durable consumer reads. -func serveAssignerPlacement(t *testing.T, ctx context.Context, js jetstream.JetStream, nc *nats.Conn) { +func serveAssignerAssignments(t *testing.T, ctx context.Context, js jetstream.JetStream, nc *nats.Conn) { t.Helper() - placement, err := js.KeyValue(ctx, index.PlacementBucket) + assignments, err := js.KeyValue(ctx, index.AssignmentsBucket) if err != nil { - if placement, err = js.CreateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: index.PlacementBucket, History: index.PlacementHistory}); err != nil { + if assignments, err = js.CreateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: index.AssignmentsBucket, History: index.AssignmentsHistory}); err != nil { t.Fatal(err) } } @@ -1124,11 +1124,11 @@ func serveAssignerPlacement(t *testing.T, ctx context.Context, js jetstream.JetS } em := views.Emitter{Owner: index.AssignerModule, Subject: process.Account{Node: "n1", Module: index.AssignerModule}.EventSubject, Publish: views.JetStreamPublish(js), Marks: marks} - if err := em.Start(ctx, index.PlacementState, placement, nil); err != nil { + if err := em.Start(ctx, index.AssignmentsState, assignments, nil); err != nil { t.Fatal(err) } late := assignerLate - sub, err := nc.Subscribe("mesh.mod.mesh-delivery-assigner.call.placement.>", func(m *nats.Msg) { + sub, err := nc.Subscribe("mesh.mod.mesh-delivery-assigner.call.assignments.>", func(m *nats.Msg) { if late != nil { // its runtime comes up after the indexer started (DLV-F5r) select { case <-late: @@ -1136,7 +1136,7 @@ func serveAssignerPlacement(t *testing.T, ctx context.Context, js jetstream.JetS return } } - a, err := views.FirstView(index.AssignerModule, index.PlacementState, jsSource{ctx, placement}, jsSource{ctx, marks}, "", time.Now()) + a, err := views.FirstView(index.AssignerModule, index.AssignmentsState, jsSource{ctx, assignments}, jsSource{ctx, marks}, "", time.Now()) out := map[string]any{"result": a, "node": "n0"} if err != nil { out = map[string]any{"error": err.Error()} diff --git a/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/main.go b/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/main.go index 6e834cc1..b65f8036 100644 --- a/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/main.go +++ b/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/main.go @@ -472,10 +472,10 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro } } // Only the toolchain reports of nodes the catalogue places the build module on count (issue 1940, ADR 0549 §3): the - // placement is settled first, so the toolchains settle through the gate already knowing it. + // assignments is settled first, so the toolchains settle through the gate already knowing it. gate := buildrun.NewToolchainGate(sortedModules(), ix.PutToolchain, logger.Printf) // What each counted node's engine reads (ADR 0624 §1), for the mesh-wide hold of a manifest every node reads (ADR 0632 - // §2): asked through the node-engine seat's verb as the indexer's own account, for every node the placement names, + // §2): asked through the node-engine seat's verb as the indexer's own account, for every node the assignments names, // kept in the state engine-reads once it is bound; each change weighs the holds again. reads := newEngineReads(calls.CallOn, gate, func() { ix.KickHolds(ctx) }, logger.Printf) gate.OnPlaced = reads.placed @@ -506,14 +506,14 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro process.AwaitBucket(ctx, js, conn, index.EngineReadsBucket, "what each node's engine reads is kept in memory only", logger.Printf).Ready(func(kv jetstream.KeyValue) { reads.bind(ctx, kv, errs, logger.Printf) }) - // The placement is the assigner's: read through its first view and its views family, never its bucket (DLV-F1r). - placementErrs := make(chan error) + // The assignments is the assigner's: read through its first view and its views family, never its bucket (DLV-F1r). + assignmentsErrs := make(chan error) // The indexer's one pull of its durable on the shared events stream (process.EventHub): every handler is registered // before it starts. hub := process.NewEventHub(logger.Printf) - followPlacement(ctx, hub, cred, calls.Call, gate, func(ctx context.Context) { + followAssignments(ctx, hub, cred, calls.Call, gate, func(ctx context.Context) { if err := ix.Recompute(ctx); err != nil { - logger.Printf("writing the modules again after the placement of a build module changed: %v", err) + logger.Printf("writing the modules again after the assignments of a build module changed: %v", err) } }, findings, process.AwaitBucket(ctx, js, conn, firstViewsBucket, "indexer_first_views reads nothing and indexer_first_view_again is not heard", logger.Printf), logger.Printf) tools := newToolchainWatch(ctx, js, toolchainSink{ @@ -649,7 +649,7 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro if err := hub.Handle(numberedSubject, versions); err != nil { return err } - _ = notHeard.Bind(process.EventsStream, "no package's series moves, no node's manifest-reads-changed and no placement view is heard until the indexer starts again", func() error { + _ = notHeard.Bind(process.EventsStream, "no package's series moves, no node's manifest-reads-changed and no assignments view is heard until the indexer starts again", func() error { if err := hub.Start(ctx, js, cred); err != nil { stopped = cmp.Or(stopped, err) return err @@ -716,7 +716,7 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro return err case err := <-sourceErrs: return err - case err := <-placementErrs: + case err := <-assignmentsErrs: return err case err := <-tools.errs: return err diff --git a/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/reads.go b/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/reads.go index 5d675018..29a7c890 100644 --- a/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/reads.go +++ b/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/reads.go @@ -9,23 +9,23 @@ import ( "git.novox.be/novox/mesh-sdk/go/process" - "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/buildrun" "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient/enginereads" + "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/buildrun" "mesh-delivery-indexer/internal/index" ) // engineReads is what each counted node's engine states it reads, for the indexer's mesh-wide hold (index.Holds; novox/hq // ADR 0632 §2, ADR 0624 §1), in the shape the builder keeps it: asked through the node-engine seat's verb manifest-reads -// for every node the placement names, on the indexer's start, on the seat's event manifest-reads-changed for that node and +// for every node the assignments names, on the indexer's start, on the seat's event manifest-reads-changed for that node and // when a person asks again (indexer_engine_reads, indexer_holds); kept in the indexer's state engine-reads, one key per -// node, so a node that is away keeps its last statement. A node the placement no longer names is forgotten. Until the -// placement is read no node is counted, and every hold stays. +// node, so a node that is away keeps its last statement. A node the assignments no longer names is forgotten. Until the +// assignments is read no node is counted, and every hold stays. type engineReads struct { keeper *enginereads.Keeper gate *buildrun.ToolchainGate mu sync.Mutex - known bool // the placement is read + known bool // the assignments is read nodes []string } @@ -54,7 +54,7 @@ func (r *engineReads) bind(ctx context.Context, kv jetstream.KeyValue, errs chan r.ask(nodes, "the state engine-reads is bound") } -// placed takes the placement in, once it is read and after each change of it: a node it names newly is asked, one it no +// placed takes the assignments in, once it is read and after each change of it: a node it names newly is asked, one it no // longer names is forgotten, key and all. func (r *engineReads) placed() { nodes := r.gate.Nodes() @@ -64,7 +64,7 @@ func (r *engineReads) placed() { r.mu.Unlock() for _, node := range nodes { if first || !slices.Contains(before, node) { - r.keeper.AskNode(node, "counted: the placement names it") + r.keeper.AskNode(node, "counted: the assignments names it") } } for _, node := range before { @@ -94,7 +94,7 @@ func (r *engineReads) heard(m jetstream.Msg, logf func(string, ...any)) bool { } // counted is index.Indexer.Counted: what each counted node states (nil, the baseline, for one with no statement), and -// whether the placement is read. +// whether the assignments is read. func (r *engineReads) counted() (map[string][]string, bool) { r.mu.Lock() nodes, known := slices.Clone(r.nodes), r.known diff --git a/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/views.go b/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/views.go index 0f9eaea0..cbf5a6ab 100644 --- a/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/views.go +++ b/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer/views.go @@ -13,15 +13,15 @@ import ( "git.novox.be/novox/mesh-sdk/go/process" sdkfindings "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient/findings" - "git.novox.be/novox/mesh-delivery/sdk/graph" "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient/heard" + "git.novox.be/novox/mesh-delivery/sdk/graph" "git.novox.be/novox/mesh-sdk/go/views" "mesh-delivery-indexer/internal/index" ) // findingsBucket is the indexer's own findings, which the condition keeper raises and clears (ADR 0564 §2): a value too -// large for its views event, and its first view of the assigner's placement unanswered past its bound. +// large for its views event, and its first view of the assigner's assignments unanswered past its bound. var findingsBucket = mcc.FindingsBucket(index.Module) // readyOf binds a state's bucket: now for one bound at the start, once it appears for one awaited. @@ -109,15 +109,15 @@ const graphState = graph.State // where indexer_first_view_again asks. var firstViewsBucket = index.Module + "_" + views.StatusState -// placementTool is the assigner's first-view tool of its placement, as the indexer's invokes names it (ADR 0621 §2). -const placementTool = index.AssignerModule + "." + index.PlacementState +// assignmentsTool is the assigner's first-view tool of its assignments, as the indexer's invokes names it (ADR 0621 §2). +const assignmentsTool = index.AssignerModule + "." + index.AssignmentsState -// followPlacement reads the seat mesh-placement's placement through the assigner's first view and then its views +// followAssignments reads the seat mesh-module-assigner's assignments through the assigner's first view and then its views // family, never its bucket (ADR 0615 §1, DLV-F1r), into gate: each key as it changes, settled once answered, with // changed told whenever what the gate passes on changed. It waits for the first answer at most views.Bound, so a // normal start, the two owners coming up together, computes the fingerprints once; past it the indexer goes on, the gate -// passing every report, and raises its condition naming the assigner and placement until the answer clears it. -func followPlacement(ctx context.Context, hub *process.EventHub, cred process.Account, call func(string, any) (json.RawMessage, error), +// passing every report, and raises its condition naming the assigner and assignments until the answer clears it. +func followAssignments(ctx context.Context, hub *process.EventHub, cred process.Account, call func(string, any) (json.RawMessage, error), gate *buildrun.ToolchainGate, changed func(context.Context), findings, status *process.AwaitedBucket, logf func(string, ...any)) *views.Follower { finding := func(s views.Status, raised bool) { w := sdkfindings.Writer(ctx, findings.KV) @@ -133,11 +133,11 @@ func followPlacement(ctx context.Context, hub *process.EventHub, cred process.Ac } } var settled atomic.Bool // the first view answered: the gate's nodes are the counted ones from now on - f := &views.Follower{Owner: index.AssignerModule, Tool: placementTool, State: index.PlacementState, - Ask: views.AskVia(call, placementTool), Logf: logf, + f := &views.Follower{Owner: index.AssignerModule, Tool: assignmentsTool, State: index.AssignmentsState, + Ask: views.AskVia(call, assignmentsTool), Logf: logf, Raise: func(s views.Status) { finding(s, true) }, Clear: func(s views.Status) { finding(s, false) }, Changed: func(key string, value json.RawMessage, deleted bool, at time.Time) { - if gate.Placement(key, value, deleted, at) { + if gate.Assignments(key, value, deleted, at) { changed(ctx) } if settled.Load() && gate.OnPlaced != nil { @@ -160,21 +160,21 @@ func followPlacement(ctx context.Context, hub *process.EventHub, cred process.Ac } settled.Store(true) if gate.OnPlaced != nil { - gate.OnPlaced() // the placement is read: its nodes are the counted ones + gate.OnPlaced() // the assignments is read: its nodes are the counted ones } close(answered) }() // The assigner's views family, on the module's one hub (the host starts it once every handler is registered); a family the // hub cannot register is the finding family-not-heard until the indexer starts again (ADR 0615 §5). - _ = notHeardWriter(ctx, findings, logf).Bind(process.EventsStream, "the placement is the first view's alone, asked again by hand, until the indexer starts again", func() error { - return hub.Handle("mesh.mod."+index.AssignerModule+".event."+views.EventPrefix+"*", views.Hear(ctx, map[string]*views.Follower{index.PlacementState: f}, logf)) + _ = notHeardWriter(ctx, findings, logf).Bind(process.EventsStream, "the assignments is the first view's alone, asked again by hand, until the indexer starts again", func() error { + return hub.Handle("mesh.mod."+index.AssignerModule+".event."+views.EventPrefix+"*", views.Hear(ctx, map[string]*views.Follower{index.AssignmentsState: f}, logf)) }) wait, cancel := context.WithTimeout(ctx, views.Bound) // a context's bound, no timer (ADR 0437 §8) defer cancel() select { case <-answered: case <-wait.Done(): - logf("the assigner has not answered placement within %s: the indexer goes on, every node's toolchain report counting until it answers", views.Bound) + logf("the assigner has not answered assignments within %s: the indexer goes on, every node's toolchain report counting until it answers", views.Bound) } return f } diff --git a/modules/mesh-delivery-indexer/go.mod b/modules/mesh-delivery-indexer/go.mod index 59d43565..1f4b6483 100644 --- a/modules/mesh-delivery-indexer/go.mod +++ b/modules/mesh-delivery-indexer/go.mod @@ -5,8 +5,8 @@ go 1.26.0 require ( git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshhandactsclient v0.0.0-00010101000000-000000000000 - git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient v0.0.0-00010101000000-000000000000 - git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliverycheckerclient v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient v0.0.0-00010101000000-000000000000 diff --git a/modules/mesh-delivery-indexer/internal/index/candidates.go b/modules/mesh-delivery-indexer/internal/index/candidates.go index 7ea7193f..bb003573 100644 --- a/modules/mesh-delivery-indexer/internal/index/candidates.go +++ b/modules/mesh-delivery-indexer/internal/index/candidates.go @@ -129,7 +129,7 @@ type Indexer struct { // capability read mesh-wide, waiting for every counted node-engine to state it. Nil, or Counted nil, holds nothing. Holds Store // Counted is what each counted node's engine states it reads (ADR 0624 §1; nil, the baseline, for a node with no - // statement), and whether the counted nodes are known (the placement is read). + // statement), and whether the counted nodes are known (the assignments is read). Counted func() (map[string][]string, bool) // AskEngines asks these nodes' engines what they read again (the node-engine seat's verb manifest-reads); nil asks none. AskEngines func(nodes []string, why string) diff --git a/modules/mesh-delivery-indexer/internal/index/holds.go b/modules/mesh-delivery-indexer/internal/index/holds.go index 68d530b3..6fe11414 100644 --- a/modules/mesh-delivery-indexer/internal/index/holds.go +++ b/modules/mesh-delivery-indexer/internal/index/holds.go @@ -28,10 +28,10 @@ import ( // it is written as its merge would have written it, judged and said. The assigner's hold of ADR 0614 §4 holds a want on // one node; this one holds the entry itself, which every node reads. // -// "Counted" is ADR 0624 §1's set: every node the placement names, present or away, each read as its engine last stated +// "Counted" is ADR 0624 §1's set: every node the assignments names, present or away, each read as its engine last stated // through the node-engine seat's verb manifest-reads (a node with no statement read as the baseline; the indexer's state -// engine-reads, nodeengineclient/enginereads). Until the placement is read no node is counted and every hold stays. The hold moves -// on the event manifest-reads-changed, the placement's change, a person's ask again, and a merge; never on a timer. +// engine-reads, nodeengineclient/enginereads). Until the assignments is read no node is counted and every hold stays. The hold moves +// on the event manifest-reads-changed, the assignments's change, a person's ask again, and a merge; never on a timer. const ( // HoldsState is the indexer's state of held entries: the bucket mesh-delivery-indexer_holds, keyed as the modules state. HoldsState = "holds" @@ -126,7 +126,7 @@ func meshWide(needs []string) []string { } // noneCounted is why a hold stays while no node is counted. -const noneCounted = "no node is counted yet (the placement is not read), so the hold stays until it is" +const noneCounted = "no node is counted yet (the assignments is not read), so the hold stays until it is" // lacking is, of the counted nodes, each whose engine lacks some of needs, with what it lacks; or why no node is counted. func (ix *Indexer) lacking(needs []string) (map[string][]string, string) { @@ -232,7 +232,7 @@ func (ix *Indexer) dropHold(ctx context.Context, key, why string) error { } // KickHolds moves every hold whose needs every counted node-engine now states, in the background, once at a time: on a -// node's statement changed, the placement's change and the holds state's binding. +// node's statement changed, the assignments's change and the holds state's binding. func (ix *Indexer) KickHolds(ctx context.Context) { if ix.Holds == nil || ix.Counted == nil { return diff --git a/modules/mesh-delivery-indexer/internal/index/names.go b/modules/mesh-delivery-indexer/internal/index/names.go index 3e35db84..6d950393 100644 --- a/modules/mesh-delivery-indexer/internal/index/names.go +++ b/modules/mesh-delivery-indexer/internal/index/names.go @@ -37,9 +37,9 @@ const ( CheckerModule = "mesh-delivery-checker" BuildGoModule = "mesh-build-go" BuildNodeModule = "mesh-build-node" - PlacementState = "placement" - PlacementBucket = AssignerModule + "_" + PlacementState - PlacementHistory = 5 + AssignmentsState = "assignments" + AssignmentsBucket = AssignerModule + "_" + AssignmentsState + AssignmentsHistory = 5 SourceBucket = GateModule + "_source" PreviewsBucket = CheckerModule + "_previews" PreviewsHistory = 5 diff --git a/modules/mesh-delivery-indexer/judge/oldsections.go b/modules/mesh-delivery-indexer/judge/oldsections.go index b3aa5709..91a2446d 100644 --- a/modules/mesh-delivery-indexer/judge/oldsections.go +++ b/modules/mesh-delivery-indexer/judge/oldsections.go @@ -17,7 +17,7 @@ import ( var OldSections = []string{"state", "logs", "streams", "objects"} // Foundation are the modules that keep using the old sections for good: exactly the nine parts the bus module's own -// composition, placement, build or delivery needs before it can answer (novox/hq ADR 0619 §1, §3): the bus module, the +// composition, assignments, build or delivery needs before it can answer (novox/hq ADR 0619 §1, §3): the bus module, the // grants part, the indexer, the assigner, the tool runner, the builder, the checker, mesh-config and the vault. Their new // state goes on their old sections too, so a foundation manifest asking bus state in the new form is refused. var Foundation = []string{"nats", "mesh-grants", "mesh-delivery-indexer", "mesh-delivery-assigner", "node-tools", diff --git a/modules/mesh-delivery-indexer/manifest_test.go b/modules/mesh-delivery-indexer/manifest_test.go index 61a5d44f..fd6d2504 100644 --- a/modules/mesh-delivery-indexer/manifest_test.go +++ b/modules/mesh-delivery-indexer/manifest_test.go @@ -106,7 +106,7 @@ func TestManifestDeclarations(t *testing.T) { // 1238/AC1, 1444 (reads the build if m.Module != "mesh-delivery-indexer" { t.Errorf("module = %q", m.Module) } - // 1126: placement is the seat mesh-placement's (ADR 0567): the indexer holds no placement state. ADR 0578 §1: it says + // 1126: assignments is the seat mesh-module-assigner's (ADR 0567): the indexer holds no assignments state. ADR 0578 §1: it says // a refused manifest as manifest-refused, and (issue 1966 D3, 1966/AC10) a newly accepted one asking for bus state as // manifest-accepted, both events of the seat mesh-catalog it holds (ADR 0636 §4), so not among its `emits`. if own := slices.DeleteFunc(slices.Clone(m.Emits), func(e string) bool { return strings.HasPrefix(e, views.EventPrefix) }); !reflect.DeepEqual(own, []string{takein.TakeInPendingEvent, takein.TakeInTakenEvent}) { @@ -136,14 +136,14 @@ func TestManifestDeclarations(t *testing.T) { // 1238/AC1, 1444 (reads the build t.Errorf("the indexer reads the bucket %s, its manifest's reads are %v (want %s)", bucket, m.Reads, read) } } - // DLV-F1r (ADR 0615 §1): the assigner's placement is read through its first view and views family, never its state. + // DLV-F1r (ADR 0615 §1): the assigner's assignments is read through its first view and views family, never its state. if want := []string{"mesh-build-go.toolchains", "mesh-build-node.toolchains", "mesh-delivery-builder.build-log", "mesh-delivery-checker.previews", "mesh-delivery-gate.source"}; !reflect.DeepEqual(sorted(m.Reads), want) { t.Errorf("reads = %v, want %v", m.Reads, want) } // "Reads FORGE" is a consumes of the indexer's stream: the broker grants only buckets by reads (mesh-control // internal/broker/state.go:62-93). // The registry seats' published and the builder's numbered fill each package's series (DLV-I5, design 51 *The versions*). - if want := []string{"go-package-registry.published", "mesh-build-go.toolchain.reported", "mesh-build-node.toolchain.reported", "mesh-delivery-assigner.views.placement", + if want := []string{"go-package-registry.published", "mesh-build-go.toolchain.reported", "mesh-build-node.toolchain.reported", "mesh-delivery-assigner.views.assignments", "mesh-delivery-builder.version.numbered", "mesh-delivery-checker.preview.written", "mesh-delivery-checker.try.ended", "mesh-delivery-checker.try.started", "mesh-delivery-checker.verdict", "mesh-delivery-forge-bridge.forge.**", "node-engine.manifest-reads-changed", "npm-package-registry.published"}; !reflect.DeepEqual(sorted(m.Consumes), want) { // ADR 0632 §2: the counted engines' reads t.Errorf("consumes = %v, want %v", m.Consumes, want) @@ -157,7 +157,7 @@ func TestManifestDeclarations(t *testing.T) { // 1238/AC1, 1444 (reads the build // Three invokes: the file of a registered module's manifest and lock files (registered.go), the forge's trees (the named paths' objects, since the indexer is the one reader of a manifest's paths: // ADR 0499 clauses 2 and 3, ADR 0495 clause 1; and the module directories of the trunk, issue 1900) and whether a // commit is in another (issue 1900). - if want := []string{"mesh-delivery-assigner.placement", "gitea.gitea_commit_trees", "gitea.gitea_contains", "gitea.gitea_get_file", "gitea.gitea_open_pulls", "mesh-delivery-checker.checker_tries", "mesh-delivery-forge-bridge.bridge_merged_message", "seat:node-engine.composition", "seat:node-engine.manifest-reads", "seat:mesh-hand-acts.hand-act"}; !reflect.DeepEqual(m.Invokes, want) { + if want := []string{"mesh-delivery-assigner.assignments", "gitea.gitea_commit_trees", "gitea.gitea_contains", "gitea.gitea_get_file", "gitea.gitea_open_pulls", "mesh-delivery-checker.checker_tries", "mesh-delivery-forge-bridge.bridge_merged_message", "seat:node-engine.composition", "seat:node-engine.manifest-reads", "seat:mesh-hand-acts.hand-act"}; !reflect.DeepEqual(m.Invokes, want) { t.Errorf("invokes = %v, want %v", m.Invokes, want) } // 1945: indexer_take_in, served by its tools bundle through the takeins state; the process serves none. The same bundle diff --git a/modules/mesh-delivery-indexer/module.json b/modules/mesh-delivery-indexer/module.json index 4123fc8f..f36f1ae7 100644 --- a/modules/mesh-delivery-indexer/module.json +++ b/modules/mesh-delivery-indexer/module.json @@ -60,7 +60,7 @@ "go-package-registry.published", "mesh-build-go.toolchain.reported", "mesh-build-node.toolchain.reported", - "mesh-delivery-assigner.views.placement", + "mesh-delivery-assigner.views.assignments", "mesh-delivery-builder.version.numbered", "mesh-delivery-checker.preview.written", "mesh-delivery-checker.try.ended", @@ -119,7 +119,7 @@ "mesh-delivery-gate.source" ], "invokes": [ - "mesh-delivery-assigner.placement", + "mesh-delivery-assigner.assignments", "gitea.gitea_commit_trees", "gitea.gitea_contains", "gitea.gitea_get_file", diff --git a/modules/mesh-delivery-teller/go.mod b/modules/mesh-delivery-teller/go.mod index 2a7f2547..789aa9ab 100644 --- a/modules/mesh-delivery-teller/go.mod +++ b/modules/mesh-delivery-teller/go.mod @@ -40,8 +40,8 @@ require ( git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient v0.0.0-00010101000000-000000000000 // indirect git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshconditions v0.0.0-00010101000000-000000000000 // indirect git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshhandacts v0.0.0-00010101000000-000000000000 // indirect - git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient v0.0.0-00010101000000-000000000000 // indirect - git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner v0.0.0-00010101000000-000000000000 // indirect + git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient v0.0.0-00010101000000-000000000000 // indirect + git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner v0.0.0-00010101000000-000000000000 // indirect git.novox.be/novox/mesh-delivery/contracts/go/clients/containerimageregistryclient v0.0.0-00010101000000-000000000000 // indirect git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient v0.0.0-00010101000000-000000000000 // indirect git.novox.be/novox/mesh-delivery/contracts/go/seats/containerimageregistry v0.0.0-00010101000000-000000000000 // indirect diff --git a/modules/mesh-delivery-teller/internal/ends/ends.go b/modules/mesh-delivery-teller/internal/ends/ends.go index edeb147c..f9bd4ae0 100644 --- a/modules/mesh-delivery-teller/internal/ends/ends.go +++ b/modules/mesh-delivery-teller/internal/ends/ends.go @@ -2,7 +2,7 @@ // how a module is spoken to"). An ask the teller holds open at the router keeps one of the three places the router gives // its asker (asks.MostOpen) until it is answered, it lapses, or the gate or the assigner takes its key away. When that last // never comes (on 2026-10-31 two merge asks of pull requests merged by another path held two places for a day, and a -// placement ask was refused for want of room), a person ends the ask by hand. The teller's process serves no request +// assignments ask was refused for want of room), a person ends the ask by hand. The teller's process serves no request // subject (ADR 0437 §4), so the tool writes an end into the teller's state ends, which the teller watches, carries out // and answers there, as the indexer answers indexer_take_in. package ends diff --git a/modules/mesh-delivery-view/cmd/mesh-delivery-cli/main_test.go b/modules/mesh-delivery-view/cmd/mesh-delivery-cli/main_test.go index 47a4ebf5..93dba240 100644 --- a/modules/mesh-delivery-view/cmd/mesh-delivery-cli/main_test.go +++ b/modules/mesh-delivery-view/cmd/mesh-delivery-cli/main_test.go @@ -4,20 +4,22 @@ import ( "bytes" "context" "encoding/json" + "maps" "mesh-delivery-view/internal/view" "os" "path/filepath" + "slices" "strings" "testing" "time" + "git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient" + "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" occ "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/clients/operatorchannelclient" oc "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel" - "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" - "git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient" - "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient" + "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdelivery" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-assigner/asks" @@ -27,11 +29,13 @@ import ( func serve(t *testing.T) string { t.Helper() m := view.New() - placed, err := meshmoduleassignerclient.EncodePlacement(meshmoduleassigner.Placement{Node: "a", Modules: []meshmoduleassigner.PlacedModule{{Module: "x"}}}) + placed, err := meshmoduleassignerclient.AssignmentsValues(meshmoduleassigner.Assignments{Node: "a", Modules: []meshmoduleassigner.PlacedModule{{Module: "x"}}}) if err != nil { t.Fatal(err) } - m.Apply(view.Entry{Bucket: view.PlacementBucket, Key: "placement.a", Value: placed}) + for _, k := range slices.Sorted(maps.Keys(placed)) { // a key per module, then the node key + m.Apply(view.Entry{Bucket: view.AssignmentsBucket, Key: k, Value: placed[k]}) + } dir, err := os.MkdirTemp(os.Getenv("TMPDIR"), "c") if err != nil { t.Fatal(err) diff --git a/modules/mesh-delivery-view/cmd/mesh-delivery-view-tools/tiers_rows_test.go b/modules/mesh-delivery-view/cmd/mesh-delivery-view-tools/tiers_rows_test.go index b43db756..94ead707 100644 --- a/modules/mesh-delivery-view/cmd/mesh-delivery-view-tools/tiers_rows_test.go +++ b/modules/mesh-delivery-view/cmd/mesh-delivery-view-tools/tiers_rows_test.go @@ -13,9 +13,9 @@ import ( "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" "git.novox.be/novox/mesh-contracts/go/seats/nodeengine" "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshwatchdogsclient" - "git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient" "git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" + "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-indexer/takein" diff --git a/modules/mesh-delivery-view/cmd/mesh-delivery-view/integration_test.go b/modules/mesh-delivery-view/cmd/mesh-delivery-view/integration_test.go index 9b1a4fd8..cd326086 100644 --- a/modules/mesh-delivery-view/cmd/mesh-delivery-view/integration_test.go +++ b/modules/mesh-delivery-view/cmd/mesh-delivery-view/integration_test.go @@ -6,23 +6,25 @@ import ( "context" "encoding/json" "io" + "maps" "mesh-delivery-view/internal/view" "os" "path/filepath" + "slices" "strings" "testing" "time" + "git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient" "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" + "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-contracts/go/seats/nodeengine" "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshwatchdogsclient" occ "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/clients/operatorchannelclient" oc "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel" - "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" - "git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient" - "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient" + "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdelivery" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-assigner/asks" @@ -70,7 +72,7 @@ func TestProcessWholeSeesTheBuckets(t *testing.T) { // 1134/AC8 ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) defer cancel() kvs := map[string]jetstream.KeyValue{} - for _, b := range []string{view.HaveBucket, view.PlacementBucket, view.PresenceBucket, view.StuckBucket} { + for _, b := range []string{view.HaveBucket, view.AssignmentsBucket, view.PresenceBucket, view.StuckBucket} { kv, err := js.CreateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: b, History: 5}) if err != nil { t.Fatal(err) @@ -90,20 +92,22 @@ func TestProcessWholeSeesTheBuckets(t *testing.T) { // 1134/AC8 } return b } - // The seat's placement (issue 774, ADR 0549 §3): x and y on a, nothing on b. - placeValue := func(node string, modules ...string) []byte { - p := meshmoduleassigner.Placement{Node: node} + // The seat's assignments (issue 774, ADR 0549 §3): x and y on a, nothing on b. + place := func(node string, modules ...string) { + p := meshmoduleassigner.Assignments{Node: node} for _, m := range modules { p.Modules = append(p.Modules, meshmoduleassigner.PlacedModule{Module: m}) } - v, err := meshmoduleassignerclient.EncodePlacement(p) + values, err := meshmoduleassignerclient.AssignmentsValues(p) if err != nil { t.Fatal(err) } - return v + for _, k := range slices.Sorted(maps.Keys(values)) { // a key per module, then the node key + put(view.AssignmentsBucket, k, values[k]) + } } - put(view.PlacementBucket, "placement.a", placeValue("a", "x", "y")) - put(view.PlacementBucket, "placement.b", placeValue("b")) + place("a", "x", "y") + place("b") put(view.HaveBucket, "a.x", haveValue("b1", nodeengineclient.PhaseChecking, nodeengineclient.HealthWaits)) put(view.HaveBucket, "b.x", haveValue("b1", nodeengineclient.PhaseRunning, nodeengineclient.HealthOK)) @@ -182,7 +186,7 @@ func TestProcessWholeShowsWhatTheWatchmanSays(t *testing.T) { // 1168/AC5 ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) defer cancel() kvs := map[string]jetstream.KeyValue{} - for _, b := range []string{view.HaveBucket, view.PlacementBucket, view.PresenceBucket, view.StuckBucket} { + for _, b := range []string{view.HaveBucket, view.AssignmentsBucket, view.PresenceBucket, view.StuckBucket} { kv, err := js.CreateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: b, History: 5}) if err != nil { t.Fatal(err) @@ -269,7 +273,7 @@ func TestProcessWholeShowsTheIndexersBucketsOnceTheyExist(t *testing.T) { // 124 } ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) defer cancel() - for _, b := range []string{view.HaveBucket, view.PlacementBucket, view.PresenceBucket, view.StuckBucket} { + for _, b := range []string{view.HaveBucket, view.AssignmentsBucket, view.PresenceBucket, view.StuckBucket} { if _, err := js.CreateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: b, History: 5}); err != nil { t.Fatal(err) } diff --git a/modules/mesh-delivery-view/cmd/mesh-delivery-view/main.go b/modules/mesh-delivery-view/cmd/mesh-delivery-view/main.go index 7045279f..471875c1 100644 --- a/modules/mesh-delivery-view/cmd/mesh-delivery-view/main.go +++ b/modules/mesh-delivery-view/cmd/mesh-delivery-view/main.go @@ -1,4 +1,4 @@ -// Command mesh-delivery-view is the operator's view of delivery (novox/hq ADR 0429 §3): it watches the have, the seat's placement +// Command mesh-delivery-view is the operator's view of delivery (novox/hq ADR 0429 §3): it watches the have, the seat's assignments // and builds buckets on its own bus credential, derives one row per module and node in memory, and serves them on a // local socket (for mesh-delivery-cli) and on a local web page. It starts, stops or retries nothing; the one thing it writes is the operator's answer to an ask that needs no proof of who // answered, under `.view` in its bucket answers (ADR 0528 clause 6). @@ -21,9 +21,9 @@ import ( "syscall" "time" + sdkfindings "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient/findings" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-view/viewstate" - sdkfindings "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient/findings" "git.novox.be/novox/mesh-delivery/sdk/trunk" "git.novox.be/novox/mesh-sdk/go/process" "git.novox.be/novox/mesh-sdk/go/views" @@ -126,7 +126,7 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro } } open := buckets{js: js, reader: reader} - for _, bucket := range []string{view.HaveBucket, view.PlacementBucket, view.PresenceBucket, view.StuckBucket, view.CandidatesBucket, view.ModulesBucket, view.AsksBucket, view.AnswersBucket, builds.BuilderBuildsBucket, view.AssignerWantsBucket, view.SourceBucket, view.AssignerAsksBucket, viewstate.ViewAnswersBucket, view.TakenBucket} { + for _, bucket := range []string{view.HaveBucket, view.AssignmentsBucket, view.PresenceBucket, view.StuckBucket, view.CandidatesBucket, view.ModulesBucket, view.AsksBucket, view.AnswersBucket, builds.BuilderBuildsBucket, view.AssignerWantsBucket, view.SourceBucket, view.AssignerAsksBucket, viewstate.ViewAnswersBucket, view.TakenBucket} { go follow(ctx, open, bucket, false, model, logger) } // The modules' and the source's history give each fingerprint's first commit and each head's source revision (ADR 0556). @@ -185,7 +185,7 @@ var ( // owner's read tool (as the view's invokes names it) and views family (its events on the shared events stream); the // view's tools ask the other six through the owners' read tools on each call. var ownersFollowed = []struct{ owner, state, tool string }{ - {view.AssignerModule, view.PlacementState, view.AssignerModule + ".assigner_read_placement"}, + {view.AssignerModule, view.AssignmentsState, view.AssignerModule + ".assigner_read_assignments"}, {view.AssignerModule, "wants", view.AssignerModule + ".assigner_read_wants"}, {view.AssignerModule, "asks", view.AssignerModule + ".assigner_read_asks"}, {view.IndexerModule, "candidates", view.IndexerModule + ".indexer_read_candidates"}, diff --git a/modules/mesh-delivery-view/go.mod b/modules/mesh-delivery-view/go.mod index 2eec5519..4af9cad7 100644 --- a/modules/mesh-delivery-view/go.mod +++ b/modules/mesh-delivery-view/go.mod @@ -7,9 +7,9 @@ require ( git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshwatchdogsclient v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-contracts/identity-and-access/go/clients/meshvaultclient v0.0.0-00010101000000-000000000000 - git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient v0.0.0-00010101000000-000000000000 - git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient v0.0.0-00010101000000-000000000000 - git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-delivery/contracts/go/clients/meshwantsclient v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog v0.0.0-00010101000000-000000000000 @@ -58,7 +58,7 @@ require ( git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshhandacts v0.0.0-00010101000000-000000000000 // indirect git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshwatchdogs v0.0.0-00010101000000-000000000000 // indirect git.novox.be/novox/mesh-contracts/identity-and-access/go/seats/meshvault v0.0.0-00010101000000-000000000000 // indirect - git.novox.be/novox/mesh-contracts/placement/go/seats/meshwants v0.0.0-00010101000000-000000000000 // indirect + git.novox.be/novox/mesh-delivery/contracts/go/seats/meshwants v0.0.0-00010101000000-000000000000 // indirect git.novox.be/novox/mesh-delivery/contracts/go/clients/containerimageregistryclient v0.0.0-00010101000000-000000000000 // indirect git.novox.be/novox/mesh-delivery/contracts/go/seats/containerimageregistry v0.0.0-00010101000000-000000000000 // indirect git.novox.be/novox/mesh-delivery/contracts/go/seats/ociartifactregistry v0.0.0-00010101000000-000000000000 // indirect diff --git a/modules/mesh-delivery-view/internal/view/holds.go b/modules/mesh-delivery-view/internal/view/holds.go index dccb6b98..9819e9d4 100644 --- a/modules/mesh-delivery-view/internal/view/holds.go +++ b/modules/mesh-delivery-view/internal/view/holds.go @@ -11,8 +11,8 @@ import ( "time" oc "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel" - "git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" + "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-assigner/asks" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-view/viewstate" ) diff --git a/modules/mesh-delivery-view/internal/view/holds_test.go b/modules/mesh-delivery-view/internal/view/holds_test.go index 2d086fd4..e16977f2 100644 --- a/modules/mesh-delivery-view/internal/view/holds_test.go +++ b/modules/mesh-delivery-view/internal/view/holds_test.go @@ -4,9 +4,9 @@ import ( "strings" "testing" - "git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient" + "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-view/viewstate" ) diff --git a/modules/mesh-delivery-view/internal/view/motion.go b/modules/mesh-delivery-view/internal/view/motion.go index b2806c0d..09b517e1 100644 --- a/modules/mesh-delivery-view/internal/view/motion.go +++ b/modules/mesh-delivery-view/internal/view/motion.go @@ -2,7 +2,7 @@ package view import ( "git.novox.be/novox/mesh-contracts/go/seats/nodeengine" - "git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient" + "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshwantsclient" "git.novox.be/novox/mesh-sdk/go/motion" ) diff --git a/modules/mesh-delivery-view/internal/view/names.go b/modules/mesh-delivery-view/internal/view/names.go index cf3698ce..0fc71ca2 100644 --- a/modules/mesh-delivery-view/internal/view/names.go +++ b/modules/mesh-delivery-view/internal/view/names.go @@ -35,8 +35,8 @@ const ( IndexerConfigCandidatesState = "config-candidates" // The assigner's states. - PlacementState = "placement" - PlacementBucket = AssignerModule + "_" + PlacementState + AssignmentsState = "assignments" + AssignmentsBucket = AssignerModule + "_" + AssignmentsState AssignerWantsBucket = AssignerModule + "_wants" AssignerAsksBucket = AssignerModule + "_asks" diff --git a/modules/mesh-delivery-view/internal/view/reach_test.go b/modules/mesh-delivery-view/internal/view/reach_test.go index d3dfd415..67dc5d06 100644 --- a/modules/mesh-delivery-view/internal/view/reach_test.go +++ b/modules/mesh-delivery-view/internal/view/reach_test.go @@ -7,9 +7,9 @@ import ( "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" "git.novox.be/novox/mesh-contracts/go/seats/nodeengine" - "git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient" + "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdelivery" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-indexer/takein" diff --git a/modules/mesh-delivery-view/internal/view/read.go b/modules/mesh-delivery-view/internal/view/read.go index d7e2825c..63d09362 100644 --- a/modules/mesh-delivery-view/internal/view/read.go +++ b/modules/mesh-delivery-view/internal/view/read.go @@ -16,8 +16,8 @@ import ( "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" "git.novox.be/novox/mesh-contracts/identity-and-access/go/clients/meshvaultclient" - "git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" + "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-indexer/takein" diff --git a/modules/mesh-delivery-view/internal/view/status.go b/modules/mesh-delivery-view/internal/view/status.go index bb5c5e41..9cac369f 100644 --- a/modules/mesh-delivery-view/internal/view/status.go +++ b/modules/mesh-delivery-view/internal/view/status.go @@ -12,7 +12,7 @@ import ( "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshwatchdogsclient" mcseat "git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshconditions" healers "git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshhealers" - "git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient" + "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshwantsclient" "git.novox.be/novox/mesh-sdk/go/motion" ) diff --git a/modules/mesh-delivery-view/internal/view/tiers.go b/modules/mesh-delivery-view/internal/view/tiers.go index dab12820..a579c044 100644 --- a/modules/mesh-delivery-view/internal/view/tiers.go +++ b/modules/mesh-delivery-view/internal/view/tiers.go @@ -11,10 +11,10 @@ import ( "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshwatchdogsclient" - "git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient" "git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient" + "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-indexer/takein" diff --git a/modules/mesh-delivery-view/internal/view/view.go b/modules/mesh-delivery-view/internal/view/view.go index cb4bc432..35f1c077 100644 --- a/modules/mesh-delivery-view/internal/view/view.go +++ b/modules/mesh-delivery-view/internal/view/view.go @@ -11,15 +11,15 @@ import ( "sync" "time" + "git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient" "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" "git.novox.be/novox/mesh-contracts/go/seats/nodeengine" "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshwatchdogsclient" occ "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/clients/operatorchannelclient" oc "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel" - "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" - "git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient" + "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdelivery" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-assigner/asks" @@ -235,7 +235,7 @@ type UnreadableRow struct { // Model holds the rows' inputs in memory and nothing else: a restart fills it again from the buckets' last values. type Model struct { mu sync.Mutex - placed *meshmoduleassignerclient.Placed // the seat's placement (the assigner's state, ADR 0567, ADR 0549 §3) + placed *meshmoduleassignerclient.Placed // the seat's assignments (the assigner's state, ADR 0567, ADR 0549 §3) builds map[string]buildEntry // by .build. groups map[string]groupEntry // the indexer's candidates, by group modules map[string]moduleEntry // the indexer's modules, by module @@ -282,10 +282,10 @@ type stuckEntry struct { err string } -// pair is a module the seat's placement puts on a node. +// pair is a module the seat's assignments puts on a node. type pair struct{ Node, Module string } -// placedPairs is every module the seat's placement puts on a node, by node then module. The caller holds mu. +// placedPairs is every module the seat's assignments puts on a node, by node then module. The caller holds mu. func (m *Model) placedPairs() []pair { byNode := m.placed.ByNode() var out []pair @@ -542,8 +542,8 @@ func (m *Model) apply(e Entry) { } st, err := meshwatchdogsclient.DecodeStuck(e.Value) m.stuck[e.Key] = stuckEntry{value: st, err: errText(err)} - case PlacementBucket: - // A key that does not read places nothing on its node; the seat's verb placement says why. + case AssignmentsBucket: + // A key that does not read places nothing on its node; the seat's verb assignments says why. _, _, _ = m.placed.Put(e.Key, e.Value, e.Deleted, e.At) case CandidatesBucket: if e.Deleted { diff --git a/modules/mesh-delivery-view/internal/view/view_test.go b/modules/mesh-delivery-view/internal/view/view_test.go index 8d849fb3..ea375a82 100644 --- a/modules/mesh-delivery-view/internal/view/view_test.go +++ b/modules/mesh-delivery-view/internal/view/view_test.go @@ -8,16 +8,16 @@ import ( "testing" "time" + "git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient" "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" + "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-contracts/go/seats/nodeengine" mcc "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient" "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshwatchdogsclient" mcseat "git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshconditions" healers "git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshhealers" - "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" - "git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient" - "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" + "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshwantsclient" "git.novox.be/novox/mesh-sdk/go/keys" "git.novox.be/novox/mesh-sdk/go/motion" ) @@ -37,20 +37,20 @@ func haveE(t *testing.T, node, module string, v []byte, at time.Time) Entry { return Entry{Bucket: HaveBucket, Key: node + "." + module, Value: v, At: at} } -// place is the seat's placement of node, as the assigner keeps it (ADR 0567): the modules placed there. +// place is the seat's assignments of node, as the assigner keeps it (ADR 0567): the modules placed there. func place(t *testing.T, node string, modules ...string) Entry { t.Helper() - p := meshmoduleassigner.Placement{Node: node} + p := meshmoduleassigner.Assignments{Node: node} for _, m := range modules { p.Modules = append(p.Modules, meshmoduleassigner.PlacedModule{Module: m}) } // Its node key, which names the modules placed (research 072 row 37). - values, err := meshmoduleassignerclient.PlacementValues(p) + values, err := meshmoduleassignerclient.AssignmentsValues(p) if err != nil { t.Fatal(err) } key, _ := meshmoduleassignerclient.NodeKey(node) - return Entry{Bucket: PlacementBucket, Key: key, Value: values[key], At: t0} + return Entry{Bucket: AssignmentsBucket, Key: key, Value: values[key], At: t0} } func find(t *testing.T, s Snapshot, node, module string) Row { @@ -100,7 +100,7 @@ func TestRowsPerAssignedModuleAndNode(t *testing.T) { // 1134/AC1 } } -func TestLeftBehindIsWhatThePlacementDoesNotPut(t *testing.T) { // 1134/AC3, issue 774 (the seat's placement) +func TestLeftBehindIsWhatTheAssignmentsDoesNotPut(t *testing.T) { // 1134/AC3, issue 774 (the seat's assignments) m := New() fill(m, base(t)...) fill(m, place(t, "b", "x")) // z taken off b @@ -113,11 +113,11 @@ func TestLeftBehindIsWhatThePlacementDoesNotPut(t *testing.T) { // 1134/AC3, iss fill(m, haveE(t, "a", "w", have(t, "b9", nodeengineclient.PhaseRunning, nodeengineclient.HealthOK, ""), t0)) // placed nowhere s = m.Snapshot() if find(t, s, "a", "w").Status != StatusLeftBehind || s.Counted() != 4 { - t.Fatalf("a have with no placement: %+v counted %d", find(t, s, "a", "w"), s.Counted()) + t.Fatalf("a have with no assignments: %+v counted %d", find(t, s, "a", "w"), s.Counted()) } - fill(m, Entry{Bucket: PlacementBucket, Key: "b.node", Deleted: true}) + fill(m, Entry{Bucket: AssignmentsBucket, Key: "b.node", Deleted: true}) if s := m.Snapshot(); s.Counted() != 2 || find(t, s, "b", "x").Status != StatusLeftBehind { - t.Fatalf("b's placement deleted: counted %d, %+v", s.Counted(), s.Rows) + t.Fatalf("b's assignments deleted: counted %d, %+v", s.Counted(), s.Rows) } } diff --git a/modules/mesh-delivery-view/internal/view/wants_test.go b/modules/mesh-delivery-view/internal/view/wants_test.go index 28a34934..6599683e 100644 --- a/modules/mesh-delivery-view/internal/view/wants_test.go +++ b/modules/mesh-delivery-view/internal/view/wants_test.go @@ -9,9 +9,9 @@ import ( "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" "git.novox.be/novox/mesh-contracts/go/seats/nodeengine" "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshwatchdogsclient" - "git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient" + "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdelivery" ) @@ -244,7 +244,7 @@ func TestARowReadsTheWantWhenTheAssignmentNamesNoBuild(t *testing.T) { // and a have nothing is assigned to keeps the module's name. func TestADottedModuleNameIsFoundAndKept(t *testing.T) { m := New() - // The seat's placement refuses a dotted module name (mesh-sdk meshmoduleassigner.Placement), so such a module is never placed: + // The seat's assignments refuses a dotted module name (mesh-sdk meshmoduleassigner.Assignments), so such a module is never placed: // its have is a row left behind under its own name. fill(m, place(t, "ace"), wantE(t, "ace", "novox.be", meshwantsclient.Want{Fp: fpNew}), haveFP(t, "ace", "novox.be", fpNew, nodeengineclient.HealthOK), haveFP(t, "ace", "old.name", fpNew, nodeengineclient.HealthOK)) diff --git a/modules/mesh-delivery-view/manifest_test.go b/modules/mesh-delivery-view/manifest_test.go index dcb952c3..47c1fdab 100644 --- a/modules/mesh-delivery-view/manifest_test.go +++ b/modules/mesh-delivery-view/manifest_test.go @@ -109,7 +109,7 @@ func TestManifestIsTakenAndReadsOnly(t *testing.T) { // 1134/AC7 } // The indexer's and the assigner's states are asked of their owners' read tools (design 25 § 12.2), the six the process // follows then heard on their views families (ADR 0615 §1, §5; DLV-M4). - if want := []string{"mesh-delivery-assigner.assigner_read_asks", "mesh-delivery-assigner.assigner_read_differences", "mesh-delivery-assigner.assigner_read_placement", + if want := []string{"mesh-delivery-assigner.assigner_read_asks", "mesh-delivery-assigner.assigner_read_differences", "mesh-delivery-assigner.assigner_read_assignments", "mesh-delivery-assigner.assigner_read_wants", "mesh-delivery-indexer.indexer_read_candidates", "mesh-delivery-indexer.indexer_read_config_candidates", "mesh-delivery-indexer.indexer_read_graph", "mesh-delivery-indexer.indexer_read_heard", "mesh-delivery-indexer.indexer_pending", "mesh-delivery-indexer.indexer_read_modules", "mesh-delivery-indexer.indexer_read_packages", "mesh-delivery-indexer.indexer_read_predefined", "mesh-delivery-indexer.indexer_read_taken", @@ -117,7 +117,7 @@ func TestManifestIsTakenAndReadsOnly(t *testing.T) { // 1134/AC7 "seat:node-engine.manifest-reads"}; !reflect.DeepEqual(m.Invokes, want) { // ADR 0618 §3: node_manifest_reads asks the node-engine seat's verb of every node; 1585/T24: view_tiers asks indexer_pending, a verb edge (ADR 0614 §2) t.Errorf("invokes = %v, want %v", m.Invokes, want) } - if want := []string{"mesh-delivery-assigner.views.asks", "mesh-delivery-assigner.views.placement", "mesh-delivery-assigner.views.wants", + if want := []string{"mesh-delivery-assigner.views.asks", "mesh-delivery-assigner.views.assignments", "mesh-delivery-assigner.views.wants", "mesh-delivery-indexer.views.candidates", "mesh-delivery-indexer.views.modules", "mesh-delivery-indexer.views.taken"}; !reflect.DeepEqual(m.Consumes, want) { t.Errorf("consumes = %v, want %v", m.Consumes, want) } @@ -166,7 +166,7 @@ func TestManifestIsTakenAndReadsOnly(t *testing.T) { // 1134/AC7 t.Errorf("the view reads %s, its manifest's reads are %v", view.SignersRead, m.Reads) } followed := map[string]string{view.ModulesBucket: view.IndexerModule + ".views.modules", view.CandidatesBucket: view.IndexerModule + ".views.candidates", - view.TakenBucket: view.IndexerModule + ".views.taken", view.PlacementBucket: view.AssignerModule + ".views.placement", + view.TakenBucket: view.IndexerModule + ".views.taken", view.AssignmentsBucket: view.AssignerModule + ".views.assignments", view.AssignerWantsBucket: view.AssignerModule + ".views.wants", view.AssignerAsksBucket: view.AssignerModule + ".views.asks"} for bucket, family := range followed { if module, state, _ := strings.Cut(family, ".views."); bucket != module+"_"+state { @@ -339,7 +339,7 @@ func TestNoSharedDirectoryIsClaimed(t *testing.T) { // 1134/AC7 } // The node-engine never places a module-owned directory below /etc, /usr, /run or /var/run: they are root's -// (mesh-host internal/apply/placement_guard.go:80, rootsOnly; novox/hq issue 339). +// (mesh-host internal/apply/assignments_guard.go:80, rootsOnly; novox/hq issue 339). func TestNoDirectoryBelowRootsTrees(t *testing.T) { // 1134/AC10 m, _ := load(t) for _, r := range m.Resources { diff --git a/modules/mesh-delivery-view/module.json b/modules/mesh-delivery-view/module.json index 35a95c3e..a56e8c08 100644 --- a/modules/mesh-delivery-view/module.json +++ b/modules/mesh-delivery-view/module.json @@ -43,7 +43,7 @@ ], "consumes": [ "mesh-delivery-assigner.views.asks", - "mesh-delivery-assigner.views.placement", + "mesh-delivery-assigner.views.assignments", "mesh-delivery-assigner.views.wants", "mesh-delivery-indexer.views.candidates", "mesh-delivery-indexer.views.modules", @@ -52,7 +52,7 @@ "invokes": [ "mesh-delivery-assigner.assigner_read_asks", "mesh-delivery-assigner.assigner_read_differences", - "mesh-delivery-assigner.assigner_read_placement", + "mesh-delivery-assigner.assigner_read_assignments", "mesh-delivery-assigner.assigner_read_wants", "mesh-delivery-indexer.indexer_read_candidates", "mesh-delivery-indexer.indexer_read_config_candidates", diff --git a/modules/mesh-delivery-view/viewstate/go.mod b/modules/mesh-delivery-view/viewstate/go.mod index 3d8e015e..bdc9f992 100644 --- a/modules/mesh-delivery-view/viewstate/go.mod +++ b/modules/mesh-delivery-view/viewstate/go.mod @@ -14,8 +14,8 @@ replace git.novox.be/novox/mesh-delivery/sdk => ../../../sdk require ( git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient v0.0.0-00010101000000-000000000000 // indirect git.novox.be/novox/mesh-contracts/go/seats/nodeengine v0.0.0-00010101000000-000000000000 // indirect - git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient v0.0.0-00010101000000-000000000000 // indirect - git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner v0.0.0-00010101000000-000000000000 // indirect + git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient v0.0.0-00010101000000-000000000000 // indirect + git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner v0.0.0-00010101000000-000000000000 // indirect git.novox.be/novox/mesh-delivery/contracts/go/clients/containerimageregistryclient v0.0.0-00010101000000-000000000000 // indirect git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient v0.0.0-00010101000000-000000000000 // indirect git.novox.be/novox/mesh-delivery/contracts/go/seats/containerimageregistry v0.0.0-00010101000000-000000000000 // indirect diff --git a/modules/mesh-delivery-watchman/cmd/mesh-delivery-watchman/integration_test.go b/modules/mesh-delivery-watchman/cmd/mesh-delivery-watchman/integration_test.go index 00d33173..df13bcfb 100644 --- a/modules/mesh-delivery-watchman/cmd/mesh-delivery-watchman/integration_test.go +++ b/modules/mesh-delivery-watchman/cmd/mesh-delivery-watchman/integration_test.go @@ -20,8 +20,8 @@ import ( "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" "git.novox.be/novox/mesh-contracts/go/seats/nodeengine" "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshwatchdogsclient" - "git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" + "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/sdk/buildstep" "git.novox.be/novox/mesh-sdk/go/buscompose" "git.novox.be/novox/mesh-sdk/go/process" diff --git a/modules/mesh-delivery-watchman/go.mod b/modules/mesh-delivery-watchman/go.mod index 239c3773..4f68ba7b 100644 --- a/modules/mesh-delivery-watchman/go.mod +++ b/modules/mesh-delivery-watchman/go.mod @@ -10,7 +10,7 @@ require ( git.novox.be/novox/mesh-contracts/go/seats/nodeengine v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshwatchdogsclient v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshwatchdogs v0.0.0-00010101000000-000000000000 - git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-delivery/contracts/go/clients/meshwantsclient v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog v0.0.0-00010101000000-000000000000 @@ -45,7 +45,7 @@ require git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshcondi require ( git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshconditions v0.0.0-00010101000000-000000000000 // indirect git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshhandacts v0.0.0-00010101000000-000000000000 // indirect - git.novox.be/novox/mesh-contracts/placement/go/seats/meshwants v0.0.0-00010101000000-000000000000 // indirect + git.novox.be/novox/mesh-delivery/contracts/go/seats/meshwants v0.0.0-00010101000000-000000000000 // indirect git.novox.be/novox/mesh-delivery/contracts/go/clients/containerimageregistryclient v0.0.0-00010101000000-000000000000 // indirect git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient v0.0.0-00010101000000-000000000000 // indirect git.novox.be/novox/mesh-delivery/contracts/go/seats/containerimageregistry v0.0.0-00010101000000-000000000000 // indirect diff --git a/modules/mesh-delivery-watchman/internal/watch/findings_test.go b/modules/mesh-delivery-watchman/internal/watch/findings_test.go index 81eb49ff..92bc72e8 100644 --- a/modules/mesh-delivery-watchman/internal/watch/findings_test.go +++ b/modules/mesh-delivery-watchman/internal/watch/findings_test.go @@ -10,8 +10,8 @@ import ( "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" "git.novox.be/novox/mesh-contracts/go/seats/nodeengine" mcc "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient" - "git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" + "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-indexer/takein" @@ -273,7 +273,7 @@ func TestTheWatchmansFindings(t *testing.T) { want string }{ {name: "not there yet", bindErr: errors.New("consumer not found"), - want: "mesh-delivery-indexer does not hear assigner-views: the placement is the first view's alone (consumer not found)"}, + want: "mesh-delivery-indexer does not hear assigner-views: the assignments is the first view's alone (consumer not found)"}, {name: "bound"}, {name: "no findings state", bindErr: errors.New("consumer not found"), noState: true}, } { @@ -285,7 +285,7 @@ func TestTheWatchmansFindings(t *testing.T) { hw.Put = func(k string, v []byte) error { kept[k] = v; return nil } hw.Delete = func(k string) error { delete(kept, k); return nil } } - if err := hw.Bind("assigner-views", "the placement is the first view's alone", func() error { return row.bindErr }); !errors.Is(err, row.bindErr) { + if err := hw.Bind("assigner-views", "the assignments is the first view's alone", func() error { return row.bindErr }); !errors.Is(err, row.bindErr) { t.Errorf("%s: Bind answered %v, want %v", row.name, err, row.bindErr) } raw, stands := kept[key] diff --git a/modules/mesh-delivery-watchman/internal/watch/mirror.go b/modules/mesh-delivery-watchman/internal/watch/mirror.go index 532583a5..01fdc06d 100644 --- a/modules/mesh-delivery-watchman/internal/watch/mirror.go +++ b/modules/mesh-delivery-watchman/internal/watch/mirror.go @@ -7,8 +7,8 @@ import ( "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" "git.novox.be/novox/mesh-contracts/go/seats/nodeengine" "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshwatchdogsclient" - "git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" + "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds" "git.novox.be/novox/mesh-delivery/sdk/buildstep" ) diff --git a/modules/mesh-delivery-watchman/internal/watch/watch_test.go b/modules/mesh-delivery-watchman/internal/watch/watch_test.go index 1fd555ea..004b7ad9 100644 --- a/modules/mesh-delivery-watchman/internal/watch/watch_test.go +++ b/modules/mesh-delivery-watchman/internal/watch/watch_test.go @@ -11,8 +11,8 @@ import ( "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" "git.novox.be/novox/mesh-contracts/go/seats/nodeengine" "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshwatchdogsclient" - "git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" + "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/sdk/buildstep" ) diff --git a/modules/mesh-enrolment/cmd/mesh-enrolment/end.go b/modules/mesh-enrolment/cmd/mesh-enrolment/end.go index 3bf64a7b..952aa8e0 100644 --- a/modules/mesh-enrolment/cmd/mesh-enrolment/end.go +++ b/modules/mesh-enrolment/cmd/mesh-enrolment/end.go @@ -11,7 +11,7 @@ import ( // Revoke ends an enrolment at any stage (ADR 0587 §5), by a person's call: at asked it withdraws the ask; at redeeming it also // undoes what redeem had not finished, the vault retiring a node account it already minted, and a node the yes added to -// placement is taken off it again. It only takes access away, so it needs no Approve; +// assignments is taken off it again. It only takes access away, so it needs no Approve; // it is recorded in the hand-act log. enrolment-revoked carries the key's content and the why; the token is then dropped by // the durable consumer of that event, never before. A revoke that stopped is finished by calling it again. func (h *Holder) Revoke(caller, node, why string) (map[string]any, error) { @@ -42,9 +42,9 @@ func (h *Holder) Revoke(caller, node, why string) (map[string]any, error) { return nil, fmt.Errorf("the vault did not retire the account minted for %s, so nothing is ended (call revoke again): %w", node, err) } } - // A node that entered placement on the yes and never confirmed leaves it again (the operator's decision of 2026-11-02 01:05). + // A node that entered assignments on the yes and never confirmed leaves it again (the operator's decision of 2026-11-02 01:05). if err := h.unplace(e, "revoked: "+why); err != nil { - return nil, fmt.Errorf("%s is not taken off placement, so nothing is ended (call revoke again): %w", node, err) + return nil, fmt.Errorf("%s is not taken off assignments, so nothing is ended (call revoke again): %w", node, err) } out := map[string]any{"node": node, "was": e.Stage} if h.Act != nil { @@ -156,12 +156,12 @@ func (h *Holder) answeredYes(ask string) (bool, error) { } // endExpired ends an enrolment whose key expired, in this order, each step safe to do again: the node, placed on the yes and -// never confirmed, is taken out of placement on the warrant of its ask (only when placement holds it); enrolment-expired names +// never confirmed, is taken out of assignments on the warrant of its ask (only when assignments holds it); enrolment-expired names // its issued event (said under one id, so said again is said once); the vault drops the token; the address key goes last, so // a step that failed is found and done on the next pass (ADR 0589 §6). func (h *Holder) endExpired(e Enrolment) error { if err := h.unplace(e, "expired"); err != nil { - return fmt.Errorf("%s, whose enrolment expired, is not taken out of placement (tried again on the next pass): %w", e.Node, err) + return fmt.Errorf("%s, whose enrolment expired, is not taken out of assignments (tried again on the next pass): %w", e.Node, err) } if err := h.say(EventExpired, expiredEvent(e), eventID(EventExpired, e)); err != nil { return fmt.Errorf("enrolment-expired of %s cannot be said (tried again on the next pass): %w", e.Node, err) diff --git a/modules/mesh-enrolment/cmd/mesh-enrolment/end_test.go b/modules/mesh-enrolment/cmd/mesh-enrolment/end_test.go index 1daccae9..7b3492cd 100644 --- a/modules/mesh-enrolment/cmd/mesh-enrolment/end_test.go +++ b/modules/mesh-enrolment/cmd/mesh-enrolment/end_test.go @@ -4,11 +4,11 @@ import ( "encoding/json" "errors" "fmt" + "git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient" + "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-contracts/identity-and-access/go/clients/meshnodeenrollerclient" "git.novox.be/novox/mesh-contracts/identity-and-access/go/seats/meshnodeenroller" oc "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel" - "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" - "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" "reflect" "slices" "strings" @@ -31,8 +31,8 @@ func revokeEndsAtAnyStageAndAnExpiryMarkerSaysExpired(t *testing.T) { if _, err := r.h.Revoke("person.operator", "ace", "never confirmed"); err != nil { t.Fatal(err) } - // The node the yes placed, never confirmed, leaves placement on the enrolment's warrant before the end is said. - if _, placed := r.rd["mesh-delivery-assigner.placement"]["ace.node"]; placed || len(r.added) == 0 || + // The node the yes placed, never confirmed, leaves assignments on the enrolment's warrant before the end is said. + if _, placed := r.rd["mesh-delivery-assigner.assignments"]["ace.node"]; placed || len(r.added) == 0 || !reflect.DeepEqual(r.added[len(r.added)-1].Change, meshmoduleassigner.ProposalChange{Op: meshmoduleassignerclient.OpRemoveNode, Node: "ace"}) || r.added[len(r.added)-1].Warrant.Ask != "ask-ace" { t.Errorf("revoked and still placed: %+v", r.added) } @@ -55,7 +55,7 @@ func revokeEndsAtAnyStageAndAnExpiryMarkerSaysExpired(t *testing.T) { if err := r.h.Heard(stdio.OpExpire, NodeKey("novox"), nil); err != nil { t.Fatal(err) } - if _, placed := r.rd["mesh-delivery-assigner.placement"]["novox.node"]; placed { + if _, placed := r.rd["mesh-delivery-assigner.assignments"]["novox.node"]; placed { t.Error("expired and still placed") } last := r.said[len(r.said)-1] @@ -65,7 +65,7 @@ func revokeEndsAtAnyStageAndAnExpiryMarkerSaysExpired(t *testing.T) { t.Errorf("said %s %s: %+v, %v; vault %v, keys %v", last.Event, last.Body, x, err, r.vault.calls, r.keys()) } // ADR 0589 §6: an enrolment whose key expired unseen (before the watch handed it over, or while the holder was down) is - // found by its address key, which names its ask and issue: it leaves placement on that ask's warrant, enrolment-expired + // found by its address key, which names its ask and issue: it leaves assignments on that ask's warrant, enrolment-expired // names its rebuilt issued event, the token is dropped and the address key goes last. A step that fails is done again on // the next pass, the address key kept until then. for _, c := range []struct { @@ -92,11 +92,11 @@ func revokeEndsAtAnyStageAndAnExpiryMarkerSaysExpired(t *testing.T) { } last := r.said[len(r.said)-1] x, err := decodeExpired(last.Body) - _, placed := r.rd["mesh-delivery-assigner.placement"][c.node+".node"] + _, placed := r.rd["mesh-delivery-assigner.assignments"][c.node+".node"] removed := r.added[len(r.added)-1] if err != nil || x.IssuedID != meshnodeenrollerclient.IssuedID(c.node, issued) || placed || removed.Change.Op != meshmoduleassignerclient.OpRemoveNode || removed.Change.Node != c.node || removed.Warrant.Ask != "ask-"+c.node || !slices.Contains(r.vault.calls, "drop "+c.node) || r.has(AddressKey("10.9.0.9")) { - t.Errorf("%s: said %s (%v), placed %v, last placement call %+v, vault %v, keys %v", c.name, last.Body, err, placed, removed, r.vault.calls, r.keys()) + t.Errorf("%s: said %s (%v), placed %v, last assignments call %+v, vault %v, keys %v", c.name, last.Body, err, placed, removed, r.vault.calls, r.keys()) } } // ADR 0589 §6, the negative rows: an address key the catch-up finds with its enrolment key gone is no expiry when its ask @@ -121,8 +121,8 @@ func revokeEndsAtAnyStageAndAnExpiryMarkerSaysExpired(t *testing.T) { r.asker.answer("ask-"+node, "no") }, false}, {"after a refused add", func(r *rig, node string) { - delete(r.rd["mesh-delivery-assigner.placement"], node+".node") // the add is tried, and refused - r.placeErr = errors.New("mesh-placement did not take the node: the proposal is refused: change 1: an address taken") + delete(r.rd["mesh-delivery-assigner.assignments"], node+".node") // the add is tried, and refused + r.placeErr = errors.New("mesh-module-assigner did not take the node: the proposal is refused: change 1: an address taken") _ = r.h.Warranted(yes, oc.Warrant{Ask: "ask-" + node}) r.asker.answer("ask-"+node, "yes") r.placeErr = nil @@ -142,7 +142,7 @@ func revokeEndsAtAnyStageAndAnExpiryMarkerSaysExpired(t *testing.T) { stage = StageLive } r.seed("joiner", "10.9.0.11", stage) - r.rd["mesh-delivery-assigner.placement"]["joiner.node"] = `{"v":1,"node":"joiner","decided":{"address":"10.9.0.11"},"modules":[]}` + r.rd["mesh-delivery-assigner.assignments"]["joiner.node"] = `{"v":1,"node":"joiner","decided":{"address":"10.9.0.11"},"modules":[]}` c.end(r, "joiner") removes := 0 for _, a := range r.added { diff --git a/modules/mesh-enrolment/cmd/mesh-enrolment/enrolment_test.go b/modules/mesh-enrolment/cmd/mesh-enrolment/enrolment_test.go index 1c6513ea..fc99d21b 100644 --- a/modules/mesh-enrolment/cmd/mesh-enrolment/enrolment_test.go +++ b/modules/mesh-enrolment/cmd/mesh-enrolment/enrolment_test.go @@ -20,7 +20,7 @@ func TestIssueTokensAndTheManifest(t *testing.T) { {"S54/AC1 tokens answers each stage and relays the sealed token of one node", tokensAnswersEachStageAndRelaysTheSealedTokenOfOneNode}, {"S54/AC2 the manifest says what the code does", theManifestSaysWhatTheCodeDoes}, {"S56 the bus node's loaded list is read from the grants part's pass", theLoadedListIsReadFromTheGrantsPass}, - {"DLV-M6 the placement is read from its first view, never from the assigner's state", thePlacementIsReadFromItsFirstView}, + {"DLV-M6 the assignments is read from its first view, never from the assigner's state", theAssignmentsIsReadFromItsFirstView}, } { t.Run(c.name, c.run) } @@ -61,18 +61,18 @@ func theLoadedListIsReadFromTheGrantsPass(t *testing.T) { } } -// thePlacementIsReadFromItsFirstView: the holder's placement comes from the picture of the assigner's placement (its first -// view and views family, ADR 0615 §1), every other state through the runtime; the runtime is never asked for placement. -func thePlacementIsReadFromItsFirstView(t *testing.T) { - pics := memPictures{PlacementRead: {"joiner.node": `{"v":1,"node":"joiner","decided":{"address":"10.9.0.11"},"modules":[]}`}} - rest := memRead{PlacementRead: {"stale.node": `{"v":1,"node":"stale","modules":[]}`}, "node-engine.have": {}} +// theAssignmentsIsReadFromItsFirstView: the holder's assignments comes from the picture of the assigner's assignments (its first +// view and views family, ADR 0615 §1), every other state through the runtime; the runtime is never asked for assignments. +func theAssignmentsIsReadFromItsFirstView(t *testing.T) { + pics := memPictures{AssignmentsRead: {"joiner.node": `{"v":1,"node":"joiner","decided":{"address":"10.9.0.11"},"modules":[]}`}} + rest := memRead{AssignmentsRead: {"stale.node": `{"v":1,"node":"stale","modules":[]}`}, "node-engine.have": {}} h := &Holder{Read: followedReader{pics: pics, rest: rest}} - p, err := h.placements() + p, err := h.assignments() if err != nil { t.Fatal(err) } if _, ok := p["joiner"]; !ok || len(p) != 1 { - t.Errorf("placements = %v, want joiner's alone, from the first view", p) + t.Errorf("assignments = %v, want joiner's alone, from the first view", p) } if keys, err := h.Read.Keys("node-engine.have"); err != nil || len(keys) != 0 { t.Errorf("node-engine.have read as %v, %v: want it through the runtime", keys, err) diff --git a/modules/mesh-enrolment/cmd/mesh-enrolment/holder.go b/modules/mesh-enrolment/cmd/mesh-enrolment/holder.go index a74dcafe..7040109e 100644 --- a/modules/mesh-enrolment/cmd/mesh-enrolment/holder.go +++ b/modules/mesh-enrolment/cmd/mesh-enrolment/holder.go @@ -11,12 +11,12 @@ import ( "sync" "time" + "git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient" "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" + "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-contracts/identity-and-access/go/clients/meshnodeenrollerclient" mv "git.novox.be/novox/mesh-contracts/identity-and-access/go/seats/meshvault" oc "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel" - "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" - "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" ) // Vault is the vault's side of enrolment (ADR 0587 §3, §5): the token and the node's account, and the signatures. The @@ -56,7 +56,7 @@ type Holder struct { Asker Asker // Say publishes a seat event under a message id the bus keeps one of. Say func(event string, body any, id string) error - // AddNode asks mesh-placement to add the node, or to take a node that never confirmed off again (ADR 0587 §7). + // AddNode asks mesh-module-assigner to add the node, or to take a node that never confirmed off again (ADR 0587 §7). AddNode func(AddNode) error // Warrant and Act record in the hand-act log, through the seat mesh-hand-acts: the operator's warrant for an ask, and an // act done by hand (a revoke). @@ -99,7 +99,7 @@ func (h *Holder) say(event string, body any, id string) error { return h.Say(event, body, id) } -// AddNode is what the holder asks mesh-placement to add (ADR 0587 §7): the proposal's one change, a joining node with the +// AddNode is what the holder asks mesh-module-assigner to add (ADR 0587 §7): the proposal's one change, a joining node with the // address the operator approved and the public halves of its keys, and the issue's warrant, which names this node and this // address (meshmoduleassigner.Warrant). type AddNode struct { @@ -108,10 +108,10 @@ type AddNode struct { Warrant meshmoduleassigner.Warrant } -// place adds the node into placement, with the address the operator approved and its tunnel key, on the issue's warrant -// (ADR 0587 §7; the operator's decision of 2026-11-02 01:05), unless placement holds it at that address already. -func (h *Holder) place(e Enrolment, placements map[string]meshmoduleassigner.Placement) error { - if p, ok := placements[e.Node]; ok && p.Decided.Address == e.Address { +// place adds the node into assignments, with the address the operator approved and its tunnel key, on the issue's warrant +// (ADR 0587 §7; the operator's decision of 2026-11-02 01:05), unless assignments holds it at that address already. +func (h *Holder) place(e Enrolment, assignments map[string]meshmoduleassigner.Assignments) error { + if p, ok := assignments[e.Node]; ok && p.Decided.Address == e.Address { return nil } w, err := h.warrantOf(e) @@ -123,14 +123,14 @@ func (h *Holder) place(e Enrolment, placements map[string]meshmoduleassigner.Pla Keys: &meshmoduleassigner.ProposalChangeKeys{Overlay: e.OverlayKey}}}) } -// unplace takes a node that never confirmed off placement again, on the same warrant, when its enrolment ends revoked, refused -// or expired: the hub stops peering a machine that will not join. A node placement does not hold is left as it is. +// unplace takes a node that never confirmed off assignments again, on the same warrant, when its enrolment ends revoked, refused +// or expired: the hub stops peering a machine that will not join. A node assignments does not hold is left as it is. func (h *Holder) unplace(e Enrolment, why string) error { - placements, err := h.placements() + assignments, err := h.assignments() if err != nil { - return fmt.Errorf("placement cannot be read: %w", err) + return fmt.Errorf("assignments cannot be read: %w", err) } - if _, ok := placements[e.Node]; !ok { + if _, ok := assignments[e.Node]; !ok { return nil } w, err := h.warrantOf(e) @@ -143,7 +143,7 @@ func (h *Holder) unplace(e Enrolment, why string) error { func (h *Holder) warrantOf(e Enrolment) (meshmoduleassigner.Warrant, error) { if h.AddNode == nil || h.WarrantOf == nil { - return meshmoduleassigner.Warrant{}, errors.New("no route to mesh-placement") + return meshmoduleassigner.Warrant{}, errors.New("no route to mesh-module-assigner") } warrant, err := h.WarrantOf(e.Ask) if err != nil { @@ -191,21 +191,21 @@ func (h *Holder) openEnrolments() (map[string]Row, map[string]Enrolment, error) return rows, out, nil } -// placements reads every node's placement, assembled from its keys. -func (h *Holder) placements() (map[string]meshmoduleassigner.Placement, error) { - keys, err := h.Read.Keys(PlacementRead) +// assignments reads every node's assignments, assembled from its keys. +func (h *Holder) assignments() (map[string]meshmoduleassigner.Assignments, error) { + keys, err := h.Read.Keys(AssignmentsRead) if err != nil { - return nil, fmt.Errorf("placement cannot be listed: %w", err) + return nil, fmt.Errorf("assignments cannot be listed: %w", err) } held := map[string][]byte{} for _, k := range keys { - if raw, found, err := h.Read.Get(PlacementRead, k); err != nil { - return nil, fmt.Errorf("placement key %s cannot be read: %w", k, err) + if raw, found, err := h.Read.Get(AssignmentsRead, k); err != nil { + return nil, fmt.Errorf("assignments key %s cannot be read: %w", k, err) } else if found { held[k] = raw } } - p, err := meshmoduleassignerclient.PlacementsOf(held) + p, err := meshmoduleassignerclient.AssignmentsOf(held) if len(p) == 0 && err != nil { return nil, err } @@ -215,19 +215,19 @@ func (h *Holder) placements() (map[string]meshmoduleassigner.Placement, error) { // firstTunnel is the joining node's first tunnel as the token carries it (ADR 0587 §3, insight of 2026-11-02; as the // controller's `token issue` builds it): the machine's own tunnel key given at issue, its address, the private network's // range (privateRange), the hub's tunnel key from its record (the key its tunnel holds; on an adopted hub -// that has not written one, the found tunnel's key kept in its placement), the hub's endpoint from its placement, and the +// that has not written one, the found tunnel's key kept in its assignments), the hub's endpoint from its assignments, and the // node's address on the private network. A mesh with no hub, or a hub with no key or no endpoint, has no tunnel to join // through, and that is said. -func (h *Holder) firstTunnel(placements map[string]meshmoduleassigner.Placement, address, overlayKey string) (meshnodeenrollerclient.TokenTunnel, error) { - var hub *meshmoduleassigner.Placement - for _, p := range placements { +func (h *Holder) firstTunnel(assignments map[string]meshmoduleassigner.Assignments, address, overlayKey string) (meshnodeenrollerclient.TokenTunnel, error) { + var hub *meshmoduleassigner.Assignments + for _, p := range assignments { if p.Decided.Hub { p := p hub = &p } } if hub == nil { - return meshnodeenrollerclient.TokenTunnel{}, errors.New("placement names no hub, so there is no tunnel to join through") + return meshnodeenrollerclient.TokenTunnel{}, errors.New("assignments names no hub, so there is no tunnel to join through") } key := "" if k, err := nodeengineclient.HaveNodeKey(hub.Node); err == nil { @@ -245,9 +245,9 @@ func (h *Holder) firstTunnel(placements map[string]meshmoduleassigner.Placement, missing = append(missing, "no tunnel key in its record") } if hub.Decided.Endpoint == "" { - missing = append(missing, "no endpoint in its placement") + missing = append(missing, "no endpoint in its assignments") } - prefix, rerr := privateRange(placements) + prefix, rerr := privateRange(assignments) if len(missing) > 0 { return meshnodeenrollerclient.TokenTunnel{}, fmt.Errorf("the hub %s has %s, so there is no tunnel to join through", hub.Node, strings.Join(missing, " and ")) } @@ -271,11 +271,11 @@ func (h *Holder) hasRecord(node string) bool { return false } -// privateRange is the private network's range: the hub's tunnel range in its placement, as the controller's `token issue` -// writes it into the token today (the adopted tunnel's range). A mesh whose hub's placement names none has no range a token +// privateRange is the private network's range: the hub's tunnel range in its assignments, as the controller's `token issue` +// writes it into the token today (the adopted tunnel's range). A mesh whose hub's assignments names none has no range a token // can carry, and that is said rather than guessed. -func privateRange(placements map[string]meshmoduleassigner.Placement) (netip.Prefix, error) { - for _, p := range placements { +func privateRange(assignments map[string]meshmoduleassigner.Assignments) (netip.Prefix, error) { + for _, p := range assignments { if t := p.Decided.Tunnel; p.Decided.Hub && t != nil && t.Range != "" { r, err := netip.ParsePrefix(t.Range) if err != nil { @@ -284,14 +284,14 @@ func privateRange(placements map[string]meshmoduleassigner.Placement) (netip.Pre return r.Masked(), nil } } - return netip.Prefix{}, errors.New("no private network is known: the hub's placement names no tunnel range") + return netip.Prefix{}, errors.New("no private network is known: the hub's assignments names no tunnel range") } -// nextAddress is the lowest address of the private network that no placement key and no reservation holds (ADR 0587 §4), +// nextAddress is the lowest address of the private network that no assignments key and no reservation holds (ADR 0587 §4), // after skip. The network is the hub's range; the hub's own address and the tunnel's are never given. -func nextAddress(placements map[string]meshmoduleassigner.Placement, reserved map[string]bool, skip map[string]bool) (string, error) { +func nextAddress(assignments map[string]meshmoduleassigner.Assignments, reserved map[string]bool, skip map[string]bool) (string, error) { taken := map[string]bool{} - for _, p := range placements { + for _, p := range assignments { if a := p.Decided.Address; a != "" { taken[a] = true } @@ -305,7 +305,7 @@ func nextAddress(placements map[string]meshmoduleassigner.Placement, reserved ma } } } - prefix, err := privateRange(placements) + prefix, err := privateRange(assignments) if err != nil { return "", err } diff --git a/modules/mesh-enrolment/cmd/mesh-enrolment/integration_test.go b/modules/mesh-enrolment/cmd/mesh-enrolment/integration_test.go index c5adee20..720eedc0 100644 --- a/modules/mesh-enrolment/cmd/mesh-enrolment/integration_test.go +++ b/modules/mesh-enrolment/cmd/mesh-enrolment/integration_test.go @@ -6,7 +6,7 @@ package main // key-value bucket `enrolments` with a time to live per key (the keys written as the runtime writes them: a create with the // expiry, a put with a message TTL, an update by revision), its events said on their real subjects, the bus's own expiry marker // heard, and the other parts stood in for — the vault (which writes the new node's key of node-logins at node-account, as the -// vault does), the operator's channel, placement, and the grants part's rule that a key at live makes the user enrol. and +// vault does), the operator's channel, assignments, and the grants part's rule that a key at live makes the user enrol. and // a key gone takes it away. import ( @@ -23,12 +23,12 @@ import ( "github.com/nats-io/nats.go" "github.com/nats-io/nats.go/jetstream" + "git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient" + "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-contracts/identity-and-access/go/clients/meshnodeenrollerclient" "git.novox.be/novox/mesh-contracts/identity-and-access/go/clients/meshvaultclient" mv "git.novox.be/novox/mesh-contracts/identity-and-access/go/seats/meshvault" oc "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel" - "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" - "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" stdio "git.novox.be/novox/mesh-sdk/go" "git.novox.be/novox/mesh-sdk/go/testbus" ) @@ -195,7 +195,7 @@ func (v *standInVault) Signer() (string, error) { return "vault-key", nil } // 774/S60/AC1: issue, a warrant, redeem as enrol. and confirm, whole on a real bus. The user enrol. is composed // while the key is live and leaves with it; the node confirms as the login its key of node-logins names; node-enrolled and -// placement-changed are said; the vault is asked to drop the token only after the end; and a token never redeemed is ended by +// assignments-changed are said; the vault is asked to drop the token only after the end; and a token never redeemed is ended by // the bus's own expiry, heard, saying enrolment-expired and bringing the drop. func TestMeshEnrolmentWholeOnARealBus(t *testing.T) { srv := testbus.Start(t) @@ -225,9 +225,9 @@ func TestMeshEnrolmentWholeOnARealBus(t *testing.T) { } enrolments := mk(enrolmentsBucket, true) // No controller lease bucket at all: the controller ended by its unassignment (issue 1126). - logins, placement := mk(nodeLoginsBucket, false), mk("mesh-delivery-assigner_placement", false) + logins, assignments := mk(nodeLoginsBucket, false), mk("mesh-delivery-assigner_assignments", false) have := mk("node-engine_have", false) - if _, err := placement.Put(ctx, "hub.node", []byte(hubPlacement)); err != nil { + if _, err := assignments.Put(ctx, "hub.node", []byte(hubAssignments)); err != nil { t.Fatal(err) } if _, err := have.Put(ctx, "hub.node", []byte(hubRecord)); err != nil { @@ -235,12 +235,12 @@ func TestMeshEnrolmentWholeOnARealBus(t *testing.T) { } // The seats' events are kept on a stream of the events, as the bus keeps them, and heard on their real subjects. if _, err := js.CreateStream(ctx, jetstream.StreamConfig{Name: "SEAT_EVENTS", - Subjects: []string{"mesh.seat.mesh-enrolment.event.>", "mesh.seat.mesh-placement.event.>"}}); err != nil { + Subjects: []string{"mesh.seat.mesh-enrolment.event.>", "mesh.seat.mesh-module-assigner.event.>"}}); err != nil { t.Fatal(err) } var heardMu sync.Mutex var heard []string - for _, subject := range []string{"mesh.seat.mesh-enrolment.event.*", "mesh.seat.mesh-placement.event.*"} { + for _, subject := range []string{"mesh.seat.mesh-enrolment.event.*", "mesh.seat.mesh-module-assigner.event.*"} { if _, err := nc.Subscribe(subject, func(m *nats.Msg) { heardMu.Lock() heard = append(heard, m.Subject[strings.LastIndex(m.Subject, ".")+1:]) @@ -260,19 +260,19 @@ func TestMeshEnrolmentWholeOnARealBus(t *testing.T) { _, err := js.Publish(ctx, "mesh.seat.mesh-enrolment.event."+event, raw, jetstream.WithMsgID(id)) return err }, - // mesh-placement, stood in: an add-node writes the node's key, a remove-node deletes it, each saying placement-changed. + // mesh-module-assigner, stood in: an add-node writes the node's key, a remove-node deletes it, each saying assignments-changed. AddNode: func(a AddNode) error { if a.Change.Op == meshmoduleassignerclient.OpRemoveNode { - if err := placement.Delete(ctx, a.Change.Node+".node"); err != nil { + if err := assignments.Delete(ctx, a.Change.Node+".node"); err != nil { return err } } else { value := `{"v":1,"node":"` + a.Change.Node + `","decided":{"address":"` + a.Change.Decided.Address + `"},"modules":[]}` - if _, err := placement.Put(ctx, a.Change.Node+".node", []byte(value)); err != nil { + if _, err := assignments.Put(ctx, a.Change.Node+".node", []byte(value)); err != nil { return err } } - _, err := js.Publish(ctx, "mesh.seat.mesh-placement.event.placement-changed", []byte(`{}`)) + _, err := js.Publish(ctx, "mesh.seat.mesh-module-assigner.event.assignments-changed", []byte(`{}`)) return err }, WarrantOf: func(ask string) (string, error) { return ask + "@2026-11-02T10:00:00Z", nil }, @@ -357,9 +357,9 @@ func TestMeshEnrolmentWholeOnARealBus(t *testing.T) { if missing := tok.Missing(); len(missing) != 0 || tok.Tunnel == nil || tok.Tunnel.Key != tunnelKey || tok.Tunnel.Address != a.Address+"/32" { t.Fatalf("the token misses %v: tunnel %+v", missing, tok.Tunnel) } - // The node is in placement before redeem: the operator's yes placed it, so the hub peers it before the machine dials. - if ace, err := placement.Get(ctx, "ace.node"); err != nil || !strings.Contains(string(ace.Value()), a.Address) { - t.Fatalf("ace is not in placement before redeem: %v", err) + // The node is in assignments before redeem: the operator's yes placed it, so the hub peers it before the machine dials. + if ace, err := assignments.Get(ctx, "ace.node"); err != nil || !strings.Contains(string(ace.Value()), a.Address) { + t.Fatalf("ace is not in assignments before redeem: %v", err) } args := redeemArgsWith("ace", 1, "serving-ace", tunnelKey) if _, err := h.Redeem("enrol.somebody", args); err == nil { @@ -381,9 +381,9 @@ func TestMeshEnrolmentWholeOnARealBus(t *testing.T) { if _, err := h.Confirm(nl.Login, ""); err != nil { t.Fatal(err) } - waitFor("node-enrolled and placement-changed are said and the user leaves the list", func() bool { + waitFor("node-enrolled and assignments-changed are said and the user leaves the list", func() bool { ev := events() - return slices.Contains(ev, EventEnrolled) && slices.Contains(ev, "placement-changed") && !hasUser("enrol.ace") + return slices.Contains(ev, EventEnrolled) && slices.Contains(ev, "assignments-changed") && !hasUser("enrol.ace") }) waitFor("the vault is asked to drop the token after the end", func() bool { vault.mu.Lock() @@ -411,9 +411,9 @@ func TestMeshEnrolmentWholeOnARealBus(t *testing.T) { if hasUser("enrol.g14") { t.Error("the user enrol.g14 stays after its key expired") } - // It never confirmed, so it left placement again before its end was said. - if _, err := placement.Get(ctx, "g14.node"); err == nil { - t.Error("g14 stays in placement after its enrolment expired") + // It never confirmed, so it left assignments again before its end was said. + if _, err := assignments.Get(ctx, "g14.node"); err == nil { + t.Error("g14 stays in assignments after its enrolment expired") } // Its address key had no expiry of its own: the holder took it last, after the drop (ADR 0589 §6). waitFor("the expired enrolment's address key is deleted", func() bool { keys, _ := store.Keys(); return len(keys) == 0 }) @@ -428,7 +428,7 @@ func TestMeshEnrolmentWholeOnARealBus(t *testing.T) { t.Fatal(err) } asker.answer(b.Ask, "yes") // the router's record of the answer, which the catch-up reads (ADR 0589 §6) - if _, err := placement.Get(ctx, "novox.node"); err != nil { + if _, err := assignments.Get(ctx, "novox.node"); err != nil { t.Fatalf("novox was not placed on the yes: %v", err) } down.Store(true) @@ -436,14 +436,14 @@ func TestMeshEnrolmentWholeOnARealBus(t *testing.T) { t.Fatal(err) } waitFor("the key is gone", func() bool { _, found, _ := store.Get(NodeKey("novox")); return !found }) - if _, err := placement.Get(ctx, "novox.node"); err != nil { - t.Fatalf("novox left placement while no holder ran: %v", err) + if _, err := assignments.Get(ctx, "novox.node"); err != nil { + t.Fatalf("novox left assignments while no holder ran: %v", err) } if err := h.Reconcile(); err != nil { t.Fatal(err) } - if _, err := placement.Get(ctx, "novox.node"); err == nil { - t.Error("novox, expired while the holder was down, stays in placement") + if _, err := assignments.Get(ctx, "novox.node"); err == nil { + t.Error("novox, expired while the holder was down, stays in assignments") } waitFor("the token of novox is dropped and its address key goes", func() bool { vault.mu.Lock() diff --git a/modules/mesh-enrolment/cmd/mesh-enrolment/issue.go b/modules/mesh-enrolment/cmd/mesh-enrolment/issue.go index 7f0a5481..4410b626 100644 --- a/modules/mesh-enrolment/cmd/mesh-enrolment/issue.go +++ b/modules/mesh-enrolment/cmd/mesh-enrolment/issue.go @@ -42,7 +42,7 @@ type IssueAnswer struct { } // Issue opens an enrolment (ADR 0587 §4): it refuses at once, naming every finding, a name that is no node name or that a -// node record, a placement key or an open enrolment holds; chooses the lowest free +// node record, an assignments key or an open enrolment holds; chooses the lowest free // address and reserves it by creating its key (the bus refuses a key that exists, so two issues never get one address); // writes the key at stage asked; and asks the operator, on a channel that proves who answered. The token is made only on // the operator's yes (Warranted). @@ -57,7 +57,7 @@ func (h *Holder) Issue(caller, node, why, overlayKey string, lasts time.Duration if strings.TrimSpace(why) == "" { findings = append(findings, "no why is given: the operator is asked why this machine joins") } - // The machine's tunnel key, made there by `nox-mesh-host key`: on the operator's yes the node enters placement with it, so + // The machine's tunnel key, made there by `nox-mesh-host key`: on the operator's yes the node enters assignments with it, so // the hub peers it before the machine redeems (the operator's decision of 2026-11-02 01:05). if err := checkTunnelKey(overlayKey); err != nil { findings = append(findings, err.Error()) @@ -68,13 +68,13 @@ func (h *Holder) Issue(caller, node, why, overlayKey string, lasts time.Duration if lasts < 0 || lasts > meshnodeenrollerclient.MostTokenLife { findings = append(findings, fmt.Sprintf("the expiry %s is outside zero to %s", lasts, meshnodeenrollerclient.MostTokenLife)) } - placements, perr := h.placements() + assignments, perr := h.assignments() if perr != nil { - findings = append(findings, "placement cannot be read: "+perr.Error()) + findings = append(findings, "assignments cannot be read: "+perr.Error()) } if CheckNode(node) == nil { - if _, ok := placements[node]; ok { - findings = append(findings, node+" has a placement key already") + if _, ok := assignments[node]; ok { + findings = append(findings, node+" has an assignments key already") } if h.hasRecord(node) { findings = append(findings, node+" has a node record already") @@ -86,7 +86,7 @@ func (h *Holder) Issue(caller, node, why, overlayKey string, lasts time.Duration } } if perr == nil { - if _, err := h.firstTunnel(placements, "0.0.0.0", overlayKey); err != nil { + if _, err := h.firstTunnel(assignments, "0.0.0.0", overlayKey); err != nil { findings = append(findings, err.Error()) } } @@ -103,7 +103,7 @@ func (h *Holder) Issue(caller, node, why, overlayKey string, lasts time.Duration if err != nil { return IssueAnswer{}, err } - addr, err := nextAddress(placements, reserved, skip) + addr, err := nextAddress(assignments, reserved, skip) if err != nil { return IssueAnswer{}, fmt.Errorf("no enrolment of %q is issued: %w", node, err) } @@ -193,7 +193,7 @@ func (h *Holder) raise(e Enrolment) error { return err } // The address key names the ask and the issue's time from now, with no expiry of its own: it outlives the enrolment key, - // so an enrolment that expires unseen can still be taken out of placement on its ask's warrant (ADR 0589 §6). + // so an enrolment that expires unseen can still be taken out of assignments on its ask's warrant (ADR 0589 §6). if _, err := h.Store.Put(AddressKey(e.Address), Reservation{Node: e.Node, Ask: e.Ask, Issued: e.Issued}, 0); err != nil { return fmt.Errorf("the address key %s cannot name the ask %s, so the operator is not asked: %w", e.Address, e.Ask, err) } @@ -221,7 +221,7 @@ func (h *Holder) byAsk(ask string) (Enrolment, Row, bool, error) { } // Warranted is the operator's answer, handed once by the asker (occ.Client.Listen): a no ends the enrolment; a yes records -// the warrant, adds the node to placement with its tunnel key, asks the vault for the token, moves the key to live with the bus's expiry at the token's, and says +// the warrant, adds the node to assignments with its tunnel key, asks the vault for the token, moves the key to live with the bus's expiry at the token's, and says // enrolment-issued (ADR 0587 §4, §5). func (h *Holder) Warranted(o oc.Option, w oc.Warrant) error { e, _, found, err := h.byAsk(w.Ask) @@ -245,22 +245,22 @@ func (h *Holder) Warranted(o oc.Option, w oc.Warrant) error { return fmt.Errorf("the warrant %s is not recorded in the hand-act log, so no token is made (mesh-enrolment.again asks again): %w", w.Ask, err) } } - placements, err := h.placements() + assignments, err := h.assignments() if err != nil { - return fmt.Errorf("placement cannot be read, so the token's tunnel is not built (mesh-enrolment.again asks again): %w", err) + return fmt.Errorf("assignments cannot be read, so the token's tunnel is not built (mesh-enrolment.again asks again): %w", err) } - tunnel, err := h.firstTunnel(placements, e.Address, e.OverlayKey) + tunnel, err := h.firstTunnel(assignments, e.Address, e.OverlayKey) if err != nil { return fmt.Errorf("the token's tunnel is not built (mesh-enrolment.again asks again): %w", err) } - // The node enters placement now, with its tunnel key, on this warrant: the hub peers it before the machine redeems over + // The node enters assignments now, with its tunnel key, on this warrant: the hub peers it before the machine redeems over // the tunnel (the operator's decision of 2026-11-02 01:05). Only then is the token made. - if err := h.place(e, placements); err != nil { + if err := h.place(e, assignments); err != nil { if addRefused(err) { - // mesh-placement refused the node itself, not the call to it: the enrolment ends (ADR 0589 §6). - return h.refuse(e, "mesh-placement refused the node: "+err.Error()) + // mesh-module-assigner refused the node itself, not the call to it: the enrolment ends (ADR 0589 §6). + return h.refuse(e, "mesh-module-assigner refused the node: "+err.Error()) } - return fmt.Errorf("the node %s is not added to placement, so no token is made (mesh-enrolment.again asks again): %w", e.Node, err) + return fmt.Errorf("the node %s is not added to assignments, so no token is made (mesh-enrolment.again asks again): %w", e.Node, err) } if _, err := h.Vault.Token(e.Node, e.Expires, tunnel); err != nil { return fmt.Errorf("the vault did not make the token of %s (mesh-enrolment.again asks the operator again): %w", e.Node, err) @@ -273,7 +273,7 @@ func (h *Holder) Warranted(o oc.Option, w oc.Warrant) error { return h.sayEvent(EventIssued, e, "") } -// addRefused says mesh-placement answered the add-node with a refusal of the proposal (the assigner's words, "the proposal is +// addRefused says mesh-module-assigner answered the add-node with a refusal of the proposal (the assigner's words, "the proposal is // refused: …"), as against a call that did not reach it or was not answered, which `again` asks once more. func addRefused(err error) bool { return err != nil && strings.Contains(err.Error(), "the proposal is refused") @@ -306,7 +306,7 @@ func (h *Holder) refuse(e Enrolment, reason string) error { } // A yes whose token failed added the node already; a refusal takes it off again. if err := h.unplace(e, reason); err != nil { - return fmt.Errorf("%s is not taken off placement, so its enrolment stays: %w", e.Node, err) + return fmt.Errorf("%s is not taken off assignments, so its enrolment stays: %w", e.Node, err) } if err := h.sayEvent(EventRefused, e, reason); err != nil { return fmt.Errorf("enrolment-refused of %s cannot be said, so its keys stay: %w", e.Node, err) diff --git a/modules/mesh-enrolment/cmd/mesh-enrolment/issue_test.go b/modules/mesh-enrolment/cmd/mesh-enrolment/issue_test.go index 1d567c32..a4283480 100644 --- a/modules/mesh-enrolment/cmd/mesh-enrolment/issue_test.go +++ b/modules/mesh-enrolment/cmd/mesh-enrolment/issue_test.go @@ -7,15 +7,15 @@ import ( "testing" "time" + "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-contracts/identity-and-access/go/clients/meshnodeenrollerclient" occ "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/clients/operatorchannelclient" oc "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel" - "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" ) var yes = oc.Option{ID: "yes"} -// 774/S55/AC1: issue refuses, naming every finding, what it must: a bad name, a name a node record, a placement key or an +// 774/S55/AC1: issue refuses, naming every finding, what it must: a bad name, a name a node record, an assignments key or an // open enrolment holds, and an expiry past a day; never the controller's lease, which no rig writes (issue 1126). func issueRefusesNamingEveryFinding(t *testing.T) { cases := []struct { @@ -26,22 +26,22 @@ func issueRefusesNamingEveryFinding(t *testing.T) { want []string }{ {"a bad name and a long expiry", nil, "Ace.1", 48 * time.Hour, []string{"not a valid node name", "outside zero to 24h0m0s"}}, - {"a placement key and a node record", func(r *rig) { - r.rd["mesh-delivery-assigner.placement"]["ace.node"] = `{"v":1,"node":"ace","modules":[]}` + {"an assignments key and a node record", func(r *rig) { + r.rd["mesh-delivery-assigner.assignments"]["ace.node"] = `{"v":1,"node":"ace","modules":[]}` r.rd["node-engine.have"]["ace"] = `{}` - }, "ace", 0, []string{"ace has a placement key already"}}, + }, "ace", 0, []string{"ace has an assignments key already"}}, {"an open enrolment", func(r *rig) { r.seed("ace", "10.9.0.2", StageLive) }, "ace", 0, []string{"an enrolment of ace is open already"}}, {"no tunnel key and no range", func(r *rig) { - r.rd["mesh-delivery-assigner.placement"]["hub.node"] = strings.Replace(hubPlacement, `,"range":"10.9.0.0/24"`, "", 1) + r.rd["mesh-delivery-assigner.assignments"]["hub.node"] = strings.Replace(hubAssignments, `,"range":"10.9.0.0/24"`, "", 1) }, "ace", -1, []string{"no tunnel key is given", "names no tunnel range"}}, // The token carries the first tunnel (ADR 0587 §3, insight of 2026-11-02): no hub, or a hub with no key or endpoint. {"no hub", func(r *rig) { - r.rd["mesh-delivery-assigner.placement"]["hub.node"] = strings.Replace(hubPlacement, `"hub":true,`, "", 1) - }, "ace", 0, []string{"placement names no hub"}}, + r.rd["mesh-delivery-assigner.assignments"]["hub.node"] = strings.Replace(hubAssignments, `"hub":true,`, "", 1) + }, "ace", 0, []string{"assignments names no hub"}}, {"a hub with no key and no endpoint", func(r *rig) { - r.rd["mesh-delivery-assigner.placement"]["hub.node"] = strings.Replace(hubPlacement, `"endpoint":"192.0.2.1:51820",`, "", 1) + r.rd["mesh-delivery-assigner.assignments"]["hub.node"] = strings.Replace(hubAssignments, `"endpoint":"192.0.2.1:51820",`, "", 1) delete(r.rd["node-engine.have"], "hub.node") - }, "ace", 0, []string{"the hub hub has no tunnel key in its record and no endpoint in its placement"}}, + }, "ace", 0, []string{"the hub hub has no tunnel key in its record and no endpoint in its assignments"}}, } for _, c := range cases { r := newRig(t) @@ -112,9 +112,9 @@ func twoIssuesAtOnceHoldTwoAddressesAndAYesMakesTheToken(t *testing.T) { HubKey: "hub-tunnel-key", HubEndpoint: "192.0.2.1:51820"}); got != want { t.Errorf("the token's tunnel %+v, want %+v", got, want) } - // The yes added the node to placement with its tunnel key alone, on the warrant, before the token was made. + // The yes added the node to assignments with its tunnel key alone, on the warrant, before the token was made. if p, tok := slices.Index(r.vault.calls, "add-node ace"), slices.Index(r.vault.calls, "token ace"); p < 0 || tok < p { - t.Errorf("the token was made before placement held the node: %v", r.vault.calls) + t.Errorf("the token was made before assignments held the node: %v", r.vault.calls) } for _, a := range r.added { if a.Change.Node == "ace" && (*a.Change.Keys != meshmoduleassigner.ProposalChangeKeys{Overlay: tunnelKey} || a.Warrant.Ask != e.Ask || a.Change.Decided.Address != live.Address) { @@ -138,7 +138,7 @@ func aDeclinedOrExpiredAskTakesBothKeysAndAVaultFailureKeepsTheEnrolmentForAgain t.Fatal(err) } if len(r.keys()) != 0 || r.events()[0] != EventRefused || len(r.added) != 0 { - t.Fatalf("keys %v events %v placement %v", r.keys(), r.events(), r.added) + t.Fatalf("keys %v events %v assignments %v", r.keys(), r.events(), r.added) } mustContain(t, string(r.said[0].Body), `"address":"`+a.Address+`"`) b, _ := r.h.Issue("person.operator", "g14", "a new machine", tunnelKey, 0) @@ -155,7 +155,7 @@ func aDeclinedOrExpiredAskTakesBothKeysAndAVaultFailureKeepsTheEnrolmentForAgain t.Fatalf("stage %s", e.Stage) } // The yes placed the node before the vault failed: a refusal now would take it off again (decline, below for ace). - if _, placed := r.rd["mesh-delivery-assigner.placement"]["novox.node"]; !placed { + if _, placed := r.rd["mesh-delivery-assigner.assignments"]["novox.node"]; !placed { t.Fatal("the yes did not place novox before the vault was asked") } r.now = r.now.Add(time.Minute) diff --git a/modules/mesh-enrolment/cmd/mesh-enrolment/kit_test.go b/modules/mesh-enrolment/cmd/mesh-enrolment/kit_test.go index d82cb107..2a4025b3 100644 --- a/modules/mesh-enrolment/cmd/mesh-enrolment/kit_test.go +++ b/modules/mesh-enrolment/cmd/mesh-enrolment/kit_test.go @@ -11,12 +11,12 @@ import ( "testing" "time" + "git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient" + "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-contracts/identity-and-access/go/clients/meshnodeenrollerclient" "git.novox.be/novox/mesh-contracts/identity-and-access/go/clients/meshvaultclient" mv "git.novox.be/novox/mesh-contracts/identity-and-access/go/seats/meshvault" oc "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel" - "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" - "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" ) // memStore is the holder's bucket in memory: revisions, and the expiry each key was written with. @@ -205,7 +205,7 @@ type said struct { Body json.RawMessage } -// rig is a holder over fakes, with no controller lease at all (issue 1126), one hub in placement and a clock the test moves. +// rig is a holder over fakes, with no controller lease at all (issue 1126), one hub in assignments and a clock the test moves. type rig struct { mu sync.Mutex t *testing.T @@ -229,13 +229,13 @@ const hubRecord = `{"v":1,"record":{"v":1,"node":"hub","tunnel-key":"hub-tunnel- // tunnelKey is a machine's tunnel public key as `nox-mesh-host key` prints it. const tunnelKey = "dHVubmVsLWtleS1vZi10aGUtbmV3LW1hY2hpbmUtMDE=" -const hubPlacement = `{"v":1,"node":"hub","decided":{"hub":true,"endpoint":"192.0.2.1:51820","address":"10.9.0.1","tunnel":{"interface":"wg0","address":"10.9.0.1/24","range":"10.9.0.0/24"}},"modules":[]}` +const hubAssignments = `{"v":1,"node":"hub","decided":{"hub":true,"endpoint":"192.0.2.1:51820","address":"10.9.0.1","tunnel":{"interface":"wg0","address":"10.9.0.1/24","range":"10.9.0.0/24"}},"modules":[]}` func newRig(t *testing.T) *rig { r := &rig{t: t, st: newMem(), vault: &fakeVault{fail: map[string]bool{}}, asker: &fakeAsker{}, now: time.Date(2026, 11, 2, 10, 0, 0, 0, time.UTC), loaded: true} r.rd = memRead{ - "mesh-delivery-assigner.placement": {"hub.node": hubPlacement}, - "node-engine.have": {"hub.node": hubRecord}, + "mesh-delivery-assigner.assignments": {"hub.node": hubAssignments}, + "node-engine.have": {"hub.node": hubRecord}, } r.h = &Holder{Store: r.st, Read: r.rd, Vault: r.vault, Asker: r.asker, AskLasts: time.Hour, Say: func(event string, body any, id string) error { @@ -248,12 +248,12 @@ func newRig(t *testing.T) *rig { AddNode: func(a AddNode) error { r.added = append(r.added, a) r.vault.calls = append(r.vault.calls, a.Change.Op+" "+a.Change.Node) - if r.placeErr == nil { // placement writes or deletes the node as the assigner does on the warrant + if r.placeErr == nil { // assignments writes or deletes the node as the assigner does on the warrant n := a.Change.Node if a.Change.Op == meshmoduleassignerclient.OpRemoveNode { - delete(r.rd["mesh-delivery-assigner.placement"], n+".node") + delete(r.rd["mesh-delivery-assigner.assignments"], n+".node") } else { - r.rd["mesh-delivery-assigner.placement"][n+".node"] = `{"v":1,"node":"` + n + `","decided":{"address":"` + a.Change.Decided.Address + `"},"modules":[]}` + r.rd["mesh-delivery-assigner.assignments"][n+".node"] = `{"v":1,"node":"` + n + `","decided":{"address":"` + a.Change.Decided.Address + `"},"modules":[]}` } } return r.placeErr diff --git a/modules/mesh-enrolment/cmd/mesh-enrolment/main.go b/modules/mesh-enrolment/cmd/mesh-enrolment/main.go index e7a62901..095bff36 100644 --- a/modules/mesh-enrolment/cmd/mesh-enrolment/main.go +++ b/modules/mesh-enrolment/cmd/mesh-enrolment/main.go @@ -1,6 +1,6 @@ // mesh-enrolment: the holder of the seat mesh-enrolment (novox/hq ADR 0587). A Go bundle served by a runtime of its own, // on its own bus account (`runs-as`), so that its account alone writes its bucket `enrolments` with per-key expiry. It -// asks the operator on their channel, the vault to mint and sign, mesh-placement to add the node and mesh-hand-acts to +// asks the operator on their channel, the vault to mint and sign, mesh-module-assigner to add the node and mesh-hand-acts to // record the acts. stdout is the MCP channel; what this module says, it says on stderr. package main @@ -86,7 +86,7 @@ func (runtimeReader) Get(state, key string) ([]byte, bool, error) { return e.Value, true, nil } -// pictures are the states the enrolment follows rather than reads (views.Reader): the assigner's placement (ADR 0615 §1). +// pictures are the states the enrolment follows rather than reads (views.Reader): the assigner's assignments (ADR 0615 §1). type pictures interface { Follows(state string) bool Keys(state string) ([]string, error) @@ -113,19 +113,19 @@ func (r followedReader) Get(state, key string) ([]byte, bool, error) { return r.rest.Get(state, key) } -// PlacementRead is the assigner's placement as the enrolment reads it, and PlacementTool the assigner's read tool the +// AssignmentsRead is the assigner's assignments as the enrolment reads it, and AssignmentsTool the assigner's read tool the // manifest invokes for its first view (ADR 0615, DLV-M6). const ( - PlacementRead = assignerModule + "." + placementState - PlacementTool = assignerModule + ".assigner_read_placement" + AssignmentsRead = assignerModule + "." + assignmentsState + AssignmentsTool = assignerModule + ".assigner_read_assignments" ) -// followed is the enrolment's pictures of the assigner's placement, started at once: its first view asked through -// PlacementTool, then the assigner's views family; past its bound the finding in the enrolment's own findings. +// followed is the enrolment's pictures of the assigner's assignments, started at once: its first view asked through +// AssignmentsTool, then the assigner's views family; past its bound the finding in the enrolment's own findings. func followed() *views.Reader { v := &views.Reader{Module: Module, Logf: func(f string, a ...any) { logf("["+Module+"] "+f, a...) }, Findings: views.OwnFindings{}, Acts: meshhandactsclient.Recorder{Ask: stdio.Ask}} - v.Follow(assignerModule, placementState, PlacementTool) + v.Follow(assignerModule, assignmentsState, AssignmentsTool) v.Start(context.Background()) return v } diff --git a/modules/mesh-enrolment/cmd/mesh-enrolment/names.go b/modules/mesh-enrolment/cmd/mesh-enrolment/names.go index d5f5171b..6f5ce00e 100644 --- a/modules/mesh-enrolment/cmd/mesh-enrolment/names.go +++ b/modules/mesh-enrolment/cmd/mesh-enrolment/names.go @@ -17,6 +17,6 @@ const ( signersKey = "key" // nodeLoginsBucket is the vault's state node-logins (the holder reads it through the grants part, not directly). nodeLoginsBucket = vaultModule + "_node-logins" - // placementState is the assigner's state placement, which the enrolment reads (reads: through its first view). - placementState = "placement" + // assignmentsState is the assigner's state assignments, which the enrolment reads (reads: through its first view). + assignmentsState = "assignments" ) diff --git a/modules/mesh-enrolment/cmd/mesh-enrolment/redeem.go b/modules/mesh-enrolment/cmd/mesh-enrolment/redeem.go index 94376b25..9af6e645 100644 --- a/modules/mesh-enrolment/cmd/mesh-enrolment/redeem.go +++ b/modules/mesh-enrolment/cmd/mesh-enrolment/redeem.go @@ -7,14 +7,14 @@ import ( "fmt" "time" + "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-contracts/identity-and-access/go/clients/meshnodeenrollerclient" - "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" ) // Redeem is the joining node-engine's one call (ADR 0587 §5, §6). It answers only a caller the runtime proves to be // enrol., for a key at stage live (or at redeeming with the same keys' digest), checks the proof against the identity key // presented, and then, each step read back and none repeated that is done: spends the token (the key to redeeming, by -// compare-and-set, with no expiry) once placement holds the node (written on the operator's yes; the vault reads the node's +// compare-and-set, with no expiry) once assignments holds the node (written on the operator's yes; the vault reads the node's // address there) and the tunnel key presented is the one issued for, asks the vault for the node's account and the certificate // of the keys presented, // waits for the bus node to load node., and answers the whole, signed by the vault. Called again with the same keys it @@ -88,15 +88,15 @@ func (h *Holder) spend(node, digest, overlay string) (Enrolment, error) { if err != nil { return Enrolment{}, err } - // The tunnel key presented is the one the operator approved at issue, which placement and the hub already hold. + // The tunnel key presented is the one the operator approved at issue, which assignments and the hub already hold. if overlay != e.OverlayKey { return Enrolment{}, fmt.Errorf("the tunnel key presented is not the one %s's enrolment was issued for: the enrolment is not spent", node) } - // The node entered placement on the operator's yes; the vault reads its address there. - if placements, err := h.placements(); err != nil { - return Enrolment{}, fmt.Errorf("placement cannot be read (call redeem again with the same keys): %w", err) - } else if p, ok := placements[node]; !ok || p.Decided.Address != e.Address { - return Enrolment{}, fmt.Errorf("placement does not hold %s at %s yet (call redeem again with the same keys)", node, e.Address) + // The node entered assignments on the operator's yes; the vault reads its address there. + if assignments, err := h.assignments(); err != nil { + return Enrolment{}, fmt.Errorf("assignments cannot be read (call redeem again with the same keys): %w", err) + } else if p, ok := assignments[node]; !ok || p.Decided.Address != e.Address { + return Enrolment{}, fmt.Errorf("assignments does not hold %s at %s yet (call redeem again with the same keys)", node, e.Address) } switch e.Stage { case StageRedeeming: diff --git a/modules/mesh-enrolment/cmd/mesh-enrolment/redeem_test.go b/modules/mesh-enrolment/cmd/mesh-enrolment/redeem_test.go index e4858f20..a721404b 100644 --- a/modules/mesh-enrolment/cmd/mesh-enrolment/redeem_test.go +++ b/modules/mesh-enrolment/cmd/mesh-enrolment/redeem_test.go @@ -25,7 +25,7 @@ func redeemArgsWith(node string, seed byte, serving, overlay string) meshnodeenr } // 774/S56/AC1: a redeem from another caller, with a bad proof, after the expiry, or with other keys than the first, is -// refused (one row each), and a good one spends the token, writes the node into placement on the issue's warrant and +// refused (one row each), and a good one spends the token, writes the node into assignments on the issue's warrant and // answers once the bus node loaded the account. func redeemRefusesWhatItMustAndSpendsTheTokenOnce(t *testing.T) { good := redeemArgs("ace", 1, "serving-a") @@ -61,7 +61,7 @@ func redeemRefusesWhatItMustAndSpendsTheTokenOnce(t *testing.T) { a.Proof = base64.StdEncoding.EncodeToString(ed25519.Sign(priv, meshnodeenrollerclient.RedeemProof("ace", a.PublicKey, a.OverlayKey, a.SealingKey, a.ServingKey))) return a }(), nil, "not the one ace's enrolment was issued for"}, - {"not in placement yet", "enrol.ace", good, func(r *rig) { delete(r.rd["mesh-delivery-assigner.placement"], "ace.node") }, "placement does not hold ace"}, + {"not in assignments yet", "enrol.ace", good, func(r *rig) { delete(r.rd["mesh-delivery-assigner.assignments"], "ace.node") }, "assignments does not hold ace"}, } for _, c := range cases { r := newRig(t) @@ -76,7 +76,7 @@ func redeemRefusesWhatItMustAndSpendsTheTokenOnce(t *testing.T) { } mustContain(t, err.Error(), c.want) if len(r.vault.calls) != 0 || len(r.added) != 0 { - t.Errorf("%s: the vault %v or placement %v was called", c.name, r.vault.calls, r.added) + t.Errorf("%s: the vault %v or assignments %v was called", c.name, r.vault.calls, r.added) } } r := newRig(t) @@ -90,9 +90,9 @@ func redeemRefusesWhatItMustAndSpendsTheTokenOnce(t *testing.T) { if e.Stage != StageRedeeming || e.KeysDigest != good.KeysDigest() || r.st.ttl[NodeKey("ace")] != 0 { t.Errorf("spent %+v, ttl %v", e, r.st.ttl[NodeKey("ace")]) } - // Redeem writes no placement: the yes did. It binds the identity keys, which the vault checks against the account's digest. + // Redeem writes no assignments: the yes did. It binds the identity keys, which the vault checks against the account's digest. if len(r.added) != 0 || ans.Signature == "" || ans.Account != "account-ace" || ans.Login != "node.ace" || ans.Signer != "vault-signer" || ans.Certificate != "PEM-serving-a" { - t.Errorf("placement %+v, answer %+v", r.added, ans) + t.Errorf("assignments %+v, answer %+v", r.added, ans) } // The certificate's call carries every key presented, which the vault checks against the account's digest. if meshnodeenrollerclient.KeysDigest(r.vault.cert.Identity, r.vault.cert.Overlay, r.vault.cert.Sealing, r.vault.cert.Serving) != good.KeysDigest() { @@ -104,7 +104,7 @@ func redeemRefusesWhatItMustAndSpendsTheTokenOnce(t *testing.T) { } } -// 774/S56/AC2: a redeem stopped after the vault's call, or after the placement call, called again with the same keys, +// 774/S56/AC2: a redeem stopped after the vault's call, or after the assignments call, called again with the same keys, // finishes and answers the same, and the vault minted once (it is asked for the same account). func aRedeemStoppedMidwayIsFinishedByTheSameKeysAgain(t *testing.T) { good := redeemArgs("ace", 1, "serving-a") @@ -126,17 +126,17 @@ func aRedeemStoppedMidwayIsFinishedByTheSameKeysAgain(t *testing.T) { t.Errorf("%s: %v, answer %+v, want %+v", stop, err, got, want) } if len(r.added) != 0 { - t.Errorf("redeem wrote placement %d times", len(r.added)) + t.Errorf("redeem wrote assignments %d times", len(r.added)) } } - // Placement that already holds the node at the approved address is not written again. + // Assignments that already holds the node at the approved address is not written again. r := newRig(t) r.seed("ace", "10.9.0.2", StageRedeeming) e, _ := r.enrolment("ace") e.KeysDigest = good.KeysDigest() _, _ = r.st.Put(NodeKey("ace"), e, 0) - r.rd["mesh-delivery-assigner.placement"]["ace.node"] = `{"v":1,"node":"ace","decided":{"address":"10.9.0.2"},"modules":[]}` + r.rd["mesh-delivery-assigner.assignments"]["ace.node"] = `{"v":1,"node":"ace","decided":{"address":"10.9.0.2"},"modules":[]}` if _, err := r.h.Redeem("enrol.ace", good); err != nil || len(r.added) != 0 { - t.Errorf("%v, placement written %d times", err, len(r.added)) + t.Errorf("%v, assignments written %d times", err, len(r.added)) } } diff --git a/modules/mesh-enrolment/cmd/mesh-enrolment/tokens_test.go b/modules/mesh-enrolment/cmd/mesh-enrolment/tokens_test.go index 7300b786..5cb2e3f2 100644 --- a/modules/mesh-enrolment/cmd/mesh-enrolment/tokens_test.go +++ b/modules/mesh-enrolment/cmd/mesh-enrolment/tokens_test.go @@ -22,7 +22,7 @@ func (r *rig) seed(node, address string, stage string) { _, _ = r.st.Put(NodeKey(node), e, 0) _, _ = r.st.Put(AddressKey(address), Reservation{Node: node, Ask: e.Ask, Issued: e.Issued}, 0) if stage != StageAsked { // the operator's yes placed it, with its tunnel key - r.rd["mesh-delivery-assigner.placement"][node+".node"] = `{"v":1,"node":"` + node + `","decided":{"address":"` + address + `"},"modules":[]}` + r.rd["mesh-delivery-assigner.assignments"][node+".node"] = `{"v":1,"node":"` + node + `","decided":{"address":"` + address + `"},"modules":[]}` } } @@ -98,28 +98,28 @@ func theManifestSaysWhatTheCodeDoes(t *testing.T) { if slices.Contains(m.Reads, "mesh-controller.lease") { t.Error("the manifest reads the controller's lease, which the code no longer reads (issue 1126)") } - // The assigner's placement is followed, never read (ADR 0615 §1, DLV-M6): its read tool invoked, its views family consumed. + // The assigner's assignments is followed, never read (ADR 0615 §1, DLV-M6): its read tool invoked, its views family consumed. for _, r := range m.Reads { if strings.HasPrefix(r, "mesh-delivery-indexer.") || strings.HasPrefix(r, "mesh-delivery-assigner.") { t.Errorf("reads %s: the indexer's and the assigner's state is read by their tools and views families alone", r) } } - if !slices.Contains(m.Invokes, PlacementTool) || !slices.Equal(m.Tools, []string{"enrolment_first_views", "enrolment_first_view_again"}) { - t.Errorf("invokes %v, tools %v: want %s and the first-view tools", m.Invokes, m.Tools, PlacementTool) + if !slices.Contains(m.Invokes, AssignmentsTool) || !slices.Equal(m.Tools, []string{"enrolment_first_views", "enrolment_first_view_again"}) { + t.Errorf("invokes %v, tools %v: want %s and the first-view tools", m.Invokes, m.Tools, AssignmentsTool) } // The holders the holder names beside its own are the manifest's (D-11): the vault's signers it reads, the seats it invokes. - if !slices.Contains(m.Reads, signersRead) || !slices.Contains(m.Invokes, placementAdd) || !slices.Contains(m.Invokes, vaultSign) || + if !slices.Contains(m.Reads, signersRead) || !slices.Contains(m.Invokes, assignmentsAdd) || !slices.Contains(m.Invokes, vaultSign) || !slices.Contains(m.Invokes, vaultToken) || !slices.Contains(m.Invokes, vaultAccount) { - t.Errorf("reads %v, invokes %v: want %s, %s, %s, %s and %s", m.Reads, m.Invokes, signersRead, placementAdd, vaultSign, vaultToken, vaultAccount) + t.Errorf("reads %v, invokes %v: want %s, %s, %s, %s and %s", m.Reads, m.Invokes, signersRead, assignmentsAdd, vaultSign, vaultToken, vaultAccount) } - if enrolmentsBucket != m.Module+"_"+State || !strings.HasPrefix(PlacementTool, assignerModule+".") { - t.Errorf("the names %s, %s are not the manifest's module %s and the assigner %s", enrolmentsBucket, PlacementTool, m.Module, assignerModule) + if enrolmentsBucket != m.Module+"_"+State || !strings.HasPrefix(AssignmentsTool, assignerModule+".") { + t.Errorf("the names %s, %s are not the manifest's module %s and the assigner %s", enrolmentsBucket, AssignmentsTool, m.Module, assignerModule) } var heard []string for _, e := range EndEvents { heard = append(heard, Seat+"."+e) } - heard = append(heard, assignerModule+".views."+placementState) + heard = append(heard, assignerModule+".views."+assignmentsState) if !slices.Equal(m.Consumes, heard) { t.Errorf("consumes %v, the end events are %v", m.Consumes, heard) } diff --git a/modules/mesh-enrolment/cmd/mesh-enrolment/tools.go b/modules/mesh-enrolment/cmd/mesh-enrolment/tools.go index 3d54ec78..aab7e7bc 100644 --- a/modules/mesh-enrolment/cmd/mesh-enrolment/tools.go +++ b/modules/mesh-enrolment/cmd/mesh-enrolment/tools.go @@ -48,9 +48,9 @@ func Tools(h *Holder) []stdio.Tool { {Name: IssueTool, Description: "Open an enrolment: let a new machine join the mesh (novox/hq ADR 0587 §4). Takes the node's name, a why and an " + "expiry (one hour unless given, at most 24 hours). Refused at once, naming every finding, for a name that is no node name " + - "or that a node record, a placement key or an open enrolment holds. Otherwise it chooses the lowest free private-network address, reserves it, " + + "or that a node record, an assignments key or an open enrolment holds. Otherwise it chooses the lowest free private-network address, reserves it, " + "writes the enrolment at stage asked and asks the operator on a channel that proves who answered; only their yes adds the " + - "node to placement with the machine's tunnel key (overlay_key, as `nox-mesh-host key` prints it there), so the hub peers " + + "node to assignments with the machine's tunnel key (overlay_key, as `nox-mesh-host key` prints it there), so the hub peers " + "it, and then makes the token, which carries the first tunnel (the vault makes it, this seat never holds it). The caller is the one the runtime proves.", Input: obj(map[string]any{"node": str("the new machine's name"), "why": str("why this machine joins"), "expires": str("how long the token lasts, e.g. 1h (default) up to 24h"), @@ -65,7 +65,7 @@ func Tools(h *Holder) []stdio.Tool { {Name: RedeemTool, Description: "The joining machine's one call (novox/hq ADR 0587 §5, §6), answered only for a caller the runtime proves to be " + "enrol.: its node-engine presents the public halves of the keys it made where it runs and a proof signed with its " + - "identity key, and the tunnel key it was issued for. The token is spent (once, with those keys) once placement holds the " + + "identity key, and the tunnel key it was issued for. The token is spent (once, with those keys) once assignments holds the " + "node (placed on the operator's yes), the vault gives the node's account and signs its certificate, and the answer is given once the bus node loaded node., " + "signed by the vault. Called again with the same keys it finishes what is not done and answers the same.", Input: obj(map[string]any{"node": str("the machine's name"), "public_key": str("its identity key, base64"), @@ -91,7 +91,7 @@ func Tools(h *Holder) []stdio.Tool { {Name: RevokeTool, Description: "End an enrolment at any stage (novox/hq ADR 0587 §5): at asked it withdraws the ask; at live or redeeming it deletes " + "the key and its address key, and at redeeming the vault retires an account it already minted; a node the yes placed " + - "and that never confirmed is taken off placement again. It only takes access away, so it needs no Approve; it is recorded as an act done by hand. Says " + + "and that never confirmed is taken off assignments again. It only takes access away, so it needs no Approve; it is recorded as an act done by hand. Says " + "enrolment-revoked; the vault then drops the token. A machine stuck at redeeming is ended by this. A revoke that stopped is " + "finished by calling it again.", Input: obj(map[string]any{"node": str("the machine"), "why": str("why, kept with the act")}, "node", "why"), diff --git a/modules/mesh-enrolment/cmd/mesh-enrolment/vault.go b/modules/mesh-enrolment/cmd/mesh-enrolment/vault.go index b989ed14..7fb41a04 100644 --- a/modules/mesh-enrolment/cmd/mesh-enrolment/vault.go +++ b/modules/mesh-enrolment/cmd/mesh-enrolment/vault.go @@ -7,19 +7,19 @@ import ( "fmt" "time" + "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-contracts/identity-and-access/go/clients/meshnodeenrollerclient" "git.novox.be/novox/mesh-contracts/identity-and-access/go/clients/meshvaultclient" mv "git.novox.be/novox/mesh-contracts/identity-and-access/go/seats/meshvault" - "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" ) // Seats the holder calls (ADR 0587 §3, §5, §7). The vault's two verbs are the ones its manifest names this seat the only // caller of; its `sign` names this seat for a node's certificate and a redeem answer. const ( - vaultToken = "seat:mesh-vault." + mv.VerbEnrolmentToken - vaultAccount = "seat:mesh-vault." + mv.VerbNodeAccount - vaultSign = "seat:mesh-vault." + mv.VerbSign - placementAdd = "seat:" + meshmoduleassigner.Seat + "." + meshmoduleassigner.VerbPropose + vaultToken = "seat:mesh-vault." + mv.VerbEnrolmentToken + vaultAccount = "seat:mesh-vault." + mv.VerbNodeAccount + vaultSign = "seat:mesh-vault." + mv.VerbSign + assignmentsAdd = "seat:" + meshmoduleassigner.Seat + "." + meshmoduleassigner.VerbPropose ) // Call asks a seat verb by its address (stdio.Ask through the runtime, or a test's). @@ -99,10 +99,10 @@ func (v vaultOver) Signer() (string, error) { return k.Key, nil } -// addNodeOver asks mesh-placement to add the node with the issue's warrant (ADR 0587 §7). +// addNodeOver asks mesh-module-assigner to add the node with the issue's warrant (ADR 0587 §7). func addNodeOver(call Call) func(AddNode) error { return func(a AddNode) error { - _, err := call(placementAdd, map[string]any{"why": a.Why, "changes": []any{a.Change}, "warrant": a.Warrant}) + _, err := call(assignmentsAdd, map[string]any{"why": a.Why, "changes": []any{a.Change}, "warrant": a.Warrant}) if err != nil { return fmt.Errorf("mesh-module-assigner did not take the node %s: %w", a.Change.Node, err) } diff --git a/modules/mesh-enrolment/go.mod b/modules/mesh-enrolment/go.mod index b0252ace..fa0d3292 100644 --- a/modules/mesh-enrolment/go.mod +++ b/modules/mesh-enrolment/go.mod @@ -6,8 +6,8 @@ require ( git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-contracts/identity-and-access/go/clients/meshnodeenrollerclient v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-contracts/identity-and-access/go/clients/meshvaultclient v0.0.0-00010101000000-000000000000 - git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient v0.0.0-00010101000000-000000000000 - git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-contracts/identity-and-access/go/seats/meshnodeenroller v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshhandactsclient v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-contracts/identity-and-access/go/seats/meshvault v0.0.0-00010101000000-000000000000 diff --git a/modules/mesh-enrolment/module.json b/modules/mesh-enrolment/module.json index b3e92e18..302cdf2b 100644 --- a/modules/mesh-enrolment/module.json +++ b/modules/mesh-enrolment/module.json @@ -21,7 +21,7 @@ "mesh-node-enroller.enrolment-refused", "mesh-node-enroller.enrolment-revoked", "mesh-node-enroller.enrolment-expired", - "mesh-delivery-assigner.views.placement" + "mesh-delivery-assigner.views.assignments" ], "uses": [ "operator-channel" @@ -33,7 +33,7 @@ "seat:mesh-vault.enrolment-token", "seat:mesh-vault.node-account", "seat:mesh-vault.sign", - "mesh-delivery-assigner.assigner_read_placement" + "mesh-delivery-assigner.assigner_read_assignments" ], "reads": [ "mesh-grants.user-list", diff --git a/modules/mesh-grants/cmd/mesh-grants/integration_test.go b/modules/mesh-grants/cmd/mesh-grants/integration_test.go index 79ef9c83..95b78f50 100644 --- a/modules/mesh-grants/cmd/mesh-grants/integration_test.go +++ b/modules/mesh-grants/cmd/mesh-grants/integration_test.go @@ -6,6 +6,7 @@ import ( "context" "encoding/json" "errors" + "maps" "os" "path/filepath" "slices" @@ -19,10 +20,10 @@ import ( "github.com/nats-io/nats.go" "github.com/nats-io/nats.go/jetstream" + "git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient" "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" + "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-contracts/go/seats/nodeengine" - "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" - "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" "git.novox.be/novox/mesh-delivery/modules/mesh-grants/grantsstate" @@ -114,10 +115,12 @@ func TestTheGrantsPartWhole(t *testing.T) { t.Fatal(err) } manifest(grants.Module, string(own)) - pl, _ := meshmoduleassignerclient.EncodePlacement(meshmoduleassigner.Placement{Node: "a", Modules: []meshmoduleassigner.PlacedModule{{Module: "nats"}, {Module: "mod-c"}, + values, _ := meshmoduleassignerclient.AssignmentsValues(meshmoduleassigner.Assignments{Node: "a", Modules: []meshmoduleassigner.PlacedModule{{Module: "nats"}, {Module: "mod-c"}, {Module: grants.Module}}}) - if _, err := kvs[grants.ReadPlacement].Put(ctx, "placement.a", pl); err != nil { - t.Fatal(err) + for _, k := range slices.Sorted(maps.Keys(values)) { // a key per module, then the node key + if _, err := kvs[grants.ReadAssignments].Put(ctx, k, values[k]); err != nil { + t.Fatal(err) + } } busKey, _ := seal.GenerateKeyPair() put(grants.ReadHave, "a.node", map[string]any{"v": 1}) @@ -241,7 +244,7 @@ func TestTheGrantsPartWhole(t *testing.T) { } until("every first view asked and refused once", func() bool { return refusedAsks.Load() >= int32(len(grants.FirstViews)) }) until("the part saying it waits on the indexer and the assigner", func() bool { - return logged.has("mesh-delivery-indexer.manifests") && logged.has("mesh-delivery-assigner.placement") && logged.has("is not answered") + return logged.has("mesh-delivery-indexer.manifests") && logged.has("mesh-delivery-assigner.assignments") && logged.has("is not answered") }) if _, err := kvs[grants.Module+"."+grants.StateMemberships].Get(ctx, "a.mod-c"); err == nil { t.Error("a membership composed before the owners answered its first views") diff --git a/modules/mesh-grants/cmd/mesh-grants/main.go b/modules/mesh-grants/cmd/mesh-grants/main.go index b52f0518..329d3e93 100644 --- a/modules/mesh-grants/cmd/mesh-grants/main.go +++ b/modules/mesh-grants/cmd/mesh-grants/main.go @@ -1,5 +1,5 @@ // Command mesh-grants is the grants part (novox/hq ADR 0547 clauses 5 to 8, issue 1945): a process on its own bus -// credential that watches placement, the assigner's wants, the indexer's manifests, the builder's builds, mesh-config's +// credential that watches assignments, the assigner's wants, the indexer's manifests, the builder's builds, mesh-config's // resolved values and the node agents' haves, and on every change composes each assignment's membership, the bus's user // list and each manifest's buckets, logs and streams with the bus composer the controller uses (mesh-sdk buscompose). // diff --git a/modules/mesh-grants/cmd/mesh-grants/tools.go b/modules/mesh-grants/cmd/mesh-grants/tools.go index 9c0f81db..6a1ce08b 100644 --- a/modules/mesh-grants/cmd/mesh-grants/tools.go +++ b/modules/mesh-grants/cmd/mesh-grants/tools.go @@ -175,7 +175,7 @@ func toolsOf(p *grants.Part) []stdio.Tool { }}, {Name: grants.ToolUserList, Description: "The bus's user list (ADR 0547 clauses 6 and 8): the list the grants part " + "composed (digest, revision, users), what the bus node says it loaded (source, revision, digest, users, error), and how " + - "they differ: users added, users dropped (and of those, gone from placement), each user's grant differences, the " + + "they differ: users added, users dropped (and of those, gone from assignments), each user's grant differences, the " + "assignments left out, every input the bus does not carry, named; and per user whether the vault holds its hash " + "(mesh-vault.bus-accounts, ADR 0547 clause 9) and how its line would differ from the loaded one (same, adds, left-out " + "for want of a credential, loses-login), never a hash. Read-only.", @@ -196,7 +196,7 @@ func toolsOf(p *grants.Part) []stdio.Tool { Input: schema(map[string]string{"module": "the module"}, "module"), Run: func(a map[string]any) (any, error) { return p.AssertOne(grants.Answer(a, "module")) }}, {Name: grants.ToolAccounts, Description: "The bus accounts the mesh wants (novox/hq ADR 0561 §2, §9): every account the vault " + - "mints that placement composes (a module's, a process's, a machine's provisioner client's), as the grants part last " + + "mints that assignments composes (a module's, a process's, a machine's provisioner client's), as the grants part last " + "wrote it to its state accounts, which the vault alone reads: user, kind, node, module, process, the bus's address and " + "fingerprint, a module's claims with the verbs each seat promises, and when the key was written; one user's or all; " + "what the last pass wrote, deleted or kept; and the durable consumers the accounts need as the last pass found them " + @@ -232,15 +232,15 @@ func toolsOf(p *grants.Part) []stdio.Tool { return p.AcceptHashes(stdio.CallerOf(a), users, grants.Answer(a, "why")) }}, {Name: grants.ToolFirstViews, Description: "The grants part's first views of the owners' states (novox/hq ADR 0615 §4 to §6, " + - "ADR 0622): the assigner's placement and wants, the indexer's manifests, modules and verdicts, each with its owner, tool " + + "ADR 0622): the assigner's assignments and wants, the indexer's manifests, modules and verdicts, each with its owner, tool " + "and the revision its picture holds, when it asked and when it was answered, how many asks and hand acts, and whether " + "its condition is raised, as the part's process last kept them. Read-only.", Run: func(map[string]any) (any, error) { return p.FirstViewsOf() }}, - {Name: grants.ToolFirstViewAgain, Description: "Ask one of the grants part's first views once more now (placement, wants, " + + {Name: grants.ToolFirstViewAgain, Description: "Ask one of the grants part's first views once more now (assignments, wants, " + "manifests, modules or verdicts; none for all), by a person's act with why, recorded as a hand act: the step after an " + "owner's state was written again from its sources (an empty answer at revision 0, condition " + "mesh-grants.first-view-empty). Asks the part's process, which alone follows them; grants_first_views shows the answer.", - Input: schema(map[string]string{"state": "placement, wants, manifests, modules or verdicts; none for all", "why": "why, kept in the hand act"}, "why"), + Input: schema(map[string]string{"state": "assignments, wants, manifests, modules or verdicts; none for all", "why": "why, kept in the hand act"}, "why"), Run: func(a map[string]any) (any, error) { return p.AskFirstViewAgain(stdio.CallerOf(a), grants.Answer(a, "state"), grants.Answer(a, "why"), recordHandAct) }}, diff --git a/modules/mesh-grants/go.mod b/modules/mesh-grants/go.mod index 2df34bcb..04d40b41 100644 --- a/modules/mesh-grants/go.mod +++ b/modules/mesh-grants/go.mod @@ -10,9 +10,9 @@ require ( git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshconditions v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshhealers v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-contracts/identity-and-access/go/clients/meshvaultclient v0.0.0-00010101000000-000000000000 - git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient v0.0.0-00010101000000-000000000000 - git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient v0.0.0-00010101000000-000000000000 - git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-delivery/contracts/go/clients/meshwantsclient v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds v0.0.0-00010101000000-000000000000 @@ -47,7 +47,7 @@ require ( require ( git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshhandacts v0.0.0-00010101000000-000000000000 // indirect git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshselfcheck v0.0.0-00010101000000-000000000000 // indirect - git.novox.be/novox/mesh-contracts/placement/go/seats/meshwants v0.0.0-00010101000000-000000000000 // indirect + git.novox.be/novox/mesh-delivery/contracts/go/seats/meshwants v0.0.0-00010101000000-000000000000 // indirect git.novox.be/novox/mesh-delivery/contracts/go/clients/containerimageregistryclient v0.0.0-00010101000000-000000000000 // indirect git.novox.be/novox/mesh-delivery/contracts/go/seats/containerimageregistry v0.0.0-00010101000000-000000000000 // indirect git.novox.be/novox/mesh-delivery/contracts/go/seats/ociartifactregistry v0.0.0-00010101000000-000000000000 // indirect diff --git a/modules/mesh-grants/grants_test.go b/modules/mesh-grants/grants_test.go index c9e45eed..6592322e 100644 --- a/modules/mesh-grants/grants_test.go +++ b/modules/mesh-grants/grants_test.go @@ -22,17 +22,17 @@ import ( "github.com/nats-io/nats.go/jetstream" + "git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient" "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" + "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-contracts/go/seats/nodeengine" "git.novox.be/novox/mesh-contracts/identity-and-access/go/clients/meshvaultclient" "git.novox.be/novox/mesh-contracts/identity-and-access/go/seats/meshnodeenroller" "git.novox.be/novox/mesh-contracts/identity-and-access/go/seats/meshvault" occ "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/clients/operatorchannelclient" oc "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel" - "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" - "git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient" - "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" + "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds" "git.novox.be/novox/mesh-delivery/modules/mesh-grants/grantsstate" @@ -339,12 +339,9 @@ func (b *fakeBus) refuse(module, raw, problem string) { Problems: []string{problem}}) } -// assign places module on node in the seat's placement, the one place the part reads it (ADR 0549 §5; issue 774). +// assign places module on node in the seat's assignments, the one place the part reads it (ADR 0549 §5; issue 774). func (b *fakeBus) assign(node, module string) { - var pl meshmoduleassigner.Placement - if raw, ok := b.states[grants.ReadPlacement]["placement."+node]; ok { - pl, _ = meshmoduleassignerclient.DecodePlacement(raw) - } + pl := b.assignmentsOf(node) pl.Node = node for _, m := range pl.Modules { if m.Module == module { @@ -352,25 +349,37 @@ func (b *fakeBus) assign(node, module string) { } } pl.Modules = append(pl.Modules, meshmoduleassigner.PlacedModule{Module: module}) - raw, err := meshmoduleassignerclient.EncodePlacement(pl) + b.setAssignments(pl) +} + +// unassign takes module off node in the seat's assignments. +func (b *fakeBus) unassign(node, module string) { + pl := b.assignmentsOf(node) + pl.Modules = slices.DeleteFunc(pl.Modules, func(m meshmoduleassigner.PlacedModule) bool { return m.Module == module }) + b.setAssignments(pl) +} + +// assignmentsOf is what the seat's state holds of node, assembled from its keys. +func (b *fakeBus) assignmentsOf(node string) meshmoduleassigner.Assignments { + got, _ := meshmoduleassignerclient.AssignmentsOf(b.states[grants.ReadAssignments]) + return got[node] +} + +// setAssignments holds a node's assignments in the seat's state as its keys: a key per module, and the node key. +func (b *fakeBus) setAssignments(pl meshmoduleassigner.Assignments) { + if b.states[grants.ReadAssignments] == nil { + b.states[grants.ReadAssignments] = map[string][]byte{} + } + for key := range b.states[grants.ReadAssignments] { + if strings.HasPrefix(key, pl.Node+".") { + delete(b.states[grants.ReadAssignments], key) + } + } + values, err := meshmoduleassignerclient.AssignmentsValues(pl) if err != nil { panic(err) } - if b.states[grants.ReadPlacement] == nil { - b.states[grants.ReadPlacement] = map[string][]byte{} - } - b.states[grants.ReadPlacement]["placement."+node] = raw -} - -// unassign takes module off node in the seat's placement. -func (b *fakeBus) unassign(node, module string) { - pl, err := meshmoduleassignerclient.DecodePlacement(b.states[grants.ReadPlacement]["placement."+node]) - if err != nil { - return - } - pl.Modules = slices.DeleteFunc(pl.Modules, func(m meshmoduleassigner.PlacedModule) bool { return m.Module == module }) - raw, _ := meshmoduleassignerclient.EncodePlacement(pl) - b.states[grants.ReadPlacement]["placement."+node] = raw + maps.Copy(b.states[grants.ReadAssignments], values) } const ( @@ -576,8 +585,8 @@ func TestGrants(t *testing.T) { key, _ := builds.BuilderBuildKey("mod-x", fp) b.put(grants.ReadBuilds, key, map[string]any{"v": keys.Version, "state": "done", "manifest": digest}) }, []string{"a.mod-x", "a.nats", "b.mod-y", "node.a", "node.b"}, "people"}, - {"placement refused is a gap; the machines come from the haves", func(b *fakeBus) { b.refused[grants.ReadPlacement] = true }, - []string{"node.a", "node.b"}, grants.ReadPlacement}, + {"assignments refused is a gap; the machines come from the haves", func(b *fakeBus) { b.refused[grants.ReadAssignments] = true }, + []string{"node.a", "node.b"}, grants.ReadAssignments}, } { t.Run(row.name, func(t *testing.T) { b := fixture() @@ -644,7 +653,7 @@ func TestGrants(t *testing.T) { } }) } - // ADR 0567 §3, §9: the seat mesh-placement's candidates are read by every node agent (mesh-sdk NodeComposeReads) + // ADR 0567 §3, §9: the seat mesh-module-assigner's candidates are read by every node agent (mesh-sdk NodeComposeReads) // and by a module whose manifest reads them (the view), written by their holder, the assigner, and read by no other. cands := fixture() cands.manifestOf("mesh-delivery-assigner", `{"module":"mesh-delivery-assigner","version":"1","own-secrets":{"broker":"/var/lib/as/broker"},"state":[{"name":"candidates","history":5}]}`) @@ -655,12 +664,12 @@ func TestGrants(t *testing.T) { } var readers []string for _, u := range shadowOf(t, cands) { - if slices.Contains(u.Publish, "$JS.API.DIRECT.GET.KV_"+buscompose.PlacementCandidatesBucket+".>") { + if slices.Contains(u.Publish, "$JS.API.DIRECT.GET.KV_"+buscompose.AssignmentsCandidatesBucket+".>") { readers = append(readers, u.User) } } if want := []string{"a.mesh-delivery-assigner", "a.mesh-delivery-view", "node.a", "node.b"}; !slices.Equal(readers, want) { - t.Errorf("the placement candidates are read by %v, want the node agents, the view and their holder %v", readers, want) + t.Errorf("the assignments candidates are read by %v, want the node agents, the view and their holder %v", readers, want) } b := fixture() users := shadowOf(t, b) @@ -880,7 +889,7 @@ func TestGrants(t *testing.T) { recs, _ := grants.Records(grants.ReadInputs(b, &grants.Manifests{})) grants.WithRootFree(&recs, b) kept := false - for _, k := range buscompose.PlacementsOf(recs, recs.Interchangeable).Kinds { + for _, k := range buscompose.AssignmentsOf(recs, recs.Interchangeable).Kinds { kept = kept || slices.Contains(k.Capabilities, "verified-sender") } if kept != row.keeps { @@ -1134,7 +1143,7 @@ func TestGrants(t *testing.T) { {"equal", shadow, nil, nil, nil, nil, true, nil, nil, 0}, {"one user the bus lacks", shadow[1:], []string{shadow[0].User}, nil, nil, nil, false, nil, []string{shadow[0].User}, 0}, {"one loaded user still placed", append(slices.Clone(shadow), grants.User{User: "a.mod-x.worker"}), nil, []string{"a.mod-x.worker"}, nil, nil, false, nil, []string{"a.mod-x.worker"}, 0}, - {"one loaded user placement no longer names", append(slices.Clone(shadow), grants.User{User: "b.gone"}), nil, []string{"b.gone"}, []string{"b.gone"}, nil, true, nil, nil, 0}, + {"one loaded user assignments no longer names", append(slices.Clone(shadow), grants.User{User: "b.gone"}), nil, []string{"b.gone"}, []string{"b.gone"}, nil, true, nil, nil, 0}, {"grants differ", changed, nil, nil, nil, []string{"a.mod-x"}, false, nil, []string{"a.mod-x"}, 1}, {"only a right added", fewer, nil, nil, nil, []string{"a.mod-x"}, false, nil, nil, 1}, {"a deny added refuses", readable, nil, nil, nil, []string{"a.mod-x"}, false, nil, []string{"a.mod-x"}, 1}, @@ -1196,9 +1205,9 @@ func TestGrants(t *testing.T) { } }) - // 1126 (ADR 0549 §1, §3): once the catalogue places the nodes, a seat held once for the mesh is held where placement - // says, not on every machine running a module that claims it; what placement adds is composed beside the assignments. - t.Run("a mesh seat is held where placement says", func(t *testing.T) { + // 1126 (ADR 0549 §1, §3): once the catalogue places the nodes, a seat held once for the mesh is held where assignments + // says, not on every machine running a module that claims it; what assignments adds is composed beside the assignments. + t.Run("a mesh seat is held where assignments says", func(t *testing.T) { store := catalogue.Manifest{Module: "store", Version: "1", DefinesSeats: []catalogue.SeatDeclaration{{Name: "store-seat", Scope: catalogue.ScopeMesh, Serves: []catalogue.Verb{{Name: "databases"}}}}, Claims: []catalogue.Claim{{Name: "store-seat", Scope: catalogue.ScopeMesh}}} @@ -1212,51 +1221,41 @@ func TestGrants(t *testing.T) { return n } if recs, _ := grants.Records(in); holds(recs, "a") != 1 || holds(recs, "b") != 1 { - t.Fatalf("before placement the seat is held on a %d and b %d times, want both", holds(recs, "a"), holds(recs, "b")) + t.Fatalf("before assignments the seat is held on a %d and b %d times, want both", holds(recs, "a"), holds(recs, "b")) } - in.Placed = map[string]meshmoduleassigner.Placement{ + in.Placed = map[string]meshmoduleassigner.Assignments{ "a": {Node: "a", Modules: []meshmoduleassigner.PlacedModule{{Module: "store"}}, Holds: []meshmoduleassigner.Holding{{Claim: "store-seat", Scope: "mesh", Module: "store"}}}, "b": {Node: "b", Modules: []meshmoduleassigner.PlacedModule{{Module: "store"}}}} if recs, _ := grants.Records(in); holds(recs, "a") != 1 || holds(recs, "b") != 0 { t.Errorf("placed, the seat is held on a %d and b %d times, want a alone", holds(recs, "a"), holds(recs, "b")) } b := fixture() - pl, _ := meshmoduleassignerclient.EncodePlacement(meshmoduleassigner.Placement{Node: "b", Modules: []meshmoduleassigner.PlacedModule{{Module: "mod-y"}, {Module: "mod-x"}}}) - b.states[grants.ReadPlacement] = map[string][]byte{"placement.b": pl} + b.setAssignments(meshmoduleassigner.Assignments{Node: "b", Modules: []meshmoduleassigner.PlacedModule{{Module: "mod-y"}, {Module: "mod-x"}}}) read := grants.ReadInputs(b, &grants.Manifests{}) if !slices.Contains(read.Assigned["b"], "mod-x") || read.Placed["b"].Node != "b" || slices.ContainsFunc(read.Gaps, func(g grants.Gap) bool { return g.Input == "seat holdings" }) { t.Errorf("read: assigned %v, placed %v, gaps %v", read.Assigned, read.Placed, read.Gaps) } - // Research 072 row 37: a node key and a key per module, the legacy mirror left out, read the same. - perModule, _ := meshmoduleassignerclient.PlacementValues(meshmoduleassigner.Placement{Node: "b", Modules: []meshmoduleassigner.PlacedModule{{Module: "mod-y"}, {Module: "mod-x"}}}) - delete(perModule, "placement.b") - nb := fixture() - nb.states[grants.ReadPlacement] = perModule - if got := grants.ReadInputs(nb, &grants.Manifests{}); !slices.Equal(meshmoduleassignerclient.Assigned(got.Placed["b"]), []string{"mod-y", "mod-x"}) || - slices.ContainsFunc(got.Gaps, func(g grants.Gap) bool { return strings.HasPrefix(g.Input, grants.ReadPlacement) }) { - t.Errorf("read of the keys per module: placed %v, gaps %v", got.Placed, got.Gaps) - } - b.refused[grants.ReadPlacement] = true - // The placement alone places (issue 774, 774/S29): not read, nothing is placed, and it is a failed read. - if read := grants.ReadInputs(b, &grants.Manifests{}); !slices.ContainsFunc(read.Gaps, func(g grants.Gap) bool { return g.Input == grants.ReadPlacement && g.Failed }) || + b.refused[grants.ReadAssignments] = true + // The assignments alone places (issue 774, 774/S29): not read, nothing is placed, and it is a failed read. + if read := grants.ReadInputs(b, &grants.Manifests{}); !slices.ContainsFunc(read.Gaps, func(g grants.Gap) bool { return g.Input == grants.ReadAssignments && g.Failed }) || len(read.Assigned) != 0 { - t.Errorf("placement not on the bus is no failed read, or something is placed: %v, %v", read.Gaps, read.Assigned) + t.Errorf("assignments not on the bus is no failed read, or something is placed: %v, %v", read.Gaps, read.Assigned) } - // Issue 1945, the user list of 2026-10-27 09:21: a placement's holds are the holders on record, not every holder. - // A mesh seat no placement holds keeps the module claiming it as its holder, wherever placement puts it. + // Issue 1945, the user list of 2026-10-27 09:21: the assignments' holds are the holders on record, not every holder. + // A mesh seat no assignments holds keeps the module claiming it as its holder, wherever assignments puts it. in.Manifests["other"] = catalogue.Manifest{Module: "other", Version: "1", Claims: []catalogue.Claim{{Name: "other-seat", Scope: catalogue.ScopeMesh}}, DefinesSeats: []catalogue.SeatDeclaration{{Name: "other-seat", Scope: catalogue.ScopeMesh, Serves: []catalogue.Verb{{Name: "v"}}}}} in.Assigned["b"] = append(in.Assigned["b"], "other") - in.Placed["b"] = meshmoduleassigner.Placement{Node: "b", Modules: []meshmoduleassigner.PlacedModule{{Module: "store"}, {Module: "other"}}} + in.Placed["b"] = meshmoduleassigner.Assignments{Node: "b", Modules: []meshmoduleassigner.PlacedModule{{Module: "store"}, {Module: "other"}}} if recs, _ := grants.Records(in); holds(recs, "a") != 1 || holds(recs, "b") != 1 { - t.Errorf("a seat no placement holds is held on b %d times, want once (and store-seat on a alone: a %d)", holds(recs, "b"), holds(recs, "a")) + t.Errorf("a seat no assignments holds is held on b %d times, want once (and store-seat on a alone: a %d)", holds(recs, "b"), holds(recs, "a")) } - in.Placed["b"] = meshmoduleassigner.Placement{Node: "b", Modules: []meshmoduleassigner.PlacedModule{{Module: "store"}, {Module: "other"}}, + in.Placed["b"] = meshmoduleassigner.Assignments{Node: "b", Modules: []meshmoduleassigner.PlacedModule{{Module: "store"}, {Module: "other"}}, Holds: []meshmoduleassigner.Holding{{Claim: "other-seat", Scope: "mesh", Module: "someone-else"}}} if recs, _ := grants.Records(in); holds(recs, "b") != 0 { - t.Errorf("a seat placement holds by another module is still held by other on b") + t.Errorf("a seat assignments holds by another module is still held by other on b") } - // The live shapes: the seed's novox placement (mesh-catalog 0c61bad, its twelve holdings, without + // The live shapes: the seed's novox assignments (mesh-catalog 0c61bad, its twelve holdings, without // anthropic-licence-manager), the licence manager's manifest (mesh-catalog c05aa5e6), and the subjects the bus // grants novox.node-tools for its seat (nats_users, 2026-10-27 19:40Z). file := func(name string, v any) { @@ -1268,9 +1267,9 @@ func TestGrants(t *testing.T) { t.Fatal(err) } } - var seed meshmoduleassigner.Placement + var seed meshmoduleassigner.Assignments var live struct{ Users []grants.User } - file("placement-novox-0c61bad.json", &seed) + file("assignments-novox-0c61bad.json", &seed) file("node-tools-novox-licence-subjects.json", &live) licence, _ := os.ReadFile(filepath.Join("testdata", "claude-licence-manager.json")) n := newFakeBus() @@ -1285,14 +1284,13 @@ func TestGrants(t *testing.T) { n.assign("novox", "claude-licence-manager") n.manifestOf("node-tools", `{"module":"node-tools","version":"1","tool-runner":true}`) // the machine's tool runner, which carries the seat n.assign("novox", "node-tools") - // The live placement names mesh-host; with its manifest's state the runner once carried it and the whole list was + // The live assignments names mesh-host; with its manifest's state the runner once carried it and the whole list was // refused as a second writer of the engine's health (issue 774, 2026-10-30): the shadow below now composes, with the states the // engine's manifest declares today (health, findings, data, packages): the findings state, written by each node's engine // alone (ADR 0636 §1), does not bring the refusal back, and the runner is granted none of them. n.manifestOf(buscompose.EngineModule, `{"module":"mesh-host","version":"1","consumes":["restic.backed-up"],"state":[{"name":"health"},{"name":"findings"},{"name":"data"},{"name":"packages"}]}`) n.put(grants.ReadHave, "novox.node", map[string]any{"v": 1}) - pl, _ = meshmoduleassignerclient.EncodePlacement(seed) - n.states[grants.ReadPlacement] = map[string][]byte{"placement.novox": pl} + n.setAssignments(seed) recs, _ := grants.Records(grants.ReadInputs(n, &grants.Manifests{})) var tools []buscompose.ProcessPrincipal for _, d := range recs.Assigned["novox"] { @@ -1305,7 +1303,7 @@ func TestGrants(t *testing.T) { t.Errorf("the bus module's principals = %+v, want the keeper keeping and nats-tools carrying both removals", tools) } if m := grants.Memberships(recs)["novox.claude-licence-manager"]; len(m.Membership.Seats) == 0 { - t.Errorf("the licence manager's membership under the seeded placement carries no seats: %+v", m.Membership) + t.Errorf("the licence manager's membership under the seeded assignments carries no seats: %+v", m.Membership) } var runtime grants.User for _, u := range shadowOf(t, n) { @@ -1318,7 +1316,7 @@ func TestGrants(t *testing.T) { } for _, s := range live.Users[0].Subscribe { if strings.Contains(s, ".call.") && !slices.Contains(runtime.Subscribe, s) { - t.Errorf("under the seeded placement novox.node-tools is not granted %s, which the bus grants it today", s) + t.Errorf("under the seeded assignments novox.node-tools is not granted %s, which the bus grants it today", s) } } }) @@ -1448,7 +1446,7 @@ func TestGrants(t *testing.T) { moved := p.Compose() if !moved.Written || moved.Revision != 2 || !slices.Equal(moved.Published, []string{"a.mod-x", "b.mod-x"}) || len(moved.NotPublished) != 0 || moved.UserList.Compare.Equal() || !slices.Equal(moved.UserList.Compare.Added, []string{"b.mod-x"}) { - t.Errorf("a composition after placement moved = %+v", moved) + t.Errorf("a composition after assignments moved = %+v", moved) } got, _ = p.UserList() ul, _ := got.(map[string]any) @@ -1483,10 +1481,10 @@ func TestGrants(t *testing.T) { var said []oc.Condition p.Say = func(c oc.Condition) error { said = append(said, c); return nil } - // settled is the placement of the lists agreeing, every hash held, and the loaded list as edit leaves it. + // settled is the assignments of the lists agreeing, every hash held, and the loaded list as edit leaves it. settled := func(edit func(modX *grants.User)) func() { return func() { - b.states[grants.ReadPlacement] = nil + b.states[grants.ReadAssignments] = nil b.assign("a", "nats") b.assign("a", "mod-x") b.assign("b", "mod-y") @@ -1981,11 +1979,11 @@ func TestGrants(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) done := make(chan struct{}) go func() { f.Run(ctx); close(done) }() - waitOpened(t, w, grants.ReadPlacement) + waitOpened(t, w, grants.ReadAssignments) if r := f.Refused(); r[grants.ReadModules] == "" { waitFor(t, func() bool { return f.Refused()[grants.ReadModules] != "" }) } - w.change(grants.ReadPlacement, "placement.a") + w.change(grants.ReadAssignments, "a.node") waitOpened(t, w, grants.ReadModules) waitFor(t, func() bool { return len(f.Refused()) == 0 }) select { @@ -2199,11 +2197,11 @@ func TestGrants(t *testing.T) { } }) - // 774/S29/AC1 (ADR 0549 §5, ADR 0571 §10): the memberships composed from the placement alone, with no read of the - // controller, are the ones the union of placement and assignments gave on the same input: the fixture's (two nodes, + // 774/S29/AC1 (ADR 0549 §5, ADR 0571 §10): the memberships composed from the assignments alone, with no read of the + // controller, are the ones the union of assignments and assignments gave on the same input: the fixture's (two nodes, // the bus and mod-x on a, mod-y on b, as placed on the live mesh's own shape of nats beside its modules), whose // assignments named the same modules. Neither the controller's assignments nor its condition events are read. - t.Run("placement alone places, with the memberships the union gave", func(t *testing.T) { + t.Run("assignments alone places, with the memberships the union gave", func(t *testing.T) { b := fixture() p := partOn(b) p.Compose() @@ -2211,7 +2209,7 @@ func TestGrants(t *testing.T) { for key := range want { raw, _, found, _ := b.ownGet(grants.StateMemberships, key) if !found { - t.Fatalf("no membership of %s from the placement", key) + t.Fatalf("no membership of %s from the assignments", key) } want[key] = string(raw) } @@ -2379,9 +2377,9 @@ func TestGrants(t *testing.T) { b.manifestOf("mesh-delivery-indexer", `{"module":"mesh-delivery-indexer","version":"1","own-secrets":{"broker":"/var/lib/i/broker"},`+ `"emits":["views.manifests","views.modules"],"streams":[{"name":"indexer-views","events":["views.*"],"max-age-days":7,"max-mib":256}]}`) b.manifestOf("mesh-delivery-assigner", `{"module":"mesh-delivery-assigner","version":"1","own-secrets":{"broker":"/var/lib/as/broker"},`+ - `"emits":["views.placement","views.seat-holders"],"streams":[{"name":"assigner-views","events":["views.*"],"max-age-days":7,"max-mib":256}]}`) + `"emits":["views.assignments","views.seat-holders"],"streams":[{"name":"assigner-views","events":["views.*"],"max-age-days":7,"max-mib":256}]}`) b.manifestOf("node-tools", `{"module":"node-tools","version":"1","tool-runner":true,"own-secrets":{"broker":"/var/lib/nt/broker"},`+ - `"consumes":["mesh-delivery-assigner.views.placement","mesh-delivery-assigner.views.seat-holders","mesh-delivery-indexer.views.manifests",`+ + `"consumes":["mesh-delivery-assigner.views.assignments","mesh-delivery-assigner.views.seat-holders","mesh-delivery-indexer.views.manifests",`+ `"mesh-delivery-indexer.views.modules"],"state":[{"name":"findings","history":5}]}`) for _, m := range []string{"mesh-delivery-indexer", "mesh-delivery-assigner", "node-tools"} { b.assign("a", m) @@ -2422,7 +2420,7 @@ func TestGrants(t *testing.T) { if fv.Tool != tool { continue } - if empty.Load() && fv.State == "placement" { + if empty.Load() && fv.State == "assignments" { return views.Answer{Revision: 0, Created: created, Values: map[string]json.RawMessage{}}, nil } values := map[string]json.RawMessage{} @@ -2484,37 +2482,37 @@ func TestGrants(t *testing.T) { if !raised(grants.FirstViewUnanswered+".mesh-delivery-indexer.verdicts", occ.StateRaised) { t.Fatalf("the refused raise was not said again at the pass: %+v", said) } - if !slices.ContainsFunc(before.UserList.Gaps, func(g grants.Gap) bool { return g.Input == grants.ReadPlacement && g.Failed }) { - t.Fatalf("placement answered empty at revision 0 was not a failed read: %+v", before.UserList.Gaps) + if !slices.ContainsFunc(before.UserList.Gaps, func(g grants.Gap) bool { return g.Input == grants.ReadAssignments && g.Failed }) { + t.Fatalf("assignments answered empty at revision 0 was not a failed read: %+v", before.UserList.Gaps) } - if !raised(grants.FirstViewEmpty+".mesh-delivery-assigner.placement", occ.StateRaised) { + if !raised(grants.FirstViewEmpty+".mesh-delivery-assigner.assignments", occ.StateRaised) { t.Fatalf("no first-view-empty: %+v", said) } empty.Store(false) down.Store(false) - if _, err := fv.Again(ctx, "placement", "the assigner composed its wants again"); err != nil { + if _, err := fv.Again(ctx, "assignments", "the assigner composed its wants again"); err != nil { t.Fatal(err) } - waitFor(t, func() bool { return fv.Followers[grants.ReadPlacement].Status().Revision == 7 }) + waitFor(t, func() bool { return fv.Followers[grants.ReadAssignments].Status().Revision == 7 }) waitFor(t, func() bool { return raised(grants.FirstViewUnanswered+".mesh-delivery-indexer.verdicts", occ.StateCleared) }) after := p.Compose() - if slices.ContainsFunc(after.UserList.Gaps, func(g grants.Gap) bool { return g.Input == grants.ReadPlacement }) || - !raised(grants.FirstViewEmpty+".mesh-delivery-assigner.placement", occ.StateCleared) { + if slices.ContainsFunc(after.UserList.Gaps, func(g grants.Gap) bool { return g.Input == grants.ReadAssignments }) || + !raised(grants.FirstViewEmpty+".mesh-delivery-assigner.assignments", occ.StateCleared) { t.Fatalf("after an answer above 0: gaps %+v, said %+v", after.UserList.Gaps, said) } if after.UserList.Users != before.UserList.Users && before.UserList.Users > after.UserList.Users { t.Errorf("the empty first view dropped users: %d then %d", before.UserList.Users, after.UserList.Users) } // An event of the owner's views family changes the picture with no ask. - asksBefore := fv.Followers[grants.ReadPlacement].Status().Asks + asksBefore := fv.Followers[grants.ReadAssignments].Status().Asks raw, _ := json.Marshal(map[string]any{"v": 1, "node": "c"}) - if gap, why := fv.Followers[grants.ReadPlacement].Apply(views.Event{V: 1, Owner: grants.AssignerModule, State: "placement", - Key: "placement.c", Revision: 8, Created: created, Value: raw}); gap { + if gap, why := fv.Followers[grants.ReadAssignments].Apply(views.Event{V: 1, Owner: grants.AssignerModule, State: "assignments", + Key: "c.node", Revision: 8, Created: created, Value: raw}); gap { t.Fatalf("a next revision was a gap: %s", why) } - if _, found, _ := fv.Get(grants.ReadPlacement, "placement.c"); !found || fv.Followers[grants.ReadPlacement].Status().Asks != asksBefore { + if _, found, _ := fv.Get(grants.ReadAssignments, "c.node"); !found || fv.Followers[grants.ReadAssignments].Status().Asks != asksBefore { t.Error("the event did not reach the picture, or asked") } // The verdicts follow the indexer's manifest-accepted and manifest-refused at the revision each carries (ADR 0622 §2): @@ -2577,9 +2575,9 @@ func TestGrants(t *testing.T) { }) // 774/AC4 (novox/hq ADR 0561 §2): every account the vault mints that the records compose is written to the state - // accounts once, with the bus's address and a module's claims, read by the vault alone; a key is deleted when placement + // accounts once, with the bus's address and a module's claims, read by the vault alone; a key is deleted when assignments // no longer names its user, and never in a pass whose reads failed. - t.Run("wanted accounts: written once each, read by the vault alone, retired only when placement drops them", func(t *testing.T) { + t.Run("wanted accounts: written once each, read by the vault alone, retired only when assignments drops them", func(t *testing.T) { b := fixture() b.loaded = shadowOf(t, b) p := partOn(b) @@ -2613,7 +2611,7 @@ func TestGrants(t *testing.T) { if !slices.Equal(readers, []string{"mesh-vault"}) || buscompose.ReaderMay("mesh-delivery-view", buscompose.GrantsAccounts) { t.Errorf("the wanted accounts are read by %v", readers) } - // A read that fails retires nothing; once read, the user placement dropped is retired. + // A read that fails retires nothing; once read, the user assignments dropped is retired. b.unassign("b", "mod-y") b.refused[grants.ReadResolved] = true if res := p.Compose(); res.Accounts == nil || len(res.Accounts.Deleted) != 0 || b.own[grants.StateAccounts]["b.mod-y"] == nil { @@ -2621,7 +2619,7 @@ func TestGrants(t *testing.T) { } delete(b.refused, grants.ReadResolved) if res := p.Compose(); res.Accounts == nil || !slices.Equal(res.Accounts.Deleted, []string{"b.mod-y"}) { - t.Fatalf("the user placement dropped is not retired: %+v", res.Accounts) + t.Fatalf("the user assignments dropped is not retired: %+v", res.Accounts) } // The tool reads them, one and all, each with when it was written. one, err := p.Accounts("a.nats") diff --git a/modules/mesh-grants/internal/buscompose/accounts.go b/modules/mesh-grants/internal/buscompose/accounts.go index 923b3056..769e46c7 100644 --- a/modules/mesh-grants/internal/buscompose/accounts.go +++ b/modules/mesh-grants/internal/buscompose/accounts.go @@ -8,7 +8,7 @@ import ( // **The accounts the grants part wants, and the one shape a held account takes** (novox/hq ADR 0561 §1, §2; issue 774). // -// The bus composer derives every user from placement, the manifests and the wants. For each account the mesh mints (a +// The bus composer derives every user from assignments, the manifests and the wants. For each account the mesh mints (a // module's, a module's process's, a machine's provisioner client's), the grants part writes one key of its state // `accounts`, keyed by the bus user, holding the account's fields that are not secret: what the controller's credential // carries today beside the password. The vault watches that state, mints the user it holds no value for, and keeps the diff --git a/modules/mesh-grants/internal/buscompose/buscompose_test.go b/modules/mesh-grants/internal/buscompose/buscompose_test.go index 16a39f85..c51ffe3e 100644 --- a/modules/mesh-grants/internal/buscompose/buscompose_test.go +++ b/modules/mesh-grants/internal/buscompose/buscompose_test.go @@ -12,8 +12,8 @@ import ( "time" "git.novox.be/novox/mesh-contracts/go/seats/meshbroker" + "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-contracts/identity-and-access/go/seats/meshnodeenroller" - "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-contracts/identity-and-access/go/clients/meshvaultclient" "git.novox.be/novox/mesh-contracts/identity-and-access/go/seats/meshvault" @@ -57,17 +57,17 @@ func TestTheCopiedComposerComposesAsTheControllers(t *testing.T) { t.Run("every node's compositions are read by their named readers alone and written by their node", everyNodesCompositions) t.Run("a module's ledger is written by its ledger account alone", aLedgerIsWrittenByItsLedgerAccountAlone) t.Run("the bus module's process alone keeps every module's bus state and serves the broker's verbs", theBusModulesProcessKeepsBusState) - // ADR 0567 §3, §9: a node reads every key of the seat's placement and its candidates (it composes with the whole + // ADR 0567 §3, §9: a node reads every key of the seat's assignments and its candidates (it composes with the whole // proposal laid over), and none of its open proposals; it writes neither. reads := strings.Join(NodeComposeReads("laptop"), " ") - for bucket, granted := range map[string]bool{PlacementBucket: true, PlacementCandidatesBucket: true, - PlacementProposalsBucket: false} { + for bucket, granted := range map[string]bool{AssignmentsBucket: true, AssignmentsCandidatesBucket: true, + AssignmentsProposalsBucket: false} { if strings.Contains(reads, "$JS.API.DIRECT.GET.KV_"+bucket+".>") != granted || strings.Contains(reads, "$KV."+bucket+".laptop") { t.Errorf("a node is granted %s: %v, want %v", bucket, !granted, granted) } } // It hears the creation of those two buckets, and of no other. - if got := NodeComposeAdvisories(); len(got) != 2 || got[0] != "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_placement" || + if got := NodeComposeAdvisories(); len(got) != 2 || got[0] != "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_assignments" || got[1] != "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_candidates" { t.Errorf("a node hears the creation of %v", got) } @@ -513,11 +513,11 @@ func TestTheCopiedComposerComposesAsTheControllers(t *testing.T) { vault := ClaimedSeat{Seat: "mesh-vault", Scope: "mesh", Serves: []string{"get", "accept", "secret-ask", "issued", "rotate", "move-in", "node-key", "own", "move-asks"}} controller := ClaimedSeat{Seat: "mesh-controller", Scope: "mesh", Serves: strings.Split("tools,calls,status,nodes,"+ - "node,placement-export,modules,seats,builds,plans,delivery-plan,delivery-order,delivery-check,deliver,"+ + "node,assignments-export,modules,seats,builds,plans,delivery-plan,delivery-order,delivery-check,deliver,"+ "delivery-stop,delivery-backups-again,delivery-walks,plan,assign,unassign,pin,unpin,push,rotate,give,issued,"+ "secret-move,secret-ask,issue,token,settings,command,queue,cancel,clear,replay,try,kill,pause,resume,hand-act,"+ "drill,warranted,hand-acts,durations,conditions,healers,doctor,upgrade,bus,retire,cleanup,judge-directories,"+ - "root-free,dead-letters,data,build,artifacts,held,collect,move-placement,move-config,mirrors,images", ",")} + "root-free,dead-letters,data,build,artifacts,held,collect,move-assignments,move-config,mirrors,images", ",")} at := "nats://10.0.0.1:4222" wanted := map[string]struct { w WantedAccount @@ -790,7 +790,7 @@ func healthChecks(t *testing.T) { } // ADR 0571 clause 1: a read naming another module's log is issued to the reader as a log it reads, never writes. if m := MembershipFor("a", Declared{Module: IndexerModule, Reads: []string{builds.BuildLogRead, "x.y"}}, - Placements{Logs: map[string]bool{builds.BuildLogRead: true}}); len(m.Logs) != 1 || m.Logs[0].Writes || + Assignments{Logs: map[string]bool{builds.BuildLogRead: true}}); len(m.Logs) != 1 || m.Logs[0].Writes || m.Logs[0].Stream != "LOG_mesh-delivery-builder_build-log" || m.Logs[0].Name != builds.BuildLogRead { t.Errorf("the indexer's membership issues the logs %+v", m.Logs) } @@ -873,7 +873,7 @@ func healthChecks(t *testing.T) { {"a flat event of a node seat held by a module is said flat", Declared{Module: "pause", Holds: []Seat{{Name: "node-power", Scope: "node", Emits: []string{"slept"}}}}, "mesh.seat.node-power.event.slept", true}, } { - m := MembershipFor("a", c.d, Placements{}) + m := MembershipFor("a", c.d, Assignments{}) listed := m.SeatTraffic != nil && slices.ContainsFunc(m.SeatTraffic.Publish, func(p string) bool { return p == c.subject }) perms, err := PermissionsFor(Principal{Kind: KindNodeTools, Node: "a", Carries: []Declared{c.d}}) if err != nil { @@ -1019,7 +1019,7 @@ func everyNodesCompositions(t *testing.T) { !MayPublish(runner, "$JS.API.DIRECT.GET.KV_composed-two.$KV.composed-two.two.node") { t.Errorf("the runtime carrying the provisioner cannot read every node's compositions") } - m := MembershipFor("one", Declared{Module: "mesh-delivery-view", Reads: []string{ComposedRead}}, Placements{}) + m := MembershipFor("one", Declared{Module: "mesh-delivery-view", Reads: []string{ComposedRead}}, Assignments{}) if !slices.ContainsFunc(m.State, func(s StateIssued) bool { return s.Name == ComposedRead && s.PerNode && s.Bucket == ComposedBucketPrefix && !s.Writes }) { diff --git a/modules/mesh-grants/internal/buscompose/creates_streams_test.go b/modules/mesh-grants/internal/buscompose/creates_streams_test.go index 7c99a208..90dc4e77 100644 --- a/modules/mesh-grants/internal/buscompose/creates_streams_test.go +++ b/modules/mesh-grants/internal/buscompose/creates_streams_test.go @@ -22,7 +22,7 @@ func theGrantsPartCreatesOnlyDeclaredStreams(t *testing.T) { Streams: []ModuleStream{{Module: "mesh-delivery-builder", Name: "BUILDASKS", Events: []string{"ask"}}}}, {Module: GrantsModule, State: []Bucket{{Module: GrantsModule, Name: "assertions"}}, Reads: []string{VaultBusAccounts}}, {Module: "node-tools", ToolRunner: true}, - // The node-engine's manifest as a placement names it (issue 774, met twice on 2026-10-30): no account of its + // The node-engine's manifest as assignments names it (issue 774, met twice on 2026-10-30): no account of its // own, so the runner would carry it, and its state is the node's to write alone. {Module: EngineModule, NoAccount: true, Consumes: []string{"restic.backed-up"}, State: []Bucket{{Module: EngineModule, Name: "health"}, {Module: EngineModule, Name: EngineActsState}}}, diff --git a/modules/mesh-grants/internal/buscompose/fixtures_test.go b/modules/mesh-grants/internal/buscompose/fixtures_test.go index aac9e87c..9b0cbef8 100644 --- a/modules/mesh-grants/internal/buscompose/fixtures_test.go +++ b/modules/mesh-grants/internal/buscompose/fixtures_test.go @@ -119,7 +119,7 @@ func ComposedFor(f UserListFixture) (string, []string, error) { // MembershipsFor is every membership a fixture's records issue, by `.`, as the composer makes them. func MembershipsFor(f UserListFixture) map[string]Membership { - where := PlacementsOf(f.Records, f.Records.Interchangeable) + where := AssignmentsOf(f.Records, f.Records.Interchangeable) out := map[string]Membership{} for node, ds := range f.Records.Assigned { for _, d := range ds { diff --git a/modules/mesh-grants/internal/buscompose/membership.go b/modules/mesh-grants/internal/buscompose/membership.go index 8601788c..9f0ea0d0 100644 --- a/modules/mesh-grants/internal/buscompose/membership.go +++ b/modules/mesh-grants/internal/buscompose/membership.go @@ -15,8 +15,8 @@ import ( // grant is the same composition read the other way. The shape issued today is the shape the mesh // already had, so nothing moves when a membership first arrives; only who decides it moves. -// Placements is where every module runs, for deciding which instance answers for the module. -type Placements struct { +// Assignments is where every module runs, for deciding which instance answers for the module. +type Assignments struct { // Nodes is each module's machines. Nodes map[string][]string // Interchangeable is each module whose definition says its instances are the same anywhere, @@ -33,14 +33,14 @@ type Placements struct { // plain subject: when it is the only instance, or when the definition says instances are // interchangeable. A stateful module on two machines gets only its machines' subjects, so a call // that names none reaches nothing rather than the wrong store. -func (p Placements) AnswersForTheModule(module string) bool { +func (p Assignments) AnswersForTheModule(module string) bool { return len(p.Nodes[module]) <= 1 || p.Interchangeable[module] } // MembershipFor composes one assignment's membership from what it declared and where everything // runs. The subjects are the ones PermissionsFor grants, derived here once more only until the // grant itself is read from the membership — which is the next step, not this one. -func MembershipFor(node string, d Declared, where Placements) Membership { +func MembershipFor(node string, d Declared, where Assignments) Membership { own := "mesh.mod." + d.Module m := Membership{ Node: node, Module: d.Module, @@ -101,9 +101,9 @@ func MembershipFor(node string, d Declared, where Placements) Membership { return m } -// PlacementsOf reads where everything runs from the records the bus's accounts are composed from. -func PlacementsOf(r Records, interchangeable map[string]bool) Placements { - p := Placements{Nodes: map[string][]string{}, Interchangeable: interchangeable, Logs: DeclaredLogs(r)} +// AssignmentsOf reads where everything runs from the records the bus's accounts are composed from. +func AssignmentsOf(r Records, interchangeable map[string]bool) Assignments { + p := Assignments{Nodes: map[string][]string{}, Interchangeable: interchangeable, Logs: DeclaredLogs(r)} for node, declared := range r.Assigned { for _, d := range declared { p.Nodes[d.Module] = append(p.Nodes[d.Module], node) @@ -151,7 +151,7 @@ func PlacementsOf(r Records, interchangeable map[string]bool) Placements { // routerSeat is the seat the router of asks holds (novox/hq ADR 0259 §3). const routerSeat = "operator-channel" -// placedCapabilities is what a kind's claim promises, as far as its placement lets the router believe it +// placedCapabilities is what a kind's claim promises, as far as its assignments lets the router believe it // (novox/hq ADR 0259 §8): `verified-sender` only from a holder that runs as an account of its own, on a bus // account of its own — never one the machine's runtime carries as the operator's account — and only while // its machine and the router's were root-free when composed (rootFree; the review of 2026-10-09, H3). The diff --git a/modules/mesh-grants/internal/buscompose/names.go b/modules/mesh-grants/internal/buscompose/names.go index 7544f911..06eaac5e 100644 --- a/modules/mesh-grants/internal/buscompose/names.go +++ b/modules/mesh-grants/internal/buscompose/names.go @@ -15,20 +15,20 @@ const ( // EnrolmentModule is the module holding the seat mesh-node-enroller. EnrolmentModule = "mesh-enrolment" - // PlacementBucket and SeatHoldersBucket are the assigner's states placement and seat-holders (reads: - // mesh-delivery-assigner.placement; the seat-holders state is read as a named reader), PlacementRead the address of the first. - PlacementBucket = AssignerModule + "_placement" - PlacementRead = AssignerModule + ".placement" + // AssignmentsBucket and SeatHoldersBucket are the assigner's states assignments and seat-holders (reads: + // mesh-delivery-assigner.assignments; the seat-holders state is read as a named reader), AssignmentsRead the address of the first. + AssignmentsBucket = AssignerModule + "_assignments" + AssignmentsRead = AssignerModule + ".assignments" SeatHoldersRead = AssignerModule + ".seat-holders" SeatHoldersBucket = AssignerModule + "_seat-holders" - AssignerWantsBucket = AssignerModule + "_wants" - PlacementCandidatesBucket = AssignerModule + "_candidates" - PlacementProposalsBucket = AssignerModule + "_placement-proposals" - CandidatesBucket = IndexerModule + "_candidates" - ModulesBucket = IndexerModule + "_modules" - ManifestsBucket = IndexerModule + "_manifests" - VerdictsBucket = IndexerModule + "_verdicts" + AssignerWantsBucket = AssignerModule + "_wants" + AssignmentsCandidatesBucket = AssignerModule + "_candidates" + AssignmentsProposalsBucket = AssignerModule + "_assignments-proposals" + CandidatesBucket = IndexerModule + "_candidates" + ModulesBucket = IndexerModule + "_modules" + ManifestsBucket = IndexerModule + "_manifests" + VerdictsBucket = IndexerModule + "_verdicts" EnrolmentsState = "enrolments" EnrolmentsBucket = EnrolmentModule + "_" + EnrolmentsState diff --git a/modules/mesh-grants/internal/buscompose/names_test.go b/modules/mesh-grants/internal/buscompose/names_test.go index e42c94c3..285b6b18 100644 --- a/modules/mesh-grants/internal/buscompose/names_test.go +++ b/modules/mesh-grants/internal/buscompose/names_test.go @@ -32,7 +32,7 @@ func namesAreTheManifests(t *testing.T) { {"reads", HaveBucket[:strings.Index(HaveBucket, "_")] + ".have", HaveBucket}, {"reads", NodeLoginsRead, NodeLoginsBucket}, {"reads", EnrolmentsRead, EnrolmentsBucket}, - {"invokes", PlacementRead, PlacementBucket}, + {"invokes", AssignmentsRead, AssignmentsBucket}, {"invokes", AssignerModule + ".wants", AssignerWantsBucket}, {"invokes", IndexerModule + ".modules", ModulesBucket}, {"invokes", IndexerModule + ".manifests", ManifestsBucket}, diff --git a/modules/mesh-grants/internal/buscompose/nats.go b/modules/mesh-grants/internal/buscompose/nats.go index 75cf55fe..eabd1507 100644 --- a/modules/mesh-grants/internal/buscompose/nats.go +++ b/modules/mesh-grants/internal/buscompose/nats.go @@ -740,7 +740,7 @@ func PermissionsFor(p Principal) (Permissions, error) { // its own modules reads the other nodes' public records, which its firewall and resolver need (ADR 0547 §2). Read // only: each node still writes its own keys alone. pub = append(pub, readGrants(HaveBucket, "*."+HaveNodeWord)...) - // And what it composes its own modules from (ADR 0547 §2, ADR 0567 §9): the seat mesh-placement's placement and + // And what it composes its own modules from (ADR 0547 §2, ADR 0567 §9): the seat mesh-module-assigner's assignments and // candidates, the indexer's modules and manifests, the builder's builds (the manifest of a build its want names), and mesh-config's resolved values of // its own node alone. Read only. pub = append(pub, NodeComposeReads(p.Node)...) @@ -779,7 +779,7 @@ func PermissionsFor(p Principal) (Permissions, error) { } sub = append(sub, subject) } - // And the creation of the seat mesh-placement's two states it composes from (ADR 0567 §9), so a node agent started + // And the creation of the seat mesh-module-assigner's two states it composes from (ADR 0567 §9), so a node agent started // before they exist binds each when the bus says it was made, with no timer; no other bucket's. sub = append(sub, NodeComposeAdvisories()...) // And, while the vault serves it, the vault's move-in in its own name and nobody else's (issue 676): the @@ -1535,7 +1535,7 @@ func CreateConsumerGrants(consumers []ModuleConsumer) []string { } // GrantsModule is the one module whose principal publishes the modules' memberships (novox/hq ADR 0547 §5): the grants -// part, which composes them from the manifests and placement. It defines no stream and is granted no other subject here. +// part, which composes them from the manifests and assignments. It defines no stream and is granted no other subject here. const GrantsModule = "mesh-grants" // The grants part's state memberships (novox/hq ADR 0571 clause 7): one key per assignment, `.`, its diff --git a/modules/mesh-grants/internal/buscompose/provisioner.go b/modules/mesh-grants/internal/buscompose/provisioner.go index 839f1c78..5a06e33d 100644 --- a/modules/mesh-grants/internal/buscompose/provisioner.go +++ b/modules/mesh-grants/internal/buscompose/provisioner.go @@ -31,22 +31,22 @@ const ProvisionerSecret = "provisioner-broker" var ConfigResolvedBucket = BucketName("mesh-config", "resolved") // ProvisionerReads are the domains' buckets a provisioner client reads its own machine's keys of, each keyed -// `.…` (research 051): placement, the controller's assignments mirror (`.`), whose value carries +// `.…` (research 051): assignments, the controller's assignments mirror (`.`), whose value carries // the provisioner's plan of a module handed to it (inventory.Provisioning, design 57 P5); and config, // mesh-config's resolved values; and the seat mesh-provisioner's plans of its machine, `.` (novox/hq ADR // 0570 clause 2). **Named one by one**, as the controller's asks are: a domain that moves to the // provisioner adds its bucket here, and a bucket not named is one the client cannot read. var ProvisionerReads = []string{ConfigResolvedBucket} -// ProvisionerReadsWhole are the buckets a provisioner client reads every key of: the seat mesh-placement's placement, +// ProvisionerReadsWhole are the buckets a provisioner client reads every key of: the seat mesh-module-assigner's assignments, // where a provider's consumers are placed on their own machines, which the provider machine's client reads to fill // ${consumers:} with each one's credential (novox/hq ADR 0373, issue 679; ADR 0567, issue 1126: the -// controller's assignments mirror it read before is gone). It holds placement, never a secret's value; the client +// controller's assignments mirror it read before is gone). It holds assignments, never a secret's value; the client // writes none of it. **The seat's plans are read whole for the same // reason** (2026-10-30 recovery, issue 774): the client V3 watches every machine's plans to bind a provider's // consumers, and granted only its own machine's keys it read none. ADR 0570 clause 3 has providers act on the seat's // provision keys instead; once the client does, plans go back to its own machine's keys. -var ProvisionerReadsWhole = []string{PlacementBucket, ProvisionerPlansBucket} +var ProvisionerReadsWhole = []string{AssignmentsBucket, ProvisionerPlansBucket} // BoundState is a providing module's state of entries, one per consumer (mesh-sdk go/provider, issue 678): a // consumer's machine's client reads its own keys of it, `..[.]`, to fill diff --git a/modules/mesh-grants/internal/buscompose/state.go b/modules/mesh-grants/internal/buscompose/state.go index c13d6619..7a57694a 100644 --- a/modules/mesh-grants/internal/buscompose/state.go +++ b/modules/mesh-grants/internal/buscompose/state.go @@ -334,24 +334,24 @@ func stateNames(buckets []Bucket) []string { const ResolvedBucket = "mesh-config_resolved" // NodeComposeReads is what a node's agent reads to compose its own modules (novox/hq ADR 0547 §2, ADR 0567 §3, §9, -// issue 1126): every key of the seat mesh-placement's placement and candidates (both the assigner's states), of the +// issue 1126): every key of the seat mesh-module-assigner's assignments and candidates (both the assigner's states), of the // indexer's modules and manifests and of the builder's builds, and of mesh-config's resolved values only its own node's, // `.>`. Every candidate key, not only `*.`: a node answering its candidate composes with the whole -// proposal laid over (the seed places every node at once, and a binding crosses nodes), and nothing in a placement is +// proposal laid over (the seed places every node at once, and a binding crosses nodes), and nothing in assignments is // secret. Reads only; the node is checked as every subject token is by the caller's principal. func NodeComposeReads(node string) []string { var out []string - for _, bucket := range []string{PlacementBucket, PlacementCandidatesBucket, ModulesBucket, + for _, bucket := range []string{AssignmentsBucket, AssignmentsCandidatesBucket, ModulesBucket, ManifestsBucket, builds.BuilderBuildsBucket} { out = append(out, readGrants(bucket, "")...) } return append(out, readGrants(ResolvedBucket, node+".>")...) } -// NodeComposeAdvisories are the stream-created advisories of the seat mesh-placement's placement and candidates +// NodeComposeAdvisories are the stream-created advisories of the seat mesh-module-assigner's assignments and candidates // (ADR 0567 §9): what a node agent hears to bind each once it is made. Those two and no wider. func NodeComposeAdvisories() []string { - return []string{StreamCreatedAdvisory(PlacementBucket), StreamCreatedAdvisory(PlacementCandidatesBucket)} + return []string{StreamCreatedAdvisory(AssignmentsBucket), StreamCreatedAdvisory(AssignmentsCandidatesBucket)} } // ServedReadGrants is the read of each key of mesh-config's resolved values named, `..` diff --git a/modules/mesh-grants/internal/buscompose/testdata/composed.conf b/modules/mesh-grants/internal/buscompose/testdata/composed.conf index 45a5c70e..83b08772 100644 --- a/modules/mesh-grants/internal/buscompose/testdata/composed.conf +++ b/modules/mesh-grants/internal/buscompose/testdata/composed.conf @@ -33,8 +33,8 @@ accounts { subscribe: { allow: ["_INBOX.enrol.one.>"] } } } { user: "node.one", password: "$2a$11$nnnnnnnnnnnnnnnnnnnnnn", permissions: { - publish: { allow: ["$JS.ACK.NODES.one.>", "$JS.API.CONSUMER.CREATE.KV_composed-one.>", "$JS.API.CONSUMER.CREATE.KV_mesh-config_resolved.*.$KV.mesh-config_resolved.one.>", "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_candidates.>", "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_placement.>", "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_wants.*.$KV.mesh-delivery-assigner_wants.want.one.>", "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-builder_builds.>", "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_candidates.>", "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_manifests.>", "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_modules.>", "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_verdicts.>", "$JS.API.CONSUMER.CREATE.KV_mesh-host_acts.*.$KV.mesh-host_acts.one.>", "$JS.API.CONSUMER.CREATE.KV_mesh-host_data.*.$KV.mesh-host_data.one.>", "$JS.API.CONSUMER.CREATE.KV_mesh-host_findings.*.$KV.mesh-host_findings.machine.one.>", "$JS.API.CONSUMER.CREATE.KV_mesh-host_health.*.$KV.mesh-host_health.one", "$JS.API.CONSUMER.CREATE.KV_mesh-host_packages.*.$KV.mesh-host_packages.one", "$JS.API.CONSUMER.CREATE.KV_mesh-provisioner_provisions.*.$KV.mesh-provisioner_provisions.one.>", "$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.*.node", "$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.one.>", "$JS.API.CONSUMER.CREATE.OBJ_declaration-files-one.*.$O.declaration-files-one.C.*", "$JS.API.CONSUMER.DELETE.KV_composed-one.>", "$JS.API.CONSUMER.DELETE.KV_mesh-config_resolved.>", "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_candidates.>", "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_placement.>", "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_wants.>", "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-builder_builds.>", "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_candidates.>", "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_manifests.>", "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_modules.>", "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_verdicts.>", "$JS.API.CONSUMER.DELETE.KV_mesh-host_acts.>", "$JS.API.CONSUMER.DELETE.KV_mesh-host_data.>", "$JS.API.CONSUMER.DELETE.KV_mesh-host_findings.>", "$JS.API.CONSUMER.DELETE.KV_mesh-host_health.>", "$JS.API.CONSUMER.DELETE.KV_mesh-host_packages.>", "$JS.API.CONSUMER.DELETE.KV_mesh-provisioner_provisions.>", "$JS.API.CONSUMER.DELETE.KV_node-engine_have.>", "$JS.API.CONSUMER.DELETE.OBJ_declaration-files-one.*", "$JS.API.CONSUMER.INFO.NODES.one", "$JS.API.DIRECT.GET.KV_composed-one.>", "$JS.API.DIRECT.GET.KV_mesh-config_resolved.$KV.mesh-config_resolved.one.>", "$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_candidates.>", "$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_placement.>", "$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_wants.$KV.mesh-delivery-assigner_wants.want.one.>", "$JS.API.DIRECT.GET.KV_mesh-delivery-builder_builds.>", "$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_candidates.>", "$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_manifests.>", "$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_modules.>", "$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_verdicts.>", "$JS.API.DIRECT.GET.KV_mesh-host_acts.$KV.mesh-host_acts.one.>", "$JS.API.DIRECT.GET.KV_mesh-host_data.$KV.mesh-host_data.one.>", "$JS.API.DIRECT.GET.KV_mesh-host_findings.$KV.mesh-host_findings.machine.one.>", "$JS.API.DIRECT.GET.KV_mesh-host_health.$KV.mesh-host_health.one", "$JS.API.DIRECT.GET.KV_mesh-host_packages.$KV.mesh-host_packages.one", "$JS.API.DIRECT.GET.KV_mesh-provisioner_provisions.$KV.mesh-provisioner_provisions.one.>", "$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.*.node", "$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.one.>", "$JS.API.DIRECT.GET.OBJ_declaration-files-one.$O.declaration-files-one.M.*", "$JS.API.STREAM.INFO.KV_composed-one", "$JS.API.STREAM.INFO.KV_mesh-config_resolved", "$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_candidates", "$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_placement", "$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_wants", "$JS.API.STREAM.INFO.KV_mesh-delivery-builder_builds", "$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_candidates", "$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_manifests", "$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_modules", "$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_verdicts", "$JS.API.STREAM.INFO.KV_mesh-host_acts", "$JS.API.STREAM.INFO.KV_mesh-host_data", "$JS.API.STREAM.INFO.KV_mesh-host_findings", "$JS.API.STREAM.INFO.KV_mesh-host_health", "$JS.API.STREAM.INFO.KV_mesh-host_packages", "$JS.API.STREAM.INFO.KV_mesh-provisioner_provisions", "$JS.API.STREAM.INFO.KV_node-engine_have", "$JS.FC.KV_composed-one.>", "$JS.FC.KV_mesh-config_resolved.>", "$JS.FC.KV_mesh-delivery-assigner_candidates.>", "$JS.FC.KV_mesh-delivery-assigner_placement.>", "$JS.FC.KV_mesh-delivery-assigner_wants.>", "$JS.FC.KV_mesh-delivery-builder_builds.>", "$JS.FC.KV_mesh-delivery-indexer_candidates.>", "$JS.FC.KV_mesh-delivery-indexer_manifests.>", "$JS.FC.KV_mesh-delivery-indexer_modules.>", "$JS.FC.KV_mesh-delivery-indexer_verdicts.>", "$JS.FC.KV_mesh-host_acts.>", "$JS.FC.KV_mesh-host_data.>", "$JS.FC.KV_mesh-host_findings.>", "$JS.FC.KV_mesh-host_health.>", "$JS.FC.KV_mesh-host_packages.>", "$JS.FC.KV_mesh-provisioner_provisions.>", "$JS.FC.KV_node-engine_have.>", "$JS.FC.OBJ_declaration-files-one.>", "$KV.composed-one.>", "$KV.mesh-host_acts.one.>", "$KV.mesh-host_data.one.>", "$KV.mesh-host_findings.machine.one.>", "$KV.mesh-host_health.one", "$KV.mesh-host_packages.one", "$KV.node-engine_have.one.>", "$SRV.INFO", "$SRV.INFO.>", "$SRV.PING", "$SRV.PING.*.one", "$SRV.PING.>", "mesh.control.one.>", "mesh.seat.node-backup.call.backed-up.one.node~one", "mesh.seat.node-backup.call.now.one.node~one", "mesh.seat.node-backup.tool.backed-up.one", "mesh.seat.node-backup.tool.now.one", "mesh.seat.node-engine.event.*.one"] } - subscribe: { allow: ["$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_candidates", "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_placement", "$SRV.INFO", "$SRV.INFO.node-engine", "$SRV.INFO.node-engine.>", "$SRV.PING", "$SRV.PING.node-engine", "$SRV.PING.node-engine.>", "$SRV.STATS", "$SRV.STATS.node-engine", "$SRV.STATS.node-engine.>", "_DELIVER.one", "_DELIVER.one.>", "_INBOX.node.one.>", "mesh.node.one.ask.access-act", "mesh.node.one.ask.hand-over", "mesh.node.one.ask.judge-directories", "mesh.node.one.ask.move-secret", "mesh.node.one.ask.report", "mesh.node.one.ask.setuid-search", "mesh.node.one.declare", "mesh.seat.node-engine.call.*.one.*", "mesh.seat.node-engine.tool.*.one"] } + publish: { allow: ["$JS.ACK.NODES.one.>", "$JS.API.CONSUMER.CREATE.KV_composed-one.>", "$JS.API.CONSUMER.CREATE.KV_mesh-config_resolved.*.$KV.mesh-config_resolved.one.>", "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_assignments.>", "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_candidates.>", "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_wants.*.$KV.mesh-delivery-assigner_wants.want.one.>", "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-builder_builds.>", "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_candidates.>", "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_manifests.>", "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_modules.>", "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_verdicts.>", "$JS.API.CONSUMER.CREATE.KV_mesh-host_acts.*.$KV.mesh-host_acts.one.>", "$JS.API.CONSUMER.CREATE.KV_mesh-host_data.*.$KV.mesh-host_data.one.>", "$JS.API.CONSUMER.CREATE.KV_mesh-host_findings.*.$KV.mesh-host_findings.machine.one.>", "$JS.API.CONSUMER.CREATE.KV_mesh-host_health.*.$KV.mesh-host_health.one", "$JS.API.CONSUMER.CREATE.KV_mesh-host_packages.*.$KV.mesh-host_packages.one", "$JS.API.CONSUMER.CREATE.KV_mesh-provisioner_provisions.*.$KV.mesh-provisioner_provisions.one.>", "$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.*.node", "$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.one.>", "$JS.API.CONSUMER.CREATE.OBJ_declaration-files-one.*.$O.declaration-files-one.C.*", "$JS.API.CONSUMER.DELETE.KV_composed-one.>", "$JS.API.CONSUMER.DELETE.KV_mesh-config_resolved.>", "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_assignments.>", "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_candidates.>", "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_wants.>", "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-builder_builds.>", "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_candidates.>", "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_manifests.>", "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_modules.>", "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_verdicts.>", "$JS.API.CONSUMER.DELETE.KV_mesh-host_acts.>", "$JS.API.CONSUMER.DELETE.KV_mesh-host_data.>", "$JS.API.CONSUMER.DELETE.KV_mesh-host_findings.>", "$JS.API.CONSUMER.DELETE.KV_mesh-host_health.>", "$JS.API.CONSUMER.DELETE.KV_mesh-host_packages.>", "$JS.API.CONSUMER.DELETE.KV_mesh-provisioner_provisions.>", "$JS.API.CONSUMER.DELETE.KV_node-engine_have.>", "$JS.API.CONSUMER.DELETE.OBJ_declaration-files-one.*", "$JS.API.CONSUMER.INFO.NODES.one", "$JS.API.DIRECT.GET.KV_composed-one.>", "$JS.API.DIRECT.GET.KV_mesh-config_resolved.$KV.mesh-config_resolved.one.>", "$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_assignments.>", "$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_candidates.>", "$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_wants.$KV.mesh-delivery-assigner_wants.want.one.>", "$JS.API.DIRECT.GET.KV_mesh-delivery-builder_builds.>", "$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_candidates.>", "$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_manifests.>", "$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_modules.>", "$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_verdicts.>", "$JS.API.DIRECT.GET.KV_mesh-host_acts.$KV.mesh-host_acts.one.>", "$JS.API.DIRECT.GET.KV_mesh-host_data.$KV.mesh-host_data.one.>", "$JS.API.DIRECT.GET.KV_mesh-host_findings.$KV.mesh-host_findings.machine.one.>", "$JS.API.DIRECT.GET.KV_mesh-host_health.$KV.mesh-host_health.one", "$JS.API.DIRECT.GET.KV_mesh-host_packages.$KV.mesh-host_packages.one", "$JS.API.DIRECT.GET.KV_mesh-provisioner_provisions.$KV.mesh-provisioner_provisions.one.>", "$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.*.node", "$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.one.>", "$JS.API.DIRECT.GET.OBJ_declaration-files-one.$O.declaration-files-one.M.*", "$JS.API.STREAM.INFO.KV_composed-one", "$JS.API.STREAM.INFO.KV_mesh-config_resolved", "$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_assignments", "$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_candidates", "$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_wants", "$JS.API.STREAM.INFO.KV_mesh-delivery-builder_builds", "$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_candidates", "$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_manifests", "$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_modules", "$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_verdicts", "$JS.API.STREAM.INFO.KV_mesh-host_acts", "$JS.API.STREAM.INFO.KV_mesh-host_data", "$JS.API.STREAM.INFO.KV_mesh-host_findings", "$JS.API.STREAM.INFO.KV_mesh-host_health", "$JS.API.STREAM.INFO.KV_mesh-host_packages", "$JS.API.STREAM.INFO.KV_mesh-provisioner_provisions", "$JS.API.STREAM.INFO.KV_node-engine_have", "$JS.FC.KV_composed-one.>", "$JS.FC.KV_mesh-config_resolved.>", "$JS.FC.KV_mesh-delivery-assigner_assignments.>", "$JS.FC.KV_mesh-delivery-assigner_candidates.>", "$JS.FC.KV_mesh-delivery-assigner_wants.>", "$JS.FC.KV_mesh-delivery-builder_builds.>", "$JS.FC.KV_mesh-delivery-indexer_candidates.>", "$JS.FC.KV_mesh-delivery-indexer_manifests.>", "$JS.FC.KV_mesh-delivery-indexer_modules.>", "$JS.FC.KV_mesh-delivery-indexer_verdicts.>", "$JS.FC.KV_mesh-host_acts.>", "$JS.FC.KV_mesh-host_data.>", "$JS.FC.KV_mesh-host_findings.>", "$JS.FC.KV_mesh-host_health.>", "$JS.FC.KV_mesh-host_packages.>", "$JS.FC.KV_mesh-provisioner_provisions.>", "$JS.FC.KV_node-engine_have.>", "$JS.FC.OBJ_declaration-files-one.>", "$KV.composed-one.>", "$KV.mesh-host_acts.one.>", "$KV.mesh-host_data.one.>", "$KV.mesh-host_findings.machine.one.>", "$KV.mesh-host_health.one", "$KV.mesh-host_packages.one", "$KV.node-engine_have.one.>", "$SRV.INFO", "$SRV.INFO.>", "$SRV.PING", "$SRV.PING.*.one", "$SRV.PING.>", "mesh.control.one.>", "mesh.seat.node-backup.call.backed-up.one.node~one", "mesh.seat.node-backup.call.now.one.node~one", "mesh.seat.node-backup.tool.backed-up.one", "mesh.seat.node-backup.tool.now.one", "mesh.seat.node-engine.event.*.one"] } + subscribe: { allow: ["$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_assignments", "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_candidates", "$SRV.INFO", "$SRV.INFO.node-engine", "$SRV.INFO.node-engine.>", "$SRV.PING", "$SRV.PING.node-engine", "$SRV.PING.node-engine.>", "$SRV.STATS", "$SRV.STATS.node-engine", "$SRV.STATS.node-engine.>", "_DELIVER.one", "_DELIVER.one.>", "_INBOX.node.one.>", "mesh.node.one.ask.access-act", "mesh.node.one.ask.hand-over", "mesh.node.one.ask.judge-directories", "mesh.node.one.ask.move-secret", "mesh.node.one.ask.report", "mesh.node.one.ask.setuid-search", "mesh.node.one.declare", "mesh.seat.node-engine.call.*.one.*", "mesh.seat.node-engine.tool.*.one"] } allow_responses: { max: 1, ttl: "1m" } } } { user: "one.mesh-delivery-watchman", password: "$2a$11$wwwwwwwwwwwwwwwwwwwwww", permissions: { diff --git a/modules/mesh-grants/internal/buscompose/testdata/controller-credentials.json b/modules/mesh-grants/internal/buscompose/testdata/controller-credentials.json index ab11cf1c..e30a620f 100644 --- a/modules/mesh-grants/internal/buscompose/testdata/controller-credentials.json +++ b/modules/mesh-grants/internal/buscompose/testdata/controller-credentials.json @@ -3,7 +3,7 @@ "rows": [ { "name": "module", - "held": "{\"url\":\"nats://10.0.0.1:4222\",\"fingerprint\":\"ab:cd\",\"node\":\"laptop\",\"module\":\"cap-bot\",\"user\":\"laptop.cap-bot\",\"password\":\"pw-module\",\"claims\":[{\"seat\":\"mesh-vault\",\"scope\":\"mesh\",\"serves\":[\"get\",\"accept\",\"secret-ask\",\"issued\",\"rotate\",\"move-in\",\"node-key\",\"own\",\"move-asks\"]},{\"seat\":\"node-service-manager\",\"scope\":\"node\",\"serves\":[\"status\"]},{\"seat\":\"mesh-controller\",\"scope\":\"mesh\",\"serves\":[\"tools\",\"calls\",\"status\",\"nodes\",\"node\",\"placement-export\",\"modules\",\"seats\",\"builds\",\"plans\",\"delivery-plan\",\"delivery-order\",\"delivery-check\",\"deliver\",\"delivery-stop\",\"delivery-backups-again\",\"delivery-walks\",\"plan\",\"assign\",\"unassign\",\"pin\",\"unpin\",\"push\",\"rotate\",\"give\",\"issued\",\"secret-move\",\"secret-ask\",\"issue\",\"token\",\"settings\",\"command\",\"queue\",\"cancel\",\"clear\",\"replay\",\"try\",\"kill\",\"pause\",\"resume\",\"hand-act\",\"drill\",\"warranted\",\"hand-acts\",\"durations\",\"conditions\",\"healers\",\"doctor\",\"upgrade\",\"bus\",\"retire\",\"cleanup\",\"judge-directories\",\"root-free\",\"dead-letters\",\"data\",\"build\",\"artifacts\",\"held\",\"collect\",\"move-placement\",\"move-config\",\"mirrors\",\"images\"]},{\"seat\":\"no-such-seat\",\"scope\":\"node\"}]}" + "held": "{\"url\":\"nats://10.0.0.1:4222\",\"fingerprint\":\"ab:cd\",\"node\":\"laptop\",\"module\":\"cap-bot\",\"user\":\"laptop.cap-bot\",\"password\":\"pw-module\",\"claims\":[{\"seat\":\"mesh-vault\",\"scope\":\"mesh\",\"serves\":[\"get\",\"accept\",\"secret-ask\",\"issued\",\"rotate\",\"move-in\",\"node-key\",\"own\",\"move-asks\"]},{\"seat\":\"node-service-manager\",\"scope\":\"node\",\"serves\":[\"status\"]},{\"seat\":\"mesh-controller\",\"scope\":\"mesh\",\"serves\":[\"tools\",\"calls\",\"status\",\"nodes\",\"node\",\"assignments-export\",\"modules\",\"seats\",\"builds\",\"plans\",\"delivery-plan\",\"delivery-order\",\"delivery-check\",\"deliver\",\"delivery-stop\",\"delivery-backups-again\",\"delivery-walks\",\"plan\",\"assign\",\"unassign\",\"pin\",\"unpin\",\"push\",\"rotate\",\"give\",\"issued\",\"secret-move\",\"secret-ask\",\"issue\",\"token\",\"settings\",\"command\",\"queue\",\"cancel\",\"clear\",\"replay\",\"try\",\"kill\",\"pause\",\"resume\",\"hand-act\",\"drill\",\"warranted\",\"hand-acts\",\"durations\",\"conditions\",\"healers\",\"doctor\",\"upgrade\",\"bus\",\"retire\",\"cleanup\",\"judge-directories\",\"root-free\",\"dead-letters\",\"data\",\"build\",\"artifacts\",\"held\",\"collect\",\"move-assignments\",\"move-config\",\"mirrors\",\"images\"]},{\"seat\":\"no-such-seat\",\"scope\":\"node\"}]}" }, { "name": "module-no-claims-no-fingerprint", diff --git a/modules/mesh-grants/internal/buscompose/testdata/user-lists.json b/modules/mesh-grants/internal/buscompose/testdata/user-lists.json index 0fe1f26b..ae674f75 100644 --- a/modules/mesh-grants/internal/buscompose/testdata/user-lists.json +++ b/modules/mesh-grants/internal/buscompose/testdata/user-lists.json @@ -8,7 +8,7 @@ }, { "Name": "the controller's someRecords", - "List": "# The mesh's users, composed by the grants part. Do not edit: the next\n# composition overwrites it. Permissions are derived from what each module\n# declares and nothing else (novox/hq ADR 0043, design 29 §2).\n\naccounts {\n MESH {\n jetstream: enabled\n imports = [\n { stream: { account: \"$SYS\", subject: \"$SYS.ACCOUNT.MESH.CONNECT\" }, to: \"mesh.bus.connect\" }\n { stream: { account: \"$SYS\", subject: \"$SYS.ACCOUNT.MESH.DISCONNECT\" }, to: \"mesh.bus.disconnect\" }\n { service: { account: \"$SYS\", subject: \"$SYS.REQ.ACCOUNT.MESH.CONNZ\" }, to: \"mesh.bus.connz\" }\n ]\n users = [\n { user: \"node.one\", password: \"$2a$11$node.onenode.onenode.ofixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.NODES.one.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_composed-one.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-config_resolved.*.$KV.mesh-config_resolved.one.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_placement.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_wants.*.$KV.mesh-delivery-assigner_wants.want.one.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_acts.*.$KV.mesh-host_acts.one.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_data.*.$KV.mesh-host_data.one.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_findings.*.$KV.mesh-host_findings.machine.one.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_health.*.$KV.mesh-host_health.one\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_packages.*.$KV.mesh-host_packages.one\", \"$JS.API.CONSUMER.CREATE.KV_mesh-provisioner_provisions.*.$KV.mesh-provisioner_provisions.one.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.*.node\", \"$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.one.\u003e\", \"$JS.API.CONSUMER.CREATE.OBJ_declaration-files-one.*.$O.declaration-files-one.C.*\", \"$JS.API.CONSUMER.DELETE.KV_composed-one.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-config_resolved.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_placement.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_wants.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_acts.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_data.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_findings.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_health.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_packages.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-provisioner_provisions.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_node-engine_have.\u003e\", \"$JS.API.CONSUMER.DELETE.OBJ_declaration-files-one.*\", \"$JS.API.CONSUMER.INFO.NODES.one\", \"$JS.API.DIRECT.GET.KV_composed-one.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-config_resolved.$KV.mesh-config_resolved.one.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_placement.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_wants.$KV.mesh-delivery-assigner_wants.want.one.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_acts.$KV.mesh-host_acts.one.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_data.$KV.mesh-host_data.one.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_findings.$KV.mesh-host_findings.machine.one.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_health.$KV.mesh-host_health.one\", \"$JS.API.DIRECT.GET.KV_mesh-host_packages.$KV.mesh-host_packages.one\", \"$JS.API.DIRECT.GET.KV_mesh-provisioner_provisions.$KV.mesh-provisioner_provisions.one.\u003e\", \"$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.*.node\", \"$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.one.\u003e\", \"$JS.API.DIRECT.GET.OBJ_declaration-files-one.$O.declaration-files-one.M.*\", \"$JS.API.STREAM.INFO.KV_composed-one\", \"$JS.API.STREAM.INFO.KV_mesh-config_resolved\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_candidates\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_placement\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_wants\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-builder_builds\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_candidates\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_manifests\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_modules\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_verdicts\", \"$JS.API.STREAM.INFO.KV_mesh-host_acts\", \"$JS.API.STREAM.INFO.KV_mesh-host_data\", \"$JS.API.STREAM.INFO.KV_mesh-host_findings\", \"$JS.API.STREAM.INFO.KV_mesh-host_health\", \"$JS.API.STREAM.INFO.KV_mesh-host_packages\", \"$JS.API.STREAM.INFO.KV_mesh-provisioner_provisions\", \"$JS.API.STREAM.INFO.KV_node-engine_have\", \"$JS.FC.KV_composed-one.\u003e\", \"$JS.FC.KV_mesh-config_resolved.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_placement.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_wants.\u003e\", \"$JS.FC.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.FC.KV_mesh-host_acts.\u003e\", \"$JS.FC.KV_mesh-host_data.\u003e\", \"$JS.FC.KV_mesh-host_findings.\u003e\", \"$JS.FC.KV_mesh-host_health.\u003e\", \"$JS.FC.KV_mesh-host_packages.\u003e\", \"$JS.FC.KV_mesh-provisioner_provisions.\u003e\", \"$JS.FC.KV_node-engine_have.\u003e\", \"$JS.FC.OBJ_declaration-files-one.\u003e\", \"$KV.composed-one.\u003e\", \"$KV.mesh-host_acts.one.\u003e\", \"$KV.mesh-host_data.one.\u003e\", \"$KV.mesh-host_findings.machine.one.\u003e\", \"$KV.mesh-host_health.one\", \"$KV.mesh-host_packages.one\", \"$KV.node-engine_have.one.\u003e\", \"$SRV.INFO\", \"$SRV.INFO.\u003e\", \"$SRV.PING\", \"$SRV.PING.*.one\", \"$SRV.PING.\u003e\", \"mesh.control.one.\u003e\", \"mesh.seat.node-backup.call.backed-up.one.node~one\", \"mesh.seat.node-backup.call.now.one.node~one\", \"mesh.seat.node-backup.tool.backed-up.one\", \"mesh.seat.node-backup.tool.now.one\", \"mesh.seat.node-engine.event.*.one\"] }\n subscribe: { allow: [\"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_candidates\", \"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_placement\", \"$SRV.INFO\", \"$SRV.INFO.node-engine\", \"$SRV.INFO.node-engine.\u003e\", \"$SRV.PING\", \"$SRV.PING.node-engine\", \"$SRV.PING.node-engine.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.node-engine\", \"$SRV.STATS.node-engine.\u003e\", \"_DELIVER.one\", \"_DELIVER.one.\u003e\", \"_INBOX.node.one.\u003e\", \"mesh.node.one.ask.access-act\", \"mesh.node.one.ask.hand-over\", \"mesh.node.one.ask.judge-directories\", \"mesh.node.one.ask.move-secret\", \"mesh.node.one.ask.report\", \"mesh.node.one.ask.setuid-search\", \"mesh.node.one.declare\", \"mesh.seat.node-engine.call.*.one.*\", \"mesh.seat.node-engine.tool.*.one\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n { user: \"node.two\", password: \"$2a$11$node.twonode.twonode.tfixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.NODES.two.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_composed-two.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-config_resolved.*.$KV.mesh-config_resolved.two.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_placement.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_wants.*.$KV.mesh-delivery-assigner_wants.want.two.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_acts.*.$KV.mesh-host_acts.two.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_data.*.$KV.mesh-host_data.two.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_findings.*.$KV.mesh-host_findings.machine.two.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_health.*.$KV.mesh-host_health.two\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_packages.*.$KV.mesh-host_packages.two\", \"$JS.API.CONSUMER.CREATE.KV_mesh-provisioner_provisions.*.$KV.mesh-provisioner_provisions.two.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.*.node\", \"$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.two.\u003e\", \"$JS.API.CONSUMER.CREATE.OBJ_declaration-files-two.*.$O.declaration-files-two.C.*\", \"$JS.API.CONSUMER.DELETE.KV_composed-two.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-config_resolved.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_placement.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_wants.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_acts.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_data.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_findings.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_health.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_packages.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-provisioner_provisions.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_node-engine_have.\u003e\", \"$JS.API.CONSUMER.DELETE.OBJ_declaration-files-two.*\", \"$JS.API.CONSUMER.INFO.NODES.two\", \"$JS.API.DIRECT.GET.KV_composed-two.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-config_resolved.$KV.mesh-config_resolved.two.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_placement.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_wants.$KV.mesh-delivery-assigner_wants.want.two.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_acts.$KV.mesh-host_acts.two.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_data.$KV.mesh-host_data.two.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_findings.$KV.mesh-host_findings.machine.two.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_health.$KV.mesh-host_health.two\", \"$JS.API.DIRECT.GET.KV_mesh-host_packages.$KV.mesh-host_packages.two\", \"$JS.API.DIRECT.GET.KV_mesh-provisioner_provisions.$KV.mesh-provisioner_provisions.two.\u003e\", \"$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.*.node\", \"$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.two.\u003e\", \"$JS.API.DIRECT.GET.OBJ_declaration-files-two.$O.declaration-files-two.M.*\", \"$JS.API.STREAM.INFO.KV_composed-two\", \"$JS.API.STREAM.INFO.KV_mesh-config_resolved\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_candidates\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_placement\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_wants\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-builder_builds\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_candidates\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_manifests\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_modules\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_verdicts\", \"$JS.API.STREAM.INFO.KV_mesh-host_acts\", \"$JS.API.STREAM.INFO.KV_mesh-host_data\", \"$JS.API.STREAM.INFO.KV_mesh-host_findings\", \"$JS.API.STREAM.INFO.KV_mesh-host_health\", \"$JS.API.STREAM.INFO.KV_mesh-host_packages\", \"$JS.API.STREAM.INFO.KV_mesh-provisioner_provisions\", \"$JS.API.STREAM.INFO.KV_node-engine_have\", \"$JS.FC.KV_composed-two.\u003e\", \"$JS.FC.KV_mesh-config_resolved.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_placement.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_wants.\u003e\", \"$JS.FC.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.FC.KV_mesh-host_acts.\u003e\", \"$JS.FC.KV_mesh-host_data.\u003e\", \"$JS.FC.KV_mesh-host_findings.\u003e\", \"$JS.FC.KV_mesh-host_health.\u003e\", \"$JS.FC.KV_mesh-host_packages.\u003e\", \"$JS.FC.KV_mesh-provisioner_provisions.\u003e\", \"$JS.FC.KV_node-engine_have.\u003e\", \"$JS.FC.OBJ_declaration-files-two.\u003e\", \"$KV.composed-two.\u003e\", \"$KV.mesh-host_acts.two.\u003e\", \"$KV.mesh-host_data.two.\u003e\", \"$KV.mesh-host_findings.machine.two.\u003e\", \"$KV.mesh-host_health.two\", \"$KV.mesh-host_packages.two\", \"$KV.node-engine_have.two.\u003e\", \"$SRV.INFO\", \"$SRV.INFO.\u003e\", \"$SRV.PING\", \"$SRV.PING.*.two\", \"$SRV.PING.\u003e\", \"mesh.control.two.\u003e\", \"mesh.seat.node-backup.call.backed-up.two.node~two\", \"mesh.seat.node-backup.call.now.two.node~two\", \"mesh.seat.node-backup.tool.backed-up.two\", \"mesh.seat.node-backup.tool.now.two\", \"mesh.seat.node-engine.event.*.two\"] }\n subscribe: { allow: [\"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_candidates\", \"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_placement\", \"$SRV.INFO\", \"$SRV.INFO.node-engine\", \"$SRV.INFO.node-engine.\u003e\", \"$SRV.PING\", \"$SRV.PING.node-engine\", \"$SRV.PING.node-engine.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.node-engine\", \"$SRV.STATS.node-engine.\u003e\", \"_DELIVER.two\", \"_DELIVER.two.\u003e\", \"_INBOX.node.two.\u003e\", \"mesh.node.two.ask.access-act\", \"mesh.node.two.ask.hand-over\", \"mesh.node.two.ask.judge-directories\", \"mesh.node.two.ask.move-secret\", \"mesh.node.two.ask.report\", \"mesh.node.two.ask.setuid-search\", \"mesh.node.two.declare\", \"mesh.seat.node-engine.call.*.two.*\", \"mesh.seat.node-engine.tool.*.two\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n { user: \"one.telegram\", password: \"$2a$11$one.telegramone.telegrfixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.EVENTS.one_telegram.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-grants_memberships.*.$KV.mesh-grants_memberships.one.telegram\", \"$JS.API.CONSUMER.DELETE.KV_mesh-grants_memberships.\u003e\", \"$JS.API.CONSUMER.INFO.EVENTS.one_telegram\", \"$JS.API.CONSUMER.MSG.NEXT.EVENTS.one_telegram\", \"$JS.API.DIRECT.GET.KV_mesh-grants_memberships.$KV.mesh-grants_memberships.one.telegram\", \"$JS.API.STREAM.INFO.KV_mesh-grants_memberships\", \"$JS.FC.KV_mesh-grants_memberships.\u003e\"] }\n subscribe: { allow: [\"$SRV.INFO\", \"$SRV.INFO.telegram\", \"$SRV.INFO.telegram.\u003e\", \"$SRV.PING\", \"$SRV.PING.telegram\", \"$SRV.PING.telegram.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.telegram\", \"$SRV.STATS.telegram.\u003e\", \"_INBOX.one.telegram.\u003e\", \"mesh.mod.telegram.call.\u003e\", \"mesh.mod.telegram.tool.\u003e\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n { user: \"person.ada\", password: \"$2a$11$person.adaperson.adapefixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$SRV.INFO\", \"$SRV.INFO.\u003e\", \"$SRV.PING\", \"$SRV.PING.\u003e\", \"mesh.mod.mesh-catalog.call.catalog_tools.*.person~ada\", \"mesh.mod.mesh-catalog.call.catalog_tools.person~ada\", \"mesh.mod.mesh-catalog.tool.catalog_tools\", \"mesh.mod.mesh-catalog.tool.catalog_tools.*\"] }\n subscribe: { allow: [\"_INBOX.person.ada.\u003e\"] }\n } }\n { user: \"two.shop\", password: \"$2a$11$two.shoptwo.shoptwo.shfixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.EVENTS.two_shop.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-grants_memberships.*.$KV.mesh-grants_memberships.two.shop\", \"$JS.API.CONSUMER.DELETE.KV_mesh-grants_memberships.\u003e\", \"$JS.API.CONSUMER.INFO.EVENTS.two_shop\", \"$JS.API.CONSUMER.MSG.NEXT.EVENTS.two_shop\", \"$JS.API.DIRECT.GET.KV_mesh-grants_memberships.$KV.mesh-grants_memberships.two.shop\", \"$JS.API.STREAM.INFO.KV_mesh-grants_memberships\", \"$JS.FC.KV_mesh-grants_memberships.\u003e\", \"mesh.mod.shop.event.order.placed\"] }\n subscribe: { allow: [\"$SRV.INFO\", \"$SRV.INFO.shop\", \"$SRV.INFO.shop.\u003e\", \"$SRV.PING\", \"$SRV.PING.shop\", \"$SRV.PING.shop.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.shop\", \"$SRV.STATS.shop.\u003e\", \"_INBOX.two.shop.\u003e\", \"mesh.mod.shop.call.\u003e\", \"mesh.mod.shop.tool.\u003e\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n ]\n }\n \"$SYS\" {\n exports = [\n { stream: \"$SYS.ACCOUNT.MESH.CONNECT\", accounts: [\"MESH\"] }\n { stream: \"$SYS.ACCOUNT.MESH.DISCONNECT\", accounts: [\"MESH\"] }\n { service: \"$SYS.REQ.ACCOUNT.MESH.CONNZ\", accounts: [\"MESH\"] }\n ]\n }\n}\n", + "List": "# The mesh's users, composed by the grants part. Do not edit: the next\n# composition overwrites it. Permissions are derived from what each module\n# declares and nothing else (novox/hq ADR 0043, design 29 §2).\n\naccounts {\n MESH {\n jetstream: enabled\n imports = [\n { stream: { account: \"$SYS\", subject: \"$SYS.ACCOUNT.MESH.CONNECT\" }, to: \"mesh.bus.connect\" }\n { stream: { account: \"$SYS\", subject: \"$SYS.ACCOUNT.MESH.DISCONNECT\" }, to: \"mesh.bus.disconnect\" }\n { service: { account: \"$SYS\", subject: \"$SYS.REQ.ACCOUNT.MESH.CONNZ\" }, to: \"mesh.bus.connz\" }\n ]\n users = [\n { user: \"node.one\", password: \"$2a$11$node.onenode.onenode.ofixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.NODES.one.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_composed-one.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-config_resolved.*.$KV.mesh-config_resolved.one.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_assignments.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_wants.*.$KV.mesh-delivery-assigner_wants.want.one.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_acts.*.$KV.mesh-host_acts.one.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_data.*.$KV.mesh-host_data.one.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_findings.*.$KV.mesh-host_findings.machine.one.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_health.*.$KV.mesh-host_health.one\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_packages.*.$KV.mesh-host_packages.one\", \"$JS.API.CONSUMER.CREATE.KV_mesh-provisioner_provisions.*.$KV.mesh-provisioner_provisions.one.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.*.node\", \"$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.one.\u003e\", \"$JS.API.CONSUMER.CREATE.OBJ_declaration-files-one.*.$O.declaration-files-one.C.*\", \"$JS.API.CONSUMER.DELETE.KV_composed-one.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-config_resolved.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_assignments.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_wants.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_acts.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_data.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_findings.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_health.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_packages.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-provisioner_provisions.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_node-engine_have.\u003e\", \"$JS.API.CONSUMER.DELETE.OBJ_declaration-files-one.*\", \"$JS.API.CONSUMER.INFO.NODES.one\", \"$JS.API.DIRECT.GET.KV_composed-one.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-config_resolved.$KV.mesh-config_resolved.one.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_assignments.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_wants.$KV.mesh-delivery-assigner_wants.want.one.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_acts.$KV.mesh-host_acts.one.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_data.$KV.mesh-host_data.one.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_findings.$KV.mesh-host_findings.machine.one.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_health.$KV.mesh-host_health.one\", \"$JS.API.DIRECT.GET.KV_mesh-host_packages.$KV.mesh-host_packages.one\", \"$JS.API.DIRECT.GET.KV_mesh-provisioner_provisions.$KV.mesh-provisioner_provisions.one.\u003e\", \"$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.*.node\", \"$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.one.\u003e\", \"$JS.API.DIRECT.GET.OBJ_declaration-files-one.$O.declaration-files-one.M.*\", \"$JS.API.STREAM.INFO.KV_composed-one\", \"$JS.API.STREAM.INFO.KV_mesh-config_resolved\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_assignments\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_candidates\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_wants\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-builder_builds\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_candidates\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_manifests\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_modules\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_verdicts\", \"$JS.API.STREAM.INFO.KV_mesh-host_acts\", \"$JS.API.STREAM.INFO.KV_mesh-host_data\", \"$JS.API.STREAM.INFO.KV_mesh-host_findings\", \"$JS.API.STREAM.INFO.KV_mesh-host_health\", \"$JS.API.STREAM.INFO.KV_mesh-host_packages\", \"$JS.API.STREAM.INFO.KV_mesh-provisioner_provisions\", \"$JS.API.STREAM.INFO.KV_node-engine_have\", \"$JS.FC.KV_composed-one.\u003e\", \"$JS.FC.KV_mesh-config_resolved.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_assignments.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_wants.\u003e\", \"$JS.FC.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.FC.KV_mesh-host_acts.\u003e\", \"$JS.FC.KV_mesh-host_data.\u003e\", \"$JS.FC.KV_mesh-host_findings.\u003e\", \"$JS.FC.KV_mesh-host_health.\u003e\", \"$JS.FC.KV_mesh-host_packages.\u003e\", \"$JS.FC.KV_mesh-provisioner_provisions.\u003e\", \"$JS.FC.KV_node-engine_have.\u003e\", \"$JS.FC.OBJ_declaration-files-one.\u003e\", \"$KV.composed-one.\u003e\", \"$KV.mesh-host_acts.one.\u003e\", \"$KV.mesh-host_data.one.\u003e\", \"$KV.mesh-host_findings.machine.one.\u003e\", \"$KV.mesh-host_health.one\", \"$KV.mesh-host_packages.one\", \"$KV.node-engine_have.one.\u003e\", \"$SRV.INFO\", \"$SRV.INFO.\u003e\", \"$SRV.PING\", \"$SRV.PING.*.one\", \"$SRV.PING.\u003e\", \"mesh.control.one.\u003e\", \"mesh.seat.node-backup.call.backed-up.one.node~one\", \"mesh.seat.node-backup.call.now.one.node~one\", \"mesh.seat.node-backup.tool.backed-up.one\", \"mesh.seat.node-backup.tool.now.one\", \"mesh.seat.node-engine.event.*.one\"] }\n subscribe: { allow: [\"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_assignments\", \"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_candidates\", \"$SRV.INFO\", \"$SRV.INFO.node-engine\", \"$SRV.INFO.node-engine.\u003e\", \"$SRV.PING\", \"$SRV.PING.node-engine\", \"$SRV.PING.node-engine.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.node-engine\", \"$SRV.STATS.node-engine.\u003e\", \"_DELIVER.one\", \"_DELIVER.one.\u003e\", \"_INBOX.node.one.\u003e\", \"mesh.node.one.ask.access-act\", \"mesh.node.one.ask.hand-over\", \"mesh.node.one.ask.judge-directories\", \"mesh.node.one.ask.move-secret\", \"mesh.node.one.ask.report\", \"mesh.node.one.ask.setuid-search\", \"mesh.node.one.declare\", \"mesh.seat.node-engine.call.*.one.*\", \"mesh.seat.node-engine.tool.*.one\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n { user: \"node.two\", password: \"$2a$11$node.twonode.twonode.tfixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.NODES.two.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_composed-two.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-config_resolved.*.$KV.mesh-config_resolved.two.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_assignments.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_wants.*.$KV.mesh-delivery-assigner_wants.want.two.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_acts.*.$KV.mesh-host_acts.two.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_data.*.$KV.mesh-host_data.two.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_findings.*.$KV.mesh-host_findings.machine.two.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_health.*.$KV.mesh-host_health.two\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_packages.*.$KV.mesh-host_packages.two\", \"$JS.API.CONSUMER.CREATE.KV_mesh-provisioner_provisions.*.$KV.mesh-provisioner_provisions.two.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.*.node\", \"$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.two.\u003e\", \"$JS.API.CONSUMER.CREATE.OBJ_declaration-files-two.*.$O.declaration-files-two.C.*\", \"$JS.API.CONSUMER.DELETE.KV_composed-two.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-config_resolved.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_assignments.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_wants.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_acts.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_data.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_findings.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_health.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_packages.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-provisioner_provisions.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_node-engine_have.\u003e\", \"$JS.API.CONSUMER.DELETE.OBJ_declaration-files-two.*\", \"$JS.API.CONSUMER.INFO.NODES.two\", \"$JS.API.DIRECT.GET.KV_composed-two.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-config_resolved.$KV.mesh-config_resolved.two.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_assignments.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_wants.$KV.mesh-delivery-assigner_wants.want.two.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_acts.$KV.mesh-host_acts.two.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_data.$KV.mesh-host_data.two.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_findings.$KV.mesh-host_findings.machine.two.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_health.$KV.mesh-host_health.two\", \"$JS.API.DIRECT.GET.KV_mesh-host_packages.$KV.mesh-host_packages.two\", \"$JS.API.DIRECT.GET.KV_mesh-provisioner_provisions.$KV.mesh-provisioner_provisions.two.\u003e\", \"$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.*.node\", \"$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.two.\u003e\", \"$JS.API.DIRECT.GET.OBJ_declaration-files-two.$O.declaration-files-two.M.*\", \"$JS.API.STREAM.INFO.KV_composed-two\", \"$JS.API.STREAM.INFO.KV_mesh-config_resolved\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_assignments\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_candidates\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_wants\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-builder_builds\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_candidates\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_manifests\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_modules\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_verdicts\", \"$JS.API.STREAM.INFO.KV_mesh-host_acts\", \"$JS.API.STREAM.INFO.KV_mesh-host_data\", \"$JS.API.STREAM.INFO.KV_mesh-host_findings\", \"$JS.API.STREAM.INFO.KV_mesh-host_health\", \"$JS.API.STREAM.INFO.KV_mesh-host_packages\", \"$JS.API.STREAM.INFO.KV_mesh-provisioner_provisions\", \"$JS.API.STREAM.INFO.KV_node-engine_have\", \"$JS.FC.KV_composed-two.\u003e\", \"$JS.FC.KV_mesh-config_resolved.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_assignments.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_wants.\u003e\", \"$JS.FC.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.FC.KV_mesh-host_acts.\u003e\", \"$JS.FC.KV_mesh-host_data.\u003e\", \"$JS.FC.KV_mesh-host_findings.\u003e\", \"$JS.FC.KV_mesh-host_health.\u003e\", \"$JS.FC.KV_mesh-host_packages.\u003e\", \"$JS.FC.KV_mesh-provisioner_provisions.\u003e\", \"$JS.FC.KV_node-engine_have.\u003e\", \"$JS.FC.OBJ_declaration-files-two.\u003e\", \"$KV.composed-two.\u003e\", \"$KV.mesh-host_acts.two.\u003e\", \"$KV.mesh-host_data.two.\u003e\", \"$KV.mesh-host_findings.machine.two.\u003e\", \"$KV.mesh-host_health.two\", \"$KV.mesh-host_packages.two\", \"$KV.node-engine_have.two.\u003e\", \"$SRV.INFO\", \"$SRV.INFO.\u003e\", \"$SRV.PING\", \"$SRV.PING.*.two\", \"$SRV.PING.\u003e\", \"mesh.control.two.\u003e\", \"mesh.seat.node-backup.call.backed-up.two.node~two\", \"mesh.seat.node-backup.call.now.two.node~two\", \"mesh.seat.node-backup.tool.backed-up.two\", \"mesh.seat.node-backup.tool.now.two\", \"mesh.seat.node-engine.event.*.two\"] }\n subscribe: { allow: [\"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_assignments\", \"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_candidates\", \"$SRV.INFO\", \"$SRV.INFO.node-engine\", \"$SRV.INFO.node-engine.\u003e\", \"$SRV.PING\", \"$SRV.PING.node-engine\", \"$SRV.PING.node-engine.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.node-engine\", \"$SRV.STATS.node-engine.\u003e\", \"_DELIVER.two\", \"_DELIVER.two.\u003e\", \"_INBOX.node.two.\u003e\", \"mesh.node.two.ask.access-act\", \"mesh.node.two.ask.hand-over\", \"mesh.node.two.ask.judge-directories\", \"mesh.node.two.ask.move-secret\", \"mesh.node.two.ask.report\", \"mesh.node.two.ask.setuid-search\", \"mesh.node.two.declare\", \"mesh.seat.node-engine.call.*.two.*\", \"mesh.seat.node-engine.tool.*.two\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n { user: \"one.telegram\", password: \"$2a$11$one.telegramone.telegrfixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.EVENTS.one_telegram.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-grants_memberships.*.$KV.mesh-grants_memberships.one.telegram\", \"$JS.API.CONSUMER.DELETE.KV_mesh-grants_memberships.\u003e\", \"$JS.API.CONSUMER.INFO.EVENTS.one_telegram\", \"$JS.API.CONSUMER.MSG.NEXT.EVENTS.one_telegram\", \"$JS.API.DIRECT.GET.KV_mesh-grants_memberships.$KV.mesh-grants_memberships.one.telegram\", \"$JS.API.STREAM.INFO.KV_mesh-grants_memberships\", \"$JS.FC.KV_mesh-grants_memberships.\u003e\"] }\n subscribe: { allow: [\"$SRV.INFO\", \"$SRV.INFO.telegram\", \"$SRV.INFO.telegram.\u003e\", \"$SRV.PING\", \"$SRV.PING.telegram\", \"$SRV.PING.telegram.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.telegram\", \"$SRV.STATS.telegram.\u003e\", \"_INBOX.one.telegram.\u003e\", \"mesh.mod.telegram.call.\u003e\", \"mesh.mod.telegram.tool.\u003e\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n { user: \"person.ada\", password: \"$2a$11$person.adaperson.adapefixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$SRV.INFO\", \"$SRV.INFO.\u003e\", \"$SRV.PING\", \"$SRV.PING.\u003e\", \"mesh.mod.mesh-catalog.call.catalog_tools.*.person~ada\", \"mesh.mod.mesh-catalog.call.catalog_tools.person~ada\", \"mesh.mod.mesh-catalog.tool.catalog_tools\", \"mesh.mod.mesh-catalog.tool.catalog_tools.*\"] }\n subscribe: { allow: [\"_INBOX.person.ada.\u003e\"] }\n } }\n { user: \"two.shop\", password: \"$2a$11$two.shoptwo.shoptwo.shfixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.EVENTS.two_shop.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-grants_memberships.*.$KV.mesh-grants_memberships.two.shop\", \"$JS.API.CONSUMER.DELETE.KV_mesh-grants_memberships.\u003e\", \"$JS.API.CONSUMER.INFO.EVENTS.two_shop\", \"$JS.API.CONSUMER.MSG.NEXT.EVENTS.two_shop\", \"$JS.API.DIRECT.GET.KV_mesh-grants_memberships.$KV.mesh-grants_memberships.two.shop\", \"$JS.API.STREAM.INFO.KV_mesh-grants_memberships\", \"$JS.FC.KV_mesh-grants_memberships.\u003e\", \"mesh.mod.shop.event.order.placed\"] }\n subscribe: { allow: [\"$SRV.INFO\", \"$SRV.INFO.shop\", \"$SRV.INFO.shop.\u003e\", \"$SRV.PING\", \"$SRV.PING.shop\", \"$SRV.PING.shop.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.shop\", \"$SRV.STATS.shop.\u003e\", \"_INBOX.two.shop.\u003e\", \"mesh.mod.shop.call.\u003e\", \"mesh.mod.shop.tool.\u003e\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n ]\n }\n \"$SYS\" {\n exports = [\n { stream: \"$SYS.ACCOUNT.MESH.CONNECT\", accounts: [\"MESH\"] }\n { stream: \"$SYS.ACCOUNT.MESH.DISCONNECT\", accounts: [\"MESH\"] }\n { service: \"$SYS.REQ.ACCOUNT.MESH.CONNZ\", accounts: [\"MESH\"] }\n ]\n }\n}\n", "Missing": null, "Err": "", "Memberships": { @@ -46,7 +46,7 @@ }, { "Name": "seats, consumers, people and a hashless user", - "List": "# The mesh's users, composed by the grants part. Do not edit: the next\n# composition overwrites it. Permissions are derived from what each module\n# declares and nothing else (novox/hq ADR 0043, design 29 §2).\n\naccounts {\n MESH {\n jetstream: enabled\n imports = [\n { stream: { account: \"$SYS\", subject: \"$SYS.ACCOUNT.MESH.CONNECT\" }, to: \"mesh.bus.connect\" }\n { stream: { account: \"$SYS\", subject: \"$SYS.ACCOUNT.MESH.DISCONNECT\" }, to: \"mesh.bus.disconnect\" }\n { service: { account: \"$SYS\", subject: \"$SYS.REQ.ACCOUNT.MESH.CONNZ\" }, to: \"mesh.bus.connz\" }\n ]\n users = [\n { user: \"node.one\", password: \"$2a$11$node.onenode.onenode.ofixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.NODES.one.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_composed-one.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-config_resolved.*.$KV.mesh-config_resolved.one.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_placement.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_wants.*.$KV.mesh-delivery-assigner_wants.want.one.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-grants_user-list.*.$KV.mesh-grants_user-list.sealed\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_acts.*.$KV.mesh-host_acts.one.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_data.*.$KV.mesh-host_data.one.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_findings.*.$KV.mesh-host_findings.machine.one.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_health.*.$KV.mesh-host_health.one\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_packages.*.$KV.mesh-host_packages.one\", \"$JS.API.CONSUMER.CREATE.KV_mesh-provisioner_provisions.*.$KV.mesh-provisioner_provisions.one.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.*.node\", \"$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.one.\u003e\", \"$JS.API.CONSUMER.CREATE.OBJ_declaration-files-one.*.$O.declaration-files-one.C.*\", \"$JS.API.CONSUMER.CREATE.OBJ_mesh-grants-user-list.*.$O.mesh-grants-user-list.C.*\", \"$JS.API.CONSUMER.DELETE.KV_composed-one.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-config_resolved.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_placement.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_wants.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-grants_user-list.*\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_acts.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_data.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_findings.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_health.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_packages.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-provisioner_provisions.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_node-engine_have.\u003e\", \"$JS.API.CONSUMER.DELETE.OBJ_declaration-files-one.*\", \"$JS.API.CONSUMER.DELETE.OBJ_mesh-grants-user-list.*\", \"$JS.API.CONSUMER.INFO.NODES.one\", \"$JS.API.DIRECT.GET.KV_composed-one.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-config_resolved.$KV.mesh-config_resolved.one.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_placement.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_wants.$KV.mesh-delivery-assigner_wants.want.one.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-grants_user-list.$KV.mesh-grants_user-list.sealed\", \"$JS.API.DIRECT.GET.KV_mesh-host_acts.$KV.mesh-host_acts.one.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_data.$KV.mesh-host_data.one.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_findings.$KV.mesh-host_findings.machine.one.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_health.$KV.mesh-host_health.one\", \"$JS.API.DIRECT.GET.KV_mesh-host_packages.$KV.mesh-host_packages.one\", \"$JS.API.DIRECT.GET.KV_mesh-provisioner_provisions.$KV.mesh-provisioner_provisions.one.\u003e\", \"$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.*.node\", \"$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.one.\u003e\", \"$JS.API.DIRECT.GET.OBJ_declaration-files-one.$O.declaration-files-one.M.*\", \"$JS.API.DIRECT.GET.OBJ_mesh-grants-user-list.$O.mesh-grants-user-list.M.*\", \"$JS.API.STREAM.INFO.KV_composed-one\", \"$JS.API.STREAM.INFO.KV_mesh-config_resolved\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_candidates\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_placement\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_wants\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-builder_builds\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_candidates\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_manifests\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_modules\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_verdicts\", \"$JS.API.STREAM.INFO.KV_mesh-grants_user-list\", \"$JS.API.STREAM.INFO.KV_mesh-host_acts\", \"$JS.API.STREAM.INFO.KV_mesh-host_data\", \"$JS.API.STREAM.INFO.KV_mesh-host_findings\", \"$JS.API.STREAM.INFO.KV_mesh-host_health\", \"$JS.API.STREAM.INFO.KV_mesh-host_packages\", \"$JS.API.STREAM.INFO.KV_mesh-provisioner_provisions\", \"$JS.API.STREAM.INFO.KV_node-engine_have\", \"$JS.FC.KV_composed-one.\u003e\", \"$JS.FC.KV_mesh-config_resolved.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_placement.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_wants.\u003e\", \"$JS.FC.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.FC.KV_mesh-grants_user-list.\u003e\", \"$JS.FC.KV_mesh-host_acts.\u003e\", \"$JS.FC.KV_mesh-host_data.\u003e\", \"$JS.FC.KV_mesh-host_findings.\u003e\", \"$JS.FC.KV_mesh-host_health.\u003e\", \"$JS.FC.KV_mesh-host_packages.\u003e\", \"$JS.FC.KV_mesh-provisioner_provisions.\u003e\", \"$JS.FC.KV_node-engine_have.\u003e\", \"$JS.FC.OBJ_declaration-files-one.\u003e\", \"$JS.FC.OBJ_mesh-grants-user-list.\u003e\", \"$KV.composed-one.\u003e\", \"$KV.mesh-host_acts.one.\u003e\", \"$KV.mesh-host_data.one.\u003e\", \"$KV.mesh-host_findings.machine.one.\u003e\", \"$KV.mesh-host_health.one\", \"$KV.mesh-host_packages.one\", \"$KV.node-engine_have.one.\u003e\", \"$SRV.INFO\", \"$SRV.INFO.\u003e\", \"$SRV.PING\", \"$SRV.PING.*.one\", \"$SRV.PING.\u003e\", \"mesh.control.one.\u003e\", \"mesh.seat.node-backup.call.backed-up.one.node~one\", \"mesh.seat.node-backup.call.now.one.node~one\", \"mesh.seat.node-backup.tool.backed-up.one\", \"mesh.seat.node-backup.tool.now.one\", \"mesh.seat.node-engine.event.*.one\"] }\n subscribe: { allow: [\"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_candidates\", \"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_placement\", \"$SRV.INFO\", \"$SRV.INFO.node-engine\", \"$SRV.INFO.node-engine.\u003e\", \"$SRV.PING\", \"$SRV.PING.node-engine\", \"$SRV.PING.node-engine.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.node-engine\", \"$SRV.STATS.node-engine.\u003e\", \"_DELIVER.one\", \"_DELIVER.one.\u003e\", \"_INBOX.node.one.\u003e\", \"mesh.node.one.ask.access-act\", \"mesh.node.one.ask.hand-over\", \"mesh.node.one.ask.judge-directories\", \"mesh.node.one.ask.move-secret\", \"mesh.node.one.ask.report\", \"mesh.node.one.ask.setuid-search\", \"mesh.node.one.declare\", \"mesh.seat.node-engine.call.*.one.*\", \"mesh.seat.node-engine.tool.*.one\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n { user: \"node.two\", password: \"$2a$11$node.twonode.twonode.tfixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.NODES.two.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_composed-two.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-config_resolved.*.$KV.mesh-config_resolved.two.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_placement.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_wants.*.$KV.mesh-delivery-assigner_wants.want.two.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_acts.*.$KV.mesh-host_acts.two.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_data.*.$KV.mesh-host_data.two.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_findings.*.$KV.mesh-host_findings.machine.two.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_health.*.$KV.mesh-host_health.two\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_packages.*.$KV.mesh-host_packages.two\", \"$JS.API.CONSUMER.CREATE.KV_mesh-provisioner_provisions.*.$KV.mesh-provisioner_provisions.two.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.*.node\", \"$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.two.\u003e\", \"$JS.API.CONSUMER.CREATE.OBJ_declaration-files-two.*.$O.declaration-files-two.C.*\", \"$JS.API.CONSUMER.DELETE.KV_composed-two.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-config_resolved.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_placement.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_wants.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_acts.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_data.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_findings.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_health.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_packages.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-provisioner_provisions.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_node-engine_have.\u003e\", \"$JS.API.CONSUMER.DELETE.OBJ_declaration-files-two.*\", \"$JS.API.CONSUMER.INFO.NODES.two\", \"$JS.API.DIRECT.GET.KV_composed-two.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-config_resolved.$KV.mesh-config_resolved.two.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_placement.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_wants.$KV.mesh-delivery-assigner_wants.want.two.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_acts.$KV.mesh-host_acts.two.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_data.$KV.mesh-host_data.two.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_findings.$KV.mesh-host_findings.machine.two.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_health.$KV.mesh-host_health.two\", \"$JS.API.DIRECT.GET.KV_mesh-host_packages.$KV.mesh-host_packages.two\", \"$JS.API.DIRECT.GET.KV_mesh-provisioner_provisions.$KV.mesh-provisioner_provisions.two.\u003e\", \"$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.*.node\", \"$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.two.\u003e\", \"$JS.API.DIRECT.GET.OBJ_declaration-files-two.$O.declaration-files-two.M.*\", \"$JS.API.STREAM.INFO.KV_composed-two\", \"$JS.API.STREAM.INFO.KV_mesh-config_resolved\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_candidates\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_placement\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_wants\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-builder_builds\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_candidates\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_manifests\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_modules\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_verdicts\", \"$JS.API.STREAM.INFO.KV_mesh-host_acts\", \"$JS.API.STREAM.INFO.KV_mesh-host_data\", \"$JS.API.STREAM.INFO.KV_mesh-host_findings\", \"$JS.API.STREAM.INFO.KV_mesh-host_health\", \"$JS.API.STREAM.INFO.KV_mesh-host_packages\", \"$JS.API.STREAM.INFO.KV_mesh-provisioner_provisions\", \"$JS.API.STREAM.INFO.KV_node-engine_have\", \"$JS.FC.KV_composed-two.\u003e\", \"$JS.FC.KV_mesh-config_resolved.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_placement.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_wants.\u003e\", \"$JS.FC.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.FC.KV_mesh-host_acts.\u003e\", \"$JS.FC.KV_mesh-host_data.\u003e\", \"$JS.FC.KV_mesh-host_findings.\u003e\", \"$JS.FC.KV_mesh-host_health.\u003e\", \"$JS.FC.KV_mesh-host_packages.\u003e\", \"$JS.FC.KV_mesh-provisioner_provisions.\u003e\", \"$JS.FC.KV_node-engine_have.\u003e\", \"$JS.FC.OBJ_declaration-files-two.\u003e\", \"$KV.composed-two.\u003e\", \"$KV.mesh-host_acts.two.\u003e\", \"$KV.mesh-host_data.two.\u003e\", \"$KV.mesh-host_findings.machine.two.\u003e\", \"$KV.mesh-host_health.two\", \"$KV.mesh-host_packages.two\", \"$KV.node-engine_have.two.\u003e\", \"$SRV.INFO\", \"$SRV.INFO.\u003e\", \"$SRV.PING\", \"$SRV.PING.*.two\", \"$SRV.PING.\u003e\", \"mesh.control.two.\u003e\", \"mesh.seat.node-backup.call.backed-up.two.node~two\", \"mesh.seat.node-backup.call.now.two.node~two\", \"mesh.seat.node-backup.tool.backed-up.two\", \"mesh.seat.node-backup.tool.now.two\", \"mesh.seat.node-engine.event.*.two\"] }\n subscribe: { allow: [\"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_candidates\", \"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_placement\", \"$SRV.INFO\", \"$SRV.INFO.node-engine\", \"$SRV.INFO.node-engine.\u003e\", \"$SRV.PING\", \"$SRV.PING.node-engine\", \"$SRV.PING.node-engine.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.node-engine\", \"$SRV.STATS.node-engine.\u003e\", \"_DELIVER.two\", \"_DELIVER.two.\u003e\", \"_INBOX.node.two.\u003e\", \"mesh.node.two.ask.access-act\", \"mesh.node.two.ask.hand-over\", \"mesh.node.two.ask.judge-directories\", \"mesh.node.two.ask.move-secret\", \"mesh.node.two.ask.report\", \"mesh.node.two.ask.setuid-search\", \"mesh.node.two.declare\", \"mesh.seat.node-engine.call.*.two.*\", \"mesh.seat.node-engine.tool.*.two\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n { user: \"one.mesh-grants\", password: \"$2a$11$one.mesh-grantsone.mesfixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.EVENTS.one_mesh-grants.\u003e\", \"$JS.API.CONSUMER.CREATE.EVENTS.two_audit\", \"$JS.API.CONSUMER.CREATE.KV_mesh-grants_memberships.*.$KV.mesh-grants_memberships.one.mesh-grants\", \"$JS.API.CONSUMER.DELETE.KV_mesh-grants_memberships.\u003e\", \"$JS.API.CONSUMER.INFO.EVENTS.one_mesh-grants\", \"$JS.API.CONSUMER.INFO.EVENTS.two_audit\", \"$JS.API.CONSUMER.MSG.NEXT.EVENTS.one_mesh-grants\", \"$JS.API.DIRECT.GET.ASSIGNMENTS.mesh.assignment.*.*\", \"$JS.API.DIRECT.GET.KV_mesh-grants_memberships.$KV.mesh-grants_memberships.one.mesh-grants\", \"$JS.API.DIRECT.GET.OBJ_mesh-grants-user-list.\u003e\", \"$JS.API.STREAM.CREATE.OBJ_mesh-grants-user-list\", \"$JS.API.STREAM.INFO.KV_mesh-grants_memberships\", \"$JS.API.STREAM.INFO.OBJ_mesh-grants-user-list\", \"$JS.API.STREAM.MSG.GET.OBJ_mesh-grants-user-list\", \"$JS.API.STREAM.PURGE.OBJ_mesh-grants-user-list\", \"$JS.FC.KV_mesh-grants_memberships.\u003e\", \"$O.mesh-grants-user-list.\u003e\", \"mesh.assignment.*.*\", \"mesh.mod.mesh-grants.event.membership-changed\"] }\n subscribe: { allow: [\"$SRV.INFO\", \"$SRV.INFO.mesh-grants\", \"$SRV.INFO.mesh-grants.\u003e\", \"$SRV.PING\", \"$SRV.PING.mesh-grants\", \"$SRV.PING.mesh-grants.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.mesh-grants\", \"$SRV.STATS.mesh-grants.\u003e\", \"_INBOX.one.mesh-grants.\u003e\", \"mesh.mod.mesh-grants.call.\u003e\", \"mesh.mod.mesh-grants.tool.\u003e\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n { user: \"one.mesh-watchman\", password: \"$2a$11$one.mesh-watchmanone.mfixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.EVENTS.one_mesh-watchman.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-grants_memberships.*.$KV.mesh-grants_memberships.one.mesh-watchman\", \"$JS.API.CONSUMER.DELETE.KV_mesh-grants_memberships.\u003e\", \"$JS.API.CONSUMER.INFO.EVENTS.one_mesh-watchman\", \"$JS.API.CONSUMER.MSG.NEXT.EVENTS.one_mesh-watchman\", \"$JS.API.DIRECT.GET.KV_mesh-grants_memberships.$KV.mesh-grants_memberships.one.mesh-watchman\", \"$JS.API.STREAM.INFO.KV_mesh-grants_memberships\", \"$JS.FC.KV_mesh-grants_memberships.\u003e\"] }\n subscribe: { allow: [\"$SRV.INFO\", \"$SRV.INFO.mesh-watchman\", \"$SRV.INFO.mesh-watchman.\u003e\", \"$SRV.PING\", \"$SRV.PING.mesh-watchman\", \"$SRV.PING.mesh-watchman.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.mesh-watchman\", \"$SRV.STATS.mesh-watchman.\u003e\", \"_INBOX.one.mesh-watchman.\u003e\", \"mesh.mod.mesh-watchman.call.\u003e\", \"mesh.mod.mesh-watchman.tool.\u003e\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n { user: \"one.nats\", password: \"$2a$11$one.natsone.natsone.nafixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.API.STREAM.INFO.*\", \"$JS.API.STREAM.NAMES\", \"$JS.API.STREAM.SNAPSHOT.*\", \"$JS.SNAPSHOT.ACK.\u003e\"] }\n subscribe: { allow: [\"_INBOX.one.nats.\u003e\"] }\n } }\n { user: \"one.telegram\", password: \"$2a$11$one.telegramone.telegrfixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.EVENTS.one_telegram.\u003e\", \"$JS.ACK.SEAT_TELEGRAM_SENDER.SEAT_TELEGRAM_SENDER_worker.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-grants_memberships.*.$KV.mesh-grants_memberships.one.telegram\", \"$JS.API.CONSUMER.DELETE.KV_mesh-grants_memberships.\u003e\", \"$JS.API.CONSUMER.INFO.EVENTS.one_telegram\", \"$JS.API.CONSUMER.INFO.SEAT_TELEGRAM_SENDER.SEAT_TELEGRAM_SENDER_worker\", \"$JS.API.CONSUMER.MSG.NEXT.EVENTS.one_telegram\", \"$JS.API.CONSUMER.MSG.NEXT.SEAT_TELEGRAM_SENDER.SEAT_TELEGRAM_SENDER_worker\", \"$JS.API.DIRECT.GET.KV_mesh-grants_memberships.$KV.mesh-grants_memberships.one.telegram\", \"$JS.API.STREAM.INFO.KV_mesh-grants_memberships\", \"$JS.FC.KV_mesh-grants_memberships.\u003e\", \"mesh.seat.telegram-sender.event.delivered\", \"mesh.seat.telegram-sender.event.failed\"] }\n subscribe: { allow: [\"$SRV.INFO\", \"$SRV.INFO.telegram\", \"$SRV.INFO.telegram.\u003e\", \"$SRV.PING\", \"$SRV.PING.telegram\", \"$SRV.PING.telegram.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.telegram\", \"$SRV.STATS.telegram.\u003e\", \"_INBOX.one.telegram.\u003e\", \"mesh.mod.telegram.call.\u003e\", \"mesh.mod.telegram.tool.\u003e\", \"mesh.seat.telegram-sender.accept.send\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n { user: \"person.ada\", password: \"$2a$11$person.adaperson.adapefixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$SRV.INFO\", \"$SRV.INFO.\u003e\", \"$SRV.PING\", \"$SRV.PING.\u003e\", \"mesh.mod.*.call.*.*.person~ada\", \"mesh.mod.*.call.*.person~ada\", \"mesh.mod.*.tool.\u003e\", \"mesh.seat.*.call.*.*.person~ada\", \"mesh.seat.*.call.*.person~ada\", \"mesh.seat.*.tool.\u003e\"], deny: [\"mesh.seat.node-launcher.tool.secret\", \"mesh.seat.node-launcher.tool.secret.*\", \"mesh.mod.*.tool.node-launcher.secret\", \"mesh.mod.*.tool.node-launcher.secret.*\", \"mesh.seat.node-launcher.call.secret.\u003e\", \"mesh.mod.*.call.node-launcher.secret.\u003e\", \"mesh.seat.mesh-broker.call.apply.\u003e\", \"mesh.seat.mesh-broker.call.create.\u003e\", \"mesh.seat.mesh-broker.call.drop.\u003e\", \"mesh.seat.mesh-broker.call.recreate.\u003e\", \"mesh.seat.mesh-broker.tool.apply\", \"mesh.seat.mesh-broker.tool.create\", \"mesh.seat.mesh-broker.tool.drop\", \"mesh.seat.mesh-broker.tool.recreate\"] }\n subscribe: { allow: [\"_INBOX.person.ada.\u003e\"] }\n } }\n { user: \"person.bob\", password: \"$2a$11$person.bobperson.bobpefixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$SRV.INFO\", \"$SRV.INFO.\u003e\", \"$SRV.PING\", \"$SRV.PING.\u003e\", \"mesh.mod.shop.call.status.*.person~bob\", \"mesh.mod.shop.call.status.person~bob\", \"mesh.mod.shop.tool.status\", \"mesh.mod.shop.tool.status.*\"] }\n subscribe: { allow: [\"_INBOX.person.bob.\u003e\"] }\n } }\n { user: \"two.shop\", password: \"$2a$11$two.shoptwo.shoptwo.shfixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.EVENTS.two_shop.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-grants_memberships.*.$KV.mesh-grants_memberships.two.shop\", \"$JS.API.CONSUMER.DELETE.KV_mesh-grants_memberships.\u003e\", \"$JS.API.CONSUMER.INFO.EVENTS.two_shop\", \"$JS.API.CONSUMER.MSG.NEXT.EVENTS.two_shop\", \"$JS.API.DIRECT.GET.KV_mesh-grants_memberships.$KV.mesh-grants_memberships.two.shop\", \"$JS.API.STREAM.INFO.KV_mesh-grants_memberships\", \"$JS.FC.KV_mesh-grants_memberships.\u003e\", \"mesh.mod.shop.event.order.placed\", \"mesh.seat.telegram-sender.accept.send\"] }\n subscribe: { allow: [\"$SRV.INFO\", \"$SRV.INFO.shop\", \"$SRV.INFO.shop.\u003e\", \"$SRV.PING\", \"$SRV.PING.shop\", \"$SRV.PING.shop.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.shop\", \"$SRV.STATS.shop.\u003e\", \"_INBOX.two.shop.\u003e\", \"mesh.mod.shop.call.\u003e\", \"mesh.mod.shop.tool.\u003e\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n ]\n }\n \"$SYS\" {\n exports = [\n { stream: \"$SYS.ACCOUNT.MESH.CONNECT\", accounts: [\"MESH\"] }\n { stream: \"$SYS.ACCOUNT.MESH.DISCONNECT\", accounts: [\"MESH\"] }\n { service: \"$SYS.REQ.ACCOUNT.MESH.CONNZ\", accounts: [\"MESH\"] }\n ]\n }\n}\n", + "List": "# The mesh's users, composed by the grants part. Do not edit: the next\n# composition overwrites it. Permissions are derived from what each module\n# declares and nothing else (novox/hq ADR 0043, design 29 §2).\n\naccounts {\n MESH {\n jetstream: enabled\n imports = [\n { stream: { account: \"$SYS\", subject: \"$SYS.ACCOUNT.MESH.CONNECT\" }, to: \"mesh.bus.connect\" }\n { stream: { account: \"$SYS\", subject: \"$SYS.ACCOUNT.MESH.DISCONNECT\" }, to: \"mesh.bus.disconnect\" }\n { service: { account: \"$SYS\", subject: \"$SYS.REQ.ACCOUNT.MESH.CONNZ\" }, to: \"mesh.bus.connz\" }\n ]\n users = [\n { user: \"node.one\", password: \"$2a$11$node.onenode.onenode.ofixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.NODES.one.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_composed-one.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-config_resolved.*.$KV.mesh-config_resolved.one.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_assignments.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_wants.*.$KV.mesh-delivery-assigner_wants.want.one.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-grants_user-list.*.$KV.mesh-grants_user-list.sealed\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_acts.*.$KV.mesh-host_acts.one.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_data.*.$KV.mesh-host_data.one.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_findings.*.$KV.mesh-host_findings.machine.one.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_health.*.$KV.mesh-host_health.one\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_packages.*.$KV.mesh-host_packages.one\", \"$JS.API.CONSUMER.CREATE.KV_mesh-provisioner_provisions.*.$KV.mesh-provisioner_provisions.one.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.*.node\", \"$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.one.\u003e\", \"$JS.API.CONSUMER.CREATE.OBJ_declaration-files-one.*.$O.declaration-files-one.C.*\", \"$JS.API.CONSUMER.CREATE.OBJ_mesh-grants-user-list.*.$O.mesh-grants-user-list.C.*\", \"$JS.API.CONSUMER.DELETE.KV_composed-one.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-config_resolved.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_assignments.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_wants.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-grants_user-list.*\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_acts.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_data.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_findings.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_health.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_packages.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-provisioner_provisions.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_node-engine_have.\u003e\", \"$JS.API.CONSUMER.DELETE.OBJ_declaration-files-one.*\", \"$JS.API.CONSUMER.DELETE.OBJ_mesh-grants-user-list.*\", \"$JS.API.CONSUMER.INFO.NODES.one\", \"$JS.API.DIRECT.GET.KV_composed-one.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-config_resolved.$KV.mesh-config_resolved.one.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_assignments.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_wants.$KV.mesh-delivery-assigner_wants.want.one.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-grants_user-list.$KV.mesh-grants_user-list.sealed\", \"$JS.API.DIRECT.GET.KV_mesh-host_acts.$KV.mesh-host_acts.one.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_data.$KV.mesh-host_data.one.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_findings.$KV.mesh-host_findings.machine.one.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_health.$KV.mesh-host_health.one\", \"$JS.API.DIRECT.GET.KV_mesh-host_packages.$KV.mesh-host_packages.one\", \"$JS.API.DIRECT.GET.KV_mesh-provisioner_provisions.$KV.mesh-provisioner_provisions.one.\u003e\", \"$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.*.node\", \"$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.one.\u003e\", \"$JS.API.DIRECT.GET.OBJ_declaration-files-one.$O.declaration-files-one.M.*\", \"$JS.API.DIRECT.GET.OBJ_mesh-grants-user-list.$O.mesh-grants-user-list.M.*\", \"$JS.API.STREAM.INFO.KV_composed-one\", \"$JS.API.STREAM.INFO.KV_mesh-config_resolved\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_assignments\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_candidates\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_wants\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-builder_builds\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_candidates\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_manifests\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_modules\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_verdicts\", \"$JS.API.STREAM.INFO.KV_mesh-grants_user-list\", \"$JS.API.STREAM.INFO.KV_mesh-host_acts\", \"$JS.API.STREAM.INFO.KV_mesh-host_data\", \"$JS.API.STREAM.INFO.KV_mesh-host_findings\", \"$JS.API.STREAM.INFO.KV_mesh-host_health\", \"$JS.API.STREAM.INFO.KV_mesh-host_packages\", \"$JS.API.STREAM.INFO.KV_mesh-provisioner_provisions\", \"$JS.API.STREAM.INFO.KV_node-engine_have\", \"$JS.FC.KV_composed-one.\u003e\", \"$JS.FC.KV_mesh-config_resolved.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_assignments.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_wants.\u003e\", \"$JS.FC.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.FC.KV_mesh-grants_user-list.\u003e\", \"$JS.FC.KV_mesh-host_acts.\u003e\", \"$JS.FC.KV_mesh-host_data.\u003e\", \"$JS.FC.KV_mesh-host_findings.\u003e\", \"$JS.FC.KV_mesh-host_health.\u003e\", \"$JS.FC.KV_mesh-host_packages.\u003e\", \"$JS.FC.KV_mesh-provisioner_provisions.\u003e\", \"$JS.FC.KV_node-engine_have.\u003e\", \"$JS.FC.OBJ_declaration-files-one.\u003e\", \"$JS.FC.OBJ_mesh-grants-user-list.\u003e\", \"$KV.composed-one.\u003e\", \"$KV.mesh-host_acts.one.\u003e\", \"$KV.mesh-host_data.one.\u003e\", \"$KV.mesh-host_findings.machine.one.\u003e\", \"$KV.mesh-host_health.one\", \"$KV.mesh-host_packages.one\", \"$KV.node-engine_have.one.\u003e\", \"$SRV.INFO\", \"$SRV.INFO.\u003e\", \"$SRV.PING\", \"$SRV.PING.*.one\", \"$SRV.PING.\u003e\", \"mesh.control.one.\u003e\", \"mesh.seat.node-backup.call.backed-up.one.node~one\", \"mesh.seat.node-backup.call.now.one.node~one\", \"mesh.seat.node-backup.tool.backed-up.one\", \"mesh.seat.node-backup.tool.now.one\", \"mesh.seat.node-engine.event.*.one\"] }\n subscribe: { allow: [\"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_assignments\", \"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_candidates\", \"$SRV.INFO\", \"$SRV.INFO.node-engine\", \"$SRV.INFO.node-engine.\u003e\", \"$SRV.PING\", \"$SRV.PING.node-engine\", \"$SRV.PING.node-engine.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.node-engine\", \"$SRV.STATS.node-engine.\u003e\", \"_DELIVER.one\", \"_DELIVER.one.\u003e\", \"_INBOX.node.one.\u003e\", \"mesh.node.one.ask.access-act\", \"mesh.node.one.ask.hand-over\", \"mesh.node.one.ask.judge-directories\", \"mesh.node.one.ask.move-secret\", \"mesh.node.one.ask.report\", \"mesh.node.one.ask.setuid-search\", \"mesh.node.one.declare\", \"mesh.seat.node-engine.call.*.one.*\", \"mesh.seat.node-engine.tool.*.one\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n { user: \"node.two\", password: \"$2a$11$node.twonode.twonode.tfixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.NODES.two.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_composed-two.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-config_resolved.*.$KV.mesh-config_resolved.two.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_assignments.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_wants.*.$KV.mesh-delivery-assigner_wants.want.two.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_acts.*.$KV.mesh-host_acts.two.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_data.*.$KV.mesh-host_data.two.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_findings.*.$KV.mesh-host_findings.machine.two.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_health.*.$KV.mesh-host_health.two\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_packages.*.$KV.mesh-host_packages.two\", \"$JS.API.CONSUMER.CREATE.KV_mesh-provisioner_provisions.*.$KV.mesh-provisioner_provisions.two.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.*.node\", \"$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.two.\u003e\", \"$JS.API.CONSUMER.CREATE.OBJ_declaration-files-two.*.$O.declaration-files-two.C.*\", \"$JS.API.CONSUMER.DELETE.KV_composed-two.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-config_resolved.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_assignments.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_wants.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_acts.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_data.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_findings.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_health.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_packages.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-provisioner_provisions.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_node-engine_have.\u003e\", \"$JS.API.CONSUMER.DELETE.OBJ_declaration-files-two.*\", \"$JS.API.CONSUMER.INFO.NODES.two\", \"$JS.API.DIRECT.GET.KV_composed-two.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-config_resolved.$KV.mesh-config_resolved.two.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_assignments.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_wants.$KV.mesh-delivery-assigner_wants.want.two.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_acts.$KV.mesh-host_acts.two.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_data.$KV.mesh-host_data.two.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_findings.$KV.mesh-host_findings.machine.two.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_health.$KV.mesh-host_health.two\", \"$JS.API.DIRECT.GET.KV_mesh-host_packages.$KV.mesh-host_packages.two\", \"$JS.API.DIRECT.GET.KV_mesh-provisioner_provisions.$KV.mesh-provisioner_provisions.two.\u003e\", \"$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.*.node\", \"$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.two.\u003e\", \"$JS.API.DIRECT.GET.OBJ_declaration-files-two.$O.declaration-files-two.M.*\", \"$JS.API.STREAM.INFO.KV_composed-two\", \"$JS.API.STREAM.INFO.KV_mesh-config_resolved\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_assignments\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_candidates\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_wants\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-builder_builds\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_candidates\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_manifests\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_modules\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_verdicts\", \"$JS.API.STREAM.INFO.KV_mesh-host_acts\", \"$JS.API.STREAM.INFO.KV_mesh-host_data\", \"$JS.API.STREAM.INFO.KV_mesh-host_findings\", \"$JS.API.STREAM.INFO.KV_mesh-host_health\", \"$JS.API.STREAM.INFO.KV_mesh-host_packages\", \"$JS.API.STREAM.INFO.KV_mesh-provisioner_provisions\", \"$JS.API.STREAM.INFO.KV_node-engine_have\", \"$JS.FC.KV_composed-two.\u003e\", \"$JS.FC.KV_mesh-config_resolved.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_assignments.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_wants.\u003e\", \"$JS.FC.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.FC.KV_mesh-host_acts.\u003e\", \"$JS.FC.KV_mesh-host_data.\u003e\", \"$JS.FC.KV_mesh-host_findings.\u003e\", \"$JS.FC.KV_mesh-host_health.\u003e\", \"$JS.FC.KV_mesh-host_packages.\u003e\", \"$JS.FC.KV_mesh-provisioner_provisions.\u003e\", \"$JS.FC.KV_node-engine_have.\u003e\", \"$JS.FC.OBJ_declaration-files-two.\u003e\", \"$KV.composed-two.\u003e\", \"$KV.mesh-host_acts.two.\u003e\", \"$KV.mesh-host_data.two.\u003e\", \"$KV.mesh-host_findings.machine.two.\u003e\", \"$KV.mesh-host_health.two\", \"$KV.mesh-host_packages.two\", \"$KV.node-engine_have.two.\u003e\", \"$SRV.INFO\", \"$SRV.INFO.\u003e\", \"$SRV.PING\", \"$SRV.PING.*.two\", \"$SRV.PING.\u003e\", \"mesh.control.two.\u003e\", \"mesh.seat.node-backup.call.backed-up.two.node~two\", \"mesh.seat.node-backup.call.now.two.node~two\", \"mesh.seat.node-backup.tool.backed-up.two\", \"mesh.seat.node-backup.tool.now.two\", \"mesh.seat.node-engine.event.*.two\"] }\n subscribe: { allow: [\"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_assignments\", \"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_candidates\", \"$SRV.INFO\", \"$SRV.INFO.node-engine\", \"$SRV.INFO.node-engine.\u003e\", \"$SRV.PING\", \"$SRV.PING.node-engine\", \"$SRV.PING.node-engine.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.node-engine\", \"$SRV.STATS.node-engine.\u003e\", \"_DELIVER.two\", \"_DELIVER.two.\u003e\", \"_INBOX.node.two.\u003e\", \"mesh.node.two.ask.access-act\", \"mesh.node.two.ask.hand-over\", \"mesh.node.two.ask.judge-directories\", \"mesh.node.two.ask.move-secret\", \"mesh.node.two.ask.report\", \"mesh.node.two.ask.setuid-search\", \"mesh.node.two.declare\", \"mesh.seat.node-engine.call.*.two.*\", \"mesh.seat.node-engine.tool.*.two\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n { user: \"one.mesh-grants\", password: \"$2a$11$one.mesh-grantsone.mesfixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.EVENTS.one_mesh-grants.\u003e\", \"$JS.API.CONSUMER.CREATE.EVENTS.two_audit\", \"$JS.API.CONSUMER.CREATE.KV_mesh-grants_memberships.*.$KV.mesh-grants_memberships.one.mesh-grants\", \"$JS.API.CONSUMER.DELETE.KV_mesh-grants_memberships.\u003e\", \"$JS.API.CONSUMER.INFO.EVENTS.one_mesh-grants\", \"$JS.API.CONSUMER.INFO.EVENTS.two_audit\", \"$JS.API.CONSUMER.MSG.NEXT.EVENTS.one_mesh-grants\", \"$JS.API.DIRECT.GET.ASSIGNMENTS.mesh.assignment.*.*\", \"$JS.API.DIRECT.GET.KV_mesh-grants_memberships.$KV.mesh-grants_memberships.one.mesh-grants\", \"$JS.API.DIRECT.GET.OBJ_mesh-grants-user-list.\u003e\", \"$JS.API.STREAM.CREATE.OBJ_mesh-grants-user-list\", \"$JS.API.STREAM.INFO.KV_mesh-grants_memberships\", \"$JS.API.STREAM.INFO.OBJ_mesh-grants-user-list\", \"$JS.API.STREAM.MSG.GET.OBJ_mesh-grants-user-list\", \"$JS.API.STREAM.PURGE.OBJ_mesh-grants-user-list\", \"$JS.FC.KV_mesh-grants_memberships.\u003e\", \"$O.mesh-grants-user-list.\u003e\", \"mesh.assignment.*.*\", \"mesh.mod.mesh-grants.event.membership-changed\"] }\n subscribe: { allow: [\"$SRV.INFO\", \"$SRV.INFO.mesh-grants\", \"$SRV.INFO.mesh-grants.\u003e\", \"$SRV.PING\", \"$SRV.PING.mesh-grants\", \"$SRV.PING.mesh-grants.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.mesh-grants\", \"$SRV.STATS.mesh-grants.\u003e\", \"_INBOX.one.mesh-grants.\u003e\", \"mesh.mod.mesh-grants.call.\u003e\", \"mesh.mod.mesh-grants.tool.\u003e\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n { user: \"one.mesh-watchman\", password: \"$2a$11$one.mesh-watchmanone.mfixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.EVENTS.one_mesh-watchman.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-grants_memberships.*.$KV.mesh-grants_memberships.one.mesh-watchman\", \"$JS.API.CONSUMER.DELETE.KV_mesh-grants_memberships.\u003e\", \"$JS.API.CONSUMER.INFO.EVENTS.one_mesh-watchman\", \"$JS.API.CONSUMER.MSG.NEXT.EVENTS.one_mesh-watchman\", \"$JS.API.DIRECT.GET.KV_mesh-grants_memberships.$KV.mesh-grants_memberships.one.mesh-watchman\", \"$JS.API.STREAM.INFO.KV_mesh-grants_memberships\", \"$JS.FC.KV_mesh-grants_memberships.\u003e\"] }\n subscribe: { allow: [\"$SRV.INFO\", \"$SRV.INFO.mesh-watchman\", \"$SRV.INFO.mesh-watchman.\u003e\", \"$SRV.PING\", \"$SRV.PING.mesh-watchman\", \"$SRV.PING.mesh-watchman.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.mesh-watchman\", \"$SRV.STATS.mesh-watchman.\u003e\", \"_INBOX.one.mesh-watchman.\u003e\", \"mesh.mod.mesh-watchman.call.\u003e\", \"mesh.mod.mesh-watchman.tool.\u003e\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n { user: \"one.nats\", password: \"$2a$11$one.natsone.natsone.nafixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.API.STREAM.INFO.*\", \"$JS.API.STREAM.NAMES\", \"$JS.API.STREAM.SNAPSHOT.*\", \"$JS.SNAPSHOT.ACK.\u003e\"] }\n subscribe: { allow: [\"_INBOX.one.nats.\u003e\"] }\n } }\n { user: \"one.telegram\", password: \"$2a$11$one.telegramone.telegrfixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.EVENTS.one_telegram.\u003e\", \"$JS.ACK.SEAT_TELEGRAM_SENDER.SEAT_TELEGRAM_SENDER_worker.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-grants_memberships.*.$KV.mesh-grants_memberships.one.telegram\", \"$JS.API.CONSUMER.DELETE.KV_mesh-grants_memberships.\u003e\", \"$JS.API.CONSUMER.INFO.EVENTS.one_telegram\", \"$JS.API.CONSUMER.INFO.SEAT_TELEGRAM_SENDER.SEAT_TELEGRAM_SENDER_worker\", \"$JS.API.CONSUMER.MSG.NEXT.EVENTS.one_telegram\", \"$JS.API.CONSUMER.MSG.NEXT.SEAT_TELEGRAM_SENDER.SEAT_TELEGRAM_SENDER_worker\", \"$JS.API.DIRECT.GET.KV_mesh-grants_memberships.$KV.mesh-grants_memberships.one.telegram\", \"$JS.API.STREAM.INFO.KV_mesh-grants_memberships\", \"$JS.FC.KV_mesh-grants_memberships.\u003e\", \"mesh.seat.telegram-sender.event.delivered\", \"mesh.seat.telegram-sender.event.failed\"] }\n subscribe: { allow: [\"$SRV.INFO\", \"$SRV.INFO.telegram\", \"$SRV.INFO.telegram.\u003e\", \"$SRV.PING\", \"$SRV.PING.telegram\", \"$SRV.PING.telegram.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.telegram\", \"$SRV.STATS.telegram.\u003e\", \"_INBOX.one.telegram.\u003e\", \"mesh.mod.telegram.call.\u003e\", \"mesh.mod.telegram.tool.\u003e\", \"mesh.seat.telegram-sender.accept.send\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n { user: \"person.ada\", password: \"$2a$11$person.adaperson.adapefixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$SRV.INFO\", \"$SRV.INFO.\u003e\", \"$SRV.PING\", \"$SRV.PING.\u003e\", \"mesh.mod.*.call.*.*.person~ada\", \"mesh.mod.*.call.*.person~ada\", \"mesh.mod.*.tool.\u003e\", \"mesh.seat.*.call.*.*.person~ada\", \"mesh.seat.*.call.*.person~ada\", \"mesh.seat.*.tool.\u003e\"], deny: [\"mesh.seat.node-launcher.tool.secret\", \"mesh.seat.node-launcher.tool.secret.*\", \"mesh.mod.*.tool.node-launcher.secret\", \"mesh.mod.*.tool.node-launcher.secret.*\", \"mesh.seat.node-launcher.call.secret.\u003e\", \"mesh.mod.*.call.node-launcher.secret.\u003e\", \"mesh.seat.mesh-broker.call.apply.\u003e\", \"mesh.seat.mesh-broker.call.create.\u003e\", \"mesh.seat.mesh-broker.call.drop.\u003e\", \"mesh.seat.mesh-broker.call.recreate.\u003e\", \"mesh.seat.mesh-broker.tool.apply\", \"mesh.seat.mesh-broker.tool.create\", \"mesh.seat.mesh-broker.tool.drop\", \"mesh.seat.mesh-broker.tool.recreate\"] }\n subscribe: { allow: [\"_INBOX.person.ada.\u003e\"] }\n } }\n { user: \"person.bob\", password: \"$2a$11$person.bobperson.bobpefixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$SRV.INFO\", \"$SRV.INFO.\u003e\", \"$SRV.PING\", \"$SRV.PING.\u003e\", \"mesh.mod.shop.call.status.*.person~bob\", \"mesh.mod.shop.call.status.person~bob\", \"mesh.mod.shop.tool.status\", \"mesh.mod.shop.tool.status.*\"] }\n subscribe: { allow: [\"_INBOX.person.bob.\u003e\"] }\n } }\n { user: \"two.shop\", password: \"$2a$11$two.shoptwo.shoptwo.shfixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.EVENTS.two_shop.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-grants_memberships.*.$KV.mesh-grants_memberships.two.shop\", \"$JS.API.CONSUMER.DELETE.KV_mesh-grants_memberships.\u003e\", \"$JS.API.CONSUMER.INFO.EVENTS.two_shop\", \"$JS.API.CONSUMER.MSG.NEXT.EVENTS.two_shop\", \"$JS.API.DIRECT.GET.KV_mesh-grants_memberships.$KV.mesh-grants_memberships.two.shop\", \"$JS.API.STREAM.INFO.KV_mesh-grants_memberships\", \"$JS.FC.KV_mesh-grants_memberships.\u003e\", \"mesh.mod.shop.event.order.placed\", \"mesh.seat.telegram-sender.accept.send\"] }\n subscribe: { allow: [\"$SRV.INFO\", \"$SRV.INFO.shop\", \"$SRV.INFO.shop.\u003e\", \"$SRV.PING\", \"$SRV.PING.shop\", \"$SRV.PING.shop.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.shop\", \"$SRV.STATS.shop.\u003e\", \"_INBOX.two.shop.\u003e\", \"mesh.mod.shop.call.\u003e\", \"mesh.mod.shop.tool.\u003e\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n ]\n }\n \"$SYS\" {\n exports = [\n { stream: \"$SYS.ACCOUNT.MESH.CONNECT\", accounts: [\"MESH\"] }\n { stream: \"$SYS.ACCOUNT.MESH.DISCONNECT\", accounts: [\"MESH\"] }\n { service: \"$SYS.REQ.ACCOUNT.MESH.CONNZ\", accounts: [\"MESH\"] }\n ]\n }\n}\n", "Missing": [ "two.audit" ], @@ -152,7 +152,7 @@ }, { "Name": "state, logs, streams, queues and processes", - "List": "# The mesh's users, composed by the grants part. Do not edit: the next\n# composition overwrites it. Permissions are derived from what each module\n# declares and nothing else (novox/hq ADR 0043, design 29 §2).\n\naccounts {\n MESH {\n jetstream: enabled\n imports = [\n { stream: { account: \"$SYS\", subject: \"$SYS.ACCOUNT.MESH.CONNECT\" }, to: \"mesh.bus.connect\" }\n { stream: { account: \"$SYS\", subject: \"$SYS.ACCOUNT.MESH.DISCONNECT\" }, to: \"mesh.bus.disconnect\" }\n { service: { account: \"$SYS\", subject: \"$SYS.REQ.ACCOUNT.MESH.CONNZ\" }, to: \"mesh.bus.connz\" }\n ]\n users = [\n { user: \"ace.plex\", password: \"$2a$11$ace.plexace.plexace.plfixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.EVENTS.ace_plex.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-grants_memberships.*.$KV.mesh-grants_memberships.ace.plex\", \"$JS.API.CONSUMER.DELETE.KV_mesh-grants_memberships.\u003e\", \"$JS.API.CONSUMER.INFO.EVENTS.ace_plex\", \"$JS.API.CONSUMER.MSG.NEXT.EVENTS.ace_plex\", \"$JS.API.DIRECT.GET.KV_mesh-grants_memberships.$KV.mesh-grants_memberships.ace.plex\", \"$JS.API.STREAM.INFO.KV_mesh-grants_memberships\", \"$JS.FC.KV_mesh-grants_memberships.\u003e\"] }\n subscribe: { allow: [\"$SRV.INFO\", \"$SRV.INFO.plex\", \"$SRV.INFO.plex.\u003e\", \"$SRV.PING\", \"$SRV.PING.plex\", \"$SRV.PING.plex.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.plex\", \"$SRV.STATS.plex.\u003e\", \"_INBOX.ace.plex.\u003e\", \"mesh.mod.plex.call.\u003e\", \"mesh.mod.plex.tool.\u003e\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n { user: \"node.ace\", password: \"$2a$11$node.acenode.acenode.afixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.NODES.ace.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_composed-ace.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-config_resolved.*.$KV.mesh-config_resolved.ace.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_placement.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_wants.*.$KV.mesh-delivery-assigner_wants.want.ace.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_acts.*.$KV.mesh-host_acts.ace.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_data.*.$KV.mesh-host_data.ace.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_findings.*.$KV.mesh-host_findings.machine.ace.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_health.*.$KV.mesh-host_health.ace\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_packages.*.$KV.mesh-host_packages.ace\", \"$JS.API.CONSUMER.CREATE.KV_mesh-provisioner_provisions.*.$KV.mesh-provisioner_provisions.ace.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.*.node\", \"$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.ace.\u003e\", \"$JS.API.CONSUMER.CREATE.OBJ_declaration-files-ace.*.$O.declaration-files-ace.C.*\", \"$JS.API.CONSUMER.DELETE.KV_composed-ace.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-config_resolved.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_placement.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_wants.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_acts.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_data.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_findings.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_health.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_packages.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-provisioner_provisions.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_node-engine_have.\u003e\", \"$JS.API.CONSUMER.DELETE.OBJ_declaration-files-ace.*\", \"$JS.API.CONSUMER.INFO.NODES.ace\", \"$JS.API.DIRECT.GET.KV_composed-ace.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-config_resolved.$KV.mesh-config_resolved.ace.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_placement.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_wants.$KV.mesh-delivery-assigner_wants.want.ace.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_acts.$KV.mesh-host_acts.ace.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_data.$KV.mesh-host_data.ace.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_findings.$KV.mesh-host_findings.machine.ace.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_health.$KV.mesh-host_health.ace\", \"$JS.API.DIRECT.GET.KV_mesh-host_packages.$KV.mesh-host_packages.ace\", \"$JS.API.DIRECT.GET.KV_mesh-provisioner_provisions.$KV.mesh-provisioner_provisions.ace.\u003e\", \"$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.*.node\", \"$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.ace.\u003e\", \"$JS.API.DIRECT.GET.OBJ_declaration-files-ace.$O.declaration-files-ace.M.*\", \"$JS.API.STREAM.INFO.KV_composed-ace\", \"$JS.API.STREAM.INFO.KV_mesh-config_resolved\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_candidates\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_placement\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_wants\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-builder_builds\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_candidates\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_manifests\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_modules\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_verdicts\", \"$JS.API.STREAM.INFO.KV_mesh-host_acts\", \"$JS.API.STREAM.INFO.KV_mesh-host_data\", \"$JS.API.STREAM.INFO.KV_mesh-host_findings\", \"$JS.API.STREAM.INFO.KV_mesh-host_health\", \"$JS.API.STREAM.INFO.KV_mesh-host_packages\", \"$JS.API.STREAM.INFO.KV_mesh-provisioner_provisions\", \"$JS.API.STREAM.INFO.KV_node-engine_have\", \"$JS.FC.KV_composed-ace.\u003e\", \"$JS.FC.KV_mesh-config_resolved.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_placement.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_wants.\u003e\", \"$JS.FC.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.FC.KV_mesh-host_acts.\u003e\", \"$JS.FC.KV_mesh-host_data.\u003e\", \"$JS.FC.KV_mesh-host_findings.\u003e\", \"$JS.FC.KV_mesh-host_health.\u003e\", \"$JS.FC.KV_mesh-host_packages.\u003e\", \"$JS.FC.KV_mesh-provisioner_provisions.\u003e\", \"$JS.FC.KV_node-engine_have.\u003e\", \"$JS.FC.OBJ_declaration-files-ace.\u003e\", \"$KV.composed-ace.\u003e\", \"$KV.mesh-host_acts.ace.\u003e\", \"$KV.mesh-host_data.ace.\u003e\", \"$KV.mesh-host_findings.machine.ace.\u003e\", \"$KV.mesh-host_health.ace\", \"$KV.mesh-host_packages.ace\", \"$KV.node-engine_have.ace.\u003e\", \"$SRV.INFO\", \"$SRV.INFO.\u003e\", \"$SRV.PING\", \"$SRV.PING.*.ace\", \"$SRV.PING.\u003e\", \"mesh.control.ace.\u003e\", \"mesh.mod.plex.call.plex_status.ace.node~ace\", \"mesh.mod.plex.tool.plex_status.ace\", \"mesh.seat.node-backup.call.backed-up.ace.node~ace\", \"mesh.seat.node-backup.call.now.ace.node~ace\", \"mesh.seat.node-backup.tool.backed-up.ace\", \"mesh.seat.node-backup.tool.now.ace\", \"mesh.seat.node-engine.event.*.ace\"] }\n subscribe: { allow: [\"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_candidates\", \"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_placement\", \"$SRV.INFO\", \"$SRV.INFO.node-engine\", \"$SRV.INFO.node-engine.\u003e\", \"$SRV.PING\", \"$SRV.PING.node-engine\", \"$SRV.PING.node-engine.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.node-engine\", \"$SRV.STATS.node-engine.\u003e\", \"_DELIVER.ace\", \"_DELIVER.ace.\u003e\", \"_INBOX.node.ace.\u003e\", \"mesh.node.ace.ask.access-act\", \"mesh.node.ace.ask.hand-over\", \"mesh.node.ace.ask.judge-directories\", \"mesh.node.ace.ask.move-secret\", \"mesh.node.ace.ask.report\", \"mesh.node.ace.ask.setuid-search\", \"mesh.node.ace.declare\", \"mesh.seat.node-engine.call.*.ace.*\", \"mesh.seat.node-engine.tool.*.ace\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n { user: \"node.novox\", password: \"$2a$11$node.novoxnode.novoxnofixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.NODES.novox.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_composed-novox.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-config_resolved.*.$KV.mesh-config_resolved.novox.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_placement.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_wants.*.$KV.mesh-delivery-assigner_wants.want.novox.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_acts.*.$KV.mesh-host_acts.novox.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_data.*.$KV.mesh-host_data.novox.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_findings.*.$KV.mesh-host_findings.machine.novox.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_health.*.$KV.mesh-host_health.novox\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_packages.*.$KV.mesh-host_packages.novox\", \"$JS.API.CONSUMER.CREATE.KV_mesh-provisioner_provisions.*.$KV.mesh-provisioner_provisions.novox.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.*.node\", \"$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.novox.\u003e\", \"$JS.API.CONSUMER.CREATE.OBJ_declaration-files-novox.*.$O.declaration-files-novox.C.*\", \"$JS.API.CONSUMER.DELETE.KV_composed-novox.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-config_resolved.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_placement.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_wants.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_acts.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_data.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_findings.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_health.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_packages.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-provisioner_provisions.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_node-engine_have.\u003e\", \"$JS.API.CONSUMER.DELETE.OBJ_declaration-files-novox.*\", \"$JS.API.CONSUMER.INFO.NODES.novox\", \"$JS.API.DIRECT.GET.KV_composed-novox.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-config_resolved.$KV.mesh-config_resolved.novox.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_placement.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_wants.$KV.mesh-delivery-assigner_wants.want.novox.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_acts.$KV.mesh-host_acts.novox.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_data.$KV.mesh-host_data.novox.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_findings.$KV.mesh-host_findings.machine.novox.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_health.$KV.mesh-host_health.novox\", \"$JS.API.DIRECT.GET.KV_mesh-host_packages.$KV.mesh-host_packages.novox\", \"$JS.API.DIRECT.GET.KV_mesh-provisioner_provisions.$KV.mesh-provisioner_provisions.novox.\u003e\", \"$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.*.node\", \"$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.novox.\u003e\", \"$JS.API.DIRECT.GET.OBJ_declaration-files-novox.$O.declaration-files-novox.M.*\", \"$JS.API.STREAM.INFO.KV_composed-novox\", \"$JS.API.STREAM.INFO.KV_mesh-config_resolved\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_candidates\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_placement\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_wants\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-builder_builds\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_candidates\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_manifests\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_modules\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_verdicts\", \"$JS.API.STREAM.INFO.KV_mesh-host_acts\", \"$JS.API.STREAM.INFO.KV_mesh-host_data\", \"$JS.API.STREAM.INFO.KV_mesh-host_findings\", \"$JS.API.STREAM.INFO.KV_mesh-host_health\", \"$JS.API.STREAM.INFO.KV_mesh-host_packages\", \"$JS.API.STREAM.INFO.KV_mesh-provisioner_provisions\", \"$JS.API.STREAM.INFO.KV_node-engine_have\", \"$JS.FC.KV_composed-novox.\u003e\", \"$JS.FC.KV_mesh-config_resolved.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_placement.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_wants.\u003e\", \"$JS.FC.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.FC.KV_mesh-host_acts.\u003e\", \"$JS.FC.KV_mesh-host_data.\u003e\", \"$JS.FC.KV_mesh-host_findings.\u003e\", \"$JS.FC.KV_mesh-host_health.\u003e\", \"$JS.FC.KV_mesh-host_packages.\u003e\", \"$JS.FC.KV_mesh-provisioner_provisions.\u003e\", \"$JS.FC.KV_node-engine_have.\u003e\", \"$JS.FC.OBJ_declaration-files-novox.\u003e\", \"$KV.composed-novox.\u003e\", \"$KV.mesh-host_acts.novox.\u003e\", \"$KV.mesh-host_data.novox.\u003e\", \"$KV.mesh-host_findings.machine.novox.\u003e\", \"$KV.mesh-host_health.novox\", \"$KV.mesh-host_packages.novox\", \"$KV.node-engine_have.novox.\u003e\", \"$SRV.INFO\", \"$SRV.INFO.\u003e\", \"$SRV.PING\", \"$SRV.PING.*.novox\", \"$SRV.PING.\u003e\", \"mesh.control.novox.\u003e\", \"mesh.seat.node-backup.call.backed-up.novox.node~novox\", \"mesh.seat.node-backup.call.now.novox.node~novox\", \"mesh.seat.node-backup.tool.backed-up.novox\", \"mesh.seat.node-backup.tool.now.novox\", \"mesh.seat.node-engine.event.*.novox\"] }\n subscribe: { allow: [\"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_candidates\", \"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_placement\", \"$SRV.INFO\", \"$SRV.INFO.node-engine\", \"$SRV.INFO.node-engine.\u003e\", \"$SRV.PING\", \"$SRV.PING.node-engine\", \"$SRV.PING.node-engine.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.node-engine\", \"$SRV.STATS.node-engine.\u003e\", \"_DELIVER.novox\", \"_DELIVER.novox.\u003e\", \"_INBOX.node.novox.\u003e\", \"mesh.node.novox.ask.access-act\", \"mesh.node.novox.ask.hand-over\", \"mesh.node.novox.ask.judge-directories\", \"mesh.node.novox.ask.move-secret\", \"mesh.node.novox.ask.report\", \"mesh.node.novox.ask.setuid-search\", \"mesh.node.novox.declare\", \"mesh.seat.node-engine.call.*.novox.*\", \"mesh.seat.node-engine.tool.*.novox\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n { user: \"novox.mesh-delivery-builder\", password: \"$2a$11$novox.mesh-delivery-bufixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.EVENTS.novox_mesh-delivery-builder.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-builder_presence.*.$KV.mesh-delivery-builder_presence.novox\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-builder_steps.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-grants_memberships.*.$KV.mesh-grants_memberships.novox.mesh-delivery-builder\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-builder_presence.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-builder_steps.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-grants_memberships.\u003e\", \"$JS.API.CONSUMER.INFO.EVENTS.novox_mesh-delivery-builder\", \"$JS.API.CONSUMER.MSG.NEXT.EVENTS.novox_mesh-delivery-builder\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-builder_presence.$KV.mesh-delivery-builder_presence.novox\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-builder_steps.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-grants_memberships.$KV.mesh-grants_memberships.novox.mesh-delivery-builder\", \"$JS.API.DIRECT.GET.LOG_mesh-delivery-builder_builds\", \"$JS.API.DIRECT.GET.LOG_mesh-delivery-builder_builds.\u003e\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-builder_presence\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-builder_steps\", \"$JS.API.STREAM.INFO.KV_mesh-grants_memberships\", \"$JS.API.STREAM.INFO.LOG_mesh-delivery-builder_builds\", \"$JS.API.STREAM.INFO.SEAT_NODE_BUILD_GO\", \"$JS.API.STREAM.INFO.SEAT_NODE_BUILD_IMAGE\", \"$JS.API.STREAM.INFO.SEAT_NODE_BUILD_NODE\", \"$JS.API.STREAM.MSG.GET.SEAT_NODE_BUILD_GO\", \"$JS.API.STREAM.MSG.GET.SEAT_NODE_BUILD_IMAGE\", \"$JS.API.STREAM.MSG.GET.SEAT_NODE_BUILD_NODE\", \"$JS.FC.KV_mesh-delivery-builder_presence.\u003e\", \"$JS.FC.KV_mesh-delivery-builder_steps.\u003e\", \"$JS.FC.KV_mesh-grants_memberships.\u003e\", \"$KV.mesh-delivery-builder_presence.novox\", \"$KV.mesh-delivery-builder_steps.\u003e\", \"mesh.log.mesh-delivery-builder.builds.\u003e\", \"mesh.mod.gitea.call.gitea_commit.*.novox~mesh-delivery-builder\", \"mesh.mod.gitea.call.gitea_commit.novox~mesh-delivery-builder\", \"mesh.mod.gitea.tool.gitea_commit\", \"mesh.mod.gitea.tool.gitea_commit.*\", \"mesh.mod.mesh-delivery-builder.event.built\", \"mesh.mod.mesh-delivery-builder.event.states\", \"mesh.stream.mesh-delivery-builder.event.ask\"] }\n subscribe: { allow: [\"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-builder_presence\", \"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-builder_steps\", \"$SRV.INFO\", \"$SRV.INFO.mesh-delivery-builder\", \"$SRV.INFO.mesh-delivery-builder.\u003e\", \"$SRV.PING\", \"$SRV.PING.mesh-delivery-builder\", \"$SRV.PING.mesh-delivery-builder.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.mesh-delivery-builder\", \"$SRV.STATS.mesh-delivery-builder.\u003e\", \"_INBOX.novox.mesh-delivery-builder.\u003e\", \"mesh.mod.mesh-delivery-builder.call.\u003e\", \"mesh.mod.mesh-delivery-builder.tool.\u003e\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n { user: \"novox.mesh-delivery-builder.worker\", password: \"$2a$11$novox.mesh-delivery-bufixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.asks.novox_mesh-delivery-builder_worker.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-builder_steps.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-builder_steps.\u003e\", \"$JS.API.CONSUMER.INFO.asks.novox_mesh-delivery-builder_worker\", \"$JS.API.CONSUMER.MSG.NEXT.asks.novox_mesh-delivery-builder_worker\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-builder_steps.\u003e\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-builder_steps\", \"$JS.FC.KV_mesh-delivery-builder_steps.\u003e\", \"$KV.mesh-delivery-builder_steps.\u003e\", \"mesh.mod.gitea.call.gitea_commit.*.novox~mesh-delivery-builder~worker\", \"mesh.mod.gitea.call.gitea_commit.novox~mesh-delivery-builder~worker\", \"mesh.mod.gitea.tool.gitea_commit\", \"mesh.mod.gitea.tool.gitea_commit.*\", \"mesh.mod.mesh-delivery-builder.event.built\", \"mesh.mod.mesh-delivery-builder.event.states\"] }\n subscribe: { allow: [\"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-builder_steps\", \"_INBOX.novox.mesh-delivery-builder.worker.\u003e\"] }\n } }\n { user: \"novox.mesh-delivery-view\", password: \"$2a$11$novox.mesh-delivery-vifixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.EVENTS.novox_mesh-delivery-view.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-builder_presence.*.$KV.mesh-delivery-builder_presence.novox\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-builder_steps.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-grants_memberships.*.$KV.mesh-grants_memberships.novox.mesh-delivery-view\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-builder_presence.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-builder_steps.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-grants_memberships.\u003e\", \"$JS.API.CONSUMER.INFO.EVENTS.novox_mesh-delivery-view\", \"$JS.API.CONSUMER.INFO.SEAT_NODE_BUILD_GO.SEAT_NODE_BUILD_GO_CHECK_worker\", \"$JS.API.CONSUMER.INFO.SEAT_NODE_BUILD_GO.SEAT_NODE_BUILD_GO_DELIVERY_worker\", \"$JS.API.CONSUMER.INFO.SEAT_NODE_BUILD_GO.SEAT_NODE_BUILD_GO_RECHECK_worker\", \"$JS.API.CONSUMER.INFO.SEAT_NODE_BUILD_IMAGE.SEAT_NODE_BUILD_IMAGE_CHECK_worker\", \"$JS.API.CONSUMER.INFO.SEAT_NODE_BUILD_IMAGE.SEAT_NODE_BUILD_IMAGE_DELIVERY_worker\", \"$JS.API.CONSUMER.INFO.SEAT_NODE_BUILD_IMAGE.SEAT_NODE_BUILD_IMAGE_RECHECK_worker\", \"$JS.API.CONSUMER.INFO.SEAT_NODE_BUILD_NODE.SEAT_NODE_BUILD_NODE_CHECK_worker\", \"$JS.API.CONSUMER.INFO.SEAT_NODE_BUILD_NODE.SEAT_NODE_BUILD_NODE_DELIVERY_worker\", \"$JS.API.CONSUMER.INFO.SEAT_NODE_BUILD_NODE.SEAT_NODE_BUILD_NODE_RECHECK_worker\", \"$JS.API.CONSUMER.MSG.NEXT.EVENTS.novox_mesh-delivery-view\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-builder_presence.$KV.mesh-delivery-builder_presence.novox\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-builder_steps.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-grants_memberships.$KV.mesh-grants_memberships.novox.mesh-delivery-view\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-builder_presence\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-builder_steps\", \"$JS.API.STREAM.INFO.KV_mesh-grants_memberships\", \"$JS.API.STREAM.INFO.SEAT_NODE_BUILD_GO\", \"$JS.API.STREAM.INFO.SEAT_NODE_BUILD_IMAGE\", \"$JS.API.STREAM.INFO.SEAT_NODE_BUILD_NODE\", \"$JS.API.STREAM.MSG.GET.SEAT_NODE_BUILD_GO\", \"$JS.API.STREAM.MSG.GET.SEAT_NODE_BUILD_IMAGE\", \"$JS.API.STREAM.MSG.GET.SEAT_NODE_BUILD_NODE\", \"$JS.FC.KV_mesh-delivery-builder_presence.\u003e\", \"$JS.FC.KV_mesh-delivery-builder_steps.\u003e\", \"$JS.FC.KV_mesh-grants_memberships.\u003e\"] }\n subscribe: { allow: [\"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-builder_presence\", \"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-builder_steps\", \"$SRV.INFO\", \"$SRV.INFO.mesh-delivery-view\", \"$SRV.INFO.mesh-delivery-view.\u003e\", \"$SRV.PING\", \"$SRV.PING.mesh-delivery-view\", \"$SRV.PING.mesh-delivery-view.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.mesh-delivery-view\", \"$SRV.STATS.mesh-delivery-view.\u003e\", \"_INBOX.novox.mesh-delivery-view.\u003e\", \"mesh.mod.mesh-delivery-view.call.\u003e\", \"mesh.mod.mesh-delivery-view.tool.\u003e\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n { user: \"novox.mesh-grants\", password: \"$2a$11$novox.mesh-grantsnovoxfixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.EVENTS.novox_mesh-grants.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-grants_assertions.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-grants_memberships.*.$KV.mesh-grants_memberships.novox.mesh-grants\", \"$JS.API.CONSUMER.CREATE.asks.novox_mesh-delivery-builder_worker\", \"$JS.API.CONSUMER.DELETE.KV_mesh-grants_assertions.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-grants_memberships.\u003e\", \"$JS.API.CONSUMER.INFO.EVENTS.novox_mesh-grants\", \"$JS.API.CONSUMER.INFO.asks.novox_mesh-delivery-builder_worker\", \"$JS.API.CONSUMER.MSG.NEXT.EVENTS.novox_mesh-grants\", \"$JS.API.DIRECT.GET.ASSIGNMENTS.mesh.assignment.*.*\", \"$JS.API.DIRECT.GET.KV_mesh-grants_assertions.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-grants_memberships.$KV.mesh-grants_memberships.novox.mesh-grants\", \"$JS.API.DIRECT.GET.OBJ_mesh-grants-user-list.\u003e\", \"$JS.API.STREAM.CREATE.KV_mesh-delivery-builder_presence\", \"$JS.API.STREAM.CREATE.KV_mesh-delivery-builder_steps\", \"$JS.API.STREAM.CREATE.KV_mesh-grants_assertions\", \"$JS.API.STREAM.CREATE.LOG_mesh-delivery-builder_builds\", \"$JS.API.STREAM.CREATE.OBJ_mesh-grants-user-list\", \"$JS.API.STREAM.CREATE.asks\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-builder_presence\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-builder_steps\", \"$JS.API.STREAM.INFO.KV_mesh-grants_assertions\", \"$JS.API.STREAM.INFO.KV_mesh-grants_memberships\", \"$JS.API.STREAM.INFO.LOG_mesh-delivery-builder_builds\", \"$JS.API.STREAM.INFO.OBJ_mesh-grants-user-list\", \"$JS.API.STREAM.INFO.asks\", \"$JS.API.STREAM.MSG.GET.OBJ_mesh-grants-user-list\", \"$JS.API.STREAM.PURGE.OBJ_mesh-grants-user-list\", \"$JS.FC.KV_mesh-grants_assertions.\u003e\", \"$JS.FC.KV_mesh-grants_memberships.\u003e\", \"$KV.mesh-grants_assertions.\u003e\", \"$O.mesh-grants-user-list.\u003e\", \"mesh.assignment.*.*\", \"mesh.mod.mesh-grants.event.membership-changed\", \"mesh.mod.mesh-grants.event.states\"] }\n subscribe: { allow: [\"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-grants_assertions\", \"$SRV.INFO\", \"$SRV.INFO.mesh-grants\", \"$SRV.INFO.mesh-grants.\u003e\", \"$SRV.PING\", \"$SRV.PING.mesh-grants\", \"$SRV.PING.mesh-grants.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.mesh-grants\", \"$SRV.STATS.mesh-grants.\u003e\", \"_INBOX.novox.mesh-grants.\u003e\", \"mesh.mod.mesh-grants.call.\u003e\", \"mesh.mod.mesh-grants.tool.\u003e\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n ]\n }\n \"$SYS\" {\n exports = [\n { stream: \"$SYS.ACCOUNT.MESH.CONNECT\", accounts: [\"MESH\"] }\n { stream: \"$SYS.ACCOUNT.MESH.DISCONNECT\", accounts: [\"MESH\"] }\n { service: \"$SYS.REQ.ACCOUNT.MESH.CONNZ\", accounts: [\"MESH\"] }\n ]\n }\n}\n", + "List": "# The mesh's users, composed by the grants part. Do not edit: the next\n# composition overwrites it. Permissions are derived from what each module\n# declares and nothing else (novox/hq ADR 0043, design 29 §2).\n\naccounts {\n MESH {\n jetstream: enabled\n imports = [\n { stream: { account: \"$SYS\", subject: \"$SYS.ACCOUNT.MESH.CONNECT\" }, to: \"mesh.bus.connect\" }\n { stream: { account: \"$SYS\", subject: \"$SYS.ACCOUNT.MESH.DISCONNECT\" }, to: \"mesh.bus.disconnect\" }\n { service: { account: \"$SYS\", subject: \"$SYS.REQ.ACCOUNT.MESH.CONNZ\" }, to: \"mesh.bus.connz\" }\n ]\n users = [\n { user: \"ace.plex\", password: \"$2a$11$ace.plexace.plexace.plfixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.EVENTS.ace_plex.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-grants_memberships.*.$KV.mesh-grants_memberships.ace.plex\", \"$JS.API.CONSUMER.DELETE.KV_mesh-grants_memberships.\u003e\", \"$JS.API.CONSUMER.INFO.EVENTS.ace_plex\", \"$JS.API.CONSUMER.MSG.NEXT.EVENTS.ace_plex\", \"$JS.API.DIRECT.GET.KV_mesh-grants_memberships.$KV.mesh-grants_memberships.ace.plex\", \"$JS.API.STREAM.INFO.KV_mesh-grants_memberships\", \"$JS.FC.KV_mesh-grants_memberships.\u003e\"] }\n subscribe: { allow: [\"$SRV.INFO\", \"$SRV.INFO.plex\", \"$SRV.INFO.plex.\u003e\", \"$SRV.PING\", \"$SRV.PING.plex\", \"$SRV.PING.plex.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.plex\", \"$SRV.STATS.plex.\u003e\", \"_INBOX.ace.plex.\u003e\", \"mesh.mod.plex.call.\u003e\", \"mesh.mod.plex.tool.\u003e\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n { user: \"node.ace\", password: \"$2a$11$node.acenode.acenode.afixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.NODES.ace.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_composed-ace.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-config_resolved.*.$KV.mesh-config_resolved.ace.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_assignments.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_wants.*.$KV.mesh-delivery-assigner_wants.want.ace.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_acts.*.$KV.mesh-host_acts.ace.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_data.*.$KV.mesh-host_data.ace.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_findings.*.$KV.mesh-host_findings.machine.ace.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_health.*.$KV.mesh-host_health.ace\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_packages.*.$KV.mesh-host_packages.ace\", \"$JS.API.CONSUMER.CREATE.KV_mesh-provisioner_provisions.*.$KV.mesh-provisioner_provisions.ace.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.*.node\", \"$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.ace.\u003e\", \"$JS.API.CONSUMER.CREATE.OBJ_declaration-files-ace.*.$O.declaration-files-ace.C.*\", \"$JS.API.CONSUMER.DELETE.KV_composed-ace.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-config_resolved.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_assignments.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_wants.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_acts.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_data.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_findings.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_health.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_packages.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-provisioner_provisions.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_node-engine_have.\u003e\", \"$JS.API.CONSUMER.DELETE.OBJ_declaration-files-ace.*\", \"$JS.API.CONSUMER.INFO.NODES.ace\", \"$JS.API.DIRECT.GET.KV_composed-ace.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-config_resolved.$KV.mesh-config_resolved.ace.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_assignments.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_wants.$KV.mesh-delivery-assigner_wants.want.ace.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_acts.$KV.mesh-host_acts.ace.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_data.$KV.mesh-host_data.ace.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_findings.$KV.mesh-host_findings.machine.ace.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_health.$KV.mesh-host_health.ace\", \"$JS.API.DIRECT.GET.KV_mesh-host_packages.$KV.mesh-host_packages.ace\", \"$JS.API.DIRECT.GET.KV_mesh-provisioner_provisions.$KV.mesh-provisioner_provisions.ace.\u003e\", \"$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.*.node\", \"$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.ace.\u003e\", \"$JS.API.DIRECT.GET.OBJ_declaration-files-ace.$O.declaration-files-ace.M.*\", \"$JS.API.STREAM.INFO.KV_composed-ace\", \"$JS.API.STREAM.INFO.KV_mesh-config_resolved\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_assignments\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_candidates\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_wants\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-builder_builds\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_candidates\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_manifests\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_modules\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_verdicts\", \"$JS.API.STREAM.INFO.KV_mesh-host_acts\", \"$JS.API.STREAM.INFO.KV_mesh-host_data\", \"$JS.API.STREAM.INFO.KV_mesh-host_findings\", \"$JS.API.STREAM.INFO.KV_mesh-host_health\", \"$JS.API.STREAM.INFO.KV_mesh-host_packages\", \"$JS.API.STREAM.INFO.KV_mesh-provisioner_provisions\", \"$JS.API.STREAM.INFO.KV_node-engine_have\", \"$JS.FC.KV_composed-ace.\u003e\", \"$JS.FC.KV_mesh-config_resolved.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_assignments.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_wants.\u003e\", \"$JS.FC.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.FC.KV_mesh-host_acts.\u003e\", \"$JS.FC.KV_mesh-host_data.\u003e\", \"$JS.FC.KV_mesh-host_findings.\u003e\", \"$JS.FC.KV_mesh-host_health.\u003e\", \"$JS.FC.KV_mesh-host_packages.\u003e\", \"$JS.FC.KV_mesh-provisioner_provisions.\u003e\", \"$JS.FC.KV_node-engine_have.\u003e\", \"$JS.FC.OBJ_declaration-files-ace.\u003e\", \"$KV.composed-ace.\u003e\", \"$KV.mesh-host_acts.ace.\u003e\", \"$KV.mesh-host_data.ace.\u003e\", \"$KV.mesh-host_findings.machine.ace.\u003e\", \"$KV.mesh-host_health.ace\", \"$KV.mesh-host_packages.ace\", \"$KV.node-engine_have.ace.\u003e\", \"$SRV.INFO\", \"$SRV.INFO.\u003e\", \"$SRV.PING\", \"$SRV.PING.*.ace\", \"$SRV.PING.\u003e\", \"mesh.control.ace.\u003e\", \"mesh.mod.plex.call.plex_status.ace.node~ace\", \"mesh.mod.plex.tool.plex_status.ace\", \"mesh.seat.node-backup.call.backed-up.ace.node~ace\", \"mesh.seat.node-backup.call.now.ace.node~ace\", \"mesh.seat.node-backup.tool.backed-up.ace\", \"mesh.seat.node-backup.tool.now.ace\", \"mesh.seat.node-engine.event.*.ace\"] }\n subscribe: { allow: [\"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_assignments\", \"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_candidates\", \"$SRV.INFO\", \"$SRV.INFO.node-engine\", \"$SRV.INFO.node-engine.\u003e\", \"$SRV.PING\", \"$SRV.PING.node-engine\", \"$SRV.PING.node-engine.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.node-engine\", \"$SRV.STATS.node-engine.\u003e\", \"_DELIVER.ace\", \"_DELIVER.ace.\u003e\", \"_INBOX.node.ace.\u003e\", \"mesh.node.ace.ask.access-act\", \"mesh.node.ace.ask.hand-over\", \"mesh.node.ace.ask.judge-directories\", \"mesh.node.ace.ask.move-secret\", \"mesh.node.ace.ask.report\", \"mesh.node.ace.ask.setuid-search\", \"mesh.node.ace.declare\", \"mesh.seat.node-engine.call.*.ace.*\", \"mesh.seat.node-engine.tool.*.ace\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n { user: \"node.novox\", password: \"$2a$11$node.novoxnode.novoxnofixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.NODES.novox.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_composed-novox.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-config_resolved.*.$KV.mesh-config_resolved.novox.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_assignments.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_wants.*.$KV.mesh-delivery-assigner_wants.want.novox.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_acts.*.$KV.mesh-host_acts.novox.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_data.*.$KV.mesh-host_data.novox.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_findings.*.$KV.mesh-host_findings.machine.novox.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_health.*.$KV.mesh-host_health.novox\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_packages.*.$KV.mesh-host_packages.novox\", \"$JS.API.CONSUMER.CREATE.KV_mesh-provisioner_provisions.*.$KV.mesh-provisioner_provisions.novox.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.*.node\", \"$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.novox.\u003e\", \"$JS.API.CONSUMER.CREATE.OBJ_declaration-files-novox.*.$O.declaration-files-novox.C.*\", \"$JS.API.CONSUMER.DELETE.KV_composed-novox.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-config_resolved.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_assignments.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_wants.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_acts.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_data.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_findings.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_health.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_packages.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-provisioner_provisions.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_node-engine_have.\u003e\", \"$JS.API.CONSUMER.DELETE.OBJ_declaration-files-novox.*\", \"$JS.API.CONSUMER.INFO.NODES.novox\", \"$JS.API.DIRECT.GET.KV_composed-novox.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-config_resolved.$KV.mesh-config_resolved.novox.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_assignments.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_wants.$KV.mesh-delivery-assigner_wants.want.novox.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_acts.$KV.mesh-host_acts.novox.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_data.$KV.mesh-host_data.novox.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_findings.$KV.mesh-host_findings.machine.novox.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_health.$KV.mesh-host_health.novox\", \"$JS.API.DIRECT.GET.KV_mesh-host_packages.$KV.mesh-host_packages.novox\", \"$JS.API.DIRECT.GET.KV_mesh-provisioner_provisions.$KV.mesh-provisioner_provisions.novox.\u003e\", \"$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.*.node\", \"$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.novox.\u003e\", \"$JS.API.DIRECT.GET.OBJ_declaration-files-novox.$O.declaration-files-novox.M.*\", \"$JS.API.STREAM.INFO.KV_composed-novox\", \"$JS.API.STREAM.INFO.KV_mesh-config_resolved\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_assignments\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_candidates\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_wants\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-builder_builds\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_candidates\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_manifests\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_modules\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_verdicts\", \"$JS.API.STREAM.INFO.KV_mesh-host_acts\", \"$JS.API.STREAM.INFO.KV_mesh-host_data\", \"$JS.API.STREAM.INFO.KV_mesh-host_findings\", \"$JS.API.STREAM.INFO.KV_mesh-host_health\", \"$JS.API.STREAM.INFO.KV_mesh-host_packages\", \"$JS.API.STREAM.INFO.KV_mesh-provisioner_provisions\", \"$JS.API.STREAM.INFO.KV_node-engine_have\", \"$JS.FC.KV_composed-novox.\u003e\", \"$JS.FC.KV_mesh-config_resolved.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_assignments.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_wants.\u003e\", \"$JS.FC.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.FC.KV_mesh-host_acts.\u003e\", \"$JS.FC.KV_mesh-host_data.\u003e\", \"$JS.FC.KV_mesh-host_findings.\u003e\", \"$JS.FC.KV_mesh-host_health.\u003e\", \"$JS.FC.KV_mesh-host_packages.\u003e\", \"$JS.FC.KV_mesh-provisioner_provisions.\u003e\", \"$JS.FC.KV_node-engine_have.\u003e\", \"$JS.FC.OBJ_declaration-files-novox.\u003e\", \"$KV.composed-novox.\u003e\", \"$KV.mesh-host_acts.novox.\u003e\", \"$KV.mesh-host_data.novox.\u003e\", \"$KV.mesh-host_findings.machine.novox.\u003e\", \"$KV.mesh-host_health.novox\", \"$KV.mesh-host_packages.novox\", \"$KV.node-engine_have.novox.\u003e\", \"$SRV.INFO\", \"$SRV.INFO.\u003e\", \"$SRV.PING\", \"$SRV.PING.*.novox\", \"$SRV.PING.\u003e\", \"mesh.control.novox.\u003e\", \"mesh.seat.node-backup.call.backed-up.novox.node~novox\", \"mesh.seat.node-backup.call.now.novox.node~novox\", \"mesh.seat.node-backup.tool.backed-up.novox\", \"mesh.seat.node-backup.tool.now.novox\", \"mesh.seat.node-engine.event.*.novox\"] }\n subscribe: { allow: [\"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_assignments\", \"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_candidates\", \"$SRV.INFO\", \"$SRV.INFO.node-engine\", \"$SRV.INFO.node-engine.\u003e\", \"$SRV.PING\", \"$SRV.PING.node-engine\", \"$SRV.PING.node-engine.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.node-engine\", \"$SRV.STATS.node-engine.\u003e\", \"_DELIVER.novox\", \"_DELIVER.novox.\u003e\", \"_INBOX.node.novox.\u003e\", \"mesh.node.novox.ask.access-act\", \"mesh.node.novox.ask.hand-over\", \"mesh.node.novox.ask.judge-directories\", \"mesh.node.novox.ask.move-secret\", \"mesh.node.novox.ask.report\", \"mesh.node.novox.ask.setuid-search\", \"mesh.node.novox.declare\", \"mesh.seat.node-engine.call.*.novox.*\", \"mesh.seat.node-engine.tool.*.novox\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n { user: \"novox.mesh-delivery-builder\", password: \"$2a$11$novox.mesh-delivery-bufixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.EVENTS.novox_mesh-delivery-builder.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-builder_presence.*.$KV.mesh-delivery-builder_presence.novox\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-builder_steps.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-grants_memberships.*.$KV.mesh-grants_memberships.novox.mesh-delivery-builder\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-builder_presence.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-builder_steps.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-grants_memberships.\u003e\", \"$JS.API.CONSUMER.INFO.EVENTS.novox_mesh-delivery-builder\", \"$JS.API.CONSUMER.MSG.NEXT.EVENTS.novox_mesh-delivery-builder\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-builder_presence.$KV.mesh-delivery-builder_presence.novox\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-builder_steps.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-grants_memberships.$KV.mesh-grants_memberships.novox.mesh-delivery-builder\", \"$JS.API.DIRECT.GET.LOG_mesh-delivery-builder_builds\", \"$JS.API.DIRECT.GET.LOG_mesh-delivery-builder_builds.\u003e\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-builder_presence\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-builder_steps\", \"$JS.API.STREAM.INFO.KV_mesh-grants_memberships\", \"$JS.API.STREAM.INFO.LOG_mesh-delivery-builder_builds\", \"$JS.API.STREAM.INFO.SEAT_NODE_BUILD_GO\", \"$JS.API.STREAM.INFO.SEAT_NODE_BUILD_IMAGE\", \"$JS.API.STREAM.INFO.SEAT_NODE_BUILD_NODE\", \"$JS.API.STREAM.MSG.GET.SEAT_NODE_BUILD_GO\", \"$JS.API.STREAM.MSG.GET.SEAT_NODE_BUILD_IMAGE\", \"$JS.API.STREAM.MSG.GET.SEAT_NODE_BUILD_NODE\", \"$JS.FC.KV_mesh-delivery-builder_presence.\u003e\", \"$JS.FC.KV_mesh-delivery-builder_steps.\u003e\", \"$JS.FC.KV_mesh-grants_memberships.\u003e\", \"$KV.mesh-delivery-builder_presence.novox\", \"$KV.mesh-delivery-builder_steps.\u003e\", \"mesh.log.mesh-delivery-builder.builds.\u003e\", \"mesh.mod.gitea.call.gitea_commit.*.novox~mesh-delivery-builder\", \"mesh.mod.gitea.call.gitea_commit.novox~mesh-delivery-builder\", \"mesh.mod.gitea.tool.gitea_commit\", \"mesh.mod.gitea.tool.gitea_commit.*\", \"mesh.mod.mesh-delivery-builder.event.built\", \"mesh.mod.mesh-delivery-builder.event.states\", \"mesh.stream.mesh-delivery-builder.event.ask\"] }\n subscribe: { allow: [\"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-builder_presence\", \"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-builder_steps\", \"$SRV.INFO\", \"$SRV.INFO.mesh-delivery-builder\", \"$SRV.INFO.mesh-delivery-builder.\u003e\", \"$SRV.PING\", \"$SRV.PING.mesh-delivery-builder\", \"$SRV.PING.mesh-delivery-builder.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.mesh-delivery-builder\", \"$SRV.STATS.mesh-delivery-builder.\u003e\", \"_INBOX.novox.mesh-delivery-builder.\u003e\", \"mesh.mod.mesh-delivery-builder.call.\u003e\", \"mesh.mod.mesh-delivery-builder.tool.\u003e\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n { user: \"novox.mesh-delivery-builder.worker\", password: \"$2a$11$novox.mesh-delivery-bufixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.asks.novox_mesh-delivery-builder_worker.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-builder_steps.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-builder_steps.\u003e\", \"$JS.API.CONSUMER.INFO.asks.novox_mesh-delivery-builder_worker\", \"$JS.API.CONSUMER.MSG.NEXT.asks.novox_mesh-delivery-builder_worker\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-builder_steps.\u003e\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-builder_steps\", \"$JS.FC.KV_mesh-delivery-builder_steps.\u003e\", \"$KV.mesh-delivery-builder_steps.\u003e\", \"mesh.mod.gitea.call.gitea_commit.*.novox~mesh-delivery-builder~worker\", \"mesh.mod.gitea.call.gitea_commit.novox~mesh-delivery-builder~worker\", \"mesh.mod.gitea.tool.gitea_commit\", \"mesh.mod.gitea.tool.gitea_commit.*\", \"mesh.mod.mesh-delivery-builder.event.built\", \"mesh.mod.mesh-delivery-builder.event.states\"] }\n subscribe: { allow: [\"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-builder_steps\", \"_INBOX.novox.mesh-delivery-builder.worker.\u003e\"] }\n } }\n { user: \"novox.mesh-delivery-view\", password: \"$2a$11$novox.mesh-delivery-vifixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.EVENTS.novox_mesh-delivery-view.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-builder_presence.*.$KV.mesh-delivery-builder_presence.novox\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-builder_steps.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-grants_memberships.*.$KV.mesh-grants_memberships.novox.mesh-delivery-view\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-builder_presence.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-builder_steps.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-grants_memberships.\u003e\", \"$JS.API.CONSUMER.INFO.EVENTS.novox_mesh-delivery-view\", \"$JS.API.CONSUMER.INFO.SEAT_NODE_BUILD_GO.SEAT_NODE_BUILD_GO_CHECK_worker\", \"$JS.API.CONSUMER.INFO.SEAT_NODE_BUILD_GO.SEAT_NODE_BUILD_GO_DELIVERY_worker\", \"$JS.API.CONSUMER.INFO.SEAT_NODE_BUILD_GO.SEAT_NODE_BUILD_GO_RECHECK_worker\", \"$JS.API.CONSUMER.INFO.SEAT_NODE_BUILD_IMAGE.SEAT_NODE_BUILD_IMAGE_CHECK_worker\", \"$JS.API.CONSUMER.INFO.SEAT_NODE_BUILD_IMAGE.SEAT_NODE_BUILD_IMAGE_DELIVERY_worker\", \"$JS.API.CONSUMER.INFO.SEAT_NODE_BUILD_IMAGE.SEAT_NODE_BUILD_IMAGE_RECHECK_worker\", \"$JS.API.CONSUMER.INFO.SEAT_NODE_BUILD_NODE.SEAT_NODE_BUILD_NODE_CHECK_worker\", \"$JS.API.CONSUMER.INFO.SEAT_NODE_BUILD_NODE.SEAT_NODE_BUILD_NODE_DELIVERY_worker\", \"$JS.API.CONSUMER.INFO.SEAT_NODE_BUILD_NODE.SEAT_NODE_BUILD_NODE_RECHECK_worker\", \"$JS.API.CONSUMER.MSG.NEXT.EVENTS.novox_mesh-delivery-view\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-builder_presence.$KV.mesh-delivery-builder_presence.novox\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-builder_steps.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-grants_memberships.$KV.mesh-grants_memberships.novox.mesh-delivery-view\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-builder_presence\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-builder_steps\", \"$JS.API.STREAM.INFO.KV_mesh-grants_memberships\", \"$JS.API.STREAM.INFO.SEAT_NODE_BUILD_GO\", \"$JS.API.STREAM.INFO.SEAT_NODE_BUILD_IMAGE\", \"$JS.API.STREAM.INFO.SEAT_NODE_BUILD_NODE\", \"$JS.API.STREAM.MSG.GET.SEAT_NODE_BUILD_GO\", \"$JS.API.STREAM.MSG.GET.SEAT_NODE_BUILD_IMAGE\", \"$JS.API.STREAM.MSG.GET.SEAT_NODE_BUILD_NODE\", \"$JS.FC.KV_mesh-delivery-builder_presence.\u003e\", \"$JS.FC.KV_mesh-delivery-builder_steps.\u003e\", \"$JS.FC.KV_mesh-grants_memberships.\u003e\"] }\n subscribe: { allow: [\"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-builder_presence\", \"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-builder_steps\", \"$SRV.INFO\", \"$SRV.INFO.mesh-delivery-view\", \"$SRV.INFO.mesh-delivery-view.\u003e\", \"$SRV.PING\", \"$SRV.PING.mesh-delivery-view\", \"$SRV.PING.mesh-delivery-view.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.mesh-delivery-view\", \"$SRV.STATS.mesh-delivery-view.\u003e\", \"_INBOX.novox.mesh-delivery-view.\u003e\", \"mesh.mod.mesh-delivery-view.call.\u003e\", \"mesh.mod.mesh-delivery-view.tool.\u003e\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n { user: \"novox.mesh-grants\", password: \"$2a$11$novox.mesh-grantsnovoxfixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.EVENTS.novox_mesh-grants.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-grants_assertions.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-grants_memberships.*.$KV.mesh-grants_memberships.novox.mesh-grants\", \"$JS.API.CONSUMER.CREATE.asks.novox_mesh-delivery-builder_worker\", \"$JS.API.CONSUMER.DELETE.KV_mesh-grants_assertions.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-grants_memberships.\u003e\", \"$JS.API.CONSUMER.INFO.EVENTS.novox_mesh-grants\", \"$JS.API.CONSUMER.INFO.asks.novox_mesh-delivery-builder_worker\", \"$JS.API.CONSUMER.MSG.NEXT.EVENTS.novox_mesh-grants\", \"$JS.API.DIRECT.GET.ASSIGNMENTS.mesh.assignment.*.*\", \"$JS.API.DIRECT.GET.KV_mesh-grants_assertions.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-grants_memberships.$KV.mesh-grants_memberships.novox.mesh-grants\", \"$JS.API.DIRECT.GET.OBJ_mesh-grants-user-list.\u003e\", \"$JS.API.STREAM.CREATE.KV_mesh-delivery-builder_presence\", \"$JS.API.STREAM.CREATE.KV_mesh-delivery-builder_steps\", \"$JS.API.STREAM.CREATE.KV_mesh-grants_assertions\", \"$JS.API.STREAM.CREATE.LOG_mesh-delivery-builder_builds\", \"$JS.API.STREAM.CREATE.OBJ_mesh-grants-user-list\", \"$JS.API.STREAM.CREATE.asks\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-builder_presence\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-builder_steps\", \"$JS.API.STREAM.INFO.KV_mesh-grants_assertions\", \"$JS.API.STREAM.INFO.KV_mesh-grants_memberships\", \"$JS.API.STREAM.INFO.LOG_mesh-delivery-builder_builds\", \"$JS.API.STREAM.INFO.OBJ_mesh-grants-user-list\", \"$JS.API.STREAM.INFO.asks\", \"$JS.API.STREAM.MSG.GET.OBJ_mesh-grants-user-list\", \"$JS.API.STREAM.PURGE.OBJ_mesh-grants-user-list\", \"$JS.FC.KV_mesh-grants_assertions.\u003e\", \"$JS.FC.KV_mesh-grants_memberships.\u003e\", \"$KV.mesh-grants_assertions.\u003e\", \"$O.mesh-grants-user-list.\u003e\", \"mesh.assignment.*.*\", \"mesh.mod.mesh-grants.event.membership-changed\", \"mesh.mod.mesh-grants.event.states\"] }\n subscribe: { allow: [\"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-grants_assertions\", \"$SRV.INFO\", \"$SRV.INFO.mesh-grants\", \"$SRV.INFO.mesh-grants.\u003e\", \"$SRV.PING\", \"$SRV.PING.mesh-grants\", \"$SRV.PING.mesh-grants.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.mesh-grants\", \"$SRV.STATS.mesh-grants.\u003e\", \"_INBOX.novox.mesh-grants.\u003e\", \"mesh.mod.mesh-grants.call.\u003e\", \"mesh.mod.mesh-grants.tool.\u003e\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n ]\n }\n \"$SYS\" {\n exports = [\n { stream: \"$SYS.ACCOUNT.MESH.CONNECT\", accounts: [\"MESH\"] }\n { stream: \"$SYS.ACCOUNT.MESH.DISCONNECT\", accounts: [\"MESH\"] }\n { service: \"$SYS.REQ.ACCOUNT.MESH.CONNZ\", accounts: [\"MESH\"] }\n ]\n }\n}\n", "Missing": null, "Err": "", "Memberships": { @@ -288,7 +288,7 @@ }, { "Name": "a node seat's events, by machine and flat", - "List": "# The mesh's users, composed by the grants part. Do not edit: the next\n# composition overwrites it. Permissions are derived from what each module\n# declares and nothing else (novox/hq ADR 0043, design 29 §2).\n\naccounts {\n MESH {\n jetstream: enabled\n imports = [\n { stream: { account: \"$SYS\", subject: \"$SYS.ACCOUNT.MESH.CONNECT\" }, to: \"mesh.bus.connect\" }\n { stream: { account: \"$SYS\", subject: \"$SYS.ACCOUNT.MESH.DISCONNECT\" }, to: \"mesh.bus.disconnect\" }\n { service: { account: \"$SYS\", subject: \"$SYS.REQ.ACCOUNT.MESH.CONNZ\" }, to: \"mesh.bus.connz\" }\n ]\n users = [\n { user: \"ace.pause\", password: \"$2a$11$ace.pauseace.pauseace.fixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.EVENTS.ace_pause.\u003e\", \"$JS.ACK.SEAT_NODE_POWER.SEAT_NODE_POWER_worker.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-grants_memberships.*.$KV.mesh-grants_memberships.ace.pause\", \"$JS.API.CONSUMER.DELETE.KV_mesh-grants_memberships.\u003e\", \"$JS.API.CONSUMER.INFO.EVENTS.ace_pause\", \"$JS.API.CONSUMER.INFO.SEAT_NODE_POWER.SEAT_NODE_POWER_worker\", \"$JS.API.CONSUMER.MSG.NEXT.EVENTS.ace_pause\", \"$JS.API.CONSUMER.MSG.NEXT.SEAT_NODE_POWER.SEAT_NODE_POWER_worker\", \"$JS.API.DIRECT.GET.KV_mesh-grants_memberships.$KV.mesh-grants_memberships.ace.pause\", \"$JS.API.STREAM.INFO.KV_mesh-grants_memberships\", \"$JS.FC.KV_mesh-grants_memberships.\u003e\", \"mesh.seat.node-power.event.paused.ace\", \"mesh.seat.node-power.event.slept\"] }\n subscribe: { allow: [\"$SRV.INFO\", \"$SRV.INFO.pause\", \"$SRV.INFO.pause.\u003e\", \"$SRV.PING\", \"$SRV.PING.pause\", \"$SRV.PING.pause.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.pause\", \"$SRV.STATS.pause.\u003e\", \"_INBOX.ace.pause.\u003e\", \"mesh.mod.pause.call.\u003e\", \"mesh.mod.pause.tool.\u003e\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n { user: \"node.ace\", password: \"$2a$11$node.acenode.acenode.afixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.NODES.ace.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_composed-ace.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-config_resolved.*.$KV.mesh-config_resolved.ace.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_placement.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_wants.*.$KV.mesh-delivery-assigner_wants.want.ace.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_acts.*.$KV.mesh-host_acts.ace.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_data.*.$KV.mesh-host_data.ace.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_findings.*.$KV.mesh-host_findings.machine.ace.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_health.*.$KV.mesh-host_health.ace\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_packages.*.$KV.mesh-host_packages.ace\", \"$JS.API.CONSUMER.CREATE.KV_mesh-provisioner_provisions.*.$KV.mesh-provisioner_provisions.ace.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.*.node\", \"$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.ace.\u003e\", \"$JS.API.CONSUMER.CREATE.OBJ_declaration-files-ace.*.$O.declaration-files-ace.C.*\", \"$JS.API.CONSUMER.DELETE.KV_composed-ace.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-config_resolved.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_placement.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_wants.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_acts.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_data.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_findings.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_health.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_packages.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-provisioner_provisions.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_node-engine_have.\u003e\", \"$JS.API.CONSUMER.DELETE.OBJ_declaration-files-ace.*\", \"$JS.API.CONSUMER.INFO.NODES.ace\", \"$JS.API.DIRECT.GET.KV_composed-ace.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-config_resolved.$KV.mesh-config_resolved.ace.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_placement.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_wants.$KV.mesh-delivery-assigner_wants.want.ace.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_acts.$KV.mesh-host_acts.ace.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_data.$KV.mesh-host_data.ace.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_findings.$KV.mesh-host_findings.machine.ace.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_health.$KV.mesh-host_health.ace\", \"$JS.API.DIRECT.GET.KV_mesh-host_packages.$KV.mesh-host_packages.ace\", \"$JS.API.DIRECT.GET.KV_mesh-provisioner_provisions.$KV.mesh-provisioner_provisions.ace.\u003e\", \"$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.*.node\", \"$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.ace.\u003e\", \"$JS.API.DIRECT.GET.OBJ_declaration-files-ace.$O.declaration-files-ace.M.*\", \"$JS.API.STREAM.INFO.KV_composed-ace\", \"$JS.API.STREAM.INFO.KV_mesh-config_resolved\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_candidates\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_placement\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_wants\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-builder_builds\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_candidates\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_manifests\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_modules\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_verdicts\", \"$JS.API.STREAM.INFO.KV_mesh-host_acts\", \"$JS.API.STREAM.INFO.KV_mesh-host_data\", \"$JS.API.STREAM.INFO.KV_mesh-host_findings\", \"$JS.API.STREAM.INFO.KV_mesh-host_health\", \"$JS.API.STREAM.INFO.KV_mesh-host_packages\", \"$JS.API.STREAM.INFO.KV_mesh-provisioner_provisions\", \"$JS.API.STREAM.INFO.KV_node-engine_have\", \"$JS.FC.KV_composed-ace.\u003e\", \"$JS.FC.KV_mesh-config_resolved.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_placement.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_wants.\u003e\", \"$JS.FC.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.FC.KV_mesh-host_acts.\u003e\", \"$JS.FC.KV_mesh-host_data.\u003e\", \"$JS.FC.KV_mesh-host_findings.\u003e\", \"$JS.FC.KV_mesh-host_health.\u003e\", \"$JS.FC.KV_mesh-host_packages.\u003e\", \"$JS.FC.KV_mesh-provisioner_provisions.\u003e\", \"$JS.FC.KV_node-engine_have.\u003e\", \"$JS.FC.OBJ_declaration-files-ace.\u003e\", \"$KV.composed-ace.\u003e\", \"$KV.mesh-host_acts.ace.\u003e\", \"$KV.mesh-host_data.ace.\u003e\", \"$KV.mesh-host_findings.machine.ace.\u003e\", \"$KV.mesh-host_health.ace\", \"$KV.mesh-host_packages.ace\", \"$KV.node-engine_have.ace.\u003e\", \"$SRV.INFO\", \"$SRV.INFO.\u003e\", \"$SRV.PING\", \"$SRV.PING.*.ace\", \"$SRV.PING.\u003e\", \"mesh.control.ace.\u003e\", \"mesh.seat.node-backup.call.backed-up.ace.node~ace\", \"mesh.seat.node-backup.call.now.ace.node~ace\", \"mesh.seat.node-backup.tool.backed-up.ace\", \"mesh.seat.node-backup.tool.now.ace\", \"mesh.seat.node-engine.event.*.ace\"] }\n subscribe: { allow: [\"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_candidates\", \"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_placement\", \"$SRV.INFO\", \"$SRV.INFO.node-engine\", \"$SRV.INFO.node-engine.\u003e\", \"$SRV.PING\", \"$SRV.PING.node-engine\", \"$SRV.PING.node-engine.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.node-engine\", \"$SRV.STATS.node-engine.\u003e\", \"_DELIVER.ace\", \"_DELIVER.ace.\u003e\", \"_INBOX.node.ace.\u003e\", \"mesh.node.ace.ask.access-act\", \"mesh.node.ace.ask.hand-over\", \"mesh.node.ace.ask.judge-directories\", \"mesh.node.ace.ask.move-secret\", \"mesh.node.ace.ask.report\", \"mesh.node.ace.ask.setuid-search\", \"mesh.node.ace.declare\", \"mesh.seat.node-engine.call.*.ace.*\", \"mesh.seat.node-engine.tool.*.ace\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n { user: \"node.novox\", password: \"$2a$11$node.novoxnode.novoxnofixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.NODES.novox.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_composed-novox.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-config_resolved.*.$KV.mesh-config_resolved.novox.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_placement.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_wants.*.$KV.mesh-delivery-assigner_wants.want.novox.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_acts.*.$KV.mesh-host_acts.novox.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_data.*.$KV.mesh-host_data.novox.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_findings.*.$KV.mesh-host_findings.machine.novox.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_health.*.$KV.mesh-host_health.novox\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_packages.*.$KV.mesh-host_packages.novox\", \"$JS.API.CONSUMER.CREATE.KV_mesh-provisioner_provisions.*.$KV.mesh-provisioner_provisions.novox.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.*.node\", \"$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.novox.\u003e\", \"$JS.API.CONSUMER.CREATE.OBJ_declaration-files-novox.*.$O.declaration-files-novox.C.*\", \"$JS.API.CONSUMER.DELETE.KV_composed-novox.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-config_resolved.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_placement.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_wants.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_acts.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_data.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_findings.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_health.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_packages.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-provisioner_provisions.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_node-engine_have.\u003e\", \"$JS.API.CONSUMER.DELETE.OBJ_declaration-files-novox.*\", \"$JS.API.CONSUMER.INFO.NODES.novox\", \"$JS.API.DIRECT.GET.KV_composed-novox.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-config_resolved.$KV.mesh-config_resolved.novox.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_placement.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_wants.$KV.mesh-delivery-assigner_wants.want.novox.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_acts.$KV.mesh-host_acts.novox.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_data.$KV.mesh-host_data.novox.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_findings.$KV.mesh-host_findings.machine.novox.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_health.$KV.mesh-host_health.novox\", \"$JS.API.DIRECT.GET.KV_mesh-host_packages.$KV.mesh-host_packages.novox\", \"$JS.API.DIRECT.GET.KV_mesh-provisioner_provisions.$KV.mesh-provisioner_provisions.novox.\u003e\", \"$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.*.node\", \"$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.novox.\u003e\", \"$JS.API.DIRECT.GET.OBJ_declaration-files-novox.$O.declaration-files-novox.M.*\", \"$JS.API.STREAM.INFO.KV_composed-novox\", \"$JS.API.STREAM.INFO.KV_mesh-config_resolved\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_candidates\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_placement\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_wants\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-builder_builds\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_candidates\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_manifests\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_modules\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_verdicts\", \"$JS.API.STREAM.INFO.KV_mesh-host_acts\", \"$JS.API.STREAM.INFO.KV_mesh-host_data\", \"$JS.API.STREAM.INFO.KV_mesh-host_findings\", \"$JS.API.STREAM.INFO.KV_mesh-host_health\", \"$JS.API.STREAM.INFO.KV_mesh-host_packages\", \"$JS.API.STREAM.INFO.KV_mesh-provisioner_provisions\", \"$JS.API.STREAM.INFO.KV_node-engine_have\", \"$JS.FC.KV_composed-novox.\u003e\", \"$JS.FC.KV_mesh-config_resolved.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_placement.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_wants.\u003e\", \"$JS.FC.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.FC.KV_mesh-host_acts.\u003e\", \"$JS.FC.KV_mesh-host_data.\u003e\", \"$JS.FC.KV_mesh-host_findings.\u003e\", \"$JS.FC.KV_mesh-host_health.\u003e\", \"$JS.FC.KV_mesh-host_packages.\u003e\", \"$JS.FC.KV_mesh-provisioner_provisions.\u003e\", \"$JS.FC.KV_node-engine_have.\u003e\", \"$JS.FC.OBJ_declaration-files-novox.\u003e\", \"$KV.composed-novox.\u003e\", \"$KV.mesh-host_acts.novox.\u003e\", \"$KV.mesh-host_data.novox.\u003e\", \"$KV.mesh-host_findings.machine.novox.\u003e\", \"$KV.mesh-host_health.novox\", \"$KV.mesh-host_packages.novox\", \"$KV.node-engine_have.novox.\u003e\", \"$SRV.INFO\", \"$SRV.INFO.\u003e\", \"$SRV.PING\", \"$SRV.PING.*.novox\", \"$SRV.PING.\u003e\", \"mesh.control.novox.\u003e\", \"mesh.seat.node-backup.call.backed-up.novox.node~novox\", \"mesh.seat.node-backup.call.now.novox.node~novox\", \"mesh.seat.node-backup.tool.backed-up.novox\", \"mesh.seat.node-backup.tool.now.novox\", \"mesh.seat.node-engine.event.*.novox\"] }\n subscribe: { allow: [\"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_candidates\", \"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_placement\", \"$SRV.INFO\", \"$SRV.INFO.node-engine\", \"$SRV.INFO.node-engine.\u003e\", \"$SRV.PING\", \"$SRV.PING.node-engine\", \"$SRV.PING.node-engine.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.node-engine\", \"$SRV.STATS.node-engine.\u003e\", \"_DELIVER.novox\", \"_DELIVER.novox.\u003e\", \"_INBOX.node.novox.\u003e\", \"mesh.node.novox.ask.access-act\", \"mesh.node.novox.ask.hand-over\", \"mesh.node.novox.ask.judge-directories\", \"mesh.node.novox.ask.move-secret\", \"mesh.node.novox.ask.report\", \"mesh.node.novox.ask.setuid-search\", \"mesh.node.novox.declare\", \"mesh.seat.node-engine.call.*.novox.*\", \"mesh.seat.node-engine.tool.*.novox\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n { user: \"novox.mesh-delivery-assigner\", password: \"$2a$11$novox.mesh-delivery-asfixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.EVENTS.novox_mesh-delivery-assigner.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-grants_memberships.*.$KV.mesh-grants_memberships.novox.mesh-delivery-assigner\", \"$JS.API.CONSUMER.DELETE.KV_mesh-grants_memberships.\u003e\", \"$JS.API.CONSUMER.INFO.EVENTS.novox_mesh-delivery-assigner\", \"$JS.API.CONSUMER.MSG.NEXT.EVENTS.novox_mesh-delivery-assigner\", \"$JS.API.DIRECT.GET.KV_mesh-grants_memberships.$KV.mesh-grants_memberships.novox.mesh-delivery-assigner\", \"$JS.API.STREAM.INFO.KV_mesh-grants_memberships\", \"$JS.FC.KV_mesh-grants_memberships.\u003e\"] }\n subscribe: { allow: [\"$SRV.INFO\", \"$SRV.INFO.mesh-delivery-assigner\", \"$SRV.INFO.mesh-delivery-assigner.\u003e\", \"$SRV.PING\", \"$SRV.PING.mesh-delivery-assigner\", \"$SRV.PING.mesh-delivery-assigner.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.mesh-delivery-assigner\", \"$SRV.STATS.mesh-delivery-assigner.\u003e\", \"_INBOX.novox.mesh-delivery-assigner.\u003e\", \"mesh.mod.mesh-delivery-assigner.call.\u003e\", \"mesh.mod.mesh-delivery-assigner.tool.\u003e\", \"mesh.seat.node-engine.event.manifest-reads-changed.*\", \"mesh.seat.node-power.event.slept\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n ]\n }\n \"$SYS\" {\n exports = [\n { stream: \"$SYS.ACCOUNT.MESH.CONNECT\", accounts: [\"MESH\"] }\n { stream: \"$SYS.ACCOUNT.MESH.DISCONNECT\", accounts: [\"MESH\"] }\n { service: \"$SYS.REQ.ACCOUNT.MESH.CONNZ\", accounts: [\"MESH\"] }\n ]\n }\n}\n", + "List": "# The mesh's users, composed by the grants part. Do not edit: the next\n# composition overwrites it. Permissions are derived from what each module\n# declares and nothing else (novox/hq ADR 0043, design 29 §2).\n\naccounts {\n MESH {\n jetstream: enabled\n imports = [\n { stream: { account: \"$SYS\", subject: \"$SYS.ACCOUNT.MESH.CONNECT\" }, to: \"mesh.bus.connect\" }\n { stream: { account: \"$SYS\", subject: \"$SYS.ACCOUNT.MESH.DISCONNECT\" }, to: \"mesh.bus.disconnect\" }\n { service: { account: \"$SYS\", subject: \"$SYS.REQ.ACCOUNT.MESH.CONNZ\" }, to: \"mesh.bus.connz\" }\n ]\n users = [\n { user: \"ace.pause\", password: \"$2a$11$ace.pauseace.pauseace.fixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.EVENTS.ace_pause.\u003e\", \"$JS.ACK.SEAT_NODE_POWER.SEAT_NODE_POWER_worker.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-grants_memberships.*.$KV.mesh-grants_memberships.ace.pause\", \"$JS.API.CONSUMER.DELETE.KV_mesh-grants_memberships.\u003e\", \"$JS.API.CONSUMER.INFO.EVENTS.ace_pause\", \"$JS.API.CONSUMER.INFO.SEAT_NODE_POWER.SEAT_NODE_POWER_worker\", \"$JS.API.CONSUMER.MSG.NEXT.EVENTS.ace_pause\", \"$JS.API.CONSUMER.MSG.NEXT.SEAT_NODE_POWER.SEAT_NODE_POWER_worker\", \"$JS.API.DIRECT.GET.KV_mesh-grants_memberships.$KV.mesh-grants_memberships.ace.pause\", \"$JS.API.STREAM.INFO.KV_mesh-grants_memberships\", \"$JS.FC.KV_mesh-grants_memberships.\u003e\", \"mesh.seat.node-power.event.paused.ace\", \"mesh.seat.node-power.event.slept\"] }\n subscribe: { allow: [\"$SRV.INFO\", \"$SRV.INFO.pause\", \"$SRV.INFO.pause.\u003e\", \"$SRV.PING\", \"$SRV.PING.pause\", \"$SRV.PING.pause.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.pause\", \"$SRV.STATS.pause.\u003e\", \"_INBOX.ace.pause.\u003e\", \"mesh.mod.pause.call.\u003e\", \"mesh.mod.pause.tool.\u003e\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n { user: \"node.ace\", password: \"$2a$11$node.acenode.acenode.afixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.NODES.ace.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_composed-ace.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-config_resolved.*.$KV.mesh-config_resolved.ace.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_assignments.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_wants.*.$KV.mesh-delivery-assigner_wants.want.ace.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_acts.*.$KV.mesh-host_acts.ace.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_data.*.$KV.mesh-host_data.ace.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_findings.*.$KV.mesh-host_findings.machine.ace.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_health.*.$KV.mesh-host_health.ace\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_packages.*.$KV.mesh-host_packages.ace\", \"$JS.API.CONSUMER.CREATE.KV_mesh-provisioner_provisions.*.$KV.mesh-provisioner_provisions.ace.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.*.node\", \"$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.ace.\u003e\", \"$JS.API.CONSUMER.CREATE.OBJ_declaration-files-ace.*.$O.declaration-files-ace.C.*\", \"$JS.API.CONSUMER.DELETE.KV_composed-ace.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-config_resolved.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_assignments.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_wants.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_acts.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_data.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_findings.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_health.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_packages.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-provisioner_provisions.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_node-engine_have.\u003e\", \"$JS.API.CONSUMER.DELETE.OBJ_declaration-files-ace.*\", \"$JS.API.CONSUMER.INFO.NODES.ace\", \"$JS.API.DIRECT.GET.KV_composed-ace.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-config_resolved.$KV.mesh-config_resolved.ace.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_assignments.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_wants.$KV.mesh-delivery-assigner_wants.want.ace.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_acts.$KV.mesh-host_acts.ace.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_data.$KV.mesh-host_data.ace.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_findings.$KV.mesh-host_findings.machine.ace.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_health.$KV.mesh-host_health.ace\", \"$JS.API.DIRECT.GET.KV_mesh-host_packages.$KV.mesh-host_packages.ace\", \"$JS.API.DIRECT.GET.KV_mesh-provisioner_provisions.$KV.mesh-provisioner_provisions.ace.\u003e\", \"$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.*.node\", \"$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.ace.\u003e\", \"$JS.API.DIRECT.GET.OBJ_declaration-files-ace.$O.declaration-files-ace.M.*\", \"$JS.API.STREAM.INFO.KV_composed-ace\", \"$JS.API.STREAM.INFO.KV_mesh-config_resolved\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_assignments\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_candidates\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_wants\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-builder_builds\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_candidates\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_manifests\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_modules\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_verdicts\", \"$JS.API.STREAM.INFO.KV_mesh-host_acts\", \"$JS.API.STREAM.INFO.KV_mesh-host_data\", \"$JS.API.STREAM.INFO.KV_mesh-host_findings\", \"$JS.API.STREAM.INFO.KV_mesh-host_health\", \"$JS.API.STREAM.INFO.KV_mesh-host_packages\", \"$JS.API.STREAM.INFO.KV_mesh-provisioner_provisions\", \"$JS.API.STREAM.INFO.KV_node-engine_have\", \"$JS.FC.KV_composed-ace.\u003e\", \"$JS.FC.KV_mesh-config_resolved.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_assignments.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_wants.\u003e\", \"$JS.FC.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.FC.KV_mesh-host_acts.\u003e\", \"$JS.FC.KV_mesh-host_data.\u003e\", \"$JS.FC.KV_mesh-host_findings.\u003e\", \"$JS.FC.KV_mesh-host_health.\u003e\", \"$JS.FC.KV_mesh-host_packages.\u003e\", \"$JS.FC.KV_mesh-provisioner_provisions.\u003e\", \"$JS.FC.KV_node-engine_have.\u003e\", \"$JS.FC.OBJ_declaration-files-ace.\u003e\", \"$KV.composed-ace.\u003e\", \"$KV.mesh-host_acts.ace.\u003e\", \"$KV.mesh-host_data.ace.\u003e\", \"$KV.mesh-host_findings.machine.ace.\u003e\", \"$KV.mesh-host_health.ace\", \"$KV.mesh-host_packages.ace\", \"$KV.node-engine_have.ace.\u003e\", \"$SRV.INFO\", \"$SRV.INFO.\u003e\", \"$SRV.PING\", \"$SRV.PING.*.ace\", \"$SRV.PING.\u003e\", \"mesh.control.ace.\u003e\", \"mesh.seat.node-backup.call.backed-up.ace.node~ace\", \"mesh.seat.node-backup.call.now.ace.node~ace\", \"mesh.seat.node-backup.tool.backed-up.ace\", \"mesh.seat.node-backup.tool.now.ace\", \"mesh.seat.node-engine.event.*.ace\"] }\n subscribe: { allow: [\"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_assignments\", \"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_candidates\", \"$SRV.INFO\", \"$SRV.INFO.node-engine\", \"$SRV.INFO.node-engine.\u003e\", \"$SRV.PING\", \"$SRV.PING.node-engine\", \"$SRV.PING.node-engine.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.node-engine\", \"$SRV.STATS.node-engine.\u003e\", \"_DELIVER.ace\", \"_DELIVER.ace.\u003e\", \"_INBOX.node.ace.\u003e\", \"mesh.node.ace.ask.access-act\", \"mesh.node.ace.ask.hand-over\", \"mesh.node.ace.ask.judge-directories\", \"mesh.node.ace.ask.move-secret\", \"mesh.node.ace.ask.report\", \"mesh.node.ace.ask.setuid-search\", \"mesh.node.ace.declare\", \"mesh.seat.node-engine.call.*.ace.*\", \"mesh.seat.node-engine.tool.*.ace\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n { user: \"node.novox\", password: \"$2a$11$node.novoxnode.novoxnofixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.NODES.novox.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_composed-novox.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-config_resolved.*.$KV.mesh-config_resolved.novox.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_assignments.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_wants.*.$KV.mesh-delivery-assigner_wants.want.novox.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_acts.*.$KV.mesh-host_acts.novox.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_data.*.$KV.mesh-host_data.novox.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_findings.*.$KV.mesh-host_findings.machine.novox.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_health.*.$KV.mesh-host_health.novox\", \"$JS.API.CONSUMER.CREATE.KV_mesh-host_packages.*.$KV.mesh-host_packages.novox\", \"$JS.API.CONSUMER.CREATE.KV_mesh-provisioner_provisions.*.$KV.mesh-provisioner_provisions.novox.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.*.node\", \"$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.novox.\u003e\", \"$JS.API.CONSUMER.CREATE.OBJ_declaration-files-novox.*.$O.declaration-files-novox.C.*\", \"$JS.API.CONSUMER.DELETE.KV_composed-novox.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-config_resolved.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_assignments.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_wants.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_acts.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_data.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_findings.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_health.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-host_packages.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-provisioner_provisions.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_node-engine_have.\u003e\", \"$JS.API.CONSUMER.DELETE.OBJ_declaration-files-novox.*\", \"$JS.API.CONSUMER.INFO.NODES.novox\", \"$JS.API.DIRECT.GET.KV_composed-novox.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-config_resolved.$KV.mesh-config_resolved.novox.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_assignments.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_wants.$KV.mesh-delivery-assigner_wants.want.novox.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_acts.$KV.mesh-host_acts.novox.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_data.$KV.mesh-host_data.novox.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_findings.$KV.mesh-host_findings.machine.novox.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-host_health.$KV.mesh-host_health.novox\", \"$JS.API.DIRECT.GET.KV_mesh-host_packages.$KV.mesh-host_packages.novox\", \"$JS.API.DIRECT.GET.KV_mesh-provisioner_provisions.$KV.mesh-provisioner_provisions.novox.\u003e\", \"$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.*.node\", \"$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.novox.\u003e\", \"$JS.API.DIRECT.GET.OBJ_declaration-files-novox.$O.declaration-files-novox.M.*\", \"$JS.API.STREAM.INFO.KV_composed-novox\", \"$JS.API.STREAM.INFO.KV_mesh-config_resolved\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_assignments\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_candidates\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_wants\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-builder_builds\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_candidates\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_manifests\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_modules\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_verdicts\", \"$JS.API.STREAM.INFO.KV_mesh-host_acts\", \"$JS.API.STREAM.INFO.KV_mesh-host_data\", \"$JS.API.STREAM.INFO.KV_mesh-host_findings\", \"$JS.API.STREAM.INFO.KV_mesh-host_health\", \"$JS.API.STREAM.INFO.KV_mesh-host_packages\", \"$JS.API.STREAM.INFO.KV_mesh-provisioner_provisions\", \"$JS.API.STREAM.INFO.KV_node-engine_have\", \"$JS.FC.KV_composed-novox.\u003e\", \"$JS.FC.KV_mesh-config_resolved.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_assignments.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_candidates.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_wants.\u003e\", \"$JS.FC.KV_mesh-delivery-builder_builds.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_candidates.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_manifests.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_verdicts.\u003e\", \"$JS.FC.KV_mesh-host_acts.\u003e\", \"$JS.FC.KV_mesh-host_data.\u003e\", \"$JS.FC.KV_mesh-host_findings.\u003e\", \"$JS.FC.KV_mesh-host_health.\u003e\", \"$JS.FC.KV_mesh-host_packages.\u003e\", \"$JS.FC.KV_mesh-provisioner_provisions.\u003e\", \"$JS.FC.KV_node-engine_have.\u003e\", \"$JS.FC.OBJ_declaration-files-novox.\u003e\", \"$KV.composed-novox.\u003e\", \"$KV.mesh-host_acts.novox.\u003e\", \"$KV.mesh-host_data.novox.\u003e\", \"$KV.mesh-host_findings.machine.novox.\u003e\", \"$KV.mesh-host_health.novox\", \"$KV.mesh-host_packages.novox\", \"$KV.node-engine_have.novox.\u003e\", \"$SRV.INFO\", \"$SRV.INFO.\u003e\", \"$SRV.PING\", \"$SRV.PING.*.novox\", \"$SRV.PING.\u003e\", \"mesh.control.novox.\u003e\", \"mesh.seat.node-backup.call.backed-up.novox.node~novox\", \"mesh.seat.node-backup.call.now.novox.node~novox\", \"mesh.seat.node-backup.tool.backed-up.novox\", \"mesh.seat.node-backup.tool.now.novox\", \"mesh.seat.node-engine.event.*.novox\"] }\n subscribe: { allow: [\"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_assignments\", \"$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_candidates\", \"$SRV.INFO\", \"$SRV.INFO.node-engine\", \"$SRV.INFO.node-engine.\u003e\", \"$SRV.PING\", \"$SRV.PING.node-engine\", \"$SRV.PING.node-engine.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.node-engine\", \"$SRV.STATS.node-engine.\u003e\", \"_DELIVER.novox\", \"_DELIVER.novox.\u003e\", \"_INBOX.node.novox.\u003e\", \"mesh.node.novox.ask.access-act\", \"mesh.node.novox.ask.hand-over\", \"mesh.node.novox.ask.judge-directories\", \"mesh.node.novox.ask.move-secret\", \"mesh.node.novox.ask.report\", \"mesh.node.novox.ask.setuid-search\", \"mesh.node.novox.declare\", \"mesh.seat.node-engine.call.*.novox.*\", \"mesh.seat.node-engine.tool.*.novox\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n { user: \"novox.mesh-delivery-assigner\", password: \"$2a$11$novox.mesh-delivery-asfixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.EVENTS.novox_mesh-delivery-assigner.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-grants_memberships.*.$KV.mesh-grants_memberships.novox.mesh-delivery-assigner\", \"$JS.API.CONSUMER.DELETE.KV_mesh-grants_memberships.\u003e\", \"$JS.API.CONSUMER.INFO.EVENTS.novox_mesh-delivery-assigner\", \"$JS.API.CONSUMER.MSG.NEXT.EVENTS.novox_mesh-delivery-assigner\", \"$JS.API.DIRECT.GET.KV_mesh-grants_memberships.$KV.mesh-grants_memberships.novox.mesh-delivery-assigner\", \"$JS.API.STREAM.INFO.KV_mesh-grants_memberships\", \"$JS.FC.KV_mesh-grants_memberships.\u003e\"] }\n subscribe: { allow: [\"$SRV.INFO\", \"$SRV.INFO.mesh-delivery-assigner\", \"$SRV.INFO.mesh-delivery-assigner.\u003e\", \"$SRV.PING\", \"$SRV.PING.mesh-delivery-assigner\", \"$SRV.PING.mesh-delivery-assigner.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.mesh-delivery-assigner\", \"$SRV.STATS.mesh-delivery-assigner.\u003e\", \"_INBOX.novox.mesh-delivery-assigner.\u003e\", \"mesh.mod.mesh-delivery-assigner.call.\u003e\", \"mesh.mod.mesh-delivery-assigner.tool.\u003e\", \"mesh.seat.node-engine.event.manifest-reads-changed.*\", \"mesh.seat.node-power.event.slept\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n ]\n }\n \"$SYS\" {\n exports = [\n { stream: \"$SYS.ACCOUNT.MESH.CONNECT\", accounts: [\"MESH\"] }\n { stream: \"$SYS.ACCOUNT.MESH.DISCONNECT\", accounts: [\"MESH\"] }\n { service: \"$SYS.REQ.ACCOUNT.MESH.CONNZ\", accounts: [\"MESH\"] }\n ]\n }\n}\n", "Missing": null, "Err": "", "Memberships": { diff --git a/modules/mesh-grants/internal/buscompose/users.go b/modules/mesh-grants/internal/buscompose/users.go index 3d6a174a..156a1214 100644 --- a/modules/mesh-grants/internal/buscompose/users.go +++ b/modules/mesh-grants/internal/buscompose/users.go @@ -75,7 +75,7 @@ type Declared struct { // 0495 clause 3, issue 1790): granted nothing, kept to be said. ReadsRefused []string // ReadLogs are, of its reads, those naming another module's log (`.`, novox/hq ADR 0571 clause 1): filled - // by Users and PlacementsOf from the logs the modules placed anywhere declare, never by a caller. + // by Users and AssignmentsOf from the logs the modules placed anywhere declare, never by a caller. ReadLogs []string // KeyedReads are keys of other modules' state it reads, each one key alone: what a seat's holder is // granted for the pieces the modules beside it offer (novox/hq ADR 0260). @@ -231,7 +231,7 @@ func Users(r Records) ([]Principal, error) { } } for _, d := range r.Assigned[node] { - // **The node-engine's manifest is no placed module's** (novox/hq ADR 0563 §2, §3; issue 774): where a placement + // **The node-engine's manifest is no placed module's** (novox/hq ADR 0563 §2, §3; issue 774): where assignments // names it, it adds no user, no process and nothing the runner carries; its grants are the node's account's. if !Composed(d) { continue @@ -541,7 +541,7 @@ func boundBucketsOf(r Records) []string { } // Composed says a placed module is composed as one: its own user, its processes and what the runner carries of it. -// The node-engine's manifest (EngineModule) is not, though a placement may name it: each node's engine speaks as the +// The node-engine's manifest (EngineModule) is not, though assignments may name it: each node's engine speaks as the // node's account, which alone writes the engine's state under its own key (engineGrants, novox/hq ADR 0563 §2, §3), and // Records.Engine creates that state. Granted to another account it is a second writer, and the writers table refuses // the whole list (issue 774: met for mesh-host_acts and then mesh-host_health, 2026-10-30). diff --git a/modules/mesh-grants/internal/grants/accounts.go b/modules/mesh-grants/internal/grants/accounts.go index cadbed51..07367198 100644 --- a/modules/mesh-grants/internal/grants/accounts.go +++ b/modules/mesh-grants/internal/grants/accounts.go @@ -12,10 +12,10 @@ import ( ) // **The accounts the mesh wants, said to the vault** (novox/hq ADR 0561 §2, issue 774). The bus composer derives every -// user from placement, the manifests and the wants; for each account the vault mints (a module's, a process's, a +// user from assignments, the manifests and the wants; for each account the vault mints (a module's, a process's, a // machine's provisioner client's), the part writes one key of its state `accounts`, keyed by the bus user, with the // fields the controller's credential carries beside the password. A key is written only when it differs, and deleted -// only when placement no longer names its user and every input of the pass was read: a refused read never retires an +// only when assignments no longer names its user and every input of the pass was read: a refused read never retires an // account. The vault alone reads the state (buscompose.NamedReaders); it mints each user it holds no value for. // StateAccounts is the part's state of wanted accounts. @@ -119,7 +119,7 @@ func (p *Part) Accounts(user string) (any, error) { } if !found { if user != "" { - return nil, fmt.Errorf("no account %s is wanted: placement composes no module, process or provisioner client of that user", user) + return nil, fmt.Errorf("no account %s is wanted: assignments composes no module, process or provisioner client of that user", user) } continue } @@ -149,7 +149,7 @@ type AccountsWritten struct { Wanted int `json:"wanted"` Written []string `json:"written,omitempty"` Deleted []string `json:"deleted,omitempty"` - // Kept are keys placement no longer names that this pass did not delete, with why. + // Kept are keys assignments no longer names that this pass did not delete, with why. Kept []string `json:"kept,omitempty"` } @@ -198,7 +198,7 @@ func (p *Part) writeAccounts(res *Result, in Inputs, recs buscompose.Records) { } switch { case placed(user): - done.Kept = append(done.Kept, user+": placement still names it (composed with no account this pass)") + done.Kept = append(done.Kept, user+": assignments still names it (composed with no account this pass)") continue case len(failed) > 0: done.Kept = append(done.Kept, user+": a read of this pass failed, so no account is retired") diff --git a/modules/mesh-grants/internal/grants/firstviews.go b/modules/mesh-grants/internal/grants/firstviews.go index b9de04e3..2871c7f5 100644 --- a/modules/mesh-grants/internal/grants/firstviews.go +++ b/modules/mesh-grants/internal/grants/firstviews.go @@ -21,7 +21,7 @@ import ( ) // **The grants part's first views** (novox/hq ADR 0615 §1, §4 to §6, ADR 0621, ADR 0622; design 25 § 12.2; DLV-F4r, -// DLV-R1). The part reads the assigner's placement and wants and the indexer's manifests, modules and verdicts no longer +// DLV-R1). The part reads the assigner's assignments and wants and the indexer's manifests, modules and verdicts no longer // from their buckets, but by each owner's first-view tool, asked once at its start, then from the owner's views family // alone (each event one changed key with its revision), asking once more only on a gap in revisions, never on a timer. // The verdicts alone are followed on the seat mesh-catalog's manifest-accepted and manifest-refused, which carry the revision @@ -48,10 +48,10 @@ const ( IndexerModule = "mesh-delivery-indexer" ) -// FirstViews are the five (ADR 0615 §3 as ADR 0622 §1 amends it: the grants part's placement, wants, manifests, +// FirstViews are the five (ADR 0615 §3 as ADR 0622 §1 amends it: the grants part's assignments, wants, manifests, // modules and verdicts). var FirstViews = []FirstView{ - {Owner: AssignerModule, Tool: AssignerModule + ".placement", State: "placement", Read: ReadPlacement}, + {Owner: AssignerModule, Tool: AssignerModule + ".assignments", State: "assignments", Read: ReadAssignments}, {Owner: AssignerModule, Tool: AssignerModule + ".wants", State: "wants", Read: ReadWants}, {Owner: IndexerModule, Tool: IndexerModule + ".manifests", State: "manifests", Read: ReadManifests}, {Owner: IndexerModule, Tool: IndexerModule + ".modules", State: "modules", Read: ReadModules}, @@ -244,7 +244,7 @@ func (v *Views) Statuses() []views.Status { return out } -// Again asks the first view of one state (its state name, `placement`, or its tool) once more, or every one when state is +// Again asks the first view of one state (its state name, `assignments`, or its tool) once more, or every one when state is // empty, each on a goroutine of its own; it answers which it asked, or refuses a state that is none of the five. func (v *Views) Again(ctx context.Context, state, why string) ([]string, error) { var asked []string @@ -257,7 +257,7 @@ func (v *Views) Again(ctx context.Context, state, why string) ([]string, error) asked = append(asked, fv.Tool) } if len(asked) == 0 { - return nil, fmt.Errorf("%q is no first view of the grants part: one of placement, wants, manifests, modules, verdicts, or none for all", state) + return nil, fmt.Errorf("%q is no first view of the grants part: one of assignments, wants, manifests, modules, verdicts, or none for all", state) } return asked, nil } @@ -459,7 +459,7 @@ func (p *Part) AskFirstViewAgain(caller, state, why string, record func(what, wh } } if which == "" { - return nil, fmt.Errorf("%q is no first view of the grants part: one of placement, wants, manifests, modules, verdicts, or none for all", state) + return nil, fmt.Errorf("%q is no first view of the grants part: one of assignments, wants, manifests, modules, verdicts, or none for all", state) } } if strings.TrimSpace(why) == "" { diff --git a/modules/mesh-grants/internal/grants/follow.go b/modules/mesh-grants/internal/grants/follow.go index 40a71a0a..1344e273 100644 --- a/modules/mesh-grants/internal/grants/follow.go +++ b/modules/mesh-grants/internal/grants/follow.go @@ -15,13 +15,13 @@ type Watcher interface { Watch(state string, changed func(key string, value []byte), ended func(error)) error } -// Followed are the states whose change composes again: placement, the indexer's manifests by module, the resolved +// Followed are the states whose change composes again: assignments, the indexer's manifests by module, the resolved // config values, the node agents' haves (a machine's first have, and the bus node's loaded list) and the assigner's // wants (the manifest each machine is wanted to run), and the vault's hashes of the bus accounts. // // And the seat mesh-enrolment's keys (ADR 0587 §4, §5): a key at live composes enrol., and its end, by the holder or by // the bus's expiry, is a change of the key that takes the user out; no event is needed beside the watch. -var Followed = []string{ReadModules, ReadVerdicts, ReadResolved, ReadHave, ReadWants, ReadAccounts, ReadPlacement, ReadEnrolments, +var Followed = []string{ReadModules, ReadVerdicts, ReadResolved, ReadHave, ReadWants, ReadAccounts, ReadAssignments, ReadEnrolments, ReadNodeLogins} // Follower composes again on every change it hears, never on a timer. It never stops for a refusal: a watch it cannot diff --git a/modules/mesh-grants/internal/grants/grants.go b/modules/mesh-grants/internal/grants/grants.go index ff296bf1..3390df0a 100644 --- a/modules/mesh-grants/internal/grants/grants.go +++ b/modules/mesh-grants/internal/grants/grants.go @@ -34,7 +34,7 @@ type GrantsWritten struct { Deleted []string `json:"deleted,omitempty"` // Kept says why a pass wrote and deleted nothing, or left a module out. Kept []string `json:"kept,omitempty"` - // Missing is each module the placement or an assignment puts on a machine that has no grant after the pass, + // Missing is each module the assignments or an assignment puts on a machine that has no grant after the pass, // `: `: the assigner's sixth rule wants none of it (MissingGrants). Never passed over in silence: once // the part's grants are the ones read, it is the condition mesh-grants.grant-missing. Missing []string `json:"missing,omitempty"` diff --git a/modules/mesh-grants/internal/grants/inputs.go b/modules/mesh-grants/internal/grants/inputs.go index b1ac5c87..e8a7b85d 100644 --- a/modules/mesh-grants/internal/grants/inputs.go +++ b/modules/mesh-grants/internal/grants/inputs.go @@ -19,15 +19,15 @@ import ( "mesh-grants/internal/buscompose" + "git.novox.be/novox/mesh-contracts/go/clients/meshmoduleassignerclient" "git.novox.be/novox/mesh-contracts/go/clients/nodeengineclient" + "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-contracts/go/seats/nodeengine" "git.novox.be/novox/mesh-contracts/identity-and-access/go/clients/meshvaultclient" "git.novox.be/novox/mesh-contracts/identity-and-access/go/seats/meshnodeenroller" "git.novox.be/novox/mesh-contracts/identity-and-access/go/seats/meshvault" - "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient" - "git.novox.be/novox/mesh-contracts/placement/go/clients/meshwantsclient" - "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" + "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshwantsclient" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds" "git.novox.be/novox/mesh-networking/modules/mesh-wireguard/go/overlay" @@ -47,9 +47,9 @@ const ( ReadBuilds = "mesh-delivery-builder.builds" // ReadAccounts is the vault's bcrypt hashes of the bus accounts it holds, by bus user (ADR 0547 clause 9). ReadAccounts = "mesh-vault.bus-accounts" - // ReadPlacement is the seat mesh-placement's placement (ADR 0567 §1, §9): each node's modules and the seats + // ReadAssignments is the seat mesh-module-assigner's assignments (ADR 0567 §1, §9): each node's modules and the seats // they hold. - ReadPlacement = buscompose.PlacementRead + ReadAssignments = buscompose.AssignmentsRead // ReadEnrolments is the seat mesh-enrolment's open enrolments, read as a named reader (ADR 0587 §2, §4): a key at // live or redeeming composes the user enrol.. ReadEnrolments = buscompose.EnrolmentsRead @@ -58,13 +58,13 @@ const ( ReadNodeLogins = buscompose.NodeLoginsRead ) -// Reads is every state the part reads, in the manifest's order. Not the controller's assignments: the seat's placement +// Reads is every state the part reads, in the manifest's order. Not the controller's assignments: the seat's assignments // alone places modules (novox/hq ADR 0549 §5, ADR 0571 §10; issue 774). var Reads = []string{ReadResolved, ReadHave, ReadBuilds, ReadAccounts, ReadEnrolments, ReadNodeLogins} // ViewReads are the owners' states the part reads by their first views and views families, never their buckets // (firstviews.go, ADR 0615): each read in Reads' place. -var ViewReads = []string{ReadModules, ReadManifests, ReadVerdicts, ReadWants, ReadPlacement} +var ViewReads = []string{ReadModules, ReadManifests, ReadVerdicts, ReadWants, ReadAssignments} // The bus module's tools the part calls, as its manifest's invokes name them: read-only, each. const ( @@ -128,13 +128,13 @@ type resolved struct { // Inputs is what the shadow composes from, read from the bus. type Inputs struct { - // Nodes is every machine seen: in a node agent's have or the placement. + // Nodes is every machine seen: in a node agent's have or the assignments. Nodes []string - // Assigned is what the seat's placement puts on each machine, sorted (issue 774: never the controller's assignments). + // Assigned is what the seat's assignments puts on each machine, sorted (issue 774: never the controller's assignments). Assigned map[string][]string - // Placed is each node's placement as the seat mesh-placement holds it (ADR 0567 §1); empty before any is read. - // Where it is, a seat held once for the mesh is held where placement says, not wherever a module claiming it runs. - Placed map[string]meshmoduleassigner.Placement + // Placed is each node's assignments as the seat mesh-module-assigner holds it (ADR 0567 §1); empty before any is read. + // Where it is, a seat held once for the mesh is held where assignments says, not wherever a module claiming it runs. + Placed map[string]meshmoduleassigner.Assignments // Manifests is each module's manifest at the indexer's trunk, by module. Manifests map[string]catalogue.Manifest // Bound is, by module, the streams of each bus-state entry the bus module said it created and has not dropped, from @@ -318,33 +318,33 @@ func ReadInputs(r Reader, cache *Manifests) Inputs { NoFingerprint: map[string]string{}, Running: map[string]catalogue.Manifest{}, Values: map[string]any{}, Gaps: append([]Gap(nil), standingGaps...)} nodes := map[string]bool{} - // **The seat's placement alone places modules** (novox/hq ADR 0549 §5, ADR 0567 §9; issue 774): the controller's + // **The seat's assignments alone places modules** (novox/hq ADR 0549 §5, ADR 0567 §9; issue 774): the controller's // assignments are read no more, and the seats held once for the mesh are held where it says. Not read is a failed // read: nothing is placed, so the list composed past it is not sealed. - if keys, err := r.Keys(ReadPlacement); err != nil { - in.Gaps = append(in.Gaps, Gap{Input: ReadPlacement, Why: "refused or absent: nothing is placed (" + err.Error() + ")", Failed: true}) + if keys, err := r.Keys(ReadAssignments); err != nil { + in.Gaps = append(in.Gaps, Gap{Input: ReadAssignments, Why: "refused or absent: nothing is placed (" + err.Error() + ")", Failed: true}) } else { - // Each node's keys in either form, assembled by the seat's one reader (meshmoduleassignerclient.PlacementsOf; research 072 - // row 37): its node key and a key per module, or its legacy key `placement.`. + // Each node's keys in either form, assembled by the seat's one reader (meshmoduleassignerclient.AssignmentsOf; research 072 + // row 37): its node key and a key per module, or its legacy key `assignments.`. values := map[string][]byte{} for _, k := range keys { - raw, found, err := r.Get(ReadPlacement, k) + raw, found, err := r.Get(ReadAssignments, k) if err != nil || !found { continue } if _, _, _, err := meshmoduleassignerclient.ParseKey(k); err != nil { - in.Gaps = append(in.Gaps, Gap{Input: ReadPlacement + " " + k, Why: "not a placement key, so it places nothing"}) + in.Gaps = append(in.Gaps, Gap{Input: ReadAssignments + " " + k, Why: "not an assignments key, so it places nothing"}) continue } values[k] = raw } - placed, err := meshmoduleassignerclient.PlacementsOf(values) + placed, err := meshmoduleassignerclient.AssignmentsOf(values) if err != nil { - in.Gaps = append(in.Gaps, Gap{Input: ReadPlacement, Why: "a key does not read, so its node is composed from what does: " + err.Error()}) + in.Gaps = append(in.Gaps, Gap{Input: ReadAssignments, Why: "a key does not read, so its node is composed from what does: " + err.Error()}) } for node, p := range placed { if in.Placed == nil { - in.Placed = map[string]meshmoduleassigner.Placement{} + in.Placed = map[string]meshmoduleassigner.Assignments{} } in.Placed[node] = p } @@ -372,7 +372,7 @@ func ReadInputs(r Reader, cache *Manifests) Inputs { nodes[node] = true } } - in.Gaps = append(in.Gaps, Gap{Input: "machines", Why: "read from the node agents' haves and the placement: a machine with neither is not seen"}) + in.Gaps = append(in.Gaps, Gap{Input: "machines", Why: "read from the node agents' haves and the assignments: a machine with neither is not seen"}) for n := range nodes { in.Nodes = append(in.Nodes, n) } diff --git a/modules/mesh-grants/internal/grants/memberships.go b/modules/mesh-grants/internal/grants/memberships.go index eb7cbbf3..55946fc1 100644 --- a/modules/mesh-grants/internal/grants/memberships.go +++ b/modules/mesh-grants/internal/grants/memberships.go @@ -40,7 +40,7 @@ type Membership struct { // Memberships composes every assignment's membership from the records (buscompose.MembershipFor, as the controller's // parked composition pass, mesh-controller 003eee8a, issued them), by `.`. Pure. func Memberships(r buscompose.Records) map[string]Membership { - where := buscompose.PlacementsOf(r, r.Interchangeable) + where := buscompose.AssignmentsOf(r, r.Interchangeable) out := map[string]Membership{} for node, declared := range r.Assigned { for _, d := range declared { diff --git a/modules/mesh-grants/internal/grants/records.go b/modules/mesh-grants/internal/grants/records.go index 64976b51..2d5667e6 100644 --- a/modules/mesh-grants/internal/grants/records.go +++ b/modules/mesh-grants/internal/grants/records.go @@ -7,7 +7,7 @@ import ( "slices" "strings" - "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner" + "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner" "git.novox.be/novox/mesh-sdk/go/catalogue" "mesh-grants/internal/buscompose" ) @@ -408,14 +408,14 @@ func hearsOf(m catalogue.Manifest, declared map[string]catalogue.Manifest, node return out, touches, onEvents } -// heldWherePlaced is a module's holds on a node as the catalogue's placement says them (ADR 0549 §1, §3). A placement's +// heldWherePlaced is a module's holds on a node as the catalogue's assignments says them (ADR 0549 §1, §3). The assignments' // holds are the holders on record, as catalogue.World's Holdings are: a seat held once for the mesh that some node's -// placement holds is held only where a placement names it (this node's naming this module), so a module claiming it on -// two machines holds it on the one placement names, not on both. A seat no placement holds is held by derivation, by the -// module claiming it wherever it is placed, as it was before placement: the seeded placements hold only the seats the +// assignments holds is held only where assignments names it (this node's naming this module), so a module claiming it on +// two machines holds it on the one assignments names, not on both. A seat no assignments holds is held by derivation, by the +// module claiming it wherever it is placed, as it was before assignments: the seeded assignments hold only the seats the // controller had on record (twelve on the control-node), and treating their holds as every holder left nine seats with // no holder in the user list of 2026-10-27 09:21 (issue 1945). A seat of every node is held wherever the module runs. -func heldWherePlaced(holds []buscompose.Seat, placed map[string]meshmoduleassigner.Placement, node, module string) []buscompose.Seat { +func heldWherePlaced(holds []buscompose.Seat, placed map[string]meshmoduleassigner.Assignments, node, module string) []buscompose.Seat { same := func(x meshmoduleassigner.Holding, h buscompose.Seat) bool { return (x.Scope == "" || x.Scope == catalogue.ScopeMesh) && sameSeat(x.Claim, h.Name) && x.Kind == h.Kind } diff --git a/modules/mesh-grants/internal/grants/userlist.go b/modules/mesh-grants/internal/grants/userlist.go index 6ddd2ce7..7c2bd8a9 100644 --- a/modules/mesh-grants/internal/grants/userlist.go +++ b/modules/mesh-grants/internal/grants/userlist.go @@ -239,7 +239,7 @@ type Comparison struct { Shadow int `json:"shadow"` Loaded int `json:"loaded"` // Added are users in the shadow list the loaded list lacks (one left out for want of a credential among them); - // Dropped are loaded users the shadow list lacks; Gone are those of Dropped whose machine or module placement no + // Dropped are loaded users the shadow list lacks; Gone are those of Dropped whose machine or module assignments no // longer names, which both lists agree are gone. Added []string `json:"added"` Dropped []string `json:"dropped"` @@ -277,7 +277,7 @@ func (c Comparison) Differing() []string { func (d UserDiff) responsesLost() bool { return strings.HasPrefix(d.Responses, "shadow false") } // Removing names every user that keeps the lists apart other than by a right the shadow list adds, each with how: a user -// the shadow list adds, a loaded user it drops that placement still names, a publish or subscribe right a loaded user +// the shadow list adds, a loaded user it drops that assignments still names, a publish or subscribe right a loaded user // would lose, or an allowance of responses lost (ADR 0562 §2). Empty when the lists agree or differ only by added rights. func (c Comparison) Removing() []string { var out []string @@ -286,7 +286,7 @@ func (c Comparison) Removing() []string { } for _, u := range c.Dropped { if !slices.Contains(c.Gone, u) { - out = append(out, u+" (dropped, and placement still names it)") + out = append(out, u+" (dropped, and assignments still names it)") } } for _, d := range c.Differ { @@ -331,7 +331,7 @@ func (c Comparison) AddedRights() []UserDiff { return out } -// Compare compares the shadow list with the loaded one. placed says whether placement still names a loaded user's +// Compare compares the shadow list with the loaded one. placed says whether assignments still names a loaded user's // machine and module; nil names every one. Pure. func Compare(shadow, loaded []User, placed func(user string) bool) Comparison { c := Comparison{Shadow: len(shadow), Loaded: len(loaded), Added: []string{}, Dropped: []string{}, Differ: []UserDiff{}} @@ -386,7 +386,7 @@ func minus(a, b []string) []string { return out } -// Placed says whether placement still names a user's machine and module: a machine's users (`node.`, +// Placed says whether assignments still names a user's machine and module: a machine's users (`node.`, // `provisioner.`) while the machine is seen, a module's or process's (`.[.]`) while the module is // assigned there. The controller, enrolment users and persons are never judged gone from here. func Placed(in Inputs) func(user string) bool { @@ -447,7 +447,7 @@ func (c Comparison) Excusing(f func(user, subject string, added, subscribe bool) var seatSubject = regexp.MustCompile(`^mesh\.seat\.([^.]+)\.(?:call|tool)\.([^.]+)(?:\.[^.]+)?$`) var seatService = regexp.MustCompile(`^\$SRV\.(?:INFO|PING|STATS)\.([^.]+)(?:\.>)?$`) -// ExcusedBy is the excuse of a comparison until placement lives in the catalogue (the operator, 2026-10-26): a verb the +// ExcusedBy is the excuse of a comparison until assignments lives in the catalogue (the operator, 2026-10-26): a verb the // seat catalogue retired on purpose, which the shadow no longer grants, is no difference; and what the shadow grants more // for a mesh seat that is claimed on several machines (the standing gap seat holdings: it composes the seat as held on each) // is named and not counted, for a user on a machine that claims the seat and only as a subscription (what it serves), since diff --git a/modules/mesh-grants/module.json b/modules/mesh-grants/module.json index ea55574d..e2684b89 100644 --- a/modules/mesh-grants/module.json +++ b/modules/mesh-grants/module.json @@ -17,7 +17,7 @@ "mesh-broker.created", "mesh-broker.dropped", "mesh-broker.recreated", - "mesh-delivery-assigner.views.placement", + "mesh-delivery-assigner.views.assignments", "mesh-delivery-assigner.views.wants", "mesh-delivery-indexer.views.manifests", "mesh-delivery-indexer.views.modules", @@ -34,7 +34,7 @@ "nats.nats_user_can", "nats.nats_users", "seat:node-engine.root-free", - "mesh-delivery-assigner.placement", + "mesh-delivery-assigner.assignments", "mesh-delivery-assigner.wants", "mesh-delivery-indexer.manifests", "mesh-delivery-indexer.modules", diff --git a/modules/mesh-grants/testdata/placement-novox-0c61bad.json b/modules/mesh-grants/testdata/assignments-novox-0c61bad.json similarity index 100% rename from modules/mesh-grants/testdata/placement-novox-0c61bad.json rename to modules/mesh-grants/testdata/assignments-novox-0c61bad.json diff --git a/modules/mesh-grants/testdata/takeover-compare-2026-10-27.json b/modules/mesh-grants/testdata/takeover-compare-2026-10-27.json index 7bb73878..31959904 100644 --- a/modules/mesh-grants/testdata/takeover-compare-2026-10-27.json +++ b/modules/mesh-grants/testdata/takeover-compare-2026-10-27.json @@ -47,8 +47,8 @@ "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-indexer_candidates", "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-indexer_manifests", "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-indexer_modules", - "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-indexer_placement", - "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-indexer_placement-candidates", + "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-indexer_assignments", + "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-indexer_assignments-candidates", "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-teller_answers", "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-watchman_presence", "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_node-engine_have" @@ -118,36 +118,36 @@ "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-builder_builds.>", "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_manifests.>", "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_modules.>", - "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_placement-candidates.>", - "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_placement.>", + "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_assignments-candidates.>", + "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_assignments.>", "$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.*.node", "$JS.API.CONSUMER.DELETE.KV_mesh-config_resolved.>", "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-builder_builds.>", "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_manifests.>", "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_modules.>", - "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_placement-candidates.>", - "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_placement.>", + "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_assignments-candidates.>", + "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_assignments.>", "$JS.API.DIRECT.GET.KV_mesh-config_resolved.$KV.mesh-config_resolved.ace.>", "$JS.API.DIRECT.GET.KV_mesh-config_resolved.$KV.mesh-config_resolved.novox.keycloak.issuer", "$JS.API.DIRECT.GET.KV_mesh-config_resolved.$KV.mesh-config_resolved.novox.mailu.domain", "$JS.API.DIRECT.GET.KV_mesh-delivery-builder_builds.>", "$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_manifests.>", "$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_modules.>", - "$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_placement-candidates.>", - "$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_placement.>", + "$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_assignments-candidates.>", + "$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_assignments.>", "$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.*.node", "$JS.API.STREAM.INFO.KV_mesh-config_resolved", "$JS.API.STREAM.INFO.KV_mesh-delivery-builder_builds", "$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_manifests", "$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_modules", - "$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_placement", - "$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_placement-candidates", + "$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_assignments", + "$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_assignments-candidates", "$JS.FC.KV_mesh-config_resolved.>", "$JS.FC.KV_mesh-delivery-builder_builds.>", "$JS.FC.KV_mesh-delivery-indexer_manifests.>", "$JS.FC.KV_mesh-delivery-indexer_modules.>", - "$JS.FC.KV_mesh-delivery-indexer_placement-candidates.>", - "$JS.FC.KV_mesh-delivery-indexer_placement.>", + "$JS.FC.KV_mesh-delivery-indexer_assignments-candidates.>", + "$JS.FC.KV_mesh-delivery-indexer_assignments.>", "mesh.seat.mesh-vault.call.own.node~ace" ], "subscribe_added": [ @@ -165,15 +165,15 @@ "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-builder_builds.>", "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_manifests.>", "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_modules.>", - "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_placement-candidates.>", - "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_placement.>", + "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_assignments-candidates.>", + "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_assignments.>", "$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.*.node", "$JS.API.CONSUMER.DELETE.KV_mesh-config_resolved.>", "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-builder_builds.>", "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_manifests.>", "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_modules.>", - "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_placement-candidates.>", - "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_placement.>", + "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_assignments-candidates.>", + "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_assignments.>", "$JS.API.DIRECT.GET.KV_mesh-config_resolved.$KV.mesh-config_resolved.ace.influxdb.bucket", "$JS.API.DIRECT.GET.KV_mesh-config_resolved.$KV.mesh-config_resolved.ace.influxdb.org", "$JS.API.DIRECT.GET.KV_mesh-config_resolved.$KV.mesh-config_resolved.g14.>", @@ -182,21 +182,21 @@ "$JS.API.DIRECT.GET.KV_mesh-delivery-builder_builds.>", "$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_manifests.>", "$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_modules.>", - "$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_placement-candidates.>", - "$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_placement.>", + "$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_assignments-candidates.>", + "$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_assignments.>", "$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.*.node", "$JS.API.STREAM.INFO.KV_mesh-config_resolved", "$JS.API.STREAM.INFO.KV_mesh-delivery-builder_builds", "$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_manifests", "$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_modules", - "$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_placement", - "$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_placement-candidates", + "$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_assignments", + "$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_assignments-candidates", "$JS.FC.KV_mesh-config_resolved.>", "$JS.FC.KV_mesh-delivery-builder_builds.>", "$JS.FC.KV_mesh-delivery-indexer_manifests.>", "$JS.FC.KV_mesh-delivery-indexer_modules.>", - "$JS.FC.KV_mesh-delivery-indexer_placement-candidates.>", - "$JS.FC.KV_mesh-delivery-indexer_placement.>", + "$JS.FC.KV_mesh-delivery-indexer_assignments-candidates.>", + "$JS.FC.KV_mesh-delivery-indexer_assignments.>", "mesh.seat.mesh-vault.call.own.node~g14" ], "subscribe_added": [ @@ -212,36 +212,36 @@ "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-builder_builds.>", "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_manifests.>", "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_modules.>", - "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_placement-candidates.>", - "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_placement.>", + "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_assignments-candidates.>", + "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_assignments.>", "$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.*.node", "$JS.API.CONSUMER.DELETE.KV_mesh-config_resolved.>", "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-builder_builds.>", "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_manifests.>", "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_modules.>", - "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_placement-candidates.>", - "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_placement.>", + "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_assignments-candidates.>", + "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_assignments.>", "$JS.API.DIRECT.GET.KV_mesh-config_resolved.$KV.mesh-config_resolved.ace.influxdb.bucket", "$JS.API.DIRECT.GET.KV_mesh-config_resolved.$KV.mesh-config_resolved.ace.influxdb.org", "$JS.API.DIRECT.GET.KV_mesh-config_resolved.$KV.mesh-config_resolved.novox.>", "$JS.API.DIRECT.GET.KV_mesh-delivery-builder_builds.>", "$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_manifests.>", "$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_modules.>", - "$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_placement-candidates.>", - "$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_placement.>", + "$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_assignments-candidates.>", + "$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_assignments.>", "$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.*.node", "$JS.API.STREAM.INFO.KV_mesh-config_resolved", "$JS.API.STREAM.INFO.KV_mesh-delivery-builder_builds", "$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_manifests", "$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_modules", - "$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_placement", - "$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_placement-candidates", + "$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_assignments", + "$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_assignments-candidates", "$JS.FC.KV_mesh-config_resolved.>", "$JS.FC.KV_mesh-delivery-builder_builds.>", "$JS.FC.KV_mesh-delivery-indexer_manifests.>", "$JS.FC.KV_mesh-delivery-indexer_modules.>", - "$JS.FC.KV_mesh-delivery-indexer_placement-candidates.>", - "$JS.FC.KV_mesh-delivery-indexer_placement.>", + "$JS.FC.KV_mesh-delivery-indexer_assignments-candidates.>", + "$JS.FC.KV_mesh-delivery-indexer_assignments.>", "mesh.seat.mesh-vault.call.own.node~novox" ], "subscribe_added": [ @@ -259,15 +259,15 @@ "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-builder_builds.>", "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_manifests.>", "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_modules.>", - "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_placement-candidates.>", - "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_placement.>", + "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_assignments-candidates.>", + "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_assignments.>", "$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.*.node", "$JS.API.CONSUMER.DELETE.KV_mesh-config_resolved.>", "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-builder_builds.>", "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_manifests.>", "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_modules.>", - "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_placement-candidates.>", - "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_placement.>", + "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_assignments-candidates.>", + "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_assignments.>", "$JS.API.DIRECT.GET.KV_mesh-config_resolved.$KV.mesh-config_resolved.ace.influxdb.bucket", "$JS.API.DIRECT.GET.KV_mesh-config_resolved.$KV.mesh-config_resolved.ace.influxdb.org", "$JS.API.DIRECT.GET.KV_mesh-config_resolved.$KV.mesh-config_resolved.novox.keycloak.issuer", @@ -276,21 +276,21 @@ "$JS.API.DIRECT.GET.KV_mesh-delivery-builder_builds.>", "$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_manifests.>", "$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_modules.>", - "$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_placement-candidates.>", - "$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_placement.>", + "$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_assignments-candidates.>", + "$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_assignments.>", "$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.*.node", "$JS.API.STREAM.INFO.KV_mesh-config_resolved", "$JS.API.STREAM.INFO.KV_mesh-delivery-builder_builds", "$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_manifests", "$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_modules", - "$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_placement", - "$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_placement-candidates", + "$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_assignments", + "$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_assignments-candidates", "$JS.FC.KV_mesh-config_resolved.>", "$JS.FC.KV_mesh-delivery-builder_builds.>", "$JS.FC.KV_mesh-delivery-indexer_manifests.>", "$JS.FC.KV_mesh-delivery-indexer_modules.>", - "$JS.FC.KV_mesh-delivery-indexer_placement-candidates.>", - "$JS.FC.KV_mesh-delivery-indexer_placement.>", + "$JS.FC.KV_mesh-delivery-indexer_assignments-candidates.>", + "$JS.FC.KV_mesh-delivery-indexer_assignments.>", "mesh.seat.mesh-vault.call.own.node~shanks" ], "subscribe_added": [ @@ -335,7 +335,7 @@ "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-controller_assignments", "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-controller_declared-config", "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-controller_predefined", - "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-indexer_placement" + "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-indexer_assignments" ], "user": "novox.mesh-config" }, @@ -384,8 +384,8 @@ "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-indexer_candidates", "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-indexer_manifests", "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-indexer_modules", - "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-indexer_placement", - "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-indexer_placement-candidates", + "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-indexer_assignments", + "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-indexer_assignments-candidates", "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-indexer_takeins", "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-indexer_taken" ], @@ -423,7 +423,7 @@ "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-builder_builds", "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-indexer_manifests", "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-indexer_modules", - "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-indexer_placement", + "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-indexer_assignments", "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-grants_assertions", "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-grants_memberships", "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-grants_takeover", @@ -578,8 +578,8 @@ "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-gate_source", "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-indexer_candidates", "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-indexer_modules", - "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-indexer_placement", - "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-indexer_placement-candidates", + "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-indexer_assignments", + "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-indexer_assignments-candidates", "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-indexer_taken", "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-teller_answers", "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-view_answers", diff --git a/sdk/composer/composer.go b/sdk/composer/composer.go index 1b534365..685fa51d 100644 --- a/sdk/composer/composer.go +++ b/sdk/composer/composer.go @@ -71,20 +71,20 @@ type Request struct { // Case is one catalogue composed: every module's manifest by name, and per node the manifests kept at the build it runs. // -// Placements are, by node, the placement the case composes that node with in place of what Input says of it: a change's -// own placement files, so a check composes a catalogue pull request's placement (issue 1126, frombus.Placed); a null -// placement leaves nothing placed on the node. A case without them composes Input's. +// Assignments are, by node, the assignments the case composes that node with in place of what Input says of it: a change's +// own assignments files, so a check composes a catalogue pull request's assignments (issue 1126, frombus.Placed); a null +// assignments leaves nothing placed on the node. A case without them composes Input's. // // Verdicts are, by module, as raw JSON (this package is seat-neutral, novox/hq ADR 0635 §4; the node-engine's composer decodes the -// mesh-catalog seat's manifest verdict, and Placements the mesh-module-assigner seat's placement), the indexer's judge's verdict on its manifest in Manifests (novox/hq ADR 0578 §5), put in by +// mesh-catalog seat's manifest verdict, and Assignments the mesh-module-assigner seat's assignments), the indexer's judge's verdict on its manifest in Manifests (novox/hq ADR 0578 §5), put in by // the assigner or the checker that runs the composer: a refused one is held, composed as its stub so what it offers // still shapes the rest, and named in the case's verdict. A module without one is accepted (the mesh-catalog seat's ManifestVerdict, taken by its readers). type Case struct { - Name string `json:"name"` - Manifests map[string]Manifest `json:"manifests"` - Verdicts map[string]json.RawMessage `json:"verdicts,omitempty"` - Kept map[string]map[string]Manifest `json:"kept,omitempty"` - Placements map[string]json.RawMessage `json:"placements,omitempty"` + Name string `json:"name"` + Manifests map[string]Manifest `json:"manifests"` + Verdicts map[string]json.RawMessage `json:"verdicts,omitempty"` + Kept map[string]map[string]Manifest `json:"kept,omitempty"` + Assignments map[string]json.RawMessage `json:"assignments,omitempty"` } // Manifest is a module's manifest as its repository holds it (Raw, the bytes), or the digest the delivery indexer keeps diff --git a/sdk/window/window.go b/sdk/window/window.go index 9d192d12..c3c41748 100644 --- a/sdk/window/window.go +++ b/sdk/window/window.go @@ -21,7 +21,7 @@ type WindowHeld struct { // StepWindow is the one rule every window keeper steps its window by (security-upgrade review F3, the second of a // kind): named is every node the read named, dropped ones included, and holding those of them that hold the window at // now. A read that names no node judges nothing, as a first view's empty answer at revision 0 is no empty state (ADR -// 0615): an empty node_haves or a placement not yet read never closes a window, and the window stays as it was. A read +// 0615): an empty node_haves or assignments not yet read never closes a window, and the window stays as it was. A read // naming nodes, none holding, closes an open window (closed true, once); one naming a holder opens it, each holder's // since kept from w. overdue are the holders whose since is at least findingAfter before now (the keeper's finding, // cleared for every other node); a read naming no node keeps w's holders' overdue as they stand at now. diff --git a/sdk/window/window_test.go b/sdk/window/window_test.go index d585c0e9..937a1672 100644 --- a/sdk/window/window_test.go +++ b/sdk/window/window_test.go @@ -7,7 +7,7 @@ import ( ) // The one rule every window keeper steps by (design 51; security-upgrade review F3): a read naming no node judges -// nothing, so an empty node_haves or an unread placement never closes a window. +// nothing, so an empty node_haves or an unread assignments never closes a window. func TestStepWindow(t *testing.T) { now := time.Date(2026, 11, 4, 17, 0, 0, 0, time.UTC) week := 7 * 24 * time.Hour