diff --git a/go/buscompose/announced.go b/go/buscompose/announced.go index 90c57d8..2353425 100644 --- a/go/buscompose/announced.go +++ b/go/buscompose/announced.go @@ -17,31 +17,6 @@ import ( // StatesAnnouncedBucket is the controller's bucket of the last announcement of each module (announce.KeptBucket). const StatesAnnouncedBucket = announce.KeptBucket -// controllerReadable are the controller's states others may read: the record mirrors (the packages each node declares -// among them, which the pacman module reads, ADR 0507 §5), the chain's buckets, and the grants issued and the -// compositions' digests, which the assigner reads (ADR 0528 clause 2, ADR 0539 clause 6). -var controllerReadable = []string{MirrorAssignments, MirrorDeclaredConfig, MirrorPredefined, ChainBuilds, ChainRollouts, - MirrorDeclaredPackages, ControllerGrants, ControllerComposed} - -// ControllerAnnouncement is what the controller announces it keeps (ADR 0495 clause 2): every bucket of its own by its local -// name, the ones others may read marked readable. It announces itself, as any module does from its code. -func ControllerAnnouncement(node string) announce.States { - s := announce.States{Module: ControllerSeat, Node: node} - readable := map[string]bool{} - for _, n := range controllerReadable { - readable[n] = true - } - for _, bucket := range ControllerBuckets() { - local, mine := strings.CutPrefix(bucket, ControllerSeat+"_") - if !mine { - continue - } - s.States = append(s.States, announce.State{Name: local, Readable: readable[local]}) - } - sort.Slice(s.States, func(i, j int) bool { return s.States[i].Name < s.States[j].Name }) - return s -} - // ReadsAnnouncedBy splits one reader module's `reads` (`.`) by what each owner announced: granted when the // owner announced the state readable (by this reader, where it named its readers: announce.State.Readers), when the // composer names the reader among the state's readers (NamedReaders, ADR 0547 clause 9), or when the owner announced @@ -56,8 +31,7 @@ func ReadsAnnouncedBy(reader string, reads []string, announced map[string]announ case reader != "" && !ReaderMay(reader, r): refused = append(refused, r) case reader != "" && namedReader(reader, r): - // A state the composer names its readers for is theirs whatever its owner announced (ADR 0561 §3: the - // controller's lease, which its announcement marks readable by nobody). + // A state the composer names its readers for is theirs whatever its owner announced. granted = append(granted, r) case reader != "" && ok && said && s.Keeps(name): if s.ReadableBy(name, reader) { diff --git a/go/buscompose/buscompose_test.go b/go/buscompose/buscompose_test.go index af04c90..8f84706 100644 --- a/go/buscompose/buscompose_test.go +++ b/go/buscompose/buscompose_test.go @@ -2,12 +2,10 @@ package buscompose import ( "encoding/json" - "errors" "maps" "os" "path/filepath" "reflect" - "regexp" "slices" "strings" "testing" @@ -16,179 +14,31 @@ import ( "git.novox.be/novox/mesh-sdk/go/announce" "git.novox.be/novox/mesh-sdk/go/catalogue" "git.novox.be/novox/mesh-sdk/go/delivery" - "git.novox.be/novox/mesh-sdk/go/membership" - "git.novox.be/novox/mesh-sdk/go/provisions" "github.com/nats-io/nats.go/jetstream" ) -// Defends issue 1377 and novox/hq ADR 0547 §5: this is the controller's bus composer copied, and it composes byte for -// byte what the controller's composes. testdata/controller-user-lists.json was written by the controller's own -// internal/broker (mesh-controller branch fix/1945-mesh-grants-publishes-memberships, and fix/1790-grants-create-buckets for the grants part creating declared streams), lists and memberships, from fixtures_test.go compiled there unchanged; composed.conf is the -// controller's golden of the whole server configuration. A row that differs is the copy drifting from the controller, -// which is a fault while both compose (until the controller loses its lease, ADR 0547 §13), but for the grants the copy -// gives beyond it on purpose (beyondTheControllers): the copy grants every user everything the controller's grants, and -// only those more. -// createdAdvisory is one subscription to a bucket's creation in a composed list, as stateCreatedHeard composes it. -var createdAdvisory = regexp.MustCompile(`"\$JS\.EVENT\.ADVISORY\.STREAM\.CREATED\.KV_[^"]*", `) +// Issue 1126 B3a (novox/hq ADR 0590): the composed user lists and the server configuration are golden files this +// composer wrote (testdata/user-lists.json, testdata/composed.conf), first from the lists the ended controller composed +// with its user and the lease's grants taken out. A change to what any user is granted shows here as a diff to read; +// MESH_SDK_UPDATE_GOLDEN=1 writes them anew. +var updateGolden = os.Getenv("MESH_SDK_UPDATE_GOLDEN") == "1" -// beyondTheControllers is what the copy grants beyond the controller's on purpose (novox/hq ADR 0547 §2, ADR 0555 §3, -// issue 1126), by user: a node's account reads every node's own facts, `.node` of the have bucket where the node's -// record rides, and what it composes its own modules from (NodeComposeReads). After the take-over (ADR 0547 §8) the -// grants part's list is the one the bus loads; a hand-back loads the controller's, which lacks these reads, and the nodes -// keep applying without composing their own until the next take-over. Each principal's hearing of its buckets' creation -// (issue 1790) is the other row beyond, matched by createdAdvisory. -// -// And the watchman's dead letters and watchdogs (novox/hq ADR 0564 clauses 4, 5, issue 1952 S5): its own consumer on -// DEAD_LETTER_NOTICES, a read of DEAD_LETTERS, and the words its watchdogs hear, the controller's jobs until the lease -// write. -func beyondTheControllers(user string) map[string]bool { - out := map[string]bool{} - if strings.HasSuffix(user, "."+WatchmanModule) { - for _, s := range append(WatchmanDeadLetterGrants(nil), WatchmanHears()...) { - out[s] = true - } - } - node, ok := strings.CutPrefix(user, "node.") - if !ok { - return out - } - for _, s := range append(append(readGrants("node-engine_have", "*.node"), NodeComposeReads(node)...), NodeComposeAdvisories()...) { - out[s] = true - } - return out -} - -// mayBeyondTheControllers is what the copy may grant beyond the controller's where the records allow it, and need not -// where they do not: a node's ask of the vault's `own` while the vault serves it (issue 1126, ADR 0550 §5). -func mayBeyondTheControllers(user string) map[string]bool { - // A runtime's read of its membership from the grants part's state (novox/hq ADR 0571 clause 7): its own key, or the - // tool runner's node's. - if parts := strings.Split(user, "."); len(parts) == 2 && parts[0] != "node" { - key := user - if parts[1] == RuntimeModule { - key = parts[0] + ".*" - } - out := map[string]bool{} - if membership.GrantsBucket != GrantsMembershipsBucket || membership.GrantsKey("a", "b") != MembershipKey("a", "b") { - panic("the membership package's grants bucket does not agree with buscompose's") // FollowMembership reads it - } - for _, s := range readGrants(GrantsMembershipsBucket, key) { - out[s] = true - } - return out - } - if _, ok := strings.CutPrefix(user, "node."); !ok { - return nil - } - return map[string]bool{VaultOwnCall(Principal{Kind: KindNode, Node: strings.TrimPrefix(user, "node.")}): true, - VaultSecretChanged: true} -} - -// engineBeyond is a node's engine holding its node seat and asking the operator in its own name (novox/hq ADR 0563 §1, -// §3), beyond the controller's on purpose: its own node's verbs, events and discovery, the bus's discovery asked, and -// the router's seat traffic under node-engine-. -func engineBeyond(user, g string) bool { - node, ok := strings.CutPrefix(user, "node.") - if !ok { - return false - } - return strings.HasPrefix(g, "mesh.seat."+EngineSeat+".") && strings.Contains(g, "."+node) || - strings.HasPrefix(g, "$SRV.") || strings.Contains(g, ".node-engine-"+node) || - strings.Contains(g, "KV_"+EngineActsBucket) || strings.HasPrefix(g, "$KV."+EngineActsBucket+"."+node+".") || - // And its node's declared packages (novox/hq ADR 0565 clause 6). - strings.Contains(g, "KV_"+EnginePackagesBucket) || g == "$KV."+EnginePackagesBucket+"."+node || - // And, read, the controller's mirror's key of its node, until the lease write (novox/hq ADR 0565 clause 6). - strings.Contains(g, "KV_"+DeclaredPackagesBucket) && !strings.HasPrefix(g, "$KV.") && - (!strings.Contains(g, "$KV.") || strings.HasSuffix(g, "$KV."+DeclaredPackagesBucket+"."+node)) || - // And its own health report (issue 774). - strings.Contains(g, "KV_"+delivery.NodeHealthBucket) || g == "$KV."+delivery.NodeHealthBucket+"."+node || - // And the provisions of its node's modules, read (novox/hq ADR 0570 clause 6). - strings.Contains(g, "KV_"+provisions.ProvisionsBucket) || - // And its node's placed modules' data items, its own node's keys (novox/hq ADR 0577 clause 1). - strings.Contains(g, "KV_"+EngineDataBucket) || strings.HasPrefix(g, "$KV."+EngineDataBucket+"."+node+".") -} - -// createsAnAccountsConsumer is the grants part's create and read of a durable consumer an account needs, by its stream -// and name (novox/hq ADR 0561 §6): beyond the controller's on purpose, since the controller made them alone. -func createsAnAccountsConsumer(user, g string) bool { - return strings.HasSuffix(user, "."+GrantsModule) && - (strings.HasPrefix(g, "$JS.API.CONSUMER.CREATE.") || strings.HasPrefix(g, "$JS.API.CONSUMER.INFO.")) -} - -// quotedToken is a quoted token of a composed list. -var quotedToken = regexp.MustCompile(`"[^"]*"`) - -// grantsByUser splits a composed list into the text outside its users and each user's quoted tokens (its name, its -// password's hash, every subject it may publish or subscribe to), by user. -func grantsByUser(list string) (string, map[string]map[string]bool) { - var outside strings.Builder - users := map[string]map[string]bool{} - user := "" - for _, line := range strings.SplitAfter(list, "\n") { - trimmed := strings.TrimSpace(line) - if name, ok := strings.CutPrefix(trimmed, `{ user: "`); ok { - user, _, _ = strings.Cut(name, `"`) - users[user] = map[string]bool{} - } else if user != "" && trimmed == "]" { - user = "" - } - if user == "" { - outside.WriteString(line) - continue - } - for _, q := range quotedToken.FindAllString(line, -1) { - users[user][strings.Trim(q, `"`)] = true - } - } - return outside.String(), users -} - -// grantsAsTheControllers fails t unless the copy's list is the controller's, user for user, but for the grants it -// names on purpose (beyondTheControllers, the creation advisories): every grant of the controller's is the copy's, and -// the copy's beyond them are exactly those, every one of them there. -func grantsAsTheControllers(t *testing.T, got, want string) { +// golden compares got with the golden file name, or writes it. +func golden(t *testing.T, name string, got []byte) { t.Helper() - gotOutside, gotUsers := grantsByUser(got) - wantOutside, wantUsers := grantsByUser(want) - if gotOutside != wantOutside { - t.Errorf("the list differs from the controller's outside its users:\n%s", gotOutside) + path := filepath.Join("testdata", name) + if updateGolden { + if err := os.WriteFile(path, got, 0o644); err != nil { + t.Fatal(err) + } + return } - for user, was := range wantUsers { - now, ok := gotUsers[user] - if !ok { - t.Errorf("the copy has no user %s", user) - continue - } - intended := beyondTheControllers(user) - for g := range was { - // A runtime's direct get of its membership from the assignments stream went with the stream (research 072 - // row 33): it reads the grants part's state memberships instead (ADR 0571 clause 7). And it no longer hears - // the subject, where the grants part stops publishing at the lease write (ADR 0571 clause 10, issue 774). - node := strings.SplitN(user, ".", 2)[0] - if !now[g] && !strings.HasPrefix(g, "$JS.API.DIRECT.GET."+AssignmentsStream+".mesh.assignment."+node+".") && - !strings.HasPrefix(g, "mesh.assignment."+node+".") { - t.Errorf("%s: the copy does not grant %s, which the controller's does", user, g) - } - } - for g := range now { - if strings.HasPrefix(g, "$JS.API.DIRECT.GET."+AssignmentsStream+".") && !strings.HasSuffix(user, "."+GrantsModule) { - t.Errorf("%s: the copy grants %s, a read of the assignments stream, which goes at the lease write", user, g) - } - if !was[g] && !intended[g] && !mayBeyondTheControllers(user)[g] && !createdAdvisory.MatchString(`"`+g+`", `) && - !createsAnAccountsConsumer(user, g) && !engineBeyond(user, g) { - t.Errorf("%s: the copy grants %s beyond the controller's, and no decision names it", user, g) - } - } - for g := range intended { - if !now[g] { - t.Errorf("%s: the copy does not grant %s, which a node composing its own modules reads", user, g) - } - } + want, err := os.ReadFile(path) + if err != nil { + t.Fatal(err) } - for user := range gotUsers { - if _, ok := wantUsers[user]; !ok { - t.Errorf("the copy has a user %s the controller's has not", user) - } + if string(got) != string(want) { + t.Errorf("%s differs from what is composed now; read the diff and, if it is meant, write it with MESH_SDK_UPDATE_GOLDEN=1", path) } } @@ -209,63 +59,33 @@ func TestTheCopiedComposerComposesAsTheControllers(t *testing.T) { got[1] != "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_candidates" { t.Errorf("a node hears the creation of %v", got) } - raw, err := os.ReadFile(filepath.Join("testdata", "controller-user-lists.json")) - if err != nil { - t.Fatal(err) - } - var want []struct { + type list struct { Name, List string Missing []string Err string Memberships json.RawMessage } - if err := json.Unmarshal(raw, &want); err != nil { + var lists []list + for _, f := range UserListFixtures() { + l, missing, err := ComposedFor(f) + row := list{Name: f.Name, List: l, Missing: missing} + if err != nil { + row.Err = err.Error() + } + if row.Memberships, err = json.Marshal(MembershipsFor(f)); err != nil { + t.Fatal(err) + } + // B3a/AC1: no list holds a user for the ended controller. + if strings.Contains(l, `user: "controller"`) { + t.Errorf("%s: the list holds the controller's user", f.Name) + } + lists = append(lists, row) + } + raw, err := json.MarshalIndent(lists, "", " ") + if err != nil { t.Fatal(err) } - fixtures := UserListFixtures() - if len(want) != len(fixtures) { - t.Fatalf("%d fixtures, %d lists the controller composed", len(fixtures), len(want)) - } - for i, f := range fixtures { - t.Run(f.Name, func(t *testing.T) { - if want[i].Name != f.Name { - t.Fatalf("fixture %q against the controller's %q", f.Name, want[i].Name) - } - list, missing, err := ComposedFor(f) - got := "" - if err != nil { - got = err.Error() - } - if got != want[i].Err { - t.Fatalf("error %q, the controller's %q", got, want[i].Err) - } - if strings.Join(missing, ",") != strings.Join(want[i].Missing, ",") { - t.Errorf("left out %v, the controller %v", missing, want[i].Missing) - } - // The one row the copy composes beyond the controller's, on purpose (novox/hq issue 1790, 2026-10-26, after - // the take-over of ADR 0547 milestone 2): each principal hears its own and read buckets created, so a state - // missing at its start is bound when it appears. The controller's composer is frozen and its list no longer - // loaded; while it is not, this row is left out of the comparison and nothing else. - grantsAsTheControllers(t, list, want[i].List) - // And every membership the records issue, as the controller's MembershipFor makes it (ADR 0547 §5): the - // grants part publishes them. - raw, err := json.Marshal(MembershipsFor(f)) - if err != nil { - t.Fatal(err) - } - var gotM, wasM any - if err := json.Unmarshal(raw, &gotM); err != nil { - t.Fatal(err) - } - if err := json.Unmarshal(want[i].Memberships, &wasM); err != nil { - t.Fatal(err) - } - heldEventsBeyond(gotM, wasM) - if !reflect.DeepEqual(gotM, wasM) { - t.Errorf("the memberships differ from the controller's:\n%s", raw) - } - }) - } + golden(t, "user-lists.json", append(raw, '\n')) // An asker on a seat pulled by kind hears what that seat's workers gave up on, and no other stream's dead letters // (novox/hq issue 734: a check ask given up during a bus reload left its try running for ever). t.Run("an asker reads its seat's dead letters", func(t *testing.T) { @@ -380,13 +200,12 @@ func TestTheCopiedComposerComposesAsTheControllers(t *testing.T) { t.Errorf("node one does not hear the vault's secret-changed: %v", perms.Subscribe) } // L3 s6 (ADR 0550 §2): while the vault serves `sign`, a node asks it in its own name alone, and reads the vault's - // `signers` and the lease key, never writing either. + // `signers`, never writing it; the ended controller's lease it reads no more (ADR 0590). signs, _ := PermissionsFor(Principal{Kind: KindNode, Node: "one", AsksTheVaultToSign: true}) signersGet := "$JS.API.DIRECT.GET.KV_" + SignersBucketName + ".$KV." + SignersBucketName + ".key" - leaseGet := "$JS.API.DIRECT.GET.KV_" + LeaseBucket + ".$KV." + LeaseBucket + ".holder" - for subject, want := range map[string]bool{VaultSignCall(one): true, signersGet: true, leaseGet: true, - VaultSignCall(Principal{Kind: KindNode, Node: "two"}): false, "$KV." + SignersBucketName + ".key": false, - "$KV." + LeaseBucket + ".holder": false} { + for subject, want := range map[string]bool{VaultSignCall(one): true, signersGet: true, + "$JS.API.DIRECT.GET.KV_mesh-controller_lease.$KV.mesh-controller_lease.holder": false, + VaultSignCall(Principal{Kind: KindNode, Node: "two"}): false, "$KV." + SignersBucketName + ".key": false} { if MayPublish(signs, subject) != want { t.Errorf("a node asking sign may publish %s: %v, want %v", subject, !want, want) } @@ -490,12 +309,12 @@ func TestTheCopiedComposerComposesAsTheControllers(t *testing.T) { t.Error("node-logins' named readers") } }) - // 774/S40/AC1 (ADR 0587 §4, §6): from the lease write an enrolling node's user may publish the redeem call in its - // own name and nothing else, and subscribe its own inbox alone; before it, the controller's subject beside. + // 774/S40/AC1 (ADR 0587 §4, §6, ADR 0590): an enrolling node's user may publish the redeem call in its own name and + // nothing else, and subscribe its own inbox alone. t.Run("an enrolling node redeems at the seat alone", func(t *testing.T) { holder := Seat{Name: catalogue.EnrolmentSeat, Serves: []string{"issue", "redeem", "confirm", "revoke", "tokens", "again"}} - for _, handed := range []bool{true, false} { - users, err := Users(Records{Nodes: []string{"ace"}, Enrolling: []string{"lab"}, LeaseHandedAway: handed, + { + users, err := Users(Records{Nodes: []string{"ace"}, Enrolling: []string{"lab"}, Assigned: map[string][]Declared{"ace": {{Module: "mesh-enrolment", Holds: []Seat{holder}}}}}) if err != nil { t.Fatal(err) @@ -514,17 +333,13 @@ func TestTheCopiedComposerComposesAsTheControllers(t *testing.T) { t.Fatal(err) } want := []string{"mesh.seat.mesh-enrolment.call.redeem.enrol~lab"} - if !handed { - want = []string{"mesh.control.enrol", "mesh.seat.mesh-enrolment.call.redeem.enrol~lab"} - } if !reflect.DeepEqual(perms.Publish, want) || !reflect.DeepEqual(perms.Subscribe, []string{"_INBOX.enrol.lab.>"}) || perms.AllowResponses { - t.Errorf("handed %v: enrol.lab publishes %v, subscribes %v, answers %v", handed, perms.Publish, perms.Subscribe, - perms.AllowResponses) + t.Errorf("enrol.lab publishes %v, subscribes %v, answers %v", perms.Publish, perms.Subscribe, perms.AllowResponses) } } - // No holder serving redeem and the lease handed: the user may enrol nowhere, refused by name. - if _, err := PermissionsFor(Principal{Kind: KindEnrolment, Node: "lab", LeaseHandedAway: true}); err == nil { + // No holder serving redeem: the user may enrol nowhere, refused by name. + if _, err := PermissionsFor(Principal{Kind: KindEnrolment, Node: "lab"}); err == nil { t.Error("an enrolment user that may enrol nowhere was composed") } }) @@ -570,14 +385,13 @@ func TestTheCopiedComposerComposesAsTheControllers(t *testing.T) { t.Error("the enrolments' named readers") } }) - t.Run("the controller's golden server configuration", func(t *testing.T) { + t.Run("the golden server configuration", func(t *testing.T) { seat := Seat{Name: "telegram-sender", Accepts: []string{"send"}, Emits: []string{"delivered", "failed"}} got, err := Compose( Server{ClientPort: 4222, MonitoringPort: 8222, StoreDir: "/data", TLSCert: "/tls/tls.crt", TLSKey: "/tls/tls.key", TLSCA: "/tls/ca.crt"}, []Principal{ - {Kind: KindController, PasswordHash: "$2a$11$cccccccccccccccccccccc"}, - {Kind: KindEnrolment, Node: "one", PasswordHash: "$2a$11$eeeeeeeeeeeeeeeeeeeeee"}, + {Kind: KindEnrolment, Node: "one", Redeems: true, PasswordHash: "$2a$11$eeeeeeeeeeeeeeeeeeeeee"}, {Kind: KindNode, Node: "one", PasswordHash: "$2a$11$nnnnnnnnnnnnnnnnnnnnnn"}, {Kind: KindModule, Node: "one", Module: "telegram", Holds: []Seat{seat}, Serves: []string{"status"}, PasswordHash: "$2a$11$tttttttttttttttttttttt"}, @@ -592,11 +406,7 @@ func TestTheCopiedComposerComposesAsTheControllers(t *testing.T) { if err != nil { t.Fatal(err) } - want, err := os.ReadFile(filepath.Join("testdata", "composed.conf")) - if err != nil { - t.Fatal(err) - } - grantsAsTheControllers(t, got, string(want)) + golden(t, "composed.conf", []byte(got)) }) // 774/AC2 (novox/hq ADR 0561 §2): a wanted account round-trips through the grants part's state `accounts`, keyed by // its user, at the subject `$KV.mesh-grants_accounts.`, read by the vault alone; and the vault's account-issued @@ -632,7 +442,7 @@ func TestTheCopiedComposerComposesAsTheControllers(t *testing.T) { {User: "ace.x", Kind: KindModule, Node: "ace", Module: "y", URL: "nats://a:1"}, {User: "ace.x.p", Kind: KindProcess, Node: "ace", Module: "x", URL: "nats://a:1"}, {User: "node.ace", Kind: KindNode, Node: "ace", URL: "nats://a:1"}, - {User: "controller", Kind: KindController, URL: "nats://a:1"}, + {User: "controller", Kind: "controller", URL: "nats://a:1"}, {User: "ace.x", Kind: KindModule, Node: "ace", Module: "x"}, } { if bad.Valid() == nil { @@ -702,52 +512,18 @@ func TestTheCopiedComposerComposesAsTheControllers(t *testing.T) { t.Error("an account was held with no password") } }) - // 774 subtasks 6 and 8 (novox/hq ADR 0561 §3, §6): minting and the grants part's consumers turn on only while the - // lease is handed to a holder that is no controller; the vault and the grants part alone read the lease, whatever the - // controller announced; a module's consumer is created as the controller's EnsureConsumer creates it. - t.Run("the lease turns minting on only when handed away; a consumer is created as the controller's", func(t *testing.T) { - for _, row := range []struct { - name string - raw string - found bool - err error - on bool - }{ - {"a controller holds it", `{"instance":"novox/1234/2026-10-27T20:00:00Z","epoch":57}`, true, nil, false}, - {"handed away", `{"handed-to":"the operator's hand-over","handed-by":"person.jochen"}`, true, nil, true}, - // ADR 0582 clause 3 (774/S3): the write names the ask and the time, which turn nothing. - {"handed to mesh-grants with its ask and time", `{"handed-to":"mesh-grants","handed-by":"person.jochen","why":"w",` + - `"ask":"lease-write-1","at":"2026-11-02T10:00:00Z"}`, true, nil, true}, - {"expired or deleted", ``, false, nil, false}, - {"unreadable", ``, false, errors.New("refused"), false}, - {"not a holder", `[1]`, true, nil, false}, - {"both a controller and a handed holder", `{"instance":"novox/1","handed-to":"x"}`, true, nil, false}, - } { - if on, why := LeaseHandedAway([]byte(row.raw), row.found, row.err); on != row.on || why == "" { - t.Errorf("%s: on %v (%s), want %v", row.name, on, why, row.on) - } - // The assigner's grants source follows it: the grants part's bucket only for a handed-away lease (issue 774). - on, _ := LeaseHandedAway([]byte(row.raw), row.found, row.err) - if bucket, _ := delivery.GrantSource(on); (bucket == delivery.PartGrantsBucket) != row.on { - t.Errorf("%s: the grants source is %s", row.name, bucket) - } - } - announced := map[string]announce.States{ControllerSeat: ControllerAnnouncement("novox")} - for reader, want := range map[string]bool{VaultSeat: true, GrantsModule: true, "mesh-delivery-assigner": true, "mesh-delivery-view": false} { - granted, _ := ReadsAnnouncedBy(reader, []string{ControllerLease}, announced) - if (len(granted) == 1) != want { - t.Errorf("%s reads the lease: %v, want %v", reader, granted, want) - } - } - // Issue 774 (sweep item 2): Users judges every read for its reader again, whatever split the caller made: the - // controller's own reader-less copy refused the vault the lease and the grants part's accounts it is named for. + // 774 subtask 8 (novox/hq ADR 0561 §6): a module's consumer is created as the grants part creates it, and every read is + // judged for its reader. + t.Run("every read is judged for its reader; a consumer is created as the grants part creates it", func(t *testing.T) { + announced := map[string]announce.States{} + // Issue 774 (sweep item 2): Users judges every read for its reader again, whatever split the caller made: a + // reader-less split refused the vault the grants part's accounts it is named for. announced["mesh-grants"] = announce.States{Module: "mesh-grants", States: []announce.State{{Name: "accounts", Readers: []string{VaultSeat}}}} announced["disk-load"] = announce.States{Module: "disk-load", States: []announce.State{{Name: "readings"}}} judged, err := Users(Records{Nodes: []string{"a"}, Announced: announced, Assigned: map[string][]Declared{"a": { - {Module: VaultSeat, ReadsRefused: []string{ControllerLease, GrantsAccounts}}, - {Module: "mesh-config", ReadsRefused: []string{ControllerLease}}, + {Module: VaultSeat, ReadsRefused: []string{GrantsAccounts}}, {Module: "restic", ReadsRefused: []string{"disk-load.readings"}}, - {Module: "messenger", Reads: []string{ControllerLease, GrantsAccounts}}}}}) + {Module: "messenger", Reads: []string{GrantsAccounts}}}}}) if err != nil { t.Fatal(err) } @@ -757,7 +533,7 @@ func TestTheCopiedComposerComposesAsTheControllers(t *testing.T) { reads[u.Module] = strings.Join(u.Reads, ",") } } - if reads[VaultSeat] != ControllerLease+","+GrantsAccounts || reads["mesh-config"] != ControllerLease || reads["restic"] != "" || reads["messenger"] != "" { + if reads[VaultSeat] != GrantsAccounts || reads["restic"] != "" || reads["messenger"] != "" { t.Errorf("the reads granted for their readers: %v", reads) } // AccountsEntry is the very lines ComposeAccounts writes for a user, so a writer of kept users repeats none. @@ -793,48 +569,6 @@ func TestTheCopiedComposerComposesAsTheControllers(t *testing.T) { t.Errorf("the grants part creates consumers as %v", u.CreatesConsumers) } } - // ADR 0582 clause 3 (issue 774, 774/S2): the lease write's cut takes from the controller the lease's key and every - // update, purge and delete of its bucket, and gives the grants part the key holder alone; without it nothing - // changes (the golden lists above are composed without it). - lease := "$KV." + LeaseBucket + ".holder" - for _, cut := range []bool{false, true} { - users, err := Users(Records{Nodes: []string{"a"}, Assigned: map[string][]Declared{"a": {{Module: GrantsModule}}}, - LeaseCut: cut}) - if err != nil { - t.Fatal(err) - } - for _, c := range []struct { - kind Kind - subject string - may bool - }{ - {KindController, lease, !cut}, - {KindController, "$JS.API.STREAM.UPDATE.KV_" + LeaseBucket, !cut}, - {KindController, "$JS.API.STREAM.PURGE.KV_" + LeaseBucket, !cut}, - {KindController, "$JS.API.STREAM.DELETE.KV_" + LeaseBucket, !cut}, - {KindController, "$JS.API.STREAM.MSG.DELETE.KV_" + LeaseBucket, !cut}, - // It still reads the lease, and keeps its other buckets. - {KindController, "$JS.API.DIRECT.GET.KV_" + LeaseBucket + ".$KV." + LeaseBucket + ".holder", true}, - {KindController, "$KV." + CallsBucket + ".x", true}, - {KindController, "$JS.API.STREAM.UPDATE.KV_" + CallsBucket, true}, - {KindModule, lease, cut}, - {KindModule, "$KV." + LeaseBucket + ".other", false}, - {KindModule, "$JS.API.STREAM.UPDATE.KV_" + LeaseBucket, false}, - } { - for _, u := range users { - if u.Kind != c.kind || (c.kind == KindModule && u.Module != GrantsModule) { - continue - } - perms, err := PermissionsFor(u) - if err != nil { - t.Fatalf("cut %v: %s: %v", cut, u.Username(), err) - } - if MayPublish(perms, c.subject) != c.may { - t.Errorf("cut %v: %s may publish %s: %v, want %v", cut, u.Username(), c.subject, !c.may, c.may) - } - } - } - } // 1952 S5 (novox/hq ADR 0564 clauses 2, 5, 8; Limits 2, 4, 8): the health holders' credentials. healthChecks(t) q := ConsumerConfig(QueueConsumer(Principal{Kind: KindProcess, Node: "a", Module: "m", Process: "p"}, @@ -848,18 +582,12 @@ func TestTheCopiedComposerComposesAsTheControllers(t *testing.T) { // healthChecks are the health holders' credential cases (ADR 0564 clauses 2, 5, 8; Limits 2, 4, 8; issue 1952 S5). func healthChecks(t *testing.T) { t.Helper() - announced := map[string]announce.States{ControllerSeat: ControllerAnnouncement("novox"), + announced := map[string]announce.States{ "mesh-healers": {Module: "mesh-healers", States: []announce.State{{Name: "attempts", Readable: true}, {Name: "would", Readable: true}}}} for _, c := range []struct { reader, read string may bool }{ - {"mesh-conditions", ControllerLease, true}, {"mesh-healers", ControllerLease, true}, {WatchmanModule, ControllerLease, true}, - {"mesh-self-check", ControllerLease, false}, {"messenger", ControllerLease, false}, {ArtifactStoreTools, ControllerLease, true}, - {"mesh-conditions", ControllerConditions, true}, {"mesh-healers", ControllerConditions, false}, {"messenger", ControllerConditions, false}, - {"mesh-healers", ControllerHandActs, true}, {"mesh-conditions", ControllerHandActs, false}, - // ADR 0565 clauses 10, 11 (issue 1953 L5-S4): mesh-hand-acts reads the controller's log beside its own. - {"mesh-hand-acts", ControllerHandActs, true}, {"mesh-conditions", "mesh-healers.attempts", true}, {"mesh-conditions", HealersWould, false}, {"mesh-delivery-view", HealersWould, false}, // ADR 0577 clause 1: the node-engine's data items are disk-load's alone to read. {"disk-load", delivery.EngineDataRead, true}, {"restic", delivery.EngineDataRead, false}, @@ -872,7 +600,7 @@ func healthChecks(t *testing.T) { bucket := func(m, n string) Bucket { return Bucket{Module: m, Name: n} } users, err := Users(Records{Nodes: []string{"a"}, Assigned: map[string][]Declared{"a": { {Module: "mesh-conditions", State: []Bucket{bucket("mesh-conditions", "conditions"), bucket("mesh-conditions", "takeover")}, - Reads: []string{"mesh-self-check.findings", "mesh-healers.attempts", ControllerConditions}}, + Reads: []string{"mesh-self-check.findings", "mesh-healers.attempts"}}, {Module: "mesh-self-check", State: []Bucket{bucket("mesh-self-check", "findings")}, Reads: []string{"restic.good"}}, // Issue 1955 L7: the grants part keeps the hearings it withholds; the self-check reads them. {Module: GrantsModule, State: []Bucket{bucket(GrantsModule, GrantsWithheldState)}}, @@ -903,7 +631,7 @@ func healthChecks(t *testing.T) { {Module: "telegram", Holds: []Seat{{Name: "telegram-sender", Accepts: []string{"send"}}}, Processes: []ProcessPrincipal{{Process: "w", Queues: []QueueOwner{{Process: "w", Stream: "M_WORK"}, {Process: "w", Stream: "KV_x"}}}}}, {Module: "nats", SnapshotsTheBus: true, Processes: []ProcessPrincipal{{Process: "nats-tools", - Removes: []string{catalogue.RemovesControllerDeadLetterConsumer, catalogue.RemovesEngineActs, catalogue.RemovesAssignmentsStream}}, {Process: "other"}}}, + Removes: []string{catalogue.RemovesEngineActs, catalogue.RemovesControllerLeftovers}}, {Process: "other"}}}, {Module: "shop", Processes: []ProcessPrincipal{{Process: "nats-tools", Removes: []string{catalogue.RemovesEngineActs}}}}, }}}) if err != nil { @@ -943,8 +671,13 @@ func healthChecks(t *testing.T) { "$KV.mesh-host_acts.g14.a1", "$JS.API.STREAM.INFO.KV_mesh-host_acts", // Research 072 row 33: the ASSIGNMENTS stream is read and removed at the lease write. "$JS.API.STREAM.INFO.ASSIGNMENTS", "$JS.API.STREAM.DELETE.ASSIGNMENTS", - // ADR 0582 clause 3 (issue 774, 774/S2): the lease's bucket's expiry, read and set by its tool. - "$JS.API.STREAM.INFO.KV_mesh-controller_lease", "$JS.API.STREAM.UPDATE.KV_mesh-controller_lease"}, + // Issue 1126 B5 (ADR 0590 clause 6): every leftover of the ended controller, each by its name, read and deleted. + "$JS.API.STREAM.INFO.KV_mesh-controller_lease", "$JS.API.STREAM.DELETE.KV_mesh-controller_lease", + "$JS.API.STREAM.DELETE.KV_mesh-controller_rollouts", "$JS.API.STREAM.DELETE.KV_mesh-controller_states-announced", + "$JS.API.STREAM.DELETE.CONTROL", "$JS.API.STREAM.DELETE.OBJ_declaration-files-a", + "$JS.API.CONSUMER.DELETE.EVENTS.controller", "$JS.API.CONSUMER.DELETE.EVENTS.controller-merges", + "$JS.API.CONSUMER.DELETE.EVENTS.novox_mesh-delivery", "$JS.API.CONSUMER.DELETE.CONTROL.controller", + "$JS.API.CONSUMER.INFO.CONTROL.controller"}, } mayNot := map[string][]string{ // The keeper writes no findings and no attempts; no observer writes the keeper's buckets (clause 2). @@ -988,10 +721,11 @@ func healthChecks(t *testing.T) { "a.nats.nats-tools": {"$JS.API.CONSUMER.DELETE.DEAD_LETTER_NOTICES.other", "$JS.API.CONSUMER.DELETE.EVENTS.a_m", "$KV.mesh-conditions_conditions.x", "$JS.API.STREAM.DELETE.KV_mesh-host_acts", "$JS.API.STREAM.PURGE.KV_mesh-host_acts", "$JS.API.STREAM.DELETE.EVENTS", "$JS.API.STREAM.PURGE.ASSIGNMENTS", "$JS.API.STREAM.DELETE.ASSIGNMENTS_X", - // It updates the lease's bucket alone, and neither purges nor deletes it nor writes its key (ADR 0582 clause 3). + // It updates nothing, purges nothing, deletes no stream but the leftovers' and writes no key of them (ADR 0590). "$JS.API.STREAM.UPDATE.KV_mesh-host_acts", "$JS.API.STREAM.UPDATE.KV_mesh-controller_calls", "$JS.API.STREAM.UPDATE.EVENTS", - "$JS.API.STREAM.PURGE.KV_mesh-controller_lease", "$JS.API.STREAM.DELETE.KV_mesh-controller_lease", - "$KV.mesh-controller_lease.holder"}, + "$JS.API.STREAM.UPDATE.KV_mesh-controller_lease", "$JS.API.STREAM.PURGE.KV_mesh-controller_lease", + "$JS.API.STREAM.DELETE.KV_mesh-controller_other", "$JS.API.STREAM.DELETE.OBJ_declaration-files-zz", + "$JS.API.CONSUMER.DELETE.EVENTS.a_mesh-delivery", "$KV.mesh-controller_lease.holder"}, "a.nats.other": {"$JS.API.CONSUMER.DELETE.DEAD_LETTER_NOTICES.controller", "$KV.mesh-host_acts.g14.a1", "$JS.API.STREAM.UPDATE.KV_mesh-controller_lease"}, "a.nats": {"$JS.API.CONSUMER.DELETE.DEAD_LETTER_NOTICES.controller", "$KV.mesh-host_acts.g14.a1"}, @@ -1024,11 +758,9 @@ func healthChecks(t *testing.T) { MayPublish(perms, "$KV.mesh-host_health.b")) { t.Errorf("the watchman reads the health reports %v, %v", MayPublish(perms, "$JS.API.DIRECT.GET.KV_mesh-host_health.$KV.mesh-host_health.b"), perms.Publish) } - // ADR 0565 clause 6: and it reads the controller's mirror's key of its own node alone, and writes none of it. + // ADR 0565 clause 6, ADR 0590: and the ended controller's mirror it reads no more. if u.Kind == KindNode && (!MayPublish(perms, "$KV.mesh-host_packages.a") || MayPublish(perms, "$KV.mesh-host_packages.b") || - !MayPublish(perms, "$JS.API.DIRECT.GET.KV_mesh-controller_declared-packages.$KV.mesh-controller_declared-packages.a") || - MayPublish(perms, "$JS.API.DIRECT.GET.KV_mesh-controller_declared-packages.$KV.mesh-controller_declared-packages.b") || - MayPublish(perms, "$KV.mesh-controller_declared-packages.a")) { + MayPublish(perms, "$JS.API.DIRECT.GET.KV_mesh-controller_declared-packages.$KV.mesh-controller_declared-packages.a")) { t.Errorf("node %s writes the packages keys %v", u.Node, perms.Publish) } if u.Kind == KindModule && MayPublish(perms, "$KV.mesh-delivery-assigner_policies.x") { @@ -1142,52 +874,3 @@ func healthChecks(t *testing.T) { } } } - -// heldEventsBeyond takes out of the copy's memberships the one row they list beyond the controller's on purpose (issue -// 1952): a held seat's own events in its holder's seat traffic, so a module the tool runner carries may say them. The -// controller's composer is frozen; any other difference is still a fault. -func heldEventsBeyond(got, was any) { - gotAll, _ := got.(map[string]any) - wasAll, _ := was.(map[string]any) - for key, m := range gotAll { - gm, _ := m.(map[string]any) - traffic, _ := gm["seat-traffic"].(map[string]any) - if traffic == nil { - continue - } - listedBefore := map[string]bool{} - if wm, _ := wasAll[key].(map[string]any); wm != nil { - if wt, _ := wm["seat-traffic"].(map[string]any); wt != nil { - for _, p := range asStrings(wt["publish"]) { - listedBefore[p] = true - } - } - } - var kept []any - for _, p := range asStrings(traffic["publish"]) { - if parts := strings.Split(p, "."); !listedBefore[p] && len(parts) == 5 && parts[0] == "mesh" && parts[1] == "seat" && parts[3] == "event" { - continue - } - kept = append(kept, p) - } - if kept == nil { - delete(traffic, "publish") - } else { - traffic["publish"] = kept - } - if len(traffic) == 0 { - delete(gm, "seat-traffic") - } - } -} - -func asStrings(v any) []string { - list, _ := v.([]any) - out := make([]string, 0, len(list)) - for _, x := range list { - if s, ok := x.(string); ok { - out = append(out, s) - } - } - return out -} diff --git a/go/buscompose/composed.go b/go/buscompose/composed.go index be03e18..9dd1285 100644 --- a/go/buscompose/composed.go +++ b/go/buscompose/composed.go @@ -1,91 +1,14 @@ package buscompose import ( - "context" "fmt" "git.novox.be/novox/mesh-sdk/go/delivery" - "github.com/nats-io/nats.go/jetstream" ) -// The compositions' buckets (novox/hq ADR 0539 clauses 2 and 4): one per node, `composed-`, holding that node's -// compositions, which only that node's node-engine reads, since a composition can carry a sealed value (ADR 0488 §2's -// reason); and `mesh-controller_composed`, holding each composition's digest and the judgement of its move, which the -// assigner reads. The controller writes both, by compare-and-set while it holds the lease; each keeps five revisions -// of a key. The names and layouts are mesh-sdk's (delivery.ComposedBucketFor, ControllerComposedBucket, ComposedKey). - -// ControllerComposed is the local name of the compositions' digests, as a reader names it in `reads` after -// `mesh-controller.`: delivery.ControllerComposedBucket. -const ControllerComposed = "composed" - -// ComposedValueBytes is the most a composition may hold: a file over 64 KiB travels by digest, so a module's -// composition is far smaller; just under the server's largest message, which also carries the subject and headers. -const ComposedValueBytes = 1<<20 - 8<<10 - -// ComposedBucketBytes is a node's composition bucket's size: five revisions of every module's compositions of the node. -const ComposedBucketBytes = 256 << 20 - -// ComposedDigestsBytes is the size of the bucket of digests: a value of a few hundred bytes per node, module and -// fingerprint kept. -const ComposedDigestsBytes = 64 << 20 - -// ComposedConfig is a node's composition bucket as the controller asserts it. -func ComposedConfig(node string) (jetstream.KeyValueConfig, error) { - bucket, err := delivery.ComposedBucketFor(node) - if err != nil { - return jetstream.KeyValueConfig{}, err - } - return jetstream.KeyValueConfig{ - Bucket: bucket, - Description: "the compositions of " + node + " (novox/hq ADR 0539): each module's resources for this node at one " + - "fingerprint, and the node's own, signed; written by the controller alone, read by this node's node-engine " + - "alone; deleted when outside the module's five newest fingerprints and named by no want", - History: delivery.ComposedHistory, - MaxValueSize: ComposedValueBytes, - MaxBytes: ComposedBucketBytes, - Storage: jetstream.FileStorage, - }, nil -} - -// ComposedDigestsConfig is the bucket of the compositions' digests as the controller asserts it. -func ComposedDigestsConfig() jetstream.KeyValueConfig { - return jetstream.KeyValueConfig{ - Bucket: delivery.ControllerComposedBucket, - Description: "each composition's digest and the judgement of its move from what the node runs (novox/hq ADR 0539 " + - "clauses 2 and 14): written by the controller alone, read by the assigner", - History: delivery.ControllerComposedHistory, - MaxValueSize: 4 << 10, - MaxBytes: ComposedDigestsBytes, - Storage: jetstream.FileStorage, - } -} - -// EnsureComposed creates the bucket of digests and each named node's composition bucket if absent and brings their -// options to match. An update, never a delete. -func EnsureComposed(ctx context.Context, js jetstream.JetStream, nodes ...string) error { - if _, err := js.CreateOrUpdateKeyValue(ctx, ComposedDigestsConfig()); err != nil { - return fmt.Errorf("asserting bucket %s: %w", delivery.ControllerComposedBucket, err) - } - for _, node := range nodes { - cfg, err := ComposedConfig(node) - if err != nil { - return err - } - if _, err := js.CreateOrUpdateKeyValue(ctx, cfg); err != nil { - return fmt.Errorf("asserting bucket %s: %w", cfg.Bucket, err) - } - } - return nil -} - -// ComposedWrites is what the controller publishes to write a node's compositions: the bucket's own subjects. -func ComposedWrites(node string) (string, error) { - bucket, err := delivery.ComposedBucketFor(node) - if err != nil { - return "", err - } - return "$KV." + bucket + ".>", nil -} +// The compositions' buckets (novox/hq ADR 0539 clauses 2 and 4): one per node, `composed-`, holding the +// compositions the ended controller made for that node, which only that node's node-engine reads. Nothing writes them +// since the controller ended (ADR 0590); a node composes its own modules (ADR 0547 §2). // ComposedReads is what a node's node-engine publishes to follow its wants and compositions (ADR 0539 clauses 1, 2 and // 9): every key of its own composition bucket, and its own wants (`want..` and `want..node`) in the @@ -126,42 +49,6 @@ var NodeAgentHears = []string{"mesh.mod." + BackupHolderModule + ".event.backed- // A second holder of the seat is a change of this grant. const BackupHolderModule = "restic" -// administers says a person may invoke every tool (`*`): the operator. -func administers(invokes []string) bool { - for _, i := range invokes { - if i == "*" { - return true - } - } - return false -} - -// HandPathWrites is what the operator's account publishes for release 6's hand path (ADR 0539 clauses 2 and 10): a want -// of any node, written and read back, in the assigner's bucket; each named node's compositions; the digests; and each -// node's object bucket for a file over 64 KiB, whose put reads an object's metadata and purges a replaced one's chunks. -func HandPathWrites(nodes []string) ([]string, error) { - out := append([]string{"$KV." + delivery.AssignerWantsBucket + ".want.>"}, readGrants(delivery.AssignerWantsBucket, "want.>")...) - out = append(out, "$KV."+delivery.ControllerComposedBucket+".>") - out = append(out, readGrants(delivery.ControllerComposedBucket, "")...) - for _, node := range nodes { - w, err := ComposedWrites(node) - if err != nil { - return nil, err - } - bucket, _ := delivery.ComposedBucketFor(node) - out = append(out, w) - out = append(out, readGrants(bucket, "")...) - files, err := DeclarationFilesWrites(node) - if err != nil { - return nil, err - } - stream := "OBJ_" + DeclarationFilesBucket + node - out = append(out, files, "$JS.API.STREAM.INFO."+stream, "$JS.API.DIRECT.GET."+stream+".>", - "$JS.API.STREAM.MSG.GET."+stream, "$JS.API.STREAM.PURGE."+stream) - } - return out, nil -} - // DeclarationsStream is the stream of each node's last declaration, `mesh.node..declare` (issue 107). Release 6 // sends none (ADR 0539 clauses 5 and 11): the stream is kept, with each node's consumer, only so a node away during the // switch finds its last declaration and with it the new node-engine; the controller deletes it once every node's have @@ -182,15 +69,3 @@ func RaiseAfterDeclarations(r Raiser) error { } return AssertMeshConsumers(r) } - -// TakeInConsumer is the durable consumer the controller takes done keys in from, on the stream of each of TakeInBuckets -// (novox/hq ADR 0544 §1). -const TakeInConsumer = "mesh-controller-take-in" - -// TakeInBuckets are the buckets the take-in consumes: the indexer's modules (each module's trunk value) and the builder's -// builds (each build's key). -var TakeInBuckets = []string{delivery.ModulesBucket, delivery.BuilderBuildsBucket} - -// ConfigFollowConsumer is the durable consumer the controller follows mesh-config's resolved values with, on that -// bucket's stream (novox/hq ADR 0545). -const ConfigFollowConsumer = "mesh-controller-config-follow" diff --git a/go/buscompose/controller_buckets.go b/go/buscompose/controller_buckets.go deleted file mode 100644 index 775f1c9..0000000 --- a/go/buscompose/controller_buckets.go +++ /dev/null @@ -1,247 +0,0 @@ -package buscompose - -import ( - "context" - "fmt" - "time" - - "git.novox.be/novox/mesh-sdk/go/delivery" - "github.com/nats-io/nats.go/jetstream" -) - -// The controller's own key-value buckets (novox/hq to-be 45 §1, §6, §7). -// -// **What the controller must remember across its own restart, it keeps on the bus.** A call's -// outcome lived in the memory of the process that served it (novox/hq issue 265), so a controller -// replaced while a push ran answered "no such call" for the one thing its caller had been told to -// ask about. The bus already outlives the controller and is the shape ADR 0201 gives a module's -// current state: one value per key, written by one owner, read by anybody granted it. These are the -// controller's, written by it alone — the writers table of to-be 45 §1 — and asserted on every start -// like the streams, so a bus raised from nothing has them before the first call is served. - -// CallsBucket keeps every call of the mesh's own verbs and what came of it; HandActsBucket every act -// a person did by hand, with why; ConditionsBucket every condition open now (to-be 45 §2), one key -// each, and ConditionHistoryBucket every transition of one — raised, changed, silenced, cleared — -// for ninety days. -// -// **The history is a bucket of its own** because its keys expire and an open condition's must not: -// a bucket has one age for every key, and a condition open longer than the history is kept would -// otherwise vanish from the store while still true. -var ( - CallsBucket = BucketName(ControllerSeat, "calls") - HandActsBucket = BucketName(ControllerSeat, "hand-acts") - ConditionsBucket = BucketName(ControllerSeat, "conditions") - ConditionHistoryBucket = BucketName(ControllerSeat, "condition-history") - // LeaseBucket holds the controller's lease (to-be 45 §6): one key, `holder`, which the instance - // allowed to act writes by compare-and-set and renews; its revision when taken is the epoch. - LeaseBucket = BucketName(ControllerSeat, "lease") - // AskedBucket keeps what the controller asked the operator about its conditions (novox/hq ADR 0259): - // each ask by its id, its options and the actions they stand for, how it ended and whether the - // controller acted on its warrant — so a restart neither asks twice nor acts twice. - AskedBucket = BucketName(ControllerSeat, "asked") - // DeliveryMergesBucket holds the merges mesh-delivery announced it made, each with the ask whose Approve it merged - // on (novox/hq ADR 0420 §7, issue 1025): per merge a key `///` holding the - // announcement, kept DeliveryMergesKeptFor. What lets the build of a moved module register it from its new - // repository: the controller reads the ask's warrant from the router and accepts only the operator's Approve of - // those heads. Written by the controller alone, from the announcement, when it holds the lease. - DeliveryMergesBucket = BucketName(ControllerSeat, "delivery-merges") -) - -// DeliveryMergesKeptFor is how long a merge mesh-delivery announced is kept: a week, as the events are. -const DeliveryMergesKeptFor = 7 * 24 * time.Hour - -// The controller's record mirrors (novox/hq ADR 0289, design 54, design 57 package F3): a bucket per kind of -// record, one key per record, written from the store by the lease holder only when the record changed, with -// one event on the controller's seat per write. Read by whatever names them in `reads` as -// `mesh-controller.` (ADR 0201 §1), written by nobody else. **No secret in either** (ADR 0201): an -// assignment's value names its module and node; a module's declared config is what its manifest, which is -// public, declares. -// -// AssignmentsBucket holds which module is assigned to which node, keyed `.`; and -// DeclaredConfigBucket each registered module's config declarations as its manifest holds them, keyed by -// the module: what mesh-config resolves a config value from (ADR 0348 clauses 2 and 4); and PredefinedBucket the -// list of predefined keys the mesh published (the newest trunk build's of the catalogue, ADR 0348 §7), one key -// `list`: what mesh-config publishes as mesh-config_predefined (design 57 K3, issue 662). -var ( - AssignmentsBucket = BucketName(ControllerSeat, MirrorAssignments) - DeclaredConfigBucket = BucketName(ControllerSeat, MirrorDeclaredConfig) - PredefinedBucket = BucketName(ControllerSeat, MirrorPredefined) - // DeclaredPackagesBucket holds, keyed by the node, each package the modules assigned to it declare, present or absent, - // with the declaring modules: what the pacman module's verbs read to say a package is declared (ADR 0507 §5). - DeclaredPackagesBucket = BucketName(ControllerSeat, MirrorDeclaredPackages) -) - -// The mirrors' local names, as a reader names them in `reads` after `mesh-controller.`. -const ( - MirrorAssignments = "assignments" - MirrorDeclaredConfig = "declared-config" - MirrorPredefined = "predefined" - // MirrorDeclaredPackages is the local name of the packages each node's modules declare (ADR 0507 §5, issue 1840/S3). - MirrorDeclaredPackages = "declared-packages" -) - -// The chain's buckets (novox/hq ADR 0408, issue 1089): builds, rollouts and assignments are one chain kept on the bus -// alone, one bucket each, one key per record, written by the lease holder by compare-and-set. The assignments bucket is -// AssignmentsBucket above, which keeps its name and its readers (ADR 0408 §3). Nothing is removed from them (§4), they keep -// ChainHistory earlier revisions of every key (§5), and a value is never cut (§6). -var ( - BuildsBucket = BucketName(ControllerSeat, ChainBuilds) - RolloutsBucket = BucketName(ControllerSeat, ChainRollouts) -) - -// **The assignments bucket keeps the marker age (MirrorMarkerTTL) it was made with as a mirror.** A stream that allows -// messages an age cannot be made to disallow them (the server refuses the update: "message TTL status can not be -// disabled", nats-server stream.go), so asserting the bucket without the age would fail the controller's start on a -// mesh that has the bucket. The chain never purges or removes a key (ADR 0408 §4), so no marker is ever written and the -// age ends nothing; it is kept only so the stream's configuration stays one the server accepts. -// -// The chain buckets' local names, as a reader names them in `reads` after `mesh-controller.`. -const ( - ChainBuilds = "builds" - ChainRollouts = "rollouts" -) - -// The chain buckets' bounds. ChainHistory is the bus's largest history (ADR 0408 §5). A value is at most ChainValueBytes, -// just under the server's largest message of a mebibyte, which also carries the subject and the headers (ADR 0408 §6): -// the controller writes none larger and the move refuses to. The buckets' size is the arithmetic of the survey of -// issue 981: 8585 builds of 1 to 40 KB, 1049 rollouts of about 2 KB a revision and 64 revisions, so 512 MiB for the two -// that grow, and the assignments, 285 of under a kilobyte, 64 MiB. -const ( - ChainHistory = 64 - ChainValueBytes = 1<<20 - 8<<10 - ChainBuildsBytes = 512 << 20 - ChainRolloutsBytes = 512 << 20 - ChainAssignmentBytes = 64 << 20 -) - -// GrantsBucket holds, keyed `.`, each fingerprint of a module whose bus credential the controller -// has issued (novox/hq ADR 0528 clause 2, ADR 0429 §6 rule 6, issue 1394): written by the controller alone once the bus's -// user list composed with the module's manifest at that fingerprint was sent to the bus's machine; read by the assigner, -// whose sixth rule moves a node's want to a fingerprint only once its grant exists. The name and layout are mesh-sdk's -// (delivery.ControllerGrantsBucket, ControllerGrantKey). -var GrantsBucket = BucketName(ControllerSeat, ControllerGrants) - -// ControllerGrants is the grants bucket's local name, as a reader names it in `reads` after `mesh-controller.`. -const ControllerGrants = "grants" - -// ChainBuckets are the three buckets of the chain: builds, rollouts and assignments. -func ChainBuckets() []string { return []string{BuildsBucket, RolloutsBucket, AssignmentsBucket} } - -// MirrorsWrittenByAnother are the controller's mirrors whose writer is not the controller, each with its reason: the rule -// that a mirror is written by the controller alone (TestTheMirrorsListAndTheWritersTableAgree) has these exceptions and no -// other. -var MirrorsWrittenByAnother = map[string]string{} - -// The mirrors' caps (design 54 §5): one value per key, no age, 64 KiB a value, 64 MiB a bucket. -const ( - MirrorValueBytes = 64 << 10 - MirrorBytes = 64 << 20 - // MirrorMarkerTTL is the longest a purge's marker is kept (design 54 §2): the bucket allows a - // per-message age for it, and the writer gives each marker a day. - MirrorMarkerTTL = 24 * time.Hour -) - -// PlanStateBuckets are the buckets of the plans' state that release 6 deletes (novox/hq ADR 0539 clause 13): the merge -// window's clock, the silent sends' waits and the building plans' hold. The controller deletes each at its first start -// and says it once (composed_start.go); none is asserted, granted or written any more. -var PlanStateBuckets = []string{BucketName(ControllerSeat, "merge-window"), BucketName(ControllerSeat, "rollout-silent"), - BucketName(ControllerSeat, "plans-building")} - -// AskedKeptFor is how long an ask is kept after it was made: a month, as the router keeps its own. -const AskedKeptFor = 30 * 24 * time.Hour - -// LeaseTTL is how long the lease's key lives unrenewed (to-be 45 §6): fifteen seconds, renewed -// every five. The bucket's age, so the bus forgets a holder that stopped renewing. -const LeaseTTL = 15 * time.Second - -// The bounds to-be 45 §6 sets for calls: the last thousand, or fourteen days, whichever is fewer. -// A call is two keys — its record, and its answer apart so a listing does not read every answer — -// so the stream holds twice as many messages as it keeps calls. -const ( - KeptCallsDurably = 1000 - CallsKeptFor = 14 * 24 * time.Hour - // CallAnswerBytes is the most of one answer kept: a whole declaration is far smaller, and an - // answer larger is cut and says so. - CallAnswerBytes = 64 << 10 - // HandActsKeptFor is as long as a condition's history (to-be 45 §2): an act by hand is read - // back beside what it addressed. - HandActsKeptFor = 90 * 24 * time.Hour - // ConditionHistoryKeptFor is how long a condition's transitions are kept (to-be 45 §2). - ConditionHistoryKeptFor = 90 * 24 * time.Hour -) - -// IsControllerBucket says a bucket is the controller's own, not a module's state nothing declares. -func IsControllerBucket(bucket string) bool { - return bucket == CallsBucket || bucket == HandActsBucket || bucket == ConditionsBucket || - bucket == ConditionHistoryBucket || bucket == LeaseBucket || bucket == AskedBucket || - bucket == AssignmentsBucket || bucket == DeclaredConfigBucket || bucket == PredefinedBucket || - bucket == DeclaredPackagesBucket || - bucket == DeliveryMergesBucket || bucket == BuildsBucket || bucket == RolloutsBucket || - bucket == delivery.HaveBucket || bucket == StatesAnnouncedBucket || - bucket == GrantsBucket || bucket == delivery.ControllerComposedBucket -} - -// ControllerBuckets are the controller's own buckets, in the order they are asserted. -func ControllerBuckets() []string { - return []string{LeaseBucket, CallsBucket, HandActsBucket, ConditionsBucket, ConditionHistoryBucket, AskedBucket, - AssignmentsBucket, DeclaredConfigBucket, PredefinedBucket, DeclaredPackagesBucket, - DeliveryMergesBucket, BuildsBucket, RolloutsBucket, delivery.HaveBucket, StatesAnnouncedBucket, GrantsBucket, - delivery.ControllerComposedBucket} -} - -// ControllerBucketsAsserter is what raising the controller's buckets needs of a connection. -type ControllerBucketsAsserter interface { - EnsureControllerBuckets() error -} - -// chainBucket is the options of a bucket of the chain: sixty-four revisions of each key, no age, a value up to -// ChainValueBytes, on disk. -func chainBucket(bucket, why string, bytes int64, markers time.Duration) jetstream.KeyValueConfig { - return jetstream.KeyValueConfig{ - Bucket: bucket, - Description: why, - History: ChainHistory, - MaxValueSize: ChainValueBytes, - MaxBytes: bytes, - Storage: jetstream.FileStorage, - LimitMarkerTTL: markers, - } -} - -// EnsureLeaseBucket creates the lease's bucket if absent and brings its options to match (to-be 45 -// §6). **Before the lease is taken, by any candidate**: it is the lease's own precondition, and asserting -// a bucket that exists changes nothing. File storage, so its revisions — the epochs — outlive a restart of -// the bus; one raised again from nothing is moved past the highest epoch issued when the lease is taken. -func EnsureLeaseBucket(ctx context.Context, js jetstream.JetStream) error { - if _, err := js.CreateOrUpdateKeyValue(ctx, jetstream.KeyValueConfig{ - Bucket: LeaseBucket, - Description: "the controller's lease (novox/hq to-be 45 §6): the key `holder`, written by compare-and-set " + - "by the one controller instance that may act and renewed every five seconds; its revision when taken " + - "is the epoch every declaration and plan write carries", - History: 1, - TTL: LeaseTTL, - MaxValueSize: 4 << 10, - MaxBytes: 1 << 20, - Storage: jetstream.FileStorage, - }); err != nil { - return fmt.Errorf("asserting bucket %s: %w", LeaseBucket, err) - } - return nil -} - -// HaveBucketConfig is the options of the have bucket (novox/hq ADR 0429 §1): delivery.HaveHistory revisions of each -// key, no age, 64 MiB on disk like the assignments. A module's have is JSON of about 500 bytes, but a node's own key -// carries its whole facts (its profile among them), several kilobytes on a desktop: a value is as large as the chain's -// buckets take, since a value is never cut (novox/hq ADR 0408 §6, issue 1162). -func HaveBucketConfig() jetstream.KeyValueConfig { - return jetstream.KeyValueConfig{ - Bucket: delivery.HaveBucket, - Description: "what each machine has of each module, one key `.` (and `.node` for the machine's " + - "own facts) (novox/hq ADR 0429, design 47): written by each machine's node-engine under its own node's keys " + - "alone, never by the controller; read by the delivery view and the watchman", - History: delivery.HaveHistory, - MaxValueSize: ChainValueBytes, - MaxBytes: 64 << 20, - Storage: jetstream.FileStorage, - } -} diff --git a/go/buscompose/engine.go b/go/buscompose/engine.go index 56d93ce..6426db7 100644 --- a/go/buscompose/engine.go +++ b/go/buscompose/engine.go @@ -74,11 +74,9 @@ func engineGrants(p Principal) (pub, sub []string) { if own, err := NodeKeysGrants(EngineDataBucket, p.Node); err == nil { pub = append(pub, own...) } - // And its node's declared packages, its own key alone (ADR 0565 clause 6); and, read, the controller's mirror's key - // of its node, which the node seat's verb packages compares it with until the lease write ends the mirror. + // And its node's declared packages, its own key alone (ADR 0565 clause 6). if safeSubject.MatchString(p.Node) { pub = append(pub, EnginePackagesGrants(p.Node)...) - pub = append(pub, readGrants(DeclaredPackagesBucket, p.Node)...) } return append(pub, tp...), append(sub, ts...) } diff --git a/go/buscompose/health_grants.go b/go/buscompose/health_grants.go index 98960f7..0b811c4 100644 --- a/go/buscompose/health_grants.go +++ b/go/buscompose/health_grants.go @@ -10,64 +10,80 @@ import ( "git.novox.be/novox/mesh-sdk/go/health" ) -// **The health holders' grants** (novox/hq ADR 0564 clauses 2, 5 and 8, Limits 2, 4 and 8; issue 1952 S5). +// **The health holders' grants** (novox/hq ADR 0564 clauses 2, 5 and 8, Limits 2, 4 and 8; issue 1952 S5; ADR 0590). // -// - The condition keeper reads the controller's open conditions until the lease write, for what clauses 8 and 9 carry -// from them, and mesh-healers the controller's hand-act log, for S15 (Limits 2): both buckets the controller -// announces readable by nobody, so each is read by its named reader alone. The keeper, mesh-healers and the -// watchman read the controller's lease (Limits 8). Nobody reads mesh-healers' `would` (clause 8): what the healers -// would have done reaches no condition. Every observer's `findings`, the healers' `attempts`, the wants and the -// haves are read through `reads` as any announced state is; and no module writes another's state, so an observer -// can never write the keeper's buckets (clause 2). -// - The watchman makes its own consumer on DEAD_LETTER_NOTICES, by its own name, pulled, and reads whether the -// controller's consumer `controller` still stands; it may neither delete nor change that consumer (clause 5). It -// lists and shows DEAD_LETTERS, which is all it does before the lease write. +// - Nobody reads mesh-healers' `would` (clause 8): what the healers would have done reaches no condition. Every +// observer's `findings`, the healers' `attempts`, the wants and the haves are read through `reads` as any announced +// state is; and no module writes another's state, so an observer can never write the keeper's buckets (clause 2). +// - The watchman makes its own consumer on DEAD_LETTER_NOTICES, by its own name, pulled, and reads whether the ended +// controller's consumer `controller` still stands; it may neither delete nor change that consumer (clause 5). // - The bus module's tools run on an account of their own, declared by their bundle's `bus` with `removes` -// (catalogue.BusAccounts), which may remove only the consumer `controller` on DEAD_LETTER_NOTICES and keys of the -// node-engine's acts bucket (Limits 4), never the backup's account `broker`, which only reads (ADR 0235). +// (catalogue.BusAccounts), which may remove only keys of the node-engine's acts bucket (Limits 4) and the ended +// controller's leftovers (ADR 0590 clause 6), never the backup's account `broker`, which only reads (ADR 0235). -// The states the composer names its readers for, beside the lease (NamedReaders). -var ( - // ControllerConditions is the controller's open conditions, read by the keeper alone (ADR 0564 clauses 8, 9). - ControllerConditions = ControllerSeat + ".conditions" - // ControllerHandActs is the controller's hand-act log, read by mesh-healers alone of the four health holders (ADR - // 0564 Limits 2). - ControllerHandActs = ControllerSeat + ".hand-acts" - // HealersWould is what mesh-healers would have done while the lease names a controller: read by nobody (clause 8). - HealersWould = health.HealersModule + "." + health.WouldState -) +// HealersWould is what mesh-healers would have done while a controller was placed: read by nobody (ADR 0564 clause 8). +var HealersWould = health.HealersModule + "." + health.WouldState -// BusRemovalGrants are the whole authority of an account whose bus declares removals (ADR 0564 Limits 4, issue 1952 -// notes 6 and 7), each removal its grant: the controller's consumer on DEAD_LETTER_NOTICES read and deleted (the lease -// write's step, clause 5); the node-engine's acts bucket read (STREAM.INFO with a subject filter lists what a removal -// would take, so the tools read before they remove) and its keys removed, which nothing else may empty when it fills; the ASSIGNMENTS stream read and deleted whole (the lease -// write, research 072 row 33). A -// removal outside the set is refused, as the indexer's judge refuses it in a manifest (novox/hq ADR 0578 §1). -func BusRemovalGrants(removes []string) ([]string, error) { +// ControllerLeftoverConsumers are the ended controller's durable consumers left on the bus, and one leftover of the +// module mesh-delivery placed nowhere (issue 1126, milestone section 3), each `.`. +var ControllerLeftoverConsumers = []string{ + "EVENTS." + ControllerName, "EVENTS." + MergesConsumerName, "EVENTS.novox_mesh-delivery", + "CONTROL." + ControllerName, DeadLetterNoticesStream + "." + ControllerName, +} + +// ControllerLeftoverBuckets are the ended controller's buckets, each by its local name (`KV_mesh-controller_`). +var ControllerLeftoverBuckets = []string{"asked", "assignments", "builds", "calls", "checks-running", "composed", + "condition-history", "conditions", "declared-config", "declared-packages", "delivery-merges", "grants", "hand-acts", + "lease", "predefined", "rollouts", "states-announced"} + +// ControllerLeftoverStreams are the ended controller's streams, by their names: CONTROL, ASSIGNMENTS, each of its buckets' +// and, for each node given, the declaration files' object bucket the controller wrote (ADR 0486 §2). +func ControllerLeftoverStreams(nodes []string) []string { + out := []string{"CONTROL", AssignmentsStream} + for _, b := range ControllerLeftoverBuckets { + out = append(out, "KV_"+BucketName(catalogue.EndedControllerName, b)) + } + for _, n := range nodes { + if safeSubject.MatchString(n) { + out = append(out, "OBJ_"+DeclarationFilesBucket+n) + } + } + return out +} + +// BusRemovalGrants are the whole authority of an account whose bus declares removals (ADR 0564 Limits 4, ADR 0590 clause +// 6), each removal its grant: the node-engine's acts bucket read (STREAM.INFO with a subject filter lists what a removal +// would take, so the tools read before they remove) and its keys removed, which nothing else may empty when it fills; and +// the ended controller's leftovers, each consumer and each stream read and deleted by its name, for the nodes given (the +// declaration files' object buckets). A removal outside the set is refused, as the indexer's judge refuses it in a +// manifest (novox/hq ADR 0578 §1). +func BusRemovalGrants(removes, nodes []string) ([]string, error) { var out []string for _, r := range removes { switch r { - case catalogue.RemovesControllerDeadLetterConsumer: - out = append(out, "$JS.API.CONSUMER.INFO."+DeadLetterNoticesStream+"."+ControllerName, - "$JS.API.CONSUMER.DELETE."+DeadLetterNoticesStream+"."+ControllerName) case catalogue.RemovesEngineActs: out = append(out, "$JS.API.STREAM.INFO.KV_"+EngineActsBucket, "$KV."+EngineActsBucket+".>") - case catalogue.RemovesAssignmentsStream: - out = append(out, "$JS.API.STREAM.INFO."+AssignmentsStream, "$JS.API.STREAM.DELETE."+AssignmentsStream) + case catalogue.RemovesControllerLeftovers: + for _, c := range ControllerLeftoverConsumers { + out = append(out, "$JS.API.CONSUMER.INFO."+c, "$JS.API.CONSUMER.DELETE."+c) + } + for _, s := range ControllerLeftoverStreams(nodes) { + out = append(out, "$JS.API.STREAM.INFO."+s, "$JS.API.STREAM.DELETE."+s) + } default: - return nil, fmt.Errorf("a bus account removes %q, which is not one of %s (novox/hq ADR 0564 Limits 4)", r, - strings.Join([]string{catalogue.RemovesControllerDeadLetterConsumer, catalogue.RemovesEngineActs, - catalogue.RemovesAssignmentsStream}, ", ")) + return nil, fmt.Errorf("a bus account removes %q, which is not one of %s (novox/hq ADR 0564 Limits 4, ADR 0590)", r, + strings.Join([]string{catalogue.RemovesEngineActs, catalogue.RemovesControllerLeftovers}, ", ")) } } return out, nil } -// removesAssignmentsStream is an account declared to remove the stream ASSIGNMENTS: the writers table's one exemption from -// stream definitions being the controller's, for deleting that one stream at the lease write (research 072 row 33). -func removesAssignmentsStream(p Principal, pattern string) bool { - return p.Kind == KindProcess && slices.Contains(p.Removes, catalogue.RemovesAssignmentsStream) && - pattern == "$JS.API.STREAM.DELETE."+AssignmentsStream +// removesLeftover is an account declared to remove the ended controller's leftovers deleting one of its streams: the +// writers table's one exemption from stream definitions being the grants part's (ADR 0590 clause 6). +func removesLeftover(p Principal, pattern string) bool { + name, ok := strings.CutPrefix(pattern, "$JS.API.STREAM.DELETE.") + return ok && p.Kind == KindProcess && slices.Contains(p.Removes, catalogue.RemovesControllerLeftovers) && + slices.Contains(ControllerLeftoverStreams(p.FilesFor), name) } // removesEngineActs is an account declared to remove keys of the node-engine's acts bucket: the writers table's one @@ -178,6 +194,5 @@ func deadLetterSourcesOf(r Records) []string { // keeps them until its lease is handed away and the watchman after (ADR 0564 clause 5). Delivering again stays the // controller's in L4. func keepsDeadLetters(p Principal, pattern string) bool { - return isController(p, pattern) || - (p.Kind == KindModule && p.Module == WatchmanModule && pattern == deadLetterPrefix+">") + return p.Kind == KindModule && p.Module == WatchmanModule && pattern == deadLetterPrefix+">" } diff --git a/go/buscompose/lease_cut.go b/go/buscompose/lease_cut.go deleted file mode 100644 index e1c2cb1..0000000 --- a/go/buscompose/lease_cut.go +++ /dev/null @@ -1,41 +0,0 @@ -package buscompose - -import "slices" - -// **The lease write's grants** (novox/hq ADR 0582 clause 3, issue 774). Composed with Records.LeaseCut, the list takes -// from the controller its write of the lease's key and every update, purge and delete of the lease's bucket, and gives -// the grants part the key `holder`, which it creates naming itself and deletes at a hand-back (clause 5). Whatever the -// records, the bus module's tools account may read and update the lease's bucket, for the tool that removes and -// restores its expiry; no other bucket's. - -// leaseStream is the lease bucket's stream. -var leaseStream = "KV_" + LeaseBucket - -// LeaseHolderWrite is the grants part's write of the lease's one key, from the cut. -func LeaseHolderWrite() string { return "$KV." + LeaseBucket + "." + leaseKey } - -// LeaseCutDenied are the controller's subjects of the lease's bucket that its `$JS.API.>` reaches and the cut refuses: -// the bucket's update (its expiry), purge and delete, and a message's delete (the key's last value). -func LeaseCutDenied() []string { - return []string{"$JS.API.STREAM.UPDATE." + leaseStream, "$JS.API.STREAM.PURGE." + leaseStream, - "$JS.API.STREAM.DELETE." + leaseStream, "$JS.API.STREAM.MSG.DELETE." + leaseStream} -} - -// LeaseExpiryGrants are the bus module's tools account's read and update of the lease's bucket, for its expiry. -func LeaseExpiryGrants() []string { - return []string{"$JS.API.STREAM.INFO." + leaseStream, "$JS.API.STREAM.UPDATE." + leaseStream} -} - -// writesTheLease is the controller holding the lease, or, from the cut, the grants part writing its key holder alone. -func writesTheLease(p Principal, pattern string) bool { - if p.Kind == KindController { - return true - } - return p.Kind == KindModule && p.Module == GrantsModule && p.LeaseCut && pattern == LeaseHolderWrite() -} - -// setsLeaseExpiry is the bus module's tools account updating the lease's bucket: the writers table's one exemption from -// stream definitions being the controller's beside ASSIGNMENTS' removal (ADR 0582 clause 3). -func setsLeaseExpiry(p Principal, pattern string) bool { - return p.Kind == KindProcess && len(p.Removes) > 0 && slices.Contains(LeaseExpiryGrants(), pattern) -} diff --git a/go/buscompose/nats.go b/go/buscompose/nats.go index ccdb259..73a0e91 100644 --- a/go/buscompose/nats.go +++ b/go/buscompose/nats.go @@ -15,7 +15,6 @@ package buscompose import ( "errors" "fmt" - "git.novox.be/novox/mesh-sdk/go/announce" "regexp" "slices" "sort" @@ -32,10 +31,9 @@ import ( type Kind string const ( - KindModule Kind = "module" - KindNode Kind = "node" - KindController Kind = "controller" - KindEnrolment Kind = "enrolment" + KindModule Kind = "module" + KindNode Kind = "node" + KindEnrolment Kind = "enrolment" // KindPerson is somebody reaching the mesh's tools from a workstation (design 25 §7). Its // authority is a list of tools and nothing else — not control, not declarations, not builds, // and no ability to answer anything, because a person asks. @@ -108,8 +106,9 @@ type Principal struct { // Username and inbox are ... Process string - // FilesFor are the nodes whose declaration-file buckets this principal writes, for the controller alone (novox/hq ADR 0486 §2): - // every node of the mesh, one bucket each. + // FilesFor are the nodes whose declaration-file buckets (novox/hq ADR 0486 §2) this principal may remove, for the bus + // module's tools removing the ended controller's leftovers alone (ADR 0590 clause 6): every node of the mesh, one + // bucket each. FilesFor []string Emits []string @@ -145,30 +144,15 @@ type Principal struct { // permission and nothing beside it. Invokes []string - // AsksIssued are the tools the controller asks in its own name for a credential a provider's software - // issued, each `.` (novox/hq ADR 0305): on the subject that names its caller, so the tool - // knows the controller asks. The vault's holder is granted the same tools in its own name (issue 723, - // vaultCalls); the controller's grant goes with its path in R1 (issue 682). Composed from the records, so the installer's first user list — a controller - // with no records — carries none, and moves with issue 464 like the rest of the controller's caller-named - // grants. + // AsksIssued are, for the vault's holder, the tools it asks in its own name for a credential a provider's software + // issued, each `.` (novox/hq ADR 0305, issue 723, vaultCalls): on the subject that names its caller, + // so the tool knows the vault asks. AsksIssued []string - // AsksTheVault are the vault's verbs the controller asks in its own name (VerbsTheControllerAsksTheVault, - // issue 675), those the vault's holder serves: on the subject that names its caller, so the vault knows the - // controller asks. Composed from the records like AsksIssued, so the installer's first user list carries none. - AsksTheVault []string - - // AsksTheConfig are the mesh-config verbs the controller asks in its own name (VerbsTheControllerAsksTheConfig, - // issue 925), those mesh-config's holder serves: on the subject that names its caller, so mesh-config takes a - // value as the controller carries it (ADR 0348 §8). Composed from the records like AsksTheVault, so the - // installer's first user list carries none. - AsksTheConfig []string - // AsksTheDesk says this principal is the vault's holder, which asks the desks' secret prompt in its own name // (novox/hq ADR 0349 clause 9, issue 723): `node-launcher.secret` on any machine, on the subject that names // its caller, and nothing else of that seat. With AsksIssued on the same principal, the vault's whole reach - // outside its own seat: it asks each issuing provider's tool in its own name (clause 10). The controller - // keeps its own grant to both until R1 (issue 682), for the modules still filled the old way (clause 17). + // outside its own seat: it asks each issuing provider's tool in its own name (clause 10). AsksTheDesk bool // State is the local names of the state this principal's module keeps, and Reads the state of @@ -202,18 +186,10 @@ type Principal struct { // Removes are, for a process or tools bundle account whose bus declares them, what it may remove from the bus // (novox/hq ADR 0564 Limits 4): its whole authority is then BusRemovalGrants of them. Removes []string - // LeaseCut is Records.LeaseCut on the controller and the grants part (novox/hq ADR 0582 clause 3): the controller - // loses its write of the lease, and the grants part may write the key `holder`. - LeaseCut bool // DeadLetterSources are, for the watchman, the streams a dead letter it keeps may come from, by name // (deadLetterSourcesOf, novox/hq ADR 0564 clause 5): never a bucket's. DeadLetterSources []string - // WitnessesController is a machine principal whose node-engine witnesses the controller's upgrades: - // the machine runs the controller (novox/hq ADR 0236, lease/witness.go). It may read the lease's one - // key, and nothing else of the bucket, so it can judge a new controller build and put the previous - // one back. - WitnessesController bool // Checks are the tools a machine principal's node-engine asks as a declared health check, each // `.` (novox/hq ADR 0240, to-be 48 §3): asked of the instance on its own machine and // nowhere else. @@ -302,17 +278,18 @@ var VerbsTheSelfCheckAsks = []SeatVerb{{Seat: "node-intrusion-prevention", Verb: // takes before it asks its first tier (novox/hq ADR 0404). var VerbsTheBusStepAsks = []SeatVerb{{Seat: "node-backup", Verb: "now"}} -// VerbsTheControllerAsksForASecret are the seat verbs `give` calls (novox/hq ADR 0259 §10): the operator's -// desk opens a prompt that does not show what is typed, and answers it sealed to the controller's call. -var VerbsTheControllerAsksForASecret = []SeatVerb{{Seat: "node-launcher", Verb: "secret"}} +// VerbsTheVaultAsksForASecret are the seat verbs the vault's holder calls for a secret typed at a desk (novox/hq ADR +// 0259 §10, ADR 0349 clause 9): the operator's desk opens a prompt that does not show what is typed, and answers it +// sealed to the vault's call. +var VerbsTheVaultAsksForASecret = []SeatVerb{{Seat: "node-launcher", Verb: "secret"}} -// ControllerOnly are the subjects the controller alone may publish, however wide another's grant (the review -// of 2026-10-09, M4): the desk's hidden prompt, on its seat's subjects and on any holder's own module -// subjects. A grant of every tool — the runtime's, which carries every agent's calls, or a person's `*` — would -// otherwise reach it, and the prompt says the controller asks: only the bus makes that true. -func ControllerOnly() []string { +// DeskOnly are the subjects the vault's holder alone may publish in its own name, however wide another's grant (the +// review of 2026-10-09, M4): the desk's hidden prompt, on its seat's subjects and on any holder's own module subjects. A +// grant of every tool — the runtime's, which carries every agent's calls, or a person's `*` — would otherwise reach it, +// and the prompt says the vault asks: only the bus makes that true. +func DeskOnly() []string { var out []string - for _, v := range VerbsTheControllerAsksForASecret { + for _, v := range VerbsTheVaultAsksForASecret { for _, base := range []string{"mesh.seat." + v.Seat + ".tool." + v.Verb, "mesh.mod.*.tool." + v.Seat + "." + v.Verb} { out = append(out, base, base+".*") } @@ -365,42 +342,6 @@ func MaySubscribe(perms Permissions, subject string) bool { return false } -// VerbsTheControllerAsksTheDeliveryOwner are the mesh-delivery seat's verbs the controller calls (novox/hq -// ADR 0239): its self-check reads `stalled`, and healer H2 takes the one transition the table allows -// through `close`. A mesh seat's verb is flat: no machine in the subject. -// -// And, since novox/hq ADR 0259, `release` and `stop`: the controller asks the operator for them about a -// delivery held past its bound, and calls them on the operator's warrant, with its why. -// -// And, since novox/hq ADR 0282, `times`: the self-check reads the delivery times to say a delivery over its budget. -// -// And, since novox/hq ADR 0306, `groups`: a merge heard from a head waits for the window while its delivery group -// has a member still to merge. -var VerbsTheControllerAsksTheDeliveryOwner = []SeatVerb{{Seat: "mesh-delivery", Verb: "stalled"}, - {Seat: "mesh-delivery", Verb: "close"}, {Seat: "mesh-delivery", Verb: "release"}, {Seat: "mesh-delivery", Verb: "stop"}, - {Seat: "mesh-delivery", Verb: "times"}, {Seat: "mesh-delivery", Verb: "groups"}} - -// VerbsTheControllerAsksTheVault are the mesh-vault seat's verbs the controller calls in its own name (novox/hq -// ADR 0349 clauses 8 to 10, design 57's V2, issue 675): it carries a value given at its terminal, typed at a desk -// or issued by a provider's software into the vault, sealed to a key the vault made for the call, and asks a -// rotation. The vault takes these from the controller alone, which it knows from the subject that names its -// caller; granted as Principal.AsksTheVault says. -// -// And `move-asks` (design 57's V3, issue 676): the controller copies its record of asks at a desk, which holds no -// value, into the vault's. -// -// And `get` (novox/hq ADR 0349 §16, issue 755): the controller asks for a core module's own credential sealed to its -// machine's key, which it carries in that machine's declaration and cannot open (mesh-vault's core.go). -var VerbsTheControllerAsksTheVault = []SeatVerb{{Seat: "mesh-vault", Verb: "accept"}, - {Seat: "mesh-vault", Verb: "secret-ask"}, {Seat: "mesh-vault", Verb: "issued"}, {Seat: "mesh-vault", Verb: "rotate"}, - {Seat: "mesh-vault", Verb: "move-asks"}, {Seat: "mesh-vault", Verb: "get"}} - -// VerbsTheControllerAsksTheConfig are the mesh-config seat's verbs the controller calls in its own name (issue 925): -// `config`, the one verb mesh-config serves, which move-config uses to set each value and the settings verb to set, -// clear and propose through mesh-config. mesh-config takes a value as the controller carries it only from the -// subject that names the controller as caller (ADR 0348 §8); granted as Principal.AsksTheConfig says. -var VerbsTheControllerAsksTheConfig = []SeatVerb{{Seat: "mesh-config", Verb: "config"}} - // VaultMoveIn is the vault's verb a machine's node-engine asks in its own name to hand in a value sealed to its // machine (design 57's V3, issue 676); granted as Principal.MovesIntoTheVault says. var VaultMoveIn = SeatVerb{Seat: "mesh-vault", Verb: "move-in"} @@ -444,124 +385,6 @@ func NodeLoginsReads(node string) []string { return readGrants(delivery.NodeLoginsBucket, node) } -// VerbsTheControllerActsOnAWarrant are the other seat verbs the controller calls when the operator's warrant -// chooses them (novox/hq ADR 0259): a machine's service restarted, and a walk started or stopped through the -// controller's own `plans`, a machine's held builds sent by its own `push` and the builds held after a failed release -// sent by its own `upgrade release-backlog` (novox/hq issue 1732). Named one by one; a node seat's on any machine, a -// mesh seat's flat. -var VerbsTheControllerActsOnAWarrant = []SeatVerb{{Seat: "node-service-manager", Verb: "restart"}, - {Seat: ControllerSeat, Verb: "plans"}, {Seat: ControllerSeat, Verb: "push"}, {Seat: ControllerSeat, Verb: "upgrade"}} - -// VerbsTheControllerAsksTheOperatorChannel are the operator channel's verbs the controller calls (novox/hq -// ADR 0380): `tell`, one plain message with no condition behind it — a module's own secret given, named, -// where and when and by whom, asking nothing. A mesh seat's verb is flat: no machine in the subject. -var VerbsTheControllerAsksTheOperatorChannel = []SeatVerb{{Seat: "operator-channel", Verb: "tell"}} - -// DegradableGrant is a publish grant whose absence from the installer's carried first-user list is not a -// bug in the template: a fallback covers the refusal. -type DegradableGrant struct { - Subject string - Reason string -} - -// DegradableGrants are the only publish grants TestTheInstallersFirstUserListIsWhatTheControllerWouldCompose -// may find missing from the installer's carried list without failing (novox/hq issue 835, the forge check -// of mesh-controller#345): a change that only edits mesh-host's examples can never satisfy a check that -// reads the mesh-host commit the machines actually run, so the carried list lags behind the controller's -// derived one until mesh-host's own module next builds. operator-channel.tell is the one grant the -// controller already has a fallback for: refused (no holder, or the bus refuses it), the controller raises -// the secret-given condition exactly as before (ADR 0380 §9), so an older carried list still comes up and -// works. Every other grant stays required — missing one of those is a controller that comes up and is -// refused on the first thing it tries, with no fallback that covers it. -var DegradableGrants = []DegradableGrant{ - { - Subject: announce.Subject(ControllerSeat), - Reason: "refused: the controller's own announcement waits for its first push, and until it is kept every reader of " + - "its states is granted from its `reads` as before (novox/hq ADR 0495, issue 1790)", - }, - { - Subject: "$KV." + StatesAnnouncedBucket + ".>", - Reason: "refused: no announcement is kept until the controller's first push, and until then every reader is " + - "granted from its `reads` as before (novox/hq ADR 0495, issue 1790)", - }, - { - Subject: "mesh.seat." + ControllerSeat + ".tool.push", - Reason: "refused: the ask of a held move (Release) is answered, and the refusal told, while the operator's own " + - "push at the terminal still sends the machine (issue 1732; ADR 0259)", - }, - { - Subject: "mesh.seat." + ControllerSeat + ".tool.upgrade", - Reason: "refused: the ask of the builds held after a failed release is answered, and the refusal told, while the " + - "operator's own `upgrade release-backlog` at the terminal still releases them (issue 1732; ADR 0259)", - }, - { - Subject: "mesh.seat." + VerbsTheControllerAsksTheOperatorChannel[0].Seat + ".tool." + VerbsTheControllerAsksTheOperatorChannel[0].Verb, - Reason: "refused: the controller raises the secret-given condition as before (ADR 0380 §9)", - }, - { - Subject: "$JS.ACK.EVENTS." + MergesConsumerName + ".>", - Reason: "carried only at genesis: the controller composes its own permissions and pushes them to the bus at " + - "its first push, which is when it acknowledges a merge (ADR 0407 decision 3, issue 973)", - }, - { - Subject: "$KV." + DeclaredPackagesBucket + ".>", - Reason: "carried only at genesis: the controller composes its own permissions and pushes them to the bus at its " + - "first push, and keeps the packages each node declares only after that, holding the lease (ADR 0507 §5, issue 1840)", - }, - { - Subject: "mesh.seat." + ControllerSeat + ".event.artifacts-gone", - Reason: "refused: the take-in is refused all the same and says why; only the builder's rebuild waits for the " + - "controller's first push, and the builder also asks held when a module's value or its own start comes (novox/hq " + - "issue 1937)", - }, - { - Subject: "mesh.seat." + ControllerSeat + ".event.sent", - Reason: "refused: the controller says a tier sent to a machine only for outsiders, and a send that could not " + - "be said is printed and goes on (ADR 0406 §5, issue 974)", - }, - { - Subject: "$KV." + DeliveryMergesBucket + ".>", - Reason: "refused: the controller then keeps no merge mesh-delivery announced, so the build of a moved module is " + - "refused as it is without the operator's Approve — failing closed (ADR 0420 §7, issue 1025)", - }, - { - Subject: "$KV." + BuildsBucket + ".>", - Reason: "carried only at genesis: the controller composes its own permissions and pushes them to the bus at its " + - "first push, and writes the chain only after that, holding the lease (ADR 0408 §2 and §8, issue 1089)", - }, - { - Subject: "mesh.seat." + ControllerSeat + ".event.bucket-deleted", - Reason: "refused: nothing is deleted at genesis; the controller composes its own permissions into the bus module's " + - "composition before a later start's deletions (novox/hq ADR 0539 clause 13)", - }, - { - Subject: "$KV." + delivery.ControllerComposedBucket + ".>", - Reason: "carried only at genesis: the controller composes its own permissions into the bus module's composition, " + - "and writes a composition's digest only after the bus applied it (novox/hq ADR 0539 clauses 2 and 7)", - }, - { - Subject: "$JS.ACK.KV_" + delivery.ModulesBucket + "." + TakeInConsumer + ".>", - Reason: "refused: an acknowledgement refused leaves the key delivered again after its wait, and the take-in's first " + - "batch with something new composes the bus's node, which grants it (novox/hq ADR 0544 §1)", - }, - { - Subject: "$JS.ACK.KV_" + delivery.BuilderBuildsBucket + "." + TakeInConsumer + ".>", - Reason: "refused: an acknowledgement refused leaves the key delivered again after its wait, and the take-in's first " + - "batch with something new composes the bus's node, which grants it (novox/hq ADR 0544 §1)", - }, - { - Subject: "$KV." + GrantsBucket + ".>", - Reason: "carried only at genesis: the controller composes its own permissions and pushes them to the bus at its " + - "first push, and writes a grant only after a send of the bus's user list, which comes after that (ADR 0528 " + - "clause 2, issue 1394)", - }, - { - Subject: "$KV." + RolloutsBucket + ".>", - Reason: "carried only at genesis: the controller composes its own permissions and pushes them to the bus at its " + - "first push, and writes the chain only after that, holding the lease (ADR 0408 §2 and §8, issue 1089)", - }, -} - // perMachineEvents are a node-scoped seat's events about the holder itself, whose last token is the // holder's machine (novox/hq ADR 0219): `paused.`, the build agent saying whether it takes work. var perMachineEvents = map[string]bool{"paused.*": true} @@ -596,8 +419,6 @@ func (p Principal) Username() string { return p.Login } return "node." + p.Node - case KindController: - return "controller" case KindProvisioner: // Named for the machine, as a node is: one client per machine, whichever module runs it, and never // `.`, which is the module's own account or the tool runner's. @@ -666,7 +487,7 @@ func (p Principal) inbox() string { // Permissions is what a principal may publish and subscribe, and whether it may answer. type Permissions struct { Publish []string - // PublishDeny are subjects refused although an allow covers them: the controller's alone (ControllerOnly), + // PublishDeny are subjects refused although an allow covers them: the vault's holder's alone (DeskOnly), // denied to everybody whose grant is wide enough to reach them. The server's deny outranks its allow. PublishDeny []string Subscribe []string @@ -741,13 +562,10 @@ func PermissionsFor(p Principal) (Permissions, error) { } if p.Kind == KindProcess && len(p.Removes) > 0 { - pub, err := BusRemovalGrants(p.Removes) + pub, err := BusRemovalGrants(p.Removes, p.FilesFor) if err != nil { return Permissions{}, err } - // And the lease's bucket's expiry, read and set, by the bus module's tool the grants part calls at the lease - // write and its hand-back (novox/hq ADR 0582 clauses 3, 5): the one bucket whose stream it may update. - pub = append(pub, LeaseExpiryGrants()...) sort.Strings(pub) if err := CheckWriters(p, pub); err != nil { return Permissions{}, err @@ -758,160 +576,6 @@ func PermissionsFor(p Principal) (Permissions, error) { var pub, sub []string switch p.Kind { - case KindController: - // The controller owns the mesh's own traffic and the streams. It is the only writer of - // stream definitions (design 25 §3), so it alone reaches the JetStream API — and it alone - // issues memberships (novox/hq ADR 0160), which it publishes into the assignments stream - // after each push; refused by the server on 2026-10-01 until this line named them. - // - // **Not what the machines say** (novox/hq to-be 45 §1): `mesh.control.>` is subscribed, never - // published — a machine's report has one writer, its node-engine, and the controller granted - // that subject would be a second (the writers table refuses it). It was granted from the first - // composition and nothing ever published under it. - pub = []string{"mesh.node.>", "mesh.assignment.>", "$JS.API.>"} - // **And where its consumers deliver.** A push consumer delivers on `_DELIVER.`, - // and a client bound to it subscribes exactly that; the server refused it for every - // principal the first time one bound a consumer (2026-09-28). Each kind below is granted - // its own consumers' delivery subjects and no other's. - sub = []string{"mesh.control.>", "$JS.API.>", "_DELIVER." + ControllerName, "_DELIVER." + ControllerName + ".>"} - - // Work the mesh's own flows submit to a role, and the outcomes they wait on (ADR 0121). A - // build is the one today: the controller asks, and reads the answer from the seat's event - // like the catalogue does — which is why no holder needs to publish into anybody's inbox. - // A node-scoped seat's work subject carries no node (novox/hq ADR 0190): the ask goes to - // the role, and whichever machine holding it is idle takes it. - // **And what the mesh says it did** (novox/hq ADR 0134). The control plane states its own - // facts under the seat it holds, because a role's events belong to the role and keep their - // address while the holder is replaced. Named one by one rather than as a whole namespace: - // least authority, and a fact nothing states is authority nobody uses. - for _, event := range ControllerStates { - pub = append(pub, seatEventSubject(ControllerSeat, event)) - } - - // Every module's tools: **the control plane is the way in** (novox/hq ADR 0095). A person - // or an agent asks through it and every question passes one process where an audit - // belongs — so it, alone among principals, may call any tool by name. The first `ask` on - // the new bus was refused the publish (2026-09-28). - pub = append(pub, "mesh.mod.*.tool.>") - - // **And the mesh's own verbs, as the seat it holds** (novox/hq ADR 0132, ADR 0154): - // `status`, `push`, `assign` are the mesh-controller seat's tools, served by its holder. The - // whole verb namespace of its own seat rather than a list: the list is the seat's protocol, - // which this package mirrors rather than reads, and a verb the seat does not declare is a - // subject nothing publishes. - sub = append(sub, "mesh.seat."+ControllerSeat+".tool.>") - // And says so (novox/hq ADR 0197): it answers discovery for the seat it serves. - sub = append(sub, announcing(ControllerSeat)...) - // And the verbs the self-check reads with, of any machine's holder (novox/hq to-be 45 §4). - for _, v := range VerbsTheSelfCheckAsks { - pub = append(pub, "mesh.seat."+v.Seat+".tool."+v.Verb+".*") - } - // And the bus's planned step: a snapshot taken now, before the bus is replaced (ADR 0236). - for _, v := range VerbsTheBusStepAsks { - pub = append(pub, "mesh.seat."+v.Seat+".tool."+v.Verb+".*") - } - // And the operator's desk, for a secret given there (ADR 0259 §10). - for _, v := range VerbsTheControllerAsksForASecret { - pub = append(pub, "mesh.seat."+v.Seat+".tool."+v.Verb+".*") - } - // And the operator channel, a mesh seat, asked flat: one message when a secret is given (ADR 0380). - for _, v := range VerbsTheControllerAsksTheOperatorChannel { - pub = append(pub, "mesh.seat."+v.Seat+".tool."+v.Verb) - } - // And the delivery's owner, a mesh seat, asked on its flat subjects (ADR 0239). - for _, v := range VerbsTheControllerAsksTheDeliveryOwner { - pub = append(pub, "mesh.seat."+v.Seat+".tool."+v.Verb) - } - // And the verbs a warrant chooses (novox/hq ADR 0259): a node seat's on any machine, its own flat. - for _, v := range VerbsTheControllerActsOnAWarrant { - if v.Seat == ControllerSeat { - pub = append(pub, "mesh.seat."+v.Seat+".tool."+v.Verb) - continue - } - pub = append(pub, "mesh.seat."+v.Seat+".tool."+v.Verb+".*") - } - // And asking the operator (novox/hq ADR 0259): an ask and its cancel under its own name, its warrants - // heard under its own name, the record of its asks read under its own name — as any user of the seat, - // derived the same way, from the seat its holder declares. - tp, ts := seatTrafficGrants(SeatTrafficOf(ControllerSeat, nil, p.Uses, nil)) - pub = append(pub, tp...) - sub = append(sub, ts...) - // And asks who answers (novox/hq to-be 45 §4, D3): the self-check finds every seat's holder by - // the same discovery the console reads. The question only; the answers come to its own inbox. - pub = append(pub, "$SRV.INFO") - - // The events it reacts to, and its ack subject on the stream they arrive from - // (streams.go). **Each named, not a pattern**: `mesh.mod.*.event.>` would make the - // controller a subscriber to every event in the mesh, and its permission list would stop - // saying what it is for. The one wildcard is the emitter of a provider's standing (ADR - // 0224) — still two named events, from whichever module provides. The ack grant below is scoped per stream because the controller's - // consumer name is the same on both and `$JS.ACK.CONTROL.controller.>` does not cover a - // delivery from EVENTS — a consumer that cannot ack has every message redelivered for - // ever, refused by the list it already has. - sub = append(sub, ControllerFollows...) - pub = append(pub, "$JS.ACK.EVENTS."+ControllerName+".>", "$JS.ACK.EVENTS."+MergesConsumerName+".>") - // And the take-in's two durable consumers on the indexer's and the builder's buckets (novox/hq ADR 0544 §1): - // acknowledged after each key is taken in, and no other consumer of those streams. - for _, bucket := range TakeInBuckets { - pub = append(pub, "$JS.ACK.KV_"+bucket+"."+TakeInConsumer+".>") - } - - // **Where an enrolment's answer goes**, and `allow_responses` does not cover it. That - // permits one reply to the reply subject of a message the user received — and a message a - // JetStream consumer delivers has had that field claimed for the consumer's own ack address - // (design 25 §2), so the address the controller actually answers is the one the request - // carried in its payload, which is not a reply subject as the server understands it. - // - // Verified against a real server before this line existed: the answer was refused with - // "Permissions Violation for Publish to _INBOX.enrol.anchor…", and every enrolment on the - // mesh would have timed out while the controller logged success. - // - // **The enrolment inbox space, not a blanket `_INBOX.>`.** Design 25 §4 refuses that, and - // this is not it: nothing but an enrolling node ever subscribes under this prefix, each - // scoped to its own token's, so the controller publishing here is the mesh answering - // enrolments and can reach nothing else. - pub = append(pub, "_INBOX."+enrolmentPrefix+".>") - - // **And its own buckets** (novox/hq to-be 45 §1): the calls it served and the acts done by - // hand, which it alone writes. A put is a publish to the bucket's subject, which `$JS.API.>` - // does not cover; each bucket named, not `$KV.>`, which would let it write any module's state. - // And says what it keeps, as every module does (novox/hq ADR 0495 clause 2, issue 1790). - pub = append(pub, announce.Subject(ControllerSeat)) - for _, bucket := range ControllerBuckets() { - if bucket == delivery.HaveBucket { - continue // each node's node-engine writes its own keys of it, the controller none (ADR 0429 §1) - } - if bucket == LeaseBucket && p.LeaseCut { - continue // the lease write took it (novox/hq ADR 0582 clause 3): a controller started fails on the lease - } - pub = append(pub, "$KV."+bucket+".>") - } - // **And the object bucket a declaration's large files travel in** (novox/hq ADR 0486 §2, issue 1697): the - // controller alone writes it, as a publish to the bucket's own subjects, which `$JS.API.>` does not cover. - for _, node := range p.FilesFor { - writes, err := DeclarationFilesWrites(node) - if err != nil { - return Permissions{}, err - } - pub = append(pub, writes) - // And each node's compositions, which it alone writes (novox/hq ADR 0539 clause 2). - composed, err := ComposedWrites(node) - if err != nil { - return Permissions{}, err - } - pub = append(pub, composed) - } - // **And what the bus says about itself, read-only** (novox/hq to-be 45 §3, S9): a durable - // consumer that gave up on a message, or one that was deleted. The server already publishes - // both in the mesh's own account; the controller says each as a condition in the mesh's words. - // Named, not `$JS.EVENT.>`: the other advisories are every API call the mesh makes. - sub = append(sub, BusAdvisories...) - // **And what a consumer gave up on, kept and delivered again** (novox/hq issue 330): the notice - // acknowledged once the message is copied, the copy kept, and an event delivered again to the one - // consumer that gave it up. An ask to a seat is not delivered again, so no seat's queue is granted. - pub = append(pub, "$JS.ACK."+DeadLetterNoticesStream+"."+ControllerName+".>", deadLetterPrefix+">", - againPrefix+">") - case KindProvisioner: var err error if pub, sub, err = provisionerGrants(p); err != nil { @@ -935,15 +599,6 @@ func PermissionsFor(p Principal) (Permissions, error) { // And may ask what answers (novox/hq ADR 0197): a question every service answers about // itself, its replies to the asker's own inbox. pub = append(pub, discovering()...) - // And, for an administrator (`*`, the operator), release 6's hand path (ADR 0539 clauses 2 and 10): a want written - // at the terminal, and a composition made by hand with mesh-hand-compose. - if administers(p.Invokes) { - hand, err := HandPathWrites(p.FilesFor) - if err != nil { - return Permissions{}, err - } - pub = append(pub, hand...) - } case KindEnrolment: // A leaked token is useless for anything but enrolling: it cannot read a declaration, hear @@ -963,20 +618,13 @@ func PermissionsFor(p Principal) (Permissions, error) { "enrolment: a token is issued for a node record, and that record's name is " + "the token's id") } - // **From the lease write a node enrols through the seat mesh-enrolment alone** (novox/hq ADR 0587 §4, §6): its - // user calls redeem in its own name, while the seat's holder serves it, and the controller's enrolment subject is - // kept only while the controller holds the lease. - pub = nil - if !p.LeaseHandedAway { - pub = append(pub, "mesh.control.enrol") - } - if p.Redeems { - pub = append(pub, EnrolmentRedeemCall(p)) - } - if len(pub) == 0 { - return Permissions{}, fmt.Errorf("the enrolment user of %s may enrol nowhere: the lease is handed away and "+ - "no holder of %s serves %s", p.Node, EnrolmentRedeem.Seat, EnrolmentRedeem.Verb) + // **A node enrols through the seat mesh-enrolment alone** (novox/hq ADR 0587 §4, §6, ADR 0590): its user calls + // redeem in its own name, while the seat's holder serves it. + if !p.Redeems { + return Permissions{}, fmt.Errorf("the enrolment user of %s may enrol nowhere: no holder of %s serves %s", + p.Node, EnrolmentRedeem.Seat, EnrolmentRedeem.Verb) } + pub = []string{EnrolmentRedeemCall(p)} sub = []string{p.inbox()} case KindNode: @@ -1082,13 +730,11 @@ func PermissionsFor(p Principal) (Permissions, error) { // on it rather than at the next unrelated change (ADR 0547 §2). sub = append(sub, VaultSecretChanged) } - // And, while the vault serves `sign`, its own node's certificate in its own name, the vault's public signing - // halves, read and watched, and the controller's lease key, read and watched, which says when the controller's key - // is no longer trusted (ADR 0550 §2). Read only. + // And, while the vault serves `sign`, its own node's certificate in its own name, and the vault's public signing + // halves, read and watched (ADR 0550 §2). Read only. if p.AsksTheVaultToSign { pub = append(pub, VaultSignCall(p)) pub = append(pub, readGrants(delivery.SignersBucket, "")...) - pub = append(pub, readGrants(LeaseBucket, leaseKey)...) } // And, while the vault rotates node accounts, its own node's key of node-logins, read and watched, and no other // key (novox/hq ADR 0583 §1 insight): the login it dials as; and the confirmation of a switch in its own name (§3). @@ -1129,11 +775,6 @@ func PermissionsFor(p Principal) (Permissions, error) { pub = append(pub, CreateConsumerGrants(p.CreatesConsumers)...) // And its own object bucket, where it writes the user list sealed to the bus node (ADR 0547 clause 7). pub = append(pub, GrantsUserListWrites()...) - // And, from the lease write's cut, the lease's one key, which it creates naming itself and deletes at a - // hand-back (novox/hq ADR 0582 clauses 3, 5): never the bucket's other keys or its stream. - if p.LeaseCut { - pub = append(pub, LeaseHolderWrite()) - } } // The view alone reads the build seats' workers for the backlog of asks (issue 1887 A8a). if p.Module == ViewModule { @@ -1446,7 +1087,7 @@ func PermissionsFor(p Principal) (Permissions, error) { sub = append(sub, p.inbox()) } - if p.Kind == KindModule || p.Kind == KindNode || p.Kind == KindController { + if p.Kind == KindModule || p.Kind == KindNode { // Its own reply space, and nothing wider. sub = append(sub, p.inbox()) @@ -1459,52 +1100,14 @@ func PermissionsFor(p Principal) (Permissions, error) { } // **And every tool grant again, naming its caller** (novox/hq issue 365): a call in this principal's own - // name, and an answer to a call naming anybody. The `tool` grants above stay for one release beside these, - // so callers and runtimes move without a gap; their retirement is hq issue 464. - // - // **Not the controller's, this release.** Its grants are also the installer's first user list - // (TestTheInstallersFirstUserListIsWhatTheControllerWouldCompose), judged against the node-engine the mesh - // runs, so a change to them waits on a node-engine delivery. It calls and serves on the subjects that name - // no caller meanwhile — a caller-named call to its seat reaches nobody and is asked again on those at once — - // and moves with issue 464. + // name, and an answer to a call naming anybody. The `tool` grants above stay beside these, so callers and runtimes + // move without a gap; their retirement is hq issue 464. // The vault's calls in its own name (issue 723), kept apart so the desk's grant is not denied below. var vaultsOwn []string - if p.Kind != KindController { - pub, sub = callerNamed(p.Username(), pub, sub) - if p.Kind == KindModule { - vaultsOwn = vaultCalls(p) - pub = append(pub, vaultsOwn...) - } - } else { - // **But the tools that answer a software-issued credential** (novox/hq ADR 0305 §2): those answer the - // controller alone, and know it only from the subject's last token, so it asks them in its own name — - // those tools, on any machine, and nothing else under `call`. - for _, key := range p.AsksIssued { - module, tool, ok := strings.Cut(key, ".") - if !ok || module == "" || tool == "" || strings.ContainsAny(key, "*> ") { - continue - } - pub = append(pub, "mesh.mod."+module+"."+CallKind+"."+tool+".*."+CallerToken(p.Username())) - } - // **And the vault's intake verbs** (issue 675): the vault takes a value from the controller alone, known - // from the subject's last token — the verbs its holder serves, flat, and nothing else of its seat. - for _, v := range VerbsTheControllerAsksTheVault { - if slices.Contains(p.AsksTheVault, v.Verb) { - pub = append(pub, "mesh.seat."+v.Seat+"."+CallKind+"."+v.Verb+"."+CallerToken(p.Username())) - } - } - // **And mesh-config's verb** (issue 925): it takes a trusted value from the controller alone, known from the - // subject's last token. - for _, v := range VerbsTheControllerAsksTheConfig { - if slices.Contains(p.AsksTheConfig, v.Verb) { - pub = append(pub, "mesh.seat."+v.Seat+"."+CallKind+"."+v.Verb+"."+CallerToken(p.Username())) - } - } - // And its consumer of mesh-config's resolved values, acknowledged once a change is composed (novox/hq ADR 0545): - // where mesh-config serves, as its verb, so genesis grants nothing of mesh-config. - if len(p.AsksTheConfig) > 0 { - pub = append(pub, "$JS.ACK.KV_"+ConfigResolvedBucket+"."+ConfigFollowConsumer+".>") - } + pub, sub = callerNamed(p.Username(), pub, sub) + if p.Kind == KindModule { + vaultsOwn = vaultCalls(p) + pub = append(pub, vaultsOwn...) } sort.Strings(pub) @@ -1514,23 +1117,16 @@ func PermissionsFor(p Principal) (Permissions, error) { if err := CheckWriters(p, pub); err != nil { return Permissions{}, err } - // What the controller alone may publish is denied to everybody else whose grant reaches it. + // What the vault's holder alone may publish is denied to everybody else whose grant reaches it. var deny []string - // And from the lease write's cut, the lease's bucket to the controller, whose `$JS.API.>` reaches it (novox/hq ADR - // 0582 clause 3): a controller started by mistake fails on the lease instead of taking it back. - if p.Kind == KindController && p.LeaseCut { - deny = append(deny, LeaseCutDenied()...) - } - if p.Kind != KindController { - for _, only := range ControllerOnly() { - for _, a := range pub { - if slices.Contains(vaultsOwn, a) { - continue - } - if SubjectsOverlap(a, only) { - deny = append(deny, only) - break - } + for _, only := range DeskOnly() { + for _, a := range pub { + if slices.Contains(vaultsOwn, a) { + continue + } + if SubjectsOverlap(a, only) { + deny = append(deny, only) + break } } } @@ -1547,13 +1143,13 @@ func PermissionsFor(p Principal) (Permissions, error) { PublishDeny: deny, Subscribe: sub, // A module answers what it was asked — a tool call reaches it on its own namespace, so the - // authority is bounded by having been asked — and so does the controller. A node is asked one + // authority is bounded by having been asked. A node is asked one // thing, a hand-over on its own subject (novox/hq issue 356), and answers that: the node is its // machine's engine, root there already, and it is delivered only its own subjects. What it answers is - // never trusted for being an answer — the controller reads the engine's words and records nothing. A + // never trusted for being an answer. A // provisioner client is asked by its own machine's tool runner alone, on its own machine's subject (novox/hq // ADR 0370 §3), and answers that. A person is never asked anything, and is granted nothing here. - AllowResponses: p.Kind == KindModule || p.Kind == KindController || p.Kind == KindNodeTools || p.Kind == KindNode || + AllowResponses: p.Kind == KindModule || p.Kind == KindNodeTools || p.Kind == KindNode || p.Kind == KindProvisioner, }, nil } @@ -1569,7 +1165,7 @@ func vaultCalls(p Principal) []string { } var out []string if p.AsksTheDesk { - for _, v := range VerbsTheControllerAsksForASecret { + for _, v := range VerbsTheVaultAsksForASecret { out = append(out, "mesh.seat."+v.Seat+"."+CallKind+"."+v.Verb+".*."+token) } } @@ -1682,8 +1278,6 @@ func consumerStream(p Principal) string { return "EVENTS" case KindNode: return "NODES" - case KindController: - return "CONTROL" } return "" } @@ -1694,8 +1288,6 @@ func consumerDurable(p Principal) string { return p.Node + "_" + p.Module case KindNode: return p.Node - case KindController: - return "controller" } return "" } @@ -1992,15 +1584,9 @@ func discovering() []string { } // WitnessSubjects are the subjects a machine's node-engine publishes to witness the core builds it -// places (novox/hq ADR 0236, lease/witness.go): its own node tools' PING, and the lease's key where it -// runs the controller. Reads only: a write to the bucket is `$KV..>`, the controller's alone -// (the writers table). The answers come to the machine's own inbox. +// places (novox/hq ADR 0236): its own node tools' PING. The answers come to the machine's own inbox. func WitnessSubjects(p Principal) []string { - out := []string{pingSubject(p.Node)} - if p.WitnessesController { - out = append(out, leaseReadSubject(LeaseBucket)) - } - return append(out, CheckSubjects(p)...) + return append([]string{pingSubject(p.Node)}, CheckSubjects(p)...) } // CheckSubjects are the tools a machine's node-engine asks as declared health checks (novox/hq ADR 0240, diff --git a/go/buscompose/provisioner.go b/go/buscompose/provisioner.go index fc25434..e279463 100644 --- a/go/buscompose/provisioner.go +++ b/go/buscompose/provisioner.go @@ -41,14 +41,15 @@ var ConfigResolvedBucket = BucketName("mesh-config", "resolved") // 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 controller's assignments mirror, -// where a provider's consumers are bound to it in their own machines' plans, which the provider machine's client -// reads to fill ${consumers:} with each one's credential (novox/hq ADR 0373, issue 679). It holds placement -// and plans, never a secret's value; the client writes none of it. **The seat's plans are read whole for the same +// ProvisionerReadsWhole are the buckets a provisioner client reads every key of: the seat mesh-placement's placement, +// 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 +// 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{AssignmentsBucket, provisions.PlansBucket} +var ProvisionerReadsWhole = []string{delivery.PlacementBucket, provisions.PlansBucket} // 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/go/buscompose/state.go b/go/buscompose/state.go index e0077a0..b64461e 100644 --- a/go/buscompose/state.go +++ b/go/buscompose/state.go @@ -309,9 +309,9 @@ func RaiseBuckets(a BucketAsserter, buckets []Bucket) (undeclared []string, err return nil, fmt.Errorf("listing the bus's state: %w", err) } for _, n := range names { - // A seat's cancelled set is the mesh's own (novox/hq ADR 0219), not a module's state; so are - // the controller's own buckets (novox/hq to-be 45 §1). - if !declared[n] && !IsCancelledSet(n) && !IsControllerBucket(n) { + // A seat's cancelled set is the mesh's own (novox/hq ADR 0219), not a module's state. The ended controller's + // buckets are undeclared like any other until they are removed (ADR 0590 clause 6). + if !declared[n] && !IsCancelledSet(n) { undeclared = append(undeclared, n) } } diff --git a/go/buscompose/testdata/composed.conf b/go/buscompose/testdata/composed.conf index 7560fd8..b2bd3d7 100644 --- a/go/buscompose/testdata/composed.conf +++ b/go/buscompose/testdata/composed.conf @@ -28,23 +28,18 @@ accounts { { service: { account: "$SYS", subject: "$SYS.REQ.ACCOUNT.MESH.CONNZ" }, to: "mesh.bus.connz" } ] users = [ - { user: "controller", password: "$2a$11$cccccccccccccccccccccc", permissions: { - publish: { allow: ["$JS.ACK.CONTROL.controller.>", "$JS.ACK.DEAD_LETTER_NOTICES.controller.>", "$JS.ACK.EVENTS.controller-merges.>", "$JS.ACK.EVENTS.controller.>", "$JS.ACK.KV_mesh-delivery-builder_builds.mesh-controller-take-in.>", "$JS.ACK.KV_mesh-delivery-indexer_modules.mesh-controller-take-in.>", "$JS.API.>", "$KV.mesh-controller_asked.>", "$KV.mesh-controller_assignments.>", "$KV.mesh-controller_builds.>", "$KV.mesh-controller_calls.>", "$KV.mesh-controller_composed.>", "$KV.mesh-controller_condition-history.>", "$KV.mesh-controller_conditions.>", "$KV.mesh-controller_declared-config.>", "$KV.mesh-controller_declared-packages.>", "$KV.mesh-controller_delivery-merges.>", "$KV.mesh-controller_grants.>", "$KV.mesh-controller_hand-acts.>", "$KV.mesh-controller_lease.>", "$KV.mesh-controller_predefined.>", "$KV.mesh-controller_rollouts.>", "$KV.mesh-controller_states-announced.>", "$SRV.INFO", "_INBOX.enrol.>", "mesh.again.>", "mesh.assignment.>", "mesh.events.dead.>", "mesh.mod.*.tool.>", "mesh.mod.mesh-controller.event.states", "mesh.node.>", "mesh.seat.mesh-controller.event.applied", "mesh.seat.mesh-controller.event.artifacts-gone", "mesh.seat.mesh-controller.event.assignment-changed", "mesh.seat.mesh-controller.event.bucket-deleted", "mesh.seat.mesh-controller.event.built-before", "mesh.seat.mesh-controller.event.checked", "mesh.seat.mesh-controller.event.condition-changed", "mesh.seat.mesh-controller.event.condition-cleared", "mesh.seat.mesh-controller.event.condition-raised", "mesh.seat.mesh-controller.event.declared-config-changed", "mesh.seat.mesh-controller.event.doctor-heartbeat", "mesh.seat.mesh-controller.event.healer-acted", "mesh.seat.mesh-controller.event.predefined-changed", "mesh.seat.mesh-controller.event.refused", "mesh.seat.mesh-controller.event.rolled-back", "mesh.seat.mesh-controller.event.secret-replaced", "mesh.seat.mesh-controller.tool.plans", "mesh.seat.mesh-controller.tool.push", "mesh.seat.mesh-controller.tool.upgrade", "mesh.seat.mesh-delivery.tool.close", "mesh.seat.mesh-delivery.tool.groups", "mesh.seat.mesh-delivery.tool.release", "mesh.seat.mesh-delivery.tool.stalled", "mesh.seat.mesh-delivery.tool.stop", "mesh.seat.mesh-delivery.tool.times", "mesh.seat.node-backup.tool.backed-up.*", "mesh.seat.node-backup.tool.now.*", "mesh.seat.node-intrusion-prevention.tool.banned.*", "mesh.seat.node-launcher.tool.secret.*", "mesh.seat.node-service-manager.tool.restart.*", "mesh.seat.operator-channel.tool.tell"] } - subscribe: { allow: ["$JS.API.>", "$JS.EVENT.ADVISORY.CONSUMER.DELETED.>", "$JS.EVENT.ADVISORY.CONSUMER.MAX_DELIVERIES.>", "$SRV.INFO", "$SRV.INFO.mesh-controller", "$SRV.INFO.mesh-controller.>", "$SRV.PING", "$SRV.PING.mesh-controller", "$SRV.PING.mesh-controller.>", "$SRV.STATS", "$SRV.STATS.mesh-controller", "$SRV.STATS.mesh-controller.>", "_DELIVER.controller", "_DELIVER.controller.>", "_INBOX.controller.>", "mesh.control.>", "mesh.mod.*.event.provisioner.failing", "mesh.mod.*.event.provisioner.recovered", "mesh.mod.*.event.provisioner.retirement", "mesh.mod.gitea.event.pull.merged", "mesh.mod.gitea.event.pull.updated", "mesh.mod.mesh-catalog.event.catching-up", "mesh.mod.mesh-catalog.event.upgraded", "mesh.seat.mesh-build-machine.event.built", "mesh.seat.mesh-controller.tool.>", "mesh.seat.node-build-agent.event.built", "mesh.seat.operator-channel.event.decided.mesh-controller"] } - allow_responses: { max: 1, ttl: "1m" } - } } { user: "enrol.one", password: "$2a$11$eeeeeeeeeeeeeeeeeeeeee", permissions: { - publish: { allow: ["mesh.control.enrol"] } + publish: { allow: ["mesh.seat.mesh-enrolment.call.redeem.enrol~one"] } 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-delivery-assigner_wants.*.$KV.mesh-delivery-assigner_wants.want.one.>", "$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-delivery-assigner_wants.>", "$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-delivery-assigner_wants.$KV.mesh-delivery-assigner_wants.want.one.>", "$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-delivery-assigner_wants", "$JS.API.STREAM.INFO.KV_node-engine_have", "$JS.FC.KV_composed-one.>", "$JS.FC.KV_mesh-delivery-assigner_wants.>", "$JS.FC.KV_node-engine_have.>", "$JS.FC.OBJ_declaration-files-one.>", "$KV.node-engine_have.one.>", "$SRV.PING.node-tools.one", "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"] } - subscribe: { allow: ["_DELIVER.one", "_DELIVER.one.>", "_INBOX.node.one.>", "mesh.mod.restic.event.backed-up", "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"] } + 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_manifests.>", "$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_modules.>", "$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_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_manifests.>", "$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_modules.>", "$JS.API.CONSUMER.DELETE.KV_mesh-host_acts.>", "$JS.API.CONSUMER.DELETE.KV_mesh-host_data.>", "$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_manifests.>", "$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_modules.>", "$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_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_manifests", "$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_modules", "$JS.API.STREAM.INFO.KV_mesh-host_acts", "$JS.API.STREAM.INFO.KV_mesh-host_data", "$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_manifests.>", "$JS.FC.KV_mesh-delivery-indexer_modules.>", "$JS.FC.KV_mesh-host_acts.>", "$JS.FC.KV_mesh-host_data.>", "$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.mesh-host_acts.one.>", "$KV.mesh-host_data.one.>", "$KV.mesh-host_health.one", "$KV.mesh-host_packages.one", "$KV.node-engine_have.one.>", "$SRV.INFO", "$SRV.INFO.>", "$SRV.PING", "$SRV.PING.>", "$SRV.PING.node-tools.one", "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.mod.restic.event.backed-up", "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: { - publish: { allow: ["$JS.ACK.EVENTS.one_mesh-delivery-watchman.>", "$JS.API.CONSUMER.INFO.EVENTS.one_mesh-delivery-watchman", "$JS.API.CONSUMER.MSG.NEXT.EVENTS.one_mesh-delivery-watchman", "$JS.API.DIRECT.GET.ASSIGNMENTS.mesh.assignment.one.mesh-delivery-watchman", "mesh.bus.connz"] } - subscribe: { allow: ["$SRV.INFO", "$SRV.INFO.mesh-delivery-watchman", "$SRV.INFO.mesh-delivery-watchman.>", "$SRV.PING", "$SRV.PING.mesh-delivery-watchman", "$SRV.PING.mesh-delivery-watchman.>", "$SRV.STATS", "$SRV.STATS.mesh-delivery-watchman", "$SRV.STATS.mesh-delivery-watchman.>", "_INBOX.one.mesh-delivery-watchman.>", "mesh.assignment.one.mesh-delivery-watchman", "mesh.bus.connect", "mesh.bus.disconnect", "mesh.mod.mesh-delivery-watchman.call.>", "mesh.mod.mesh-delivery-watchman.tool.>"] } + publish: { allow: ["$JS.ACK.DEAD_LETTER_NOTICES.mesh-delivery-watchman.>", "$JS.ACK.EVENTS.one_mesh-delivery-watchman.>", "$JS.API.CONSUMER.CREATE.DEAD_LETTER_NOTICES.mesh-delivery-watchman", "$JS.API.CONSUMER.CREATE.KV_mesh-grants_memberships.*.$KV.mesh-grants_memberships.one.mesh-delivery-watchman", "$JS.API.CONSUMER.DELETE.KV_mesh-grants_memberships.>", "$JS.API.CONSUMER.INFO.DEAD_LETTER_NOTICES.controller", "$JS.API.CONSUMER.INFO.DEAD_LETTER_NOTICES.mesh-delivery-watchman", "$JS.API.CONSUMER.INFO.EVENTS.one_mesh-delivery-watchman", "$JS.API.CONSUMER.MSG.NEXT.DEAD_LETTER_NOTICES.mesh-delivery-watchman", "$JS.API.CONSUMER.MSG.NEXT.EVENTS.one_mesh-delivery-watchman", "$JS.API.DIRECT.GET.KV_mesh-grants_memberships.$KV.mesh-grants_memberships.one.mesh-delivery-watchman", "$JS.API.DIRECT.GET.PROV_APPLIED.>", "$JS.API.STREAM.INFO.DEAD_LETTERS", "$JS.API.STREAM.INFO.DEAD_LETTER_NOTICES", "$JS.API.STREAM.INFO.KV_mesh-grants_memberships", "$JS.API.STREAM.INFO.PROV_APPLIED", "$JS.API.STREAM.MSG.GET.DEAD_LETTERS", "$JS.API.STREAM.MSG.GET.PROV_APPLIED", "$JS.FC.KV_mesh-grants_memberships.>", "mesh.bus.connz", "mesh.events.dead.>"] } + subscribe: { allow: ["$JS.EVENT.ADVISORY.CONSUMER.DELETED.>", "$SRV.INFO", "$SRV.INFO.mesh-delivery-watchman", "$SRV.INFO.mesh-delivery-watchman.>", "$SRV.PING", "$SRV.PING.mesh-delivery-watchman", "$SRV.PING.mesh-delivery-watchman.>", "$SRV.STATS", "$SRV.STATS.mesh-delivery-watchman", "$SRV.STATS.mesh-delivery-watchman.>", "_INBOX.one.mesh-delivery-watchman.>", "mesh.bus.connect", "mesh.bus.disconnect", "mesh.control.*.tools-alive", "mesh.events.dead.>", "mesh.mod.*.event.provisioner.failing", "mesh.mod.*.event.provisioner.recovered", "mesh.mod.mesh-delivery-watchman.call.>", "mesh.mod.mesh-delivery-watchman.tool.>", "mesh.mod.nats.event.bus.connection-closed", "mesh.mod.nats.event.bus.slow-consumer", "mesh.mod.power.event.*", "mesh.seat.mesh-self-check.event.checked", "prov.applied.>"] } allow_responses: { max: 1, ttl: "1m" } } } { user: "one.nats", password: "$2a$11$bbbbbbbbbbbbbbbbbbbbbb", permissions: { @@ -52,18 +47,18 @@ accounts { subscribe: { allow: ["_INBOX.one.nats.>"] } } } { user: "one.telegram", password: "$2a$11$tttttttttttttttttttttt", permissions: { - publish: { allow: ["$JS.ACK.EVENTS.one_telegram.>", "$JS.ACK.SEAT_TELEGRAM_SENDER.SEAT_TELEGRAM_SENDER_worker.>", "$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.ASSIGNMENTS.mesh.assignment.one.telegram", "mesh.seat.telegram-sender.event.delivered", "mesh.seat.telegram-sender.event.failed"] } - subscribe: { allow: ["$SRV.INFO", "$SRV.INFO.telegram", "$SRV.INFO.telegram.>", "$SRV.PING", "$SRV.PING.telegram", "$SRV.PING.telegram.>", "$SRV.STATS", "$SRV.STATS.telegram", "$SRV.STATS.telegram.>", "_INBOX.one.telegram.>", "mesh.assignment.one.telegram", "mesh.mod.telegram.call.>", "mesh.mod.telegram.tool.>", "mesh.seat.telegram-sender.accept.send"] } + publish: { allow: ["$JS.ACK.EVENTS.one_telegram.>", "$JS.ACK.SEAT_TELEGRAM_SENDER.SEAT_TELEGRAM_SENDER_worker.>", "$JS.API.CONSUMER.CREATE.KV_mesh-grants_memberships.*.$KV.mesh-grants_memberships.one.telegram", "$JS.API.CONSUMER.DELETE.KV_mesh-grants_memberships.>", "$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.>", "mesh.seat.telegram-sender.event.delivered", "mesh.seat.telegram-sender.event.failed"] } + subscribe: { allow: ["$SRV.INFO", "$SRV.INFO.telegram", "$SRV.INFO.telegram.>", "$SRV.PING", "$SRV.PING.telegram", "$SRV.PING.telegram.>", "$SRV.STATS", "$SRV.STATS.telegram", "$SRV.STATS.telegram.>", "_INBOX.one.telegram.>", "mesh.mod.telegram.call.>", "mesh.mod.telegram.tool.>", "mesh.seat.telegram-sender.accept.send"] } allow_responses: { max: 1, ttl: "1m" } } } { user: "two.audit", password: "$2a$11$aaaaaaaaaaaaaaaaaaaaaa", permissions: { - publish: { allow: ["$JS.ACK.EVENTS.two_audit.>", "$JS.API.CONSUMER.INFO.EVENTS.two_audit", "$JS.API.CONSUMER.MSG.NEXT.EVENTS.two_audit", "$JS.API.DIRECT.GET.ASSIGNMENTS.mesh.assignment.two.audit"] } - subscribe: { allow: ["$SRV.INFO", "$SRV.INFO.audit", "$SRV.INFO.audit.>", "$SRV.PING", "$SRV.PING.audit", "$SRV.PING.audit.>", "$SRV.STATS", "$SRV.STATS.audit", "$SRV.STATS.audit.>", "_INBOX.two.audit.>", "mesh.assignment.two.audit", "mesh.mod.audit.call.>", "mesh.mod.audit.tool.>", "mesh.mod.shop.event.order.placed"] } + publish: { allow: ["$JS.ACK.EVENTS.two_audit.>", "$JS.API.CONSUMER.CREATE.KV_mesh-grants_memberships.*.$KV.mesh-grants_memberships.two.audit", "$JS.API.CONSUMER.DELETE.KV_mesh-grants_memberships.>", "$JS.API.CONSUMER.INFO.EVENTS.two_audit", "$JS.API.CONSUMER.MSG.NEXT.EVENTS.two_audit", "$JS.API.DIRECT.GET.KV_mesh-grants_memberships.$KV.mesh-grants_memberships.two.audit", "$JS.API.STREAM.INFO.KV_mesh-grants_memberships", "$JS.FC.KV_mesh-grants_memberships.>"] } + subscribe: { allow: ["$SRV.INFO", "$SRV.INFO.audit", "$SRV.INFO.audit.>", "$SRV.PING", "$SRV.PING.audit", "$SRV.PING.audit.>", "$SRV.STATS", "$SRV.STATS.audit", "$SRV.STATS.audit.>", "_INBOX.two.audit.>", "mesh.mod.audit.call.>", "mesh.mod.audit.tool.>", "mesh.mod.shop.event.order.placed"] } allow_responses: { max: 1, ttl: "1m" } } } { user: "two.shop", password: "$2a$11$ssssssssssssssssssssss", permissions: { - publish: { allow: ["$JS.ACK.EVENTS.two_shop.>", "$JS.API.CONSUMER.INFO.EVENTS.two_shop", "$JS.API.CONSUMER.MSG.NEXT.EVENTS.two_shop", "$JS.API.DIRECT.GET.ASSIGNMENTS.mesh.assignment.two.shop", "mesh.mod.shop.event.order.placed", "mesh.seat.telegram-sender.accept.send"] } - subscribe: { allow: ["$SRV.INFO", "$SRV.INFO.shop", "$SRV.INFO.shop.>", "$SRV.PING", "$SRV.PING.shop", "$SRV.PING.shop.>", "$SRV.STATS", "$SRV.STATS.shop", "$SRV.STATS.shop.>", "_INBOX.two.shop.>", "mesh.assignment.two.shop", "mesh.mod.shop.call.>", "mesh.mod.shop.tool.>"] } + publish: { allow: ["$JS.ACK.EVENTS.two_shop.>", "$JS.API.CONSUMER.CREATE.KV_mesh-grants_memberships.*.$KV.mesh-grants_memberships.two.shop", "$JS.API.CONSUMER.DELETE.KV_mesh-grants_memberships.>", "$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.>", "mesh.mod.shop.event.order.placed", "mesh.seat.telegram-sender.accept.send"] } + subscribe: { allow: ["$SRV.INFO", "$SRV.INFO.shop", "$SRV.INFO.shop.>", "$SRV.PING", "$SRV.PING.shop", "$SRV.PING.shop.>", "$SRV.STATS", "$SRV.STATS.shop", "$SRV.STATS.shop.>", "_INBOX.two.shop.>", "mesh.mod.shop.call.>", "mesh.mod.shop.tool.>"] } allow_responses: { max: 1, ttl: "1m" } } } ] diff --git a/go/buscompose/testdata/controller-user-lists.json b/go/buscompose/testdata/controller-user-lists.json deleted file mode 100644 index a4da9e9..0000000 --- a/go/buscompose/testdata/controller-user-lists.json +++ /dev/null @@ -1,283 +0,0 @@ -[ - { - "Name": "empty", - "List": "# The mesh's users, composed by the controller. 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: \"controller\", password: \"$2a$11$controllercontrollercofixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.CONTROL.controller.\u003e\", \"$JS.ACK.DEAD_LETTER_NOTICES.controller.\u003e\", \"$JS.ACK.EVENTS.controller-merges.\u003e\", \"$JS.ACK.EVENTS.controller.\u003e\", \"$JS.ACK.KV_mesh-delivery-builder_builds.mesh-controller-take-in.\u003e\", \"$JS.ACK.KV_mesh-delivery-indexer_modules.mesh-controller-take-in.\u003e\", \"$JS.API.\u003e\", \"$KV.mesh-controller_asked.\u003e\", \"$KV.mesh-controller_assignments.\u003e\", \"$KV.mesh-controller_builds.\u003e\", \"$KV.mesh-controller_calls.\u003e\", \"$KV.mesh-controller_composed.\u003e\", \"$KV.mesh-controller_condition-history.\u003e\", \"$KV.mesh-controller_conditions.\u003e\", \"$KV.mesh-controller_declared-config.\u003e\", \"$KV.mesh-controller_declared-packages.\u003e\", \"$KV.mesh-controller_delivery-merges.\u003e\", \"$KV.mesh-controller_grants.\u003e\", \"$KV.mesh-controller_hand-acts.\u003e\", \"$KV.mesh-controller_lease.\u003e\", \"$KV.mesh-controller_predefined.\u003e\", \"$KV.mesh-controller_rollouts.\u003e\", \"$KV.mesh-controller_states-announced.\u003e\", \"$SRV.INFO\", \"_INBOX.enrol.\u003e\", \"mesh.again.\u003e\", \"mesh.assignment.\u003e\", \"mesh.events.dead.\u003e\", \"mesh.mod.*.tool.\u003e\", \"mesh.mod.mesh-controller.event.states\", \"mesh.node.\u003e\", \"mesh.seat.mesh-controller.event.applied\", \"mesh.seat.mesh-controller.event.artifacts-gone\", \"mesh.seat.mesh-controller.event.assignment-changed\", \"mesh.seat.mesh-controller.event.bucket-deleted\", \"mesh.seat.mesh-controller.event.built-before\", \"mesh.seat.mesh-controller.event.checked\", \"mesh.seat.mesh-controller.event.condition-changed\", \"mesh.seat.mesh-controller.event.condition-cleared\", \"mesh.seat.mesh-controller.event.condition-raised\", \"mesh.seat.mesh-controller.event.declared-config-changed\", \"mesh.seat.mesh-controller.event.doctor-heartbeat\", \"mesh.seat.mesh-controller.event.healer-acted\", \"mesh.seat.mesh-controller.event.predefined-changed\", \"mesh.seat.mesh-controller.event.refused\", \"mesh.seat.mesh-controller.event.rolled-back\", \"mesh.seat.mesh-controller.event.secret-replaced\", \"mesh.seat.mesh-controller.tool.plans\", \"mesh.seat.mesh-controller.tool.push\", \"mesh.seat.mesh-controller.tool.upgrade\", \"mesh.seat.mesh-delivery.tool.close\", \"mesh.seat.mesh-delivery.tool.groups\", \"mesh.seat.mesh-delivery.tool.release\", \"mesh.seat.mesh-delivery.tool.stalled\", \"mesh.seat.mesh-delivery.tool.stop\", \"mesh.seat.mesh-delivery.tool.times\", \"mesh.seat.node-backup.tool.backed-up.*\", \"mesh.seat.node-backup.tool.now.*\", \"mesh.seat.node-intrusion-prevention.tool.banned.*\", \"mesh.seat.node-launcher.tool.secret.*\", \"mesh.seat.node-service-manager.tool.restart.*\", \"mesh.seat.operator-channel.tool.tell\"] }\n subscribe: { allow: [\"$JS.API.\u003e\", \"$JS.EVENT.ADVISORY.CONSUMER.DELETED.\u003e\", \"$JS.EVENT.ADVISORY.CONSUMER.MAX_DELIVERIES.\u003e\", \"$SRV.INFO\", \"$SRV.INFO.mesh-controller\", \"$SRV.INFO.mesh-controller.\u003e\", \"$SRV.PING\", \"$SRV.PING.mesh-controller\", \"$SRV.PING.mesh-controller.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.mesh-controller\", \"$SRV.STATS.mesh-controller.\u003e\", \"_DELIVER.controller\", \"_DELIVER.controller.\u003e\", \"_INBOX.controller.\u003e\", \"mesh.control.\u003e\", \"mesh.mod.*.event.provisioner.failing\", \"mesh.mod.*.event.provisioner.recovered\", \"mesh.mod.*.event.provisioner.retirement\", \"mesh.mod.gitea.event.pull.merged\", \"mesh.mod.gitea.event.pull.updated\", \"mesh.mod.mesh-catalog.event.catching-up\", \"mesh.mod.mesh-catalog.event.upgraded\", \"mesh.seat.mesh-build-machine.event.built\", \"mesh.seat.mesh-controller.tool.\u003e\", \"mesh.seat.node-build-agent.event.built\", \"mesh.seat.operator-channel.event.decided.mesh-controller\"] }\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": {} - }, - { - "Name": "the controller's someRecords", - "List": "# The mesh's users, composed by the controller. 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: \"controller\", password: \"$2a$11$controllercontrollercofixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.CONTROL.controller.\u003e\", \"$JS.ACK.DEAD_LETTER_NOTICES.controller.\u003e\", \"$JS.ACK.EVENTS.controller-merges.\u003e\", \"$JS.ACK.EVENTS.controller.\u003e\", \"$JS.ACK.KV_mesh-delivery-builder_builds.mesh-controller-take-in.\u003e\", \"$JS.ACK.KV_mesh-delivery-indexer_modules.mesh-controller-take-in.\u003e\", \"$JS.API.\u003e\", \"$KV.composed-one.\u003e\", \"$KV.composed-two.\u003e\", \"$KV.mesh-controller_asked.\u003e\", \"$KV.mesh-controller_assignments.\u003e\", \"$KV.mesh-controller_builds.\u003e\", \"$KV.mesh-controller_calls.\u003e\", \"$KV.mesh-controller_composed.\u003e\", \"$KV.mesh-controller_condition-history.\u003e\", \"$KV.mesh-controller_conditions.\u003e\", \"$KV.mesh-controller_declared-config.\u003e\", \"$KV.mesh-controller_declared-packages.\u003e\", \"$KV.mesh-controller_delivery-merges.\u003e\", \"$KV.mesh-controller_grants.\u003e\", \"$KV.mesh-controller_hand-acts.\u003e\", \"$KV.mesh-controller_lease.\u003e\", \"$KV.mesh-controller_predefined.\u003e\", \"$KV.mesh-controller_rollouts.\u003e\", \"$KV.mesh-controller_states-announced.\u003e\", \"$O.declaration-files-one.\u003e\", \"$O.declaration-files-two.\u003e\", \"$SRV.INFO\", \"_INBOX.enrol.\u003e\", \"mesh.again.\u003e\", \"mesh.assignment.\u003e\", \"mesh.events.dead.\u003e\", \"mesh.mod.*.tool.\u003e\", \"mesh.mod.mesh-controller.event.states\", \"mesh.node.\u003e\", \"mesh.seat.mesh-controller.event.applied\", \"mesh.seat.mesh-controller.event.artifacts-gone\", \"mesh.seat.mesh-controller.event.assignment-changed\", \"mesh.seat.mesh-controller.event.bucket-deleted\", \"mesh.seat.mesh-controller.event.built-before\", \"mesh.seat.mesh-controller.event.checked\", \"mesh.seat.mesh-controller.event.condition-changed\", \"mesh.seat.mesh-controller.event.condition-cleared\", \"mesh.seat.mesh-controller.event.condition-raised\", \"mesh.seat.mesh-controller.event.declared-config-changed\", \"mesh.seat.mesh-controller.event.doctor-heartbeat\", \"mesh.seat.mesh-controller.event.healer-acted\", \"mesh.seat.mesh-controller.event.predefined-changed\", \"mesh.seat.mesh-controller.event.refused\", \"mesh.seat.mesh-controller.event.rolled-back\", \"mesh.seat.mesh-controller.event.secret-replaced\", \"mesh.seat.mesh-controller.tool.plans\", \"mesh.seat.mesh-controller.tool.push\", \"mesh.seat.mesh-controller.tool.upgrade\", \"mesh.seat.mesh-delivery.tool.close\", \"mesh.seat.mesh-delivery.tool.groups\", \"mesh.seat.mesh-delivery.tool.release\", \"mesh.seat.mesh-delivery.tool.stalled\", \"mesh.seat.mesh-delivery.tool.stop\", \"mesh.seat.mesh-delivery.tool.times\", \"mesh.seat.node-backup.tool.backed-up.*\", \"mesh.seat.node-backup.tool.now.*\", \"mesh.seat.node-intrusion-prevention.tool.banned.*\", \"mesh.seat.node-launcher.tool.secret.*\", \"mesh.seat.node-service-manager.tool.restart.*\", \"mesh.seat.operator-channel.tool.tell\"] }\n subscribe: { allow: [\"$JS.API.\u003e\", \"$JS.EVENT.ADVISORY.CONSUMER.DELETED.\u003e\", \"$JS.EVENT.ADVISORY.CONSUMER.MAX_DELIVERIES.\u003e\", \"$SRV.INFO\", \"$SRV.INFO.mesh-controller\", \"$SRV.INFO.mesh-controller.\u003e\", \"$SRV.PING\", \"$SRV.PING.mesh-controller\", \"$SRV.PING.mesh-controller.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.mesh-controller\", \"$SRV.STATS.mesh-controller.\u003e\", \"_DELIVER.controller\", \"_DELIVER.controller.\u003e\", \"_INBOX.controller.\u003e\", \"mesh.control.\u003e\", \"mesh.mod.*.event.provisioner.failing\", \"mesh.mod.*.event.provisioner.recovered\", \"mesh.mod.*.event.provisioner.retirement\", \"mesh.mod.gitea.event.pull.merged\", \"mesh.mod.gitea.event.pull.updated\", \"mesh.mod.mesh-catalog.event.catching-up\", \"mesh.mod.mesh-catalog.event.upgraded\", \"mesh.seat.mesh-build-machine.event.built\", \"mesh.seat.mesh-controller.tool.\u003e\", \"mesh.seat.node-build-agent.event.built\", \"mesh.seat.operator-channel.event.decided.mesh-controller\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n { user: \"enrol.three\", password: \"$2a$11$enrol.threeenrol.threefixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"mesh.control.enrol\"] }\n subscribe: { allow: [\"_INBOX.enrol.three.\u003e\"] }\n } }\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-delivery-assigner_wants.*.$KV.mesh-delivery-assigner_wants.want.one.\u003e\", \"$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-delivery-assigner_wants.\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-delivery-assigner_wants.$KV.mesh-delivery-assigner_wants.want.one.\u003e\", \"$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-delivery-assigner_wants\", \"$JS.API.STREAM.INFO.KV_node-engine_have\", \"$JS.FC.KV_composed-one.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_wants.\u003e\", \"$JS.FC.KV_node-engine_have.\u003e\", \"$JS.FC.OBJ_declaration-files-one.\u003e\", \"$KV.node-engine_have.one.\u003e\", \"$SRV.PING.node-tools.one\", \"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\"] }\n subscribe: { allow: [\"_DELIVER.one\", \"_DELIVER.one.\u003e\", \"_INBOX.node.one.\u003e\", \"mesh.mod.restic.event.backed-up\", \"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\"] }\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-delivery-assigner_wants.*.$KV.mesh-delivery-assigner_wants.want.two.\u003e\", \"$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-delivery-assigner_wants.\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-delivery-assigner_wants.$KV.mesh-delivery-assigner_wants.want.two.\u003e\", \"$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-delivery-assigner_wants\", \"$JS.API.STREAM.INFO.KV_node-engine_have\", \"$JS.FC.KV_composed-two.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_wants.\u003e\", \"$JS.FC.KV_node-engine_have.\u003e\", \"$JS.FC.OBJ_declaration-files-two.\u003e\", \"$KV.node-engine_have.two.\u003e\", \"$SRV.PING.node-tools.two\", \"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\"] }\n subscribe: { allow: [\"_DELIVER.two\", \"_DELIVER.two.\u003e\", \"_INBOX.node.two.\u003e\", \"mesh.mod.restic.event.backed-up\", \"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\"] }\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.INFO.EVENTS.one_telegram\", \"$JS.API.CONSUMER.MSG.NEXT.EVENTS.one_telegram\", \"$JS.API.DIRECT.GET.ASSIGNMENTS.mesh.assignment.one.telegram\"] }\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.assignment.one.telegram\", \"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.INFO.EVENTS.two_shop\", \"$JS.API.CONSUMER.MSG.NEXT.EVENTS.two_shop\", \"$JS.API.DIRECT.GET.ASSIGNMENTS.mesh.assignment.two.shop\", \"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.assignment.two.shop\", \"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": { - "one.telegram": { - "node": "one", - "module": "telegram", - "serves": [ - { - "subject": "mesh.mod.telegram.tool.{tool}.one" - }, - { - "subject": "mesh.mod.telegram.tool.{tool}", - "queue": "serve.telegram" - } - ], - "emits": "mesh.mod.telegram.event.{event}", - "tools": "mesh.mod.telegram.tool.tools" - }, - "two.shop": { - "node": "two", - "module": "shop", - "serves": [ - { - "subject": "mesh.mod.shop.tool.{tool}.two" - }, - { - "subject": "mesh.mod.shop.tool.{tool}", - "queue": "serve.shop" - } - ], - "emits": "mesh.mod.shop.event.{event}", - "tools": "mesh.mod.shop.tool.tools" - } - } - }, - { - "Name": "seats, consumers, people and a hashless user", - "List": "# The mesh's users, composed by the controller. 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: \"controller\", password: \"$2a$11$controllercontrollercofixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.CONTROL.controller.\u003e\", \"$JS.ACK.DEAD_LETTER_NOTICES.controller.\u003e\", \"$JS.ACK.EVENTS.controller-merges.\u003e\", \"$JS.ACK.EVENTS.controller.\u003e\", \"$JS.ACK.KV_mesh-delivery-builder_builds.mesh-controller-take-in.\u003e\", \"$JS.ACK.KV_mesh-delivery-indexer_modules.mesh-controller-take-in.\u003e\", \"$JS.API.\u003e\", \"$KV.composed-one.\u003e\", \"$KV.composed-two.\u003e\", \"$KV.mesh-controller_asked.\u003e\", \"$KV.mesh-controller_assignments.\u003e\", \"$KV.mesh-controller_builds.\u003e\", \"$KV.mesh-controller_calls.\u003e\", \"$KV.mesh-controller_composed.\u003e\", \"$KV.mesh-controller_condition-history.\u003e\", \"$KV.mesh-controller_conditions.\u003e\", \"$KV.mesh-controller_declared-config.\u003e\", \"$KV.mesh-controller_declared-packages.\u003e\", \"$KV.mesh-controller_delivery-merges.\u003e\", \"$KV.mesh-controller_grants.\u003e\", \"$KV.mesh-controller_hand-acts.\u003e\", \"$KV.mesh-controller_lease.\u003e\", \"$KV.mesh-controller_predefined.\u003e\", \"$KV.mesh-controller_rollouts.\u003e\", \"$KV.mesh-controller_states-announced.\u003e\", \"$O.declaration-files-one.\u003e\", \"$O.declaration-files-two.\u003e\", \"$SRV.INFO\", \"_INBOX.enrol.\u003e\", \"mesh.again.\u003e\", \"mesh.assignment.\u003e\", \"mesh.events.dead.\u003e\", \"mesh.mod.*.tool.\u003e\", \"mesh.mod.mesh-controller.event.states\", \"mesh.node.\u003e\", \"mesh.seat.mesh-controller.event.applied\", \"mesh.seat.mesh-controller.event.artifacts-gone\", \"mesh.seat.mesh-controller.event.assignment-changed\", \"mesh.seat.mesh-controller.event.bucket-deleted\", \"mesh.seat.mesh-controller.event.built-before\", \"mesh.seat.mesh-controller.event.checked\", \"mesh.seat.mesh-controller.event.condition-changed\", \"mesh.seat.mesh-controller.event.condition-cleared\", \"mesh.seat.mesh-controller.event.condition-raised\", \"mesh.seat.mesh-controller.event.declared-config-changed\", \"mesh.seat.mesh-controller.event.doctor-heartbeat\", \"mesh.seat.mesh-controller.event.healer-acted\", \"mesh.seat.mesh-controller.event.predefined-changed\", \"mesh.seat.mesh-controller.event.refused\", \"mesh.seat.mesh-controller.event.rolled-back\", \"mesh.seat.mesh-controller.event.secret-replaced\", \"mesh.seat.mesh-controller.tool.plans\", \"mesh.seat.mesh-controller.tool.push\", \"mesh.seat.mesh-controller.tool.upgrade\", \"mesh.seat.mesh-delivery.tool.close\", \"mesh.seat.mesh-delivery.tool.groups\", \"mesh.seat.mesh-delivery.tool.release\", \"mesh.seat.mesh-delivery.tool.stalled\", \"mesh.seat.mesh-delivery.tool.stop\", \"mesh.seat.mesh-delivery.tool.times\", \"mesh.seat.node-backup.tool.backed-up.*\", \"mesh.seat.node-backup.tool.now.*\", \"mesh.seat.node-intrusion-prevention.tool.banned.*\", \"mesh.seat.node-launcher.tool.secret.*\", \"mesh.seat.node-service-manager.tool.restart.*\", \"mesh.seat.operator-channel.tool.tell\"] }\n subscribe: { allow: [\"$JS.API.\u003e\", \"$JS.EVENT.ADVISORY.CONSUMER.DELETED.\u003e\", \"$JS.EVENT.ADVISORY.CONSUMER.MAX_DELIVERIES.\u003e\", \"$SRV.INFO\", \"$SRV.INFO.mesh-controller\", \"$SRV.INFO.mesh-controller.\u003e\", \"$SRV.PING\", \"$SRV.PING.mesh-controller\", \"$SRV.PING.mesh-controller.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.mesh-controller\", \"$SRV.STATS.mesh-controller.\u003e\", \"_DELIVER.controller\", \"_DELIVER.controller.\u003e\", \"_INBOX.controller.\u003e\", \"mesh.control.\u003e\", \"mesh.mod.*.event.provisioner.failing\", \"mesh.mod.*.event.provisioner.recovered\", \"mesh.mod.*.event.provisioner.retirement\", \"mesh.mod.gitea.event.pull.merged\", \"mesh.mod.gitea.event.pull.updated\", \"mesh.mod.mesh-catalog.event.catching-up\", \"mesh.mod.mesh-catalog.event.upgraded\", \"mesh.seat.mesh-build-machine.event.built\", \"mesh.seat.mesh-controller.tool.\u003e\", \"mesh.seat.node-build-agent.event.built\", \"mesh.seat.operator-channel.event.decided.mesh-controller\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\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-delivery-assigner_wants.*.$KV.mesh-delivery-assigner_wants.want.one.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-grants_user-list.*.$KV.mesh-grants_user-list.sealed\", \"$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-delivery-assigner_wants.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-grants_user-list.*\", \"$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-delivery-assigner_wants.$KV.mesh-delivery-assigner_wants.want.one.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-grants_user-list.$KV.mesh-grants_user-list.sealed\", \"$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-delivery-assigner_wants\", \"$JS.API.STREAM.INFO.KV_mesh-grants_user-list\", \"$JS.API.STREAM.INFO.KV_node-engine_have\", \"$JS.FC.KV_composed-one.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_wants.\u003e\", \"$JS.FC.KV_mesh-grants_user-list.\u003e\", \"$JS.FC.KV_node-engine_have.\u003e\", \"$JS.FC.OBJ_declaration-files-one.\u003e\", \"$JS.FC.OBJ_mesh-grants-user-list.\u003e\", \"$KV.node-engine_have.one.\u003e\", \"$SRV.PING.node-tools.one\", \"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\"] }\n subscribe: { allow: [\"_DELIVER.one\", \"_DELIVER.one.\u003e\", \"_INBOX.node.one.\u003e\", \"mesh.mod.restic.event.backed-up\", \"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\"] }\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-delivery-assigner_wants.*.$KV.mesh-delivery-assigner_wants.want.two.\u003e\", \"$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-delivery-assigner_wants.\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-delivery-assigner_wants.$KV.mesh-delivery-assigner_wants.want.two.\u003e\", \"$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-delivery-assigner_wants\", \"$JS.API.STREAM.INFO.KV_node-engine_have\", \"$JS.FC.KV_composed-two.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_wants.\u003e\", \"$JS.FC.KV_node-engine_have.\u003e\", \"$JS.FC.OBJ_declaration-files-two.\u003e\", \"$KV.node-engine_have.two.\u003e\", \"$SRV.PING.node-tools.two\", \"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\"] }\n subscribe: { allow: [\"_DELIVER.two\", \"_DELIVER.two.\u003e\", \"_INBOX.node.two.\u003e\", \"mesh.mod.restic.event.backed-up\", \"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\"] }\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.INFO.EVENTS.one_mesh-grants\", \"$JS.API.CONSUMER.MSG.NEXT.EVENTS.one_mesh-grants\", \"$JS.API.DIRECT.GET.ASSIGNMENTS.mesh.assignment.*.*\", \"$JS.API.DIRECT.GET.ASSIGNMENTS.mesh.assignment.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.OBJ_mesh-grants-user-list\", \"$JS.API.STREAM.MSG.GET.OBJ_mesh-grants-user-list\", \"$JS.API.STREAM.PURGE.OBJ_mesh-grants-user-list\", \"$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.assignment.one.mesh-grants\", \"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.INFO.EVENTS.one_mesh-watchman\", \"$JS.API.CONSUMER.MSG.NEXT.EVENTS.one_mesh-watchman\", \"$JS.API.DIRECT.GET.ASSIGNMENTS.mesh.assignment.one.mesh-watchman\"] }\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.assignment.one.mesh-watchman\", \"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.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.ASSIGNMENTS.mesh.assignment.one.telegram\", \"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.assignment.one.telegram\", \"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: [\"$JS.API.CONSUMER.CREATE.KV_composed-one.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_composed-two.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-controller_composed.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-assigner_wants.*.$KV.mesh-delivery-assigner_wants.want.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_composed-one.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_composed-two.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-controller_composed.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-assigner_wants.\u003e\", \"$JS.API.DIRECT.GET.KV_composed-one.\u003e\", \"$JS.API.DIRECT.GET.KV_composed-two.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-controller_composed.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-assigner_wants.$KV.mesh-delivery-assigner_wants.want.\u003e\", \"$JS.API.DIRECT.GET.OBJ_declaration-files-one.\u003e\", \"$JS.API.DIRECT.GET.OBJ_declaration-files-two.\u003e\", \"$JS.API.STREAM.INFO.KV_composed-one\", \"$JS.API.STREAM.INFO.KV_composed-two\", \"$JS.API.STREAM.INFO.KV_mesh-controller_composed\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-assigner_wants\", \"$JS.API.STREAM.INFO.OBJ_declaration-files-one\", \"$JS.API.STREAM.INFO.OBJ_declaration-files-two\", \"$JS.API.STREAM.MSG.GET.OBJ_declaration-files-one\", \"$JS.API.STREAM.MSG.GET.OBJ_declaration-files-two\", \"$JS.API.STREAM.PURGE.OBJ_declaration-files-one\", \"$JS.API.STREAM.PURGE.OBJ_declaration-files-two\", \"$JS.FC.KV_composed-one.\u003e\", \"$JS.FC.KV_composed-two.\u003e\", \"$JS.FC.KV_mesh-controller_composed.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_wants.\u003e\", \"$KV.composed-one.\u003e\", \"$KV.composed-two.\u003e\", \"$KV.mesh-controller_composed.\u003e\", \"$KV.mesh-delivery-assigner_wants.want.\u003e\", \"$O.declaration-files-one.\u003e\", \"$O.declaration-files-two.\u003e\", \"$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\"] }\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.INFO.EVENTS.two_shop\", \"$JS.API.CONSUMER.MSG.NEXT.EVENTS.two_shop\", \"$JS.API.DIRECT.GET.ASSIGNMENTS.mesh.assignment.two.shop\", \"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.assignment.two.shop\", \"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" - ], - "Err": "", - "Memberships": { - "one.mesh-grants": { - "node": "one", - "module": "mesh-grants", - "serves": [ - { - "subject": "mesh.mod.mesh-grants.tool.{tool}.one" - }, - { - "subject": "mesh.mod.mesh-grants.tool.{tool}", - "queue": "serve.mesh-grants" - } - ], - "emits": "mesh.mod.mesh-grants.event.{event}", - "tools": "mesh.mod.mesh-grants.tool.tools" - }, - "one.mesh-watchman": { - "node": "one", - "module": "mesh-watchman", - "serves": [ - { - "subject": "mesh.mod.mesh-watchman.tool.{tool}.one" - }, - { - "subject": "mesh.mod.mesh-watchman.tool.{tool}", - "queue": "serve.mesh-watchman" - } - ], - "emits": "mesh.mod.mesh-watchman.event.{event}", - "tools": "mesh.mod.mesh-watchman.tool.tools" - }, - "one.nats": { - "node": "one", - "module": "nats", - "serves": [ - { - "subject": "mesh.mod.nats.tool.{tool}.one" - }, - { - "subject": "mesh.mod.nats.tool.{tool}", - "queue": "serve.nats" - } - ], - "emits": "mesh.mod.nats.event.{event}", - "tools": "mesh.mod.nats.tool.tools" - }, - "one.telegram": { - "node": "one", - "module": "telegram", - "serves": [ - { - "subject": "mesh.mod.telegram.tool.{tool}.one" - }, - { - "subject": "mesh.mod.telegram.tool.{tool}", - "queue": "serve.telegram" - } - ], - "emits": "mesh.mod.telegram.event.{event}", - "tools": "mesh.mod.telegram.tool.tools" - }, - "two.audit": { - "node": "two", - "module": "audit", - "serves": [ - { - "subject": "mesh.mod.audit.tool.{tool}.two" - }, - { - "subject": "mesh.mod.audit.tool.{tool}", - "queue": "serve.audit" - } - ], - "emits": "mesh.mod.audit.event.{event}", - "tools": "mesh.mod.audit.tool.tools" - }, - "two.shop": { - "node": "two", - "module": "shop", - "serves": [ - { - "subject": "mesh.mod.shop.tool.{tool}.two" - }, - { - "subject": "mesh.mod.shop.tool.{tool}", - "queue": "serve.shop" - } - ], - "emits": "mesh.mod.shop.event.{event}", - "tools": "mesh.mod.shop.tool.tools" - } - } - }, - { - "Name": "state, logs, streams, queues and processes", - "List": "# The mesh's users, composed by the controller. 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.INFO.EVENTS.ace_plex\", \"$JS.API.CONSUMER.MSG.NEXT.EVENTS.ace_plex\", \"$JS.API.DIRECT.GET.ASSIGNMENTS.mesh.assignment.ace.plex\"] }\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.assignment.ace.plex\", \"mesh.mod.plex.call.\u003e\", \"mesh.mod.plex.tool.\u003e\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n { user: \"controller\", password: \"$2a$11$controllercontrollercofixturefixturefixturefixture00\", permissions: {\n publish: { allow: [\"$JS.ACK.CONTROL.controller.\u003e\", \"$JS.ACK.DEAD_LETTER_NOTICES.controller.\u003e\", \"$JS.ACK.EVENTS.controller-merges.\u003e\", \"$JS.ACK.EVENTS.controller.\u003e\", \"$JS.ACK.KV_mesh-delivery-builder_builds.mesh-controller-take-in.\u003e\", \"$JS.ACK.KV_mesh-delivery-indexer_modules.mesh-controller-take-in.\u003e\", \"$JS.API.\u003e\", \"$KV.composed-ace.\u003e\", \"$KV.composed-novox.\u003e\", \"$KV.mesh-controller_asked.\u003e\", \"$KV.mesh-controller_assignments.\u003e\", \"$KV.mesh-controller_builds.\u003e\", \"$KV.mesh-controller_calls.\u003e\", \"$KV.mesh-controller_composed.\u003e\", \"$KV.mesh-controller_condition-history.\u003e\", \"$KV.mesh-controller_conditions.\u003e\", \"$KV.mesh-controller_declared-config.\u003e\", \"$KV.mesh-controller_declared-packages.\u003e\", \"$KV.mesh-controller_delivery-merges.\u003e\", \"$KV.mesh-controller_grants.\u003e\", \"$KV.mesh-controller_hand-acts.\u003e\", \"$KV.mesh-controller_lease.\u003e\", \"$KV.mesh-controller_predefined.\u003e\", \"$KV.mesh-controller_rollouts.\u003e\", \"$KV.mesh-controller_states-announced.\u003e\", \"$O.declaration-files-ace.\u003e\", \"$O.declaration-files-novox.\u003e\", \"$SRV.INFO\", \"_INBOX.enrol.\u003e\", \"mesh.again.\u003e\", \"mesh.assignment.\u003e\", \"mesh.events.dead.\u003e\", \"mesh.mod.*.tool.\u003e\", \"mesh.mod.mesh-controller.event.states\", \"mesh.node.\u003e\", \"mesh.seat.mesh-controller.event.applied\", \"mesh.seat.mesh-controller.event.artifacts-gone\", \"mesh.seat.mesh-controller.event.assignment-changed\", \"mesh.seat.mesh-controller.event.bucket-deleted\", \"mesh.seat.mesh-controller.event.built-before\", \"mesh.seat.mesh-controller.event.checked\", \"mesh.seat.mesh-controller.event.condition-changed\", \"mesh.seat.mesh-controller.event.condition-cleared\", \"mesh.seat.mesh-controller.event.condition-raised\", \"mesh.seat.mesh-controller.event.declared-config-changed\", \"mesh.seat.mesh-controller.event.doctor-heartbeat\", \"mesh.seat.mesh-controller.event.healer-acted\", \"mesh.seat.mesh-controller.event.predefined-changed\", \"mesh.seat.mesh-controller.event.refused\", \"mesh.seat.mesh-controller.event.rolled-back\", \"mesh.seat.mesh-controller.event.secret-replaced\", \"mesh.seat.mesh-controller.tool.plans\", \"mesh.seat.mesh-controller.tool.push\", \"mesh.seat.mesh-controller.tool.upgrade\", \"mesh.seat.mesh-delivery.tool.close\", \"mesh.seat.mesh-delivery.tool.groups\", \"mesh.seat.mesh-delivery.tool.release\", \"mesh.seat.mesh-delivery.tool.stalled\", \"mesh.seat.mesh-delivery.tool.stop\", \"mesh.seat.mesh-delivery.tool.times\", \"mesh.seat.node-backup.tool.backed-up.*\", \"mesh.seat.node-backup.tool.now.*\", \"mesh.seat.node-intrusion-prevention.tool.banned.*\", \"mesh.seat.node-launcher.tool.secret.*\", \"mesh.seat.node-service-manager.tool.restart.*\", \"mesh.seat.operator-channel.tool.tell\"] }\n subscribe: { allow: [\"$JS.API.\u003e\", \"$JS.EVENT.ADVISORY.CONSUMER.DELETED.\u003e\", \"$JS.EVENT.ADVISORY.CONSUMER.MAX_DELIVERIES.\u003e\", \"$SRV.INFO\", \"$SRV.INFO.mesh-controller\", \"$SRV.INFO.mesh-controller.\u003e\", \"$SRV.PING\", \"$SRV.PING.mesh-controller\", \"$SRV.PING.mesh-controller.\u003e\", \"$SRV.STATS\", \"$SRV.STATS.mesh-controller\", \"$SRV.STATS.mesh-controller.\u003e\", \"_DELIVER.controller\", \"_DELIVER.controller.\u003e\", \"_INBOX.controller.\u003e\", \"mesh.control.\u003e\", \"mesh.mod.*.event.provisioner.failing\", \"mesh.mod.*.event.provisioner.recovered\", \"mesh.mod.*.event.provisioner.retirement\", \"mesh.mod.gitea.event.pull.merged\", \"mesh.mod.gitea.event.pull.updated\", \"mesh.mod.mesh-catalog.event.catching-up\", \"mesh.mod.mesh-catalog.event.upgraded\", \"mesh.seat.mesh-build-machine.event.built\", \"mesh.seat.mesh-controller.tool.\u003e\", \"mesh.seat.node-build-agent.event.built\", \"mesh.seat.operator-channel.event.decided.mesh-controller\"] }\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-delivery-assigner_wants.*.$KV.mesh-delivery-assigner_wants.want.ace.\u003e\", \"$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-delivery-assigner_wants.\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-delivery-assigner_wants.$KV.mesh-delivery-assigner_wants.want.ace.\u003e\", \"$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-delivery-assigner_wants\", \"$JS.API.STREAM.INFO.KV_node-engine_have\", \"$JS.FC.KV_composed-ace.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_wants.\u003e\", \"$JS.FC.KV_node-engine_have.\u003e\", \"$JS.FC.OBJ_declaration-files-ace.\u003e\", \"$KV.node-engine_have.ace.\u003e\", \"$SRV.PING.node-tools.ace\", \"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\"] }\n subscribe: { allow: [\"_DELIVER.ace\", \"_DELIVER.ace.\u003e\", \"_INBOX.node.ace.\u003e\", \"mesh.mod.restic.event.backed-up\", \"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\"] }\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-delivery-assigner_wants.*.$KV.mesh-delivery-assigner_wants.want.novox.\u003e\", \"$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-delivery-assigner_wants.\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-delivery-assigner_wants.$KV.mesh-delivery-assigner_wants.want.novox.\u003e\", \"$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-delivery-assigner_wants\", \"$JS.API.STREAM.INFO.KV_node-engine_have\", \"$JS.FC.KV_composed-novox.\u003e\", \"$JS.FC.KV_mesh-delivery-assigner_wants.\u003e\", \"$JS.FC.KV_node-engine_have.\u003e\", \"$JS.FC.OBJ_declaration-files-novox.\u003e\", \"$KV.node-engine_have.novox.\u003e\", \"$SRV.PING.node-tools.novox\", \"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\"] }\n subscribe: { allow: [\"_DELIVER.novox\", \"_DELIVER.novox.\u003e\", \"_INBOX.node.novox.\u003e\", \"mesh.mod.restic.event.backed-up\", \"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\"] }\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.DELETE.KV_mesh-delivery-builder_presence.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-builder_steps.\u003e\", \"$JS.API.CONSUMER.INFO.EVENTS.novox_mesh-delivery-builder\", \"$JS.API.CONSUMER.MSG.NEXT.EVENTS.novox_mesh-delivery-builder\", \"$JS.API.DIRECT.GET.ASSIGNMENTS.mesh.assignment.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.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.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\", \"$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: [\"$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.assignment.novox.mesh-delivery-builder\", \"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: [\"_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.DELETE.KV_mesh-delivery-builder_presence.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-builder_steps.\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.ASSIGNMENTS.mesh.assignment.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.STREAM.INFO.KV_mesh-delivery-builder_presence\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-builder_steps\", \"$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\"] }\n subscribe: { allow: [\"$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.assignment.novox.mesh-delivery-view\", \"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.DELETE.KV_mesh-grants_assertions.\u003e\", \"$JS.API.CONSUMER.INFO.EVENTS.novox_mesh-grants\", \"$JS.API.CONSUMER.MSG.NEXT.EVENTS.novox_mesh-grants\", \"$JS.API.DIRECT.GET.ASSIGNMENTS.mesh.assignment.*.*\", \"$JS.API.DIRECT.GET.ASSIGNMENTS.mesh.assignment.novox.mesh-grants\", \"$JS.API.DIRECT.GET.KV_mesh-grants_assertions.\u003e\", \"$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.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\", \"$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: [\"$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.assignment.novox.mesh-grants\", \"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": { - "ace.plex": { - "node": "ace", - "module": "plex", - "serves": [ - { - "subject": "mesh.mod.plex.tool.{tool}.ace" - }, - { - "subject": "mesh.mod.plex.tool.{tool}", - "queue": "serve.plex" - } - ], - "emits": "mesh.mod.plex.event.{event}", - "tools": "mesh.mod.plex.tool.tools" - }, - "ace.silent": { - "node": "ace", - "module": "silent", - "serves": [ - { - "subject": "mesh.mod.silent.tool.{tool}.ace" - }, - { - "subject": "mesh.mod.silent.tool.{tool}", - "queue": "serve.silent" - } - ], - "emits": "mesh.mod.silent.event.{event}", - "tools": "mesh.mod.silent.tool.tools" - }, - "novox.mesh-delivery-builder": { - "node": "novox", - "module": "mesh-delivery-builder", - "serves": [ - { - "subject": "mesh.mod.mesh-delivery-builder.tool.{tool}.novox" - }, - { - "subject": "mesh.mod.mesh-delivery-builder.tool.{tool}", - "queue": "serve.mesh-delivery-builder" - } - ], - "emits": "mesh.mod.mesh-delivery-builder.event.{event}", - "reaches": { - "gitea.gitea_commit": [ - "mesh.mod.gitea.tool.gitea_commit" - ] - }, - "tools": "mesh.mod.mesh-delivery-builder.tool.tools", - "state": [ - { - "name": "steps", - "bucket": "mesh-delivery-builder_steps", - "writes": true - }, - { - "name": "presence", - "bucket": "mesh-delivery-builder_presence", - "writes": true, - "key": "novox" - } - ], - "logs": [ - { - "name": "builds", - "stream": "LOG_mesh-delivery-builder_builds", - "subject": "mesh.log.mesh-delivery-builder.builds", - "writes": true - } - ], - "streams": [ - { - "stream": "asks", - "events": [ - "ask" - ], - "subject": "mesh.stream.mesh-delivery-builder.event.{event}" - } - ] - }, - "novox.mesh-delivery-view": { - "node": "novox", - "module": "mesh-delivery-view", - "serves": [ - { - "subject": "mesh.mod.mesh-delivery-view.tool.{tool}.novox" - }, - { - "subject": "mesh.mod.mesh-delivery-view.tool.{tool}", - "queue": "serve.mesh-delivery-view" - } - ], - "emits": "mesh.mod.mesh-delivery-view.event.{event}", - "tools": "mesh.mod.mesh-delivery-view.tool.tools", - "state": [ - { - "name": "mesh-delivery-builder.steps", - "bucket": "mesh-delivery-builder_steps" - }, - { - "name": "mesh-delivery-builder.presence", - "bucket": "mesh-delivery-builder_presence", - "key": "novox" - } - ] - }, - "novox.mesh-grants": { - "node": "novox", - "module": "mesh-grants", - "serves": [ - { - "subject": "mesh.mod.mesh-grants.tool.{tool}.novox" - }, - { - "subject": "mesh.mod.mesh-grants.tool.{tool}", - "queue": "serve.mesh-grants" - } - ], - "emits": "mesh.mod.mesh-grants.event.{event}", - "tools": "mesh.mod.mesh-grants.tool.tools", - "state": [ - { - "name": "assertions", - "bucket": "mesh-grants_assertions", - "writes": true - } - ] - } - } - } -] \ No newline at end of file diff --git a/go/buscompose/testdata/user-lists.json b/go/buscompose/testdata/user-lists.json new file mode 100644 index 0000000..acf9bd7 --- /dev/null +++ b/go/buscompose/testdata/user-lists.json @@ -0,0 +1,289 @@ +[ + { + "Name": "empty", + "List": "# The mesh's users, composed by the controller. 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 ]\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": {} + }, + { + "Name": "the controller's someRecords", + "List": "# The mesh's users, composed by the controller. 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_manifests.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_modules.\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_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_manifests.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_modules.\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_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_manifests.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_modules.\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_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_manifests\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_modules\", \"$JS.API.STREAM.INFO.KV_mesh-host_acts\", \"$JS.API.STREAM.INFO.KV_mesh-host_data\", \"$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_manifests.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.FC.KV_mesh-host_acts.\u003e\", \"$JS.FC.KV_mesh-host_data.\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.mesh-host_acts.one.\u003e\", \"$KV.mesh-host_data.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.\u003e\", \"$SRV.PING.node-tools.one\", \"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.mod.restic.event.backed-up\", \"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_manifests.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_modules.\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_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_manifests.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_modules.\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_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_manifests.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_modules.\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_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_manifests\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_modules\", \"$JS.API.STREAM.INFO.KV_mesh-host_acts\", \"$JS.API.STREAM.INFO.KV_mesh-host_data\", \"$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_manifests.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.FC.KV_mesh-host_acts.\u003e\", \"$JS.FC.KV_mesh-host_data.\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.mesh-host_acts.two.\u003e\", \"$KV.mesh-host_data.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.\u003e\", \"$SRV.PING.node-tools.two\", \"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.mod.restic.event.backed-up\", \"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": { + "one.telegram": { + "node": "one", + "module": "telegram", + "serves": [ + { + "subject": "mesh.mod.telegram.tool.{tool}.one" + }, + { + "subject": "mesh.mod.telegram.tool.{tool}", + "queue": "serve.telegram" + } + ], + "emits": "mesh.mod.telegram.event.{event}", + "tools": "mesh.mod.telegram.tool.tools" + }, + "two.shop": { + "node": "two", + "module": "shop", + "serves": [ + { + "subject": "mesh.mod.shop.tool.{tool}.two" + }, + { + "subject": "mesh.mod.shop.tool.{tool}", + "queue": "serve.shop" + } + ], + "emits": "mesh.mod.shop.event.{event}", + "tools": "mesh.mod.shop.tool.tools" + } + } + }, + { + "Name": "seats, consumers, people and a hashless user", + "List": "# The mesh's users, composed by the controller. 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_manifests.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_modules.\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_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_manifests.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_modules.\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_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_manifests.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_modules.\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_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_manifests\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_modules\", \"$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_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_manifests.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_modules.\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_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.mesh-host_acts.one.\u003e\", \"$KV.mesh-host_data.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.\u003e\", \"$SRV.PING.node-tools.one\", \"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.mod.restic.event.backed-up\", \"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_manifests.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_modules.\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_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_manifests.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_modules.\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_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_manifests.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_modules.\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_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_manifests\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_modules\", \"$JS.API.STREAM.INFO.KV_mesh-host_acts\", \"$JS.API.STREAM.INFO.KV_mesh-host_data\", \"$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_manifests.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.FC.KV_mesh-host_acts.\u003e\", \"$JS.FC.KV_mesh-host_data.\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.mesh-host_acts.two.\u003e\", \"$KV.mesh-host_data.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.\u003e\", \"$SRV.PING.node-tools.two\", \"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.mod.restic.event.backed-up\", \"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\"] }\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" + ], + "Err": "", + "Memberships": { + "one.mesh-grants": { + "node": "one", + "module": "mesh-grants", + "serves": [ + { + "subject": "mesh.mod.mesh-grants.tool.{tool}.one" + }, + { + "subject": "mesh.mod.mesh-grants.tool.{tool}", + "queue": "serve.mesh-grants" + } + ], + "emits": "mesh.mod.mesh-grants.event.{event}", + "tools": "mesh.mod.mesh-grants.tool.tools" + }, + "one.mesh-watchman": { + "node": "one", + "module": "mesh-watchman", + "serves": [ + { + "subject": "mesh.mod.mesh-watchman.tool.{tool}.one" + }, + { + "subject": "mesh.mod.mesh-watchman.tool.{tool}", + "queue": "serve.mesh-watchman" + } + ], + "emits": "mesh.mod.mesh-watchman.event.{event}", + "tools": "mesh.mod.mesh-watchman.tool.tools" + }, + "one.nats": { + "node": "one", + "module": "nats", + "serves": [ + { + "subject": "mesh.mod.nats.tool.{tool}.one" + }, + { + "subject": "mesh.mod.nats.tool.{tool}", + "queue": "serve.nats" + } + ], + "emits": "mesh.mod.nats.event.{event}", + "tools": "mesh.mod.nats.tool.tools" + }, + "one.telegram": { + "node": "one", + "module": "telegram", + "serves": [ + { + "subject": "mesh.mod.telegram.tool.{tool}.one" + }, + { + "subject": "mesh.mod.telegram.tool.{tool}", + "queue": "serve.telegram" + } + ], + "emits": "mesh.mod.telegram.event.{event}", + "tools": "mesh.mod.telegram.tool.tools", + "seat-traffic": { + "publish": [ + "mesh.seat.telegram-sender.event.delivered", + "mesh.seat.telegram-sender.event.failed" + ] + } + }, + "two.audit": { + "node": "two", + "module": "audit", + "serves": [ + { + "subject": "mesh.mod.audit.tool.{tool}.two" + }, + { + "subject": "mesh.mod.audit.tool.{tool}", + "queue": "serve.audit" + } + ], + "emits": "mesh.mod.audit.event.{event}", + "tools": "mesh.mod.audit.tool.tools" + }, + "two.shop": { + "node": "two", + "module": "shop", + "serves": [ + { + "subject": "mesh.mod.shop.tool.{tool}.two" + }, + { + "subject": "mesh.mod.shop.tool.{tool}", + "queue": "serve.shop" + } + ], + "emits": "mesh.mod.shop.event.{event}", + "tools": "mesh.mod.shop.tool.tools" + } + } + }, + { + "Name": "state, logs, streams, queues and processes", + "List": "# The mesh's users, composed by the controller. 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_manifests.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_modules.\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_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_manifests.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_modules.\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_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_manifests.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_modules.\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_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_manifests\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_modules\", \"$JS.API.STREAM.INFO.KV_mesh-host_acts\", \"$JS.API.STREAM.INFO.KV_mesh-host_data\", \"$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_manifests.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.FC.KV_mesh-host_acts.\u003e\", \"$JS.FC.KV_mesh-host_data.\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.mesh-host_acts.ace.\u003e\", \"$KV.mesh-host_data.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.\u003e\", \"$SRV.PING.node-tools.ace\", \"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.mod.restic.event.backed-up\", \"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_manifests.\u003e\", \"$JS.API.CONSUMER.CREATE.KV_mesh-delivery-indexer_modules.\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_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_manifests.\u003e\", \"$JS.API.CONSUMER.DELETE.KV_mesh-delivery-indexer_modules.\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_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_manifests.\u003e\", \"$JS.API.DIRECT.GET.KV_mesh-delivery-indexer_modules.\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_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_manifests\", \"$JS.API.STREAM.INFO.KV_mesh-delivery-indexer_modules\", \"$JS.API.STREAM.INFO.KV_mesh-host_acts\", \"$JS.API.STREAM.INFO.KV_mesh-host_data\", \"$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_manifests.\u003e\", \"$JS.FC.KV_mesh-delivery-indexer_modules.\u003e\", \"$JS.FC.KV_mesh-host_acts.\u003e\", \"$JS.FC.KV_mesh-host_data.\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.mesh-host_acts.novox.\u003e\", \"$KV.mesh-host_data.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.\u003e\", \"$SRV.PING.node-tools.novox\", \"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.mod.restic.event.backed-up\", \"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": { + "ace.plex": { + "node": "ace", + "module": "plex", + "serves": [ + { + "subject": "mesh.mod.plex.tool.{tool}.ace" + }, + { + "subject": "mesh.mod.plex.tool.{tool}", + "queue": "serve.plex" + } + ], + "emits": "mesh.mod.plex.event.{event}", + "tools": "mesh.mod.plex.tool.tools" + }, + "ace.silent": { + "node": "ace", + "module": "silent", + "serves": [ + { + "subject": "mesh.mod.silent.tool.{tool}.ace" + }, + { + "subject": "mesh.mod.silent.tool.{tool}", + "queue": "serve.silent" + } + ], + "emits": "mesh.mod.silent.event.{event}", + "tools": "mesh.mod.silent.tool.tools" + }, + "novox.mesh-delivery-builder": { + "node": "novox", + "module": "mesh-delivery-builder", + "serves": [ + { + "subject": "mesh.mod.mesh-delivery-builder.tool.{tool}.novox" + }, + { + "subject": "mesh.mod.mesh-delivery-builder.tool.{tool}", + "queue": "serve.mesh-delivery-builder" + } + ], + "emits": "mesh.mod.mesh-delivery-builder.event.{event}", + "reaches": { + "gitea.gitea_commit": [ + "mesh.mod.gitea.tool.gitea_commit" + ] + }, + "tools": "mesh.mod.mesh-delivery-builder.tool.tools", + "state": [ + { + "name": "steps", + "bucket": "mesh-delivery-builder_steps", + "writes": true + }, + { + "name": "presence", + "bucket": "mesh-delivery-builder_presence", + "writes": true, + "key": "novox" + } + ], + "logs": [ + { + "name": "builds", + "stream": "LOG_mesh-delivery-builder_builds", + "subject": "mesh.log.mesh-delivery-builder.builds", + "writes": true + } + ], + "streams": [ + { + "stream": "asks", + "events": [ + "ask" + ], + "subject": "mesh.stream.mesh-delivery-builder.event.{event}" + } + ] + }, + "novox.mesh-delivery-view": { + "node": "novox", + "module": "mesh-delivery-view", + "serves": [ + { + "subject": "mesh.mod.mesh-delivery-view.tool.{tool}.novox" + }, + { + "subject": "mesh.mod.mesh-delivery-view.tool.{tool}", + "queue": "serve.mesh-delivery-view" + } + ], + "emits": "mesh.mod.mesh-delivery-view.event.{event}", + "tools": "mesh.mod.mesh-delivery-view.tool.tools", + "state": [ + { + "name": "mesh-delivery-builder.steps", + "bucket": "mesh-delivery-builder_steps" + }, + { + "name": "mesh-delivery-builder.presence", + "bucket": "mesh-delivery-builder_presence", + "key": "novox" + } + ] + }, + "novox.mesh-grants": { + "node": "novox", + "module": "mesh-grants", + "serves": [ + { + "subject": "mesh.mod.mesh-grants.tool.{tool}.novox" + }, + { + "subject": "mesh.mod.mesh-grants.tool.{tool}", + "queue": "serve.mesh-grants" + } + ], + "emits": "mesh.mod.mesh-grants.event.{event}", + "tools": "mesh.mod.mesh-grants.tool.tools", + "state": [ + { + "name": "assertions", + "bucket": "mesh-grants_assertions", + "writes": true + } + ] + } + } + } +] diff --git a/go/buscompose/userlist_switch.go b/go/buscompose/userlist_switch.go index 5eee315..11ea7b9 100644 --- a/go/buscompose/userlist_switch.go +++ b/go/buscompose/userlist_switch.go @@ -7,7 +7,6 @@ import ( "git.novox.be/novox/mesh-sdk/go/announce" "git.novox.be/novox/mesh-sdk/go/delivery" - "git.novox.be/novox/mesh-sdk/go/handacts" "git.novox.be/novox/mesh-sdk/go/health" ) @@ -88,20 +87,9 @@ const ArtifactStoreTools = "artifact-store-tools" const ConfigModule = "mesh-config" // NamedReaders are the states only the modules named may read, by `.`: the grants part's wanted accounts -// are read by the vault alone (novox/hq ADR 0561 §2). -// The controller's lease is read by the parts that turn on at its write (ADR 0561 §3, §6), and by the assigner, which -// reads the catalogue's placement alone from then (ADR 0549 §5, issue 774). -// -// The health holders (novox/hq ADR 0564 Limits 2 and 8, clauses 8 and 9): the condition keeper, mesh-healers and the -// watchman read the lease; the keeper alone the controller's conditions; mesh-healers and mesh-hand-acts its hand-act log -// (ADR 0565 clauses 10, 11: every reader of acts reads both logs); and nobody the healers' `would` (health_grants.go). -var NamedReaders = map[string][]string{VaultBusAccounts: {GrantsModule}, GrantsAccounts: {VaultSeat}, - ControllerLease: {VaultSeat, GrantsModule, delivery.AssignerModule, health.KeeperModule, health.HealersModule, WatchmanModule, - // ADR 0571 clause 2: the artifact store's seat sweeps only once the lease names no controller. - ArtifactStoreTools, - // ADR 0561 §3: mesh-config takes over the config only once the lease is handed away (its takeover.go leaseAway). - ConfigModule}, - ControllerConditions: {health.KeeperModule}, ControllerHandActs: {health.HealersModule, handacts.Module}, HealersWould: {}, +// are read by the vault alone (novox/hq ADR 0561 §2); nobody reads the healers' `would` (ADR 0564 clause 8, +// health_grants.go). +var NamedReaders = map[string][]string{VaultBusAccounts: {GrantsModule}, GrantsAccounts: {VaultSeat}, HealersWould: {}, // ADR 0577 clause 1: the node-engine's data items are read by disk-load alone, each node's its own node's keys // (NodeKeyedReads); the engine announces nothing of its own, so its readers are named here. delivery.EngineDataRead: {health.DiskLoadModule}, diff --git a/go/buscompose/users.go b/go/buscompose/users.go index 647075c..ea1668f 100644 --- a/go/buscompose/users.go +++ b/go/buscompose/users.go @@ -7,6 +7,7 @@ import ( "strings" "git.novox.be/novox/mesh-sdk/go/announce" + "git.novox.be/novox/mesh-sdk/go/catalogue" "git.novox.be/novox/mesh-sdk/go/delivery" ) @@ -148,44 +149,25 @@ type Records struct { // grants part as a placed module's are; it adds no principal, since each node's engine speaks as the node's account. // Nil where its manifest was not read. Engine *Declared - // LeaseHandedAway says the controller's lease names a holder that is no controller (ADR 0582 §3), as the grants part - // reads it: an enrolment user then enrols through the seat mesh-enrolment alone (novox/hq ADR 0587 §6). The - // controller's composer never sets it. It is not LeaseCut: the cut is composed before the holder is written, so the - // grants part may write it, and outlasts the holder at a hand-back; the two are true together only in between. - LeaseHandedAway bool // NodeLogins is each node's key of the vault's node-logins (novox/hq ADR 0583 §1): the login in use and the one // rotating, each composed as a user of the node's permissions. A node absent logs in as `node.` alone. NodeLogins map[string]delivery.NodeLogin // Announced is each module's last announcement of the states it keeps (novox/hq ADR 0495, issue 1790), as the // controller keeps it; a module absent has announced nothing yet. Nil where none was read. Announced map[string]announce.States - // LeaseCut composes the list of the lease write (novox/hq ADR 0582 clause 3, issue 774): the controller may neither - // write the lease's key nor update, purge or delete its bucket, and the grants part may write the key `holder`. - // Set by the grants part from the write's second step until a hand-back composes without it (clause 5); the - // controller's composer never sets it. - LeaseCut bool } // Users is every user the composed file should contain, in the order it will be written. -// -// The controller is always first and always present: a mesh whose own controller is not in the file -// is a mesh that cannot be told anything, and there is no state of the records in which that is -// correct. func Users(r Records) ([]Principal, error) { - out := []Principal{{Kind: KindController, Uses: asksSeatOf(r), AsksIssued: issuedOf(r), AsksTheVault: vaultVerbsOf(r), - AsksTheConfig: servedOf(r, VerbsTheControllerAsksTheConfig), FilesFor: sortedCopy(r.Nodes), LeaseCut: r.LeaseCut}} + var out []Principal served := servedReadsOf(r) logs := DeclaredLogs(r) grants := -1 for _, node := range sortedCopy(r.Nodes) { - witness := false var checks []string bus := false for _, d := range r.Assigned[node] { - if d.Module == controllerModule { - witness = true - } if d.SnapshotsTheBus { bus = true } @@ -194,7 +176,7 @@ func Users(r Records) ([]Principal, error) { // **One user per login the node has** (novox/hq ADR 0583 §1): the one in use, and while it rotates the other too, // each with the node's permissions. for _, login := range nodeLoginsOf(r, node) { - out = append(out, Principal{Kind: KindNode, Node: node, Login: login, WitnessesController: witness, Checks: checks, + out = append(out, Principal{Kind: KindNode, Node: node, Login: login, Checks: checks, MovesIntoTheVault: servesMoveIn(r), StatesItsKeyToTheVault: servesVerb(r, VaultNodeKey), AsksItsOwnSecrets: servesVerb(r, VaultOwn), AsksTheVaultToSign: servesVerb(r, VaultSign), Uses: asksSeatOf(r), FollowsGrantsUserList: bus, ServedReads: servedOn(served, node), RotatesItsLogin: servesVerb(r, VaultRotateNode)}) @@ -238,6 +220,10 @@ func Users(r Records) ([]Principal, error) { Emits: pr.Emits, Queues: pr.Queues, State: pr.State, Uses: d.Uses, Asks: pr.Asks, Invokes: pr.Invokes, Removes: pr.Removes, }) + // The ended controller's leftovers include each node's declaration files' bucket (ADR 0590 clause 6). + if slices.Contains(pr.Removes, catalogue.RemovesControllerLeftovers) { + out[len(out)-1].FilesFor = sortedCopy(r.Nodes) + } } if runtimeHere && d.Module == RuntimeModule { continue @@ -266,7 +252,6 @@ func Users(r Records) ([]Principal, error) { } if d.Module == GrantsModule { out[len(out)-1].CreatesStreams = declaredStreamsOf(r) - out[len(out)-1].LeaseCut = r.LeaseCut grants = len(out) - 1 } // **The vault's holder asks the desks and the issuing providers itself** (issue 723), under the @@ -289,17 +274,15 @@ func Users(r Records) ([]Principal, error) { out = append(out, Principal{Kind: KindNodeTools, Node: node, Module: RuntimeModule, Carries: carried}) } } - for _, node := range sortedCopy(r.Enrolling) { - out = append(out, Principal{Kind: KindEnrolment, Node: node, Redeems: servesVerb(r, EnrolmentRedeem), - LeaseHandedAway: r.LeaseHandedAway}) + // A node enrols through the seat mesh-enrolment alone (ADR 0587 §6, ADR 0590): while no holder serves redeem, an + // enrolling node has nowhere to enrol, and gets no user. + if servesVerb(r, EnrolmentRedeem) { + for _, node := range sortedCopy(r.Enrolling) { + out = append(out, Principal{Kind: KindEnrolment, Node: node, Redeems: true}) + } } for _, person := range sortedNames(r.People) { - p := Principal{Kind: KindPerson, Module: person, Invokes: r.People[person]} - if administers(p.Invokes) { - // The operator's hand path writes each node's compositions (ADR 0539 clause 10). - p.FilesFor = sortedCopy(r.Nodes) - } - out = append(out, p) + out = append(out, Principal{Kind: KindPerson, Module: person, Invokes: r.People[person]}) } // The vault's enrolment verbs are mesh-enrolment's alone, however wide another's grant (novox/hq ADR 0587 §1). @@ -368,9 +351,6 @@ func sortedNames(in map[string][]string) []string { return out } -// controllerModule is the controller's module: the machine assigned it witnesses its upgrades. -const controllerModule = "mesh-controller" - // asksSeatOf is the seat an ask is made on, as its holder declares it (novox/hq ADR 0259): the controller // asks the operator through it like any other user, and is granted what its declaration names for a caller. // None while nothing holds it. @@ -441,10 +421,6 @@ func holdsSeat(d Declared, seat string) bool { return false } -// vaultVerbsOf are the vault's intake verbs its holder serves, as the records say (issue 675): what the controller -// is granted to ask it in its own name. None while no holder serves them. -func vaultVerbsOf(r Records) []string { return servedOf(r, VerbsTheControllerAsksTheVault) } - // servedOf are the verbs of asked that a seat's holder serves, as the records say, by name. func servedOf(r Records, asked []SeatVerb) []string { seen := map[string]bool{} diff --git a/go/buscompose/writers.go b/go/buscompose/writers.go index fe5db6f..8ae64d6 100644 --- a/go/buscompose/writers.go +++ b/go/buscompose/writers.go @@ -25,7 +25,7 @@ type WriterRow struct { KeptIn string Others string // Subjects are the bus subjects a write of this state is a publish to; none for state kept off the - // bus (the controller's store, a module's own) or not built yet. + // bus (a module's own store) or not built yet. Subjects []string // Writes says a principal granted a publish pattern overlapping Subjects is this state's writer — // given the pattern, because a machine writes its own report and no other's. @@ -34,16 +34,13 @@ type WriterRow struct { Shared string } -// isController, and the other writers' tests, are who a row's writer is as a principal. -func isController(p Principal, _ string) bool { return p.Kind == KindController } - -// createsDeclared is the controller, or the grants part creating one stream a manifest declares, by its own name: the -// create alone, never an update or a delete (novox/hq ADR 0547 §5; the operator, 2026-10-26 08:54). +// createsDeclared, and the other writers' tests, are who a row's writer is as a principal. +// +// createsDeclared is the grants part creating one stream a manifest declares, by its own name: the create alone, never an +// update or a delete (novox/hq ADR 0547 §5; the operator, 2026-10-26 08:54); or the bus module's tools deleting one of +// the ended controller's leftover streams (ADR 0590 clause 6). func createsDeclared(p Principal, pattern string) bool { - if isController(p, pattern) { - return true - } - if removesAssignmentsStream(p, pattern) || setsLeaseExpiry(p, pattern) { + if removesLeftover(p, pattern) { return true } name, ok := strings.CutPrefix(pattern, "$JS.API.STREAM.CREATE.") @@ -109,17 +106,14 @@ func moduleNamed(module string) func(Principal, string) bool { } } -// ownAnnouncer is a module, a process of it or the node tools carrying it saying what that module keeps, or the -// controller saying what it keeps itself (novox/hq ADR 0495 clause 2): each under its own name alone. +// ownAnnouncer is a module, a process of it or the node tools carrying it saying what that module keeps (novox/hq ADR +// 0495 clause 2): each under its own name alone. func ownAnnouncer(p Principal, pattern string) bool { tokens := strings.Split(pattern, ".") if len(tokens) < 3 { return false } - switch p.Kind { - case KindController: - return tokens[2] == ControllerSeat - case KindProcess: + if p.Kind == KindProcess { return p.Module == tokens[2] } return ownModule(p, pattern) @@ -144,88 +138,39 @@ func kvOf(bucket string) []string { return []string{"$KV." + bucket + ".>"} } // WritersTable is to-be 45 §1, in its order. var WritersTable = []WriterRow{ - {State: "a machine's declaration", Writer: "controller (lease holder)", KeptIn: "the bus, last per subject", - Others: "read", Subjects: []string{"mesh.node.*.declare"}, Writes: isController}, - // The operator's hand-over of a directory used as found, asked of the machine's engine at the controller's - // terminal (novox/hq issue 356). One publisher; and because a responder can still reach the subject through a - // reply, the ask is signed with the mesh's key and the engine verifies it — the row bounds who is granted the - // publish, the signature who is believed. - {State: "a hand-over asked of a machine", Writer: "controller, at its terminal", KeptIn: "the machine, beside its state", - Others: "the engine verifies the mesh's signature and records it, or refuses", - Subjects: []string{"mesh.node.*.ask.hand-over"}, Writes: isController}, - // The operator asking a machine's engine for a fresh search for setuid programs, at the controller's - // terminal (novox/hq issue 361): signed as a hand-over is, under a signing context of its own. - {State: "a fresh setuid search asked of a machine", Writer: "controller, at its terminal", - KeptIn: "the machine, which throws its last search away", Others: "the engine verifies the mesh's signature and starts it, or refuses", - Subjects: []string{"mesh.node.*.ask.setuid-search"}, Writes: isController}, - // A read-only judgement of a machine's declared directories asked of its engine (novox/hq issue 358): signed as a - // hand-over is, under a signing context of its own. It changes nothing on the machine. - {State: "a judgement of a machine's directories asked of it", Writer: "controller", - KeptIn: "nowhere: the engine answers it and keeps nothing", Others: "the engine verifies the mesh's signature and answers, or refuses", - Subjects: []string{"mesh.node.*.ask.judge-directories"}, Writes: isController}, {State: "a machine's applied state and its report", Writer: "the node-engine's apply queue", KeptIn: "the machine; the report on the bus", Others: "the reconcile and a delivery enqueue, never apply", // And its health statement between reports (novox/hq ADR 0240): the same writer stating the same // machine, inside the grant it already had (`mesh.control..>`). And a line mesh-cli was given - // on the machine (novox/hq ADR 0272): the node is what the controller judges the terminal by, so that - // subject is this machine's engine's alone. + // on the machine (novox/hq ADR 0272): that subject is this machine's engine's alone. Subjects: []string{"mesh.control.*.report", "mesh.control.*.health", "mesh.control.*.cli"}, Writes: ownMachine}, - {State: "the controller lease", Writer: "the controller instance holding it", KeptIn: "key-value " + LeaseBucket, - Others: "a candidate waits", Subjects: kvOf(LeaseBucket), Writes: writesTheLease, - Shared: "from the lease write's cut the controller writes it no more and the grants part writes its key holder alone (novox/hq ADR 0582 clause 3)"}, - // The merges mesh-delivery announced, for a moved module's registration (ADR 0420 §7), written by the lease holder. - {State: "the merges mesh-delivery announced", Writer: "controller (lease holder)", - KeptIn: "key-value " + DeliveryMergesBucket, Others: "read at a moved module's registration", - Subjects: kvOf(DeliveryMergesBucket), Writes: isController}, - // And the compositions (novox/hq ADR 0539 clauses 2 and 4): each node's in its own bucket, which only that node's - // node-engine reads, and each digest with the judgement of its move, which the assigner reads. - {State: "the compositions and their digests", Writer: "controller (lease holder), compare-and-set", - KeptIn: "key-value composed- for each node, and " + delivery.ControllerComposedBucket, - Others: "a node-engine reads its own node's; the assigner reads the digests", - Subjects: kvOf(delivery.ControllerComposedBucket), Writes: isControllerOrTheHandPath}, - {State: "conditions", Writer: "controller", KeptIn: "key-value " + ConditionsBucket + " (and its history, " + - ConditionHistoryBucket + "); how often each was observed, when last and its newest evidence in the " + - "controller's store, so a look that changes nothing writes no bucket", Others: "raise or clear only through observations the controller reads", - Subjects: append(kvOf(ConditionsBucket), kvOf(ConditionHistoryBucket)...), Writes: isController}, - {State: "calls and their outcomes", Writer: "controller", KeptIn: "key-value " + CallsBucket, - Others: "read by id", Subjects: kvOf(CallsBucket), Writes: isController}, - {State: "the hand-act log", Writer: "controller, through the verbs that act", KeptIn: "key-value " + HandActsBucket, - Others: "—", Subjects: kvOf(HandActsBucket), Writes: isController}, - // What the healers did (novox/hq to-be 45 §7, Phase 3): the controller's alone, in its store — the - // budgets and the mesh-wide brake are counted from it, so a controller restarting cannot reset them. - {State: "the healers' acts and their brake", Writer: "controller (lease holder), each act begun before it is made", - KeptIn: "the controller's store", Others: "read through healers; each act said as healer-acted and in its condition's tried"}, - {State: "stream definitions and bus permissions", Writer: "controller", KeptIn: "the bus", Others: "—", + {State: "stream definitions and bus permissions", Writer: "the grants part", KeptIn: "the bus", Others: "—", // A stream's definition, and a durable consumer's by the API that names it so. Not every // consumer create: a module watching its own bucket makes and deletes an ordered consumer on the // bucket's stream (ADR 0201), which defines nothing the mesh keeps. Subjects: []string{"$JS.API.STREAM.CREATE.>", "$JS.API.STREAM.UPDATE.>", "$JS.API.STREAM.DELETE.>", "$JS.API.CONSUMER.DURABLE.CREATE.>"}, Writes: createsDeclared, - Shared: "the grants part creates a stream a manifest declares, by its name, and never updates or deletes one (novox/hq ADR 0547 §5; the operator, 2026-10-26 08:54); the bus module's tools account deletes the stream ASSIGNMENTS at the lease write, and no other (research 072 row 33), and updates the lease's bucket for its expiry alone (novox/hq ADR 0582 clause 3)"}, + Shared: "the grants part creates a stream a manifest declares, by its name, and never updates or deletes one (novox/hq ADR 0547 §5; the operator, 2026-10-26 08:54); the bus module's tools account deletes the ended controller's leftover streams, each by its name, and no other (novox/hq ADR 0590 clause 6)"}, {State: "builds and their outcomes", Writer: "the build seat's holder", KeptIn: "its own state", - Others: "the controller asks", + Others: "the builder asks", Subjects: []string{"mesh.seat.node-build-agent.event.built", "mesh.seat.mesh-build-machine.event.built"}, Writes: holdsSeatOf, Shared: "every machine holding the build seat answers the asks it took; each outcome names its ask"}, - // What a module keeps and who may read it (novox/hq ADR 0495 clauses 2 and 3, issue 1790): said by the module, kept by - // the controller. + // What a module keeps and who may read it (novox/hq ADR 0495 clauses 2 and 3, issue 1790): said by the module. {State: "a module's states announced", Writer: "the module itself, from its code, under its own name", - KeptIn: "the bus (EVENTS)", Others: "the controller keeps the last of each and grants readers from it", + KeptIn: "the bus (EVENTS)", Others: "the grants part grants readers from it", Subjects: []string{"mesh.mod.*.event.states"}, Writes: ownAnnouncer}, - {State: "the states announced", Writer: "controller, from each module's announcement", - KeptIn: "key-value " + StatesAnnouncedBucket, Others: "the composition of the bus's users reads it", - Subjects: kvOf(StatesAnnouncedBucket), Writes: isController}, {State: "a merge announced", Writer: "one announcer per forge (the hook, or the poll when the hook is absent — never both)", KeptIn: "the bus", Others: "—", Subjects: []string{"mesh.mod.*.event.pull.merged"}, Writes: ownModule}, // A pull request's head, before it merges (novox/hq to-be 45 §9): the same one announcer. {State: "a pull request's head announced", Writer: "the forge's announcer, the one that announces its merges", - KeptIn: "the bus", Others: "the controller asks the build seat to check it", Subjects: []string{"mesh.mod.*.event.pull.updated"}, + KeptIn: "the bus", Others: "the checker checks it", Subjects: []string{"mesh.mod.*.event.pull.updated"}, Writes: ownModule}, - {State: "a pull request's merge check", Writer: "the build seat's holder that ran it, said as the controller's `checked`", + {State: "a pull request's merge check", Writer: "the checker that ran it", KeptIn: "the bus", Others: "the forge's holder sets it as the pull request's status"}, {State: "a provider's standing", Writer: "the provider", KeptIn: "the provider's events", - Others: "the controller keeps the newest word as a condition", + Others: "the watchman keeps the newest word as a condition", Subjects: []string{"mesh.mod.*.event.provisioner.failing", "mesh.mod.*.event.provisioner.recovered", "mesh.mod.*.event.provisioner.retirement"}, Writes: ownModule}, @@ -241,35 +186,13 @@ var WritersTable = []WriterRow{ {State: "a node's logins", Writer: "the vault", KeptIn: "key-value " + delivery.NodeLoginsBucket, Others: "the grants part reads every key, each node's engine its own node's key alone", Subjects: kvOf(delivery.NodeLoginsBucket), Writes: moduleNamed(delivery.VaultModule)}, - {State: "the facts snapshot", Writer: "controller", KeptIn: "the artifact store, facts/latest", - Others: "the build seat reads"}, - // What a consumer gave up on (novox/hq issue 330): copied by the controller from the server's notice, - // and delivered again or dropped only through its verb, with why. - {State: "a message a consumer gave up on", Writer: "controller", KeptIn: "the bus, the stream " + DeadLettersStream, - Others: "read, delivered again or dropped through the controller's dead-letters verb", - Subjects: []string{deadLetterPrefix + ">", againPrefix + ">"}, Writes: keepsDeadLetters, - Shared: "the watchman keeps the copies from the controller's lease write, the controller before it; delivering again stays the controller's (novox/hq ADR 0564 clause 5)"}, - // The record mirrors (novox/hq ADR 0289 §9, design 54 §2): rows of this table, so a grant that lets - // anything else write them is refused at composition. Built so far: the assignments and each registered - // module's declared config (design 57 F3), and the list of predefined keys kept (design 57 K3). - {State: "the assignments mirror", Writer: "controller (lease holder): the verbs that assign, and the sends that record what runs; " + - "a mirror of its store only until the assignments are moved to the bus (novox/hq ADR 0408)", - KeptIn: "key-value " + AssignmentsBucket + ", the record once moved", Others: "read through `reads` as mesh-controller." + MirrorAssignments, - Subjects: kvOf(AssignmentsBucket), Writes: isController}, - // The chain (novox/hq ADR 0408): builds and rollouts beside the assignments above, which keep their bucket. Not - // mirrors of the store but the record, written by the lease holder by compare-and-set and removed by nobody. - {State: "the builds of the chain", Writer: "controller (lease holder), compare-and-set on the key's revision", - KeptIn: "key-value " + BuildsBucket + ", sixty-four revisions of each key, none removed", - Others: "read through `reads` as mesh-controller." + ChainBuilds, Subjects: kvOf(BuildsBucket), Writes: isController}, - {State: "the rollouts of the chain", Writer: "controller (lease holder), compare-and-set on the key's revision", - KeptIn: "key-value " + RolloutsBucket + ", sixty-four revisions of each key, none removed", - Others: "read through `reads` as mesh-controller." + ChainRollouts, Subjects: kvOf(RolloutsBucket), Writes: isController}, - // The grants issued (novox/hq ADR 0528 clause 2, issue 1394): the controller's, read by the assigner for its sixth rule. - {State: "the grants issued", Writer: "controller (lease holder), once the bus's user list carrying a module's manifest at a fingerprint was sent", - KeptIn: "key-value " + GrantsBucket + ", one key a module's fingerprint, none removed", - Others: "read through `reads` as mesh-controller." + ControllerGrants, Subjects: kvOf(GrantsBucket), Writes: isController}, + // What a consumer gave up on (novox/hq issue 330): copied by the watchman from the server's notice (ADR 0564 + // clause 5, ADR 0590 clause 2). + {State: "a message a consumer gave up on", Writer: "the watchman", KeptIn: "the bus, the stream " + DeadLettersStream, + Others: "read through the watchman's dead-letters; delivered again by nobody until the hand-act log's home is delivered", + Subjects: []string{deadLetterPrefix + ">", againPrefix + ">"}, Writes: keepsDeadLetters}, // What each machine has of each module (novox/hq ADR 0429 §1, design 47): written by that machine's node-engine under - // its own node's keys, never by the controller; the view and the watchman read it. + // its own node's keys; the view and the watchman read it. {State: "a machine's have", Writer: "the node-engine of the machine, under its own keys `.>`", KeptIn: "key-value " + delivery.HaveBucket + ", twenty revisions of each key", Others: "read through `reads` as node-engine.have", Subjects: kvOf(delivery.HaveBucket), Writes: ownNodeKeys}, @@ -294,34 +217,23 @@ var WritersTable = []WriterRow{ return ownNodeKeys(p, pattern) || removesEngineActs(p, pattern) }, Shared: "the bus module's tools account removes keys of a full bucket, for removal only (novox/hq ADR 0564 Limits 4)"}, - // The contents of a declaration's files over 64 KiB (novox/hq ADR 0486 §2): the controller alone writes the bucket, each - // object under the digest of its content; a node-engine reads one by its name, with no list and no write. - {State: "a declaration's large files", Writer: "controller, each object under the sha256 digest of its content; the " + - "operator's account on release 6's hand path alone (ADR 0539 clause 10)", + // The object buckets (novox/hq ADR 0486 §2, ADR 0547 clause 7): the declaration files' buckets the ended controller + // wrote are written by nobody (ADR 0590); the grants part writes its own. + {State: "an object bucket", Writer: "the grants part, its own bucket alone", KeptIn: "the object bucket " + DeclarationFilesBucket + "- of each node, an object removed a day after the " + "node's latest declaration no longer names it", Others: "a node-engine reads an object of its own bucket by its name and may neither list the bucket nor write it", Subjects: []string{"$O.*.>"}, Writes: func(p Principal, pattern string) bool { - return isControllerOrTheHandPath(p, pattern) || grantsOwnObjects(p, pattern) + return grantsOwnObjects(p, pattern) }, - Shared: "the grants part writes its own object bucket " + GrantsUserListObjects + ", the user list sealed to the bus node, and no other (novox/hq ADR 0547 clause 7)"}, - {State: "the declared-config mirror", Writer: "controller (lease holder), from its store, only on a change", - KeptIn: "key-value " + DeclaredConfigBucket, Others: "read through `reads` as mesh-controller." + MirrorDeclaredConfig, - Subjects: kvOf(DeclaredConfigBucket), Writes: isController}, - {State: "the predefined-keys mirror", Writer: "controller (lease holder), from its store, only on a change", - KeptIn: "key-value " + PredefinedBucket, Others: "read through `reads` as mesh-controller." + MirrorPredefined, - Subjects: kvOf(PredefinedBucket), Writes: isController}, - {State: "the declared-packages mirror", Writer: "controller (lease holder), from the composition of each node, only on a change", - KeptIn: "key-value " + DeclaredPackagesBucket, Others: "read through `reads` as mesh-controller." + MirrorDeclaredPackages + - ", and each node's engine its node's key, until the lease write (ADR 0565 clause 6)", - Subjects: kvOf(DeclaredPackagesBucket), Writes: isController}, + Shared: "nobody else: the grants part writes its own object bucket " + GrantsUserListObjects + ", the user list sealed to the bus node, and no other (novox/hq ADR 0547 clause 7)"}, // What a machine's provisioner client placed of a module's files (novox/hq ADR 0349 §4, design 57 P6): its own // machine's word alone, which a walk's end and a gate wait for (ADR 0362, ADR 0349 §15). Kept newest per module // in the stream ProvAppliedStream, and by the client in the provisioner server's `applied` bucket. {State: "a module's files placed on a machine", Writer: "that machine's provisioner client", KeptIn: "the bus, the stream " + ProvAppliedStream + ", newest per module; and key-value " + ProvisionerAppliedBucket, - Others: "the controller reads it at a walk's end and a gate; a failure is that machine's failed apply", + Others: "the watchman reads it; a failure is that machine's failed apply", Subjects: []string{"prov.applied.*.>"}, Writes: ownProvisioner}, } @@ -368,9 +280,3 @@ func SubjectsOverlap(a, b string) bool { } } } - -// isControllerOrTheHandPath is the controller, or the operator's account (a person who may invoke every tool) writing -// release 6's hand path: the one second writer ADR 0539 clauses 2 and 10 name, for a composition made by hand. -func isControllerOrTheHandPath(p Principal, pattern string) bool { - return isController(p, pattern) || (p.Kind == KindPerson && administers(p.Invokes)) -} diff --git a/go/catalogue/process_bus.go b/go/catalogue/process_bus.go index 347e244..4037284 100644 --- a/go/catalogue/process_bus.go +++ b/go/catalogue/process_bus.go @@ -33,15 +33,16 @@ type ProcessBus struct { Removes []string `json:"removes,omitempty"` } -// What a bus account may remove (novox/hq ADR 0564 Limits 4), each its own grant: the controller's consumer on -// DEAD_LETTER_NOTICES (the lease write's step, clause 5), and keys of the node-engine's acts bucket. A further consumer -// or bucket is a change of this set, through a decision. +// What a bus account may remove (novox/hq ADR 0564 Limits 4), each its own grant: keys of the node-engine's acts +// bucket, and the ended controller's leftovers on the bus (ADR 0590 clause 6): its consumers, its CONTROL and ASSIGNMENTS +// streams, its buckets and the declaration files' object buckets, each by its name. A further consumer, stream or bucket +// is a change of this set, through a decision. const ( - RemovesControllerDeadLetterConsumer = "DEAD_LETTER_NOTICES.controller" - RemovesEngineActs = "mesh-host_acts" - // RemovesAssignmentsStream is the stream ASSIGNMENTS, removed whole at the lease write (novox/hq research 072 row 33): - // its 350 messages have no consumer, and the placement is the provisioner's from then on. - RemovesAssignmentsStream = "ASSIGNMENTS" + RemovesEngineActs = "mesh-host_acts" + // RemovesControllerLeftovers is the closed set of the ended controller's leftovers (buscompose.ControllerLeftovers). + // The word does not name the controller's module, so the check that refuses any manifest naming it (ADR 0590 clause + // 5) holds over the bus module's manifest too. + RemovesControllerLeftovers = "controller-leftovers" ) // BusAccount is one account of a module's own beside its `broker`: a process's (ADR 0436) or a tools bundle's (ADR 0564