package broker import ( "slices" "testing" ) // The provisioner client's own bus account (novox/hq ADR 0349 clauses 6 and 11, design 57 package P3, issue // 669): one per machine running it, apart from the machine's tool runner, granted to read its own machine's keys // in each domain's bucket, to say what it applied on that machine, and to ask the vault in its own name; nothing // else. func provisionerRecords() Records { client := Declared{Module: "mesh-provisioner", ProvisionsNode: true} return Records{ Nodes: []string{"g14", "shanks", "ace"}, Assigned: map[string][]Declared{ "g14": {client, {Module: RuntimeModule}}, "shanks": {client}, "ace": {{Module: "dunst"}}, }, } } // One account per machine that runs the client, named for the machine and apart from its tool runner's: the // vault tells the client from an agent on that machine by the name the bus proves. func TestEachMachineRunningTheProvisionerClientHasAnAccountOfItsOwn(t *testing.T) { users, err := Users(provisionerRecords()) if err != nil { t.Fatal(err) } var got []string for _, u := range users { if u.Kind == KindProvisioner { got = append(got, u.Username()) if u.Module != "mesh-provisioner" { t.Errorf("%s is delivered to %q, want the module that runs it", u.Username(), u.Module) } } } if want := []string{"provisioner.g14", "provisioner.shanks"}; !slices.Equal(got, want) { t.Fatalf("the provisioner clients' accounts are %v, want %v (none for a machine that does not run it)", got, want) } for _, u := range users { if u.Kind == KindNodeTools && u.Username() == "provisioner.g14" { t.Fatal("the client shares the tool runner's account") } } } // Its whole authority, subject by subject: a read of its own machine's keys in each domain's bucket, a read and a // write of its own machine's keys in the provisioner server's needs, applied and collections, a read of the // server's registry (design 57 P4), a publish of what it applied on its own machine, the vault's `get` in its own // name, its own inbox and the vault's secret-changed. func TestTheProvisionerClientIsGrantedItsMachinesKeysAndNothingElse(t *testing.T) { perms, err := PermissionsFor(Principal{Kind: KindProvisioner, Node: "g14", Module: "mesh-provisioner", BoundBuckets: []string{"postgres_bound"}}) if err != nil { t.Fatal(err) } var wantPub []string for _, bucket := range []string{"mesh-config_resolved", "postgres_bound"} { stream := "KV_" + bucket wantPub = append(wantPub, "$JS.API.STREAM.INFO."+stream, "$JS.API.DIRECT.GET."+stream+".$KV."+bucket+".g14.>", "$JS.API.CONSUMER.CREATE."+stream+".*.$KV."+bucket+".g14.>", "$JS.API.CONSUMER.DELETE."+stream+".>", "$JS.FC."+stream+".>") } // The assignments mirror whole, read and never written: a provider's consumers are bound to it in their own // machines' plans, which its machine's client reads to fill ${consumers:} (novox/hq ADR 0373, issue // 679). The mirror holds placement and plans, never a value of a secret. wantPub = append(wantPub, "$JS.API.STREAM.INFO.KV_mesh-controller_assignments", "$JS.API.DIRECT.GET.KV_mesh-controller_assignments.>", "$JS.API.CONSUMER.CREATE.KV_mesh-controller_assignments.>", "$JS.API.CONSUMER.DELETE.KV_mesh-controller_assignments.>", "$JS.FC.KV_mesh-controller_assignments.>") for _, bucket := range []string{"mesh-provisioner_needs", "mesh-provisioner_applied", "mesh-provisioner_collect"} { stream := "KV_" + bucket wantPub = append(wantPub, "$JS.API.STREAM.INFO."+stream, "$JS.API.DIRECT.GET."+stream+".$KV."+bucket+".g14.>", "$JS.API.CONSUMER.CREATE."+stream+".*.$KV."+bucket+".g14.>", "$JS.API.CONSUMER.DELETE."+stream+".>", "$JS.FC."+stream+".>", "$KV."+bucket+".g14.>") } wantPub = append(wantPub, "$JS.API.STREAM.INFO.KV_mesh-provisioner_registry", "$JS.API.DIRECT.GET.KV_mesh-provisioner_registry.>", "$JS.API.CONSUMER.CREATE.KV_mesh-provisioner_registry.>", "$JS.API.CONSUMER.DELETE.KV_mesh-provisioner_registry.>", "$JS.FC.KV_mesh-provisioner_registry.>") wantPub = append(wantPub, "prov.applied.g14.>", "mesh.seat.mesh-vault.call.get.provisioner~g14", "prov.changed.g14.>") slices.Sort(wantPub) if !slices.Equal(perms.Publish, wantPub) { t.Errorf("publish:\n have %q\n want %q", perms.Publish, wantPub) } // Its own inbox, what the vault says changed (`secret-changed`: which, by whom, never a value), so a rotated // secret reaches its file without a new declaration (design 57 V4), and its own machine's tool runner asking // it for a carried module's config (ADR 0370 §3), which it answers on the ask's reply. if want := []string{"_INBOX.provisioner.g14.>", "mesh.mod.mesh-vault.event.secret-changed", "prov.ask.g14"}; !slices.Equal(perms.Subscribe, want) { t.Errorf("subscribe:\n have %q\n want %q", perms.Subscribe, want) } if !perms.AllowResponses || len(perms.PublishDeny) > 0 { t.Errorf("it answers what it is asked and needs no deny: %+v", perms) } // No write of any bucket but its own machine's keys of the server's needs, applied and collections: a domain // writes its own (ADR 0349 clause 3, research 051), and the registry is the server's alone. for _, p := range perms.Publish { if !SubjectsOverlap(p, "$KV.>") { continue } if !slices.Contains([]string{"$KV.mesh-provisioner_needs.g14.>", "$KV.mesh-provisioner_applied.g14.>", "$KV.mesh-provisioner_collect.g14.>"}, p) { t.Errorf("it may write a bucket: %s", p) } } } // Each provider's bucket of entries, `_bound` (mesh-sdk go/provider, issue 678), is read by every machine's // client for that machine's consumers' keys alone (`.>`), to fill ${bound::} (issue 679): the // buckets of every module the mesh places that keeps state named bound, wherever it runs, and no other. func TestTheProvisionerClientReadsItsMachinesKeysOfEachProvidersBoundBucket(t *testing.T) { records := provisionerRecords() records.Assigned["ace"] = append(records.Assigned["ace"], Declared{Module: "postgres", State: []Bucket{{Module: "postgres", Name: "bound"}, {Module: "postgres", Name: "kept"}}}, Declared{Module: "keycloak", State: []Bucket{{Module: "keycloak", Name: "bound"}}}) records.Assigned["shanks"] = append(records.Assigned["shanks"], Declared{Module: "postgres", State: []Bucket{{Module: "postgres", Name: "bound"}}}) users, err := Users(records) if err != nil { t.Fatal(err) } for _, u := range users { if u.Kind != KindProvisioner { continue } if want := []string{"keycloak_bound", "postgres_bound"}; !slices.Equal(u.BoundBuckets, want) { t.Errorf("%s reads the bound buckets %v, want %v", u.Username(), u.BoundBuckets, want) } perms, err := PermissionsFor(u) if err != nil { t.Fatal(err) } mine := "$JS.API.DIRECT.GET.KV_postgres_bound.$KV.postgres_bound." + u.Node + ".>" if !slices.Contains(perms.Publish, mine) { t.Errorf("%s may not read its machine's keys of postgres_bound", u.Username()) } for _, p := range perms.Publish { if SubjectsOverlap(p, "$JS.API.DIRECT.GET.KV_postgres_kept.>") || SubjectsOverlap(p, "$KV.postgres_bound.>") || SubjectsOverlap(p, "$JS.API.DIRECT.GET.KV_postgres_bound.$KV.postgres_bound.ace.>") { t.Errorf("%s is granted %s: another bucket, a write, or another machine's consumers", u.Username(), p) } } } if _, err := PermissionsFor(Principal{Kind: KindProvisioner, Node: "g14", Module: "mesh-provisioner", BoundBuckets: []string{"postgres_bound.>"}}); err == nil { t.Error("a bound bucket that is a pattern composed") } } // A machine with no usable name reaches nothing: its grants would otherwise be a pattern over every machine. func TestAProvisionerClientNamingNoMachineIsRefused(t *testing.T) { for _, node := range []string{"", "g14.>", "*"} { if _, err := PermissionsFor(Principal{Kind: KindProvisioner, Node: node, Module: "mesh-provisioner"}); err == nil { t.Errorf("a provisioner client on %q composed", node) } } } // A whole mesh composes with the clients beside every other kind: nobody is granted a second writer's subject. func TestAMeshWithProvisionerClientsComposes(t *testing.T) { users, err := Users(provisionerRecords()) if err != nil { t.Fatal(err) } for i := range users { users[i].PasswordHash = "x" } if _, err := ComposeAccounts(users); err != nil { t.Fatal(err) } } // The client is asked on a subject its own machine's tool runner alone may publish (novox/hq ADR 0370 §3, issue // 685): across a whole mesh, no principal but g14's tool runner may ask g14's client, none but g14's client hears // it, and the client's word of what moved is heard by g14's tool runner alone. func TestTheProvisionerClientIsAskedByItsOwnMachinesToolRunnerAlone(t *testing.T) { records := provisionerRecords() records.Assigned["shanks"] = append(records.Assigned["shanks"], Declared{Module: RuntimeModule}, Declared{Module: "dunst"}) records.Assigned["g14"] = append(records.Assigned["g14"], Declared{Module: "claude-code"}) users, err := Users(records) if err != nil { t.Fatal(err) } users = append(users, Principal{Kind: KindPerson, Module: "jochen", Invokes: []string{"*"}}) var asks, hears, told []string for _, u := range users { perms, err := PermissionsFor(u) if err != nil { t.Fatalf("%s: %v", u.Username(), err) } if slices.ContainsFunc(perms.Publish, func(p string) bool { return SubjectsOverlap(p, ProvisionerAskSubject("g14")) }) { asks = append(asks, u.Username()) } if slices.ContainsFunc(perms.Subscribe, func(p string) bool { return SubjectsOverlap(p, ProvisionerAskSubject("g14")) }) { hears = append(hears, u.Username()) } if slices.ContainsFunc(perms.Subscribe, func(p string) bool { return SubjectsOverlap(p, ProvisionerChangedSubject("g14", "claude-code")) }) { told = append(told, u.Username()) } } if want := []string{"g14.node-tools"}; !slices.Equal(asks, want) { t.Errorf("g14's client may be asked by %v, want %v alone", asks, want) } if want := []string{"provisioner.g14"}; !slices.Equal(hears, want) { t.Errorf("g14's client's asks are heard by %v, want %v alone", hears, want) } if want := []string{"g14.node-tools"}; !slices.Equal(told, want) { t.Errorf("what g14's client says moved is heard by %v, want %v alone", told, want) } }