The controller composes one tool runtime per node: its principal, the bundles, its process, and the gate (hq ADR 0175, to-be 38 WP2) #223
@@ -557,7 +557,7 @@ func issueOnTheNewBus(ctx context.Context, inv *inventory.Inventory, m catalogue
|
|||||||
|
|
||||||
user := broker.Principal{Kind: broker.KindModule, Node: node, Module: m.Module}.Username()
|
user := broker.Principal{Kind: broker.KindModule, Node: node, Module: m.Module}.Username()
|
||||||
password, err := inv.MintBusPassword(ctx, inventory.BusUser{
|
password, err := inv.MintBusPassword(ctx, inventory.BusUser{
|
||||||
Username: user, Kind: inventory.BusModule, Node: node, Module: m.Module,
|
Username: user, Kind: busKindOf(m.Module), Node: node, Module: m.Module,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -577,6 +577,16 @@ func issueOnTheNewBus(ctx context.Context, inv *inventory.Inventory, m catalogue
|
|||||||
return issueWith(ctx, inv, m, node, busAddress, known, reachable, user, password)
|
return issueWith(ctx, inv, m, node, busAddress, known, reachable, user, password)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// busKindOf is what a module's bus user is recorded as: the node's tool runtime where the module is
|
||||||
|
// the runtime (novox/hq ADR 0175), a module otherwise. The username is the same either way — the
|
||||||
|
// runtime is issued through this same path — and the kind is what a reader of the records sees.
|
||||||
|
func busKindOf(module string) string {
|
||||||
|
if module == catalogue.RuntimeModule {
|
||||||
|
return inventory.BusNodeTools
|
||||||
|
}
|
||||||
|
return inventory.BusModule
|
||||||
|
}
|
||||||
|
|
||||||
// issueWith is the delivery half: the minted password sealed to the machine as the module's broker
|
// issueWith is the delivery half: the minted password sealed to the machine as the module's broker
|
||||||
// secret, and the module's consumer created where the bus can be reached. Split from the minting
|
// secret, and the module's consumer created where the bus can be reached. Split from the minting
|
||||||
// so the move can issue every module against a bus whose address it worked out itself
|
// so the move can issue every module against a bus whose address it worked out itself
|
||||||
|
|||||||
@@ -346,7 +346,9 @@ func rolloutMint(ctx context.Context, again bool) error {
|
|||||||
}
|
}
|
||||||
machines++
|
machines++
|
||||||
|
|
||||||
case broker.KindModule:
|
case broker.KindModule, broker.KindNodeTools:
|
||||||
|
// The runtime is minted and delivered exactly as a module is (novox/hq ADR 0175): it is
|
||||||
|
// issued as the module it stands for, to that module's `broker` secret.
|
||||||
if p.Module == "mesh-controller" {
|
if p.Module == "mesh-controller" {
|
||||||
// The control plane is a module too, and its `broker` secret is the old bus's
|
// The control plane is a module too, and its `broker` secret is the old bus's
|
||||||
// credential it is still using while this runs. Writing the new bus's blob there
|
// credential it is still using while this runs. Writing the new bus's blob there
|
||||||
@@ -365,7 +367,7 @@ func rolloutMint(ctx context.Context, again bool) error {
|
|||||||
skipped++
|
skipped++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
password, err := inv.MintBusPassword(ctx, inventory.BusUser{Username: p.Username(), Kind: inventory.BusModule, Node: p.Node, Module: p.Module})
|
password, err := inv.MintBusPassword(ctx, inventory.BusUser{Username: p.Username(), Kind: busKindOf(p.Module), Node: p.Node, Module: p.Module})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -234,3 +234,13 @@ func admitsSubject(pattern, subject []string) bool {
|
|||||||
}
|
}
|
||||||
return len(pattern) == len(subject)
|
return len(pattern) == len(subject)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The two packages name the runtime module separately — the broker's types stay free of the
|
||||||
|
// catalogue's on purpose — so this is what holds them to one string. A rename that reached only one
|
||||||
|
// side would compose a runtime principal for a module nobody assigns, silently, and leave the one
|
||||||
|
// that is assigned with a module's own grants.
|
||||||
|
func TestTheBrokerAndTheCatalogueAgreeOnTheRuntimeModule(t *testing.T) {
|
||||||
|
if RuntimeModule != catalogue.RuntimeModule {
|
||||||
|
t.Fatalf("the broker calls the runtime %q and the catalogue %q", RuntimeModule, catalogue.RuntimeModule)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+85
-2
@@ -34,8 +34,20 @@ const (
|
|||||||
// authority is a list of tools and nothing else — not control, not declarations, not builds,
|
// 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.
|
// and no ability to answer anything, because a person asks.
|
||||||
KindPerson Kind = "person"
|
KindPerson Kind = "person"
|
||||||
|
// KindNodeTools is a machine's tool runtime (novox/hq ADR 0175, to-be 38): one process per
|
||||||
|
// node, on the host side, serving every assigned module's tools and every held seat's verbs.
|
||||||
|
// Its authority is the union of what the modules it carries would each have had for their
|
||||||
|
// tools — and nothing of what they consume, because tools are what it runs, not reactions.
|
||||||
|
KindNodeTools Kind = "node-tools"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// RuntimeModule is the module that IS the node's tool runtime (novox/hq ADR 0175). Where it is
|
||||||
|
// assigned, the mesh composes one runtime principal for the machine in place of that module's own,
|
||||||
|
// and the per-module containers that served tools until then stop being the way tools reach a node.
|
||||||
|
// Mirrored in the catalogue package, which the agreement test holds to the same string; one
|
||||||
|
// constant, so a rename is one edit and the two packages cannot drift.
|
||||||
|
const RuntimeModule = "node-tools"
|
||||||
|
|
||||||
// Seat is a role on the bus as a principal relates to it: the subjects it accepts, and those it
|
// Seat is a role on the bus as a principal relates to it: the subjects it accepts, and those it
|
||||||
// emits (novox/hq ADR 0118, design 29 §5).
|
// emits (novox/hq ADR 0118, design 29 §5).
|
||||||
type Seat struct {
|
type Seat struct {
|
||||||
@@ -74,6 +86,13 @@ type Principal struct {
|
|||||||
// a namespace no such module owns. Every service started and the graph stayed empty.
|
// a namespace no such module owns. Every service started and the graph stayed empty.
|
||||||
Watches []Seat
|
Watches []Seat
|
||||||
|
|
||||||
|
// Carries are the modules whose tools this principal serves, for a KindNodeTools principal
|
||||||
|
// (novox/hq ADR 0175): every module assigned to its node, as each declares itself. Its
|
||||||
|
// serving authority is the union of theirs — each module's own tool namespace and each held
|
||||||
|
// seat's verbs on this node — derived from the same declarations the modules' own principals
|
||||||
|
// are, so the runtime can serve nothing a module could not have served for itself.
|
||||||
|
Carries []Declared
|
||||||
|
|
||||||
// Invokes are the tools this principal may call, as `<module>.<tool>`; a single `*` is every
|
// Invokes are the tools this principal may call, as `<module>.<tool>`; a single `*` is every
|
||||||
// tool. A person's whole authority (design 25 §7), and a module's only if its manifest says so
|
// tool. A person's whole authority (design 25 §7), and a module's only if its manifest says so
|
||||||
// (novox/hq ADR 0152) — the console's does, and nothing else's.
|
// (novox/hq ADR 0152) — the console's does, and nothing else's.
|
||||||
@@ -112,7 +131,10 @@ func (p Principal) Username() string {
|
|||||||
switch p.Kind {
|
switch p.Kind {
|
||||||
case KindPerson:
|
case KindPerson:
|
||||||
return "person." + p.Module
|
return "person." + p.Module
|
||||||
case KindModule:
|
case KindModule, KindNodeTools:
|
||||||
|
// The runtime is named exactly as the module it stands for would have been: the mesh
|
||||||
|
// issues its credential through the same path a module's takes (`module issue`), and
|
||||||
|
// that path knows the node and the module, not the kind.
|
||||||
return p.Node + "." + p.Module
|
return p.Node + "." + p.Module
|
||||||
case KindNode:
|
case KindNode:
|
||||||
return "node." + p.Node
|
return "node." + p.Node
|
||||||
@@ -375,6 +397,48 @@ func PermissionsFor(p Principal) (Permissions, error) {
|
|||||||
pub = append(pub, seatToolSubject(s, t, "*"))
|
pub = append(pub, seatToolSubject(s, t, "*"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case KindNodeTools:
|
||||||
|
// **One process serves what every module on the machine would have served for itself**
|
||||||
|
// (novox/hq ADR 0175). Each carried module's whole tool namespace — the same grant that
|
||||||
|
// module's own principal has, for the same reason: the tools a module serves are what its
|
||||||
|
// code answers, and a list here would be a second copy of it. Each held seat's verbs on
|
||||||
|
// this node, as the holder's own principal would be granted them.
|
||||||
|
for _, d := range p.Carries {
|
||||||
|
if !safeSubject.MatchString(d.Module) {
|
||||||
|
return Permissions{}, fmt.Errorf(
|
||||||
|
"%q cannot be part of a subject: a permission is a subject pattern, and this would widen it", d.Module)
|
||||||
|
}
|
||||||
|
own := "mesh.mod." + d.Module
|
||||||
|
sub = append(sub, own+".tool.>")
|
||||||
|
// A tool that emits an event is the module's code and emits under the module's name
|
||||||
|
// (ADR 0042); the runtime carrying that code may publish what the module declared it
|
||||||
|
// emits, and nothing it did not.
|
||||||
|
for _, e := range d.Emits {
|
||||||
|
pub = append(pub, own+".event."+e)
|
||||||
|
}
|
||||||
|
for _, s := range d.Holds {
|
||||||
|
for _, t := range s.Serves {
|
||||||
|
sub = append(sub, seatToolSubject(s, t, p.Node))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Every assigned module's membership on this node (ADR 0160): one per module, read
|
||||||
|
// directly from the stream and followed live. This node's and no other's — the one token
|
||||||
|
// that varies is the module, so the pattern is the machine's own assignments.
|
||||||
|
sub = append(sub, "mesh.assignment."+p.Node+".*")
|
||||||
|
pub = append(pub, "$JS.API.DIRECT.GET."+AssignmentsStream+".mesh.assignment."+p.Node+".*")
|
||||||
|
// And every tool on the mesh (ADR 0175, decision 5): any node may call any tool on any
|
||||||
|
// node, as the console already could — the runtime is the console's serving mode.
|
||||||
|
invoked, err := invokedSubjects([]string{"*"})
|
||||||
|
if err != nil {
|
||||||
|
return Permissions{}, err
|
||||||
|
}
|
||||||
|
pub = append(pub, invoked...)
|
||||||
|
// Nothing about consumers: it consumes nothing. A module's reactions to events are its
|
||||||
|
// own long-lived process, which ADR 0175 leaves where it is; what moves here is tools.
|
||||||
|
sub = unique(sub)
|
||||||
|
pub = unique(pub)
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.Kind == KindPerson {
|
if p.Kind == KindPerson {
|
||||||
@@ -382,6 +446,11 @@ func PermissionsFor(p Principal) (Permissions, error) {
|
|||||||
// consumer, because nothing is delivered to a person — they ask and are answered.
|
// consumer, because nothing is delivered to a person — they ask and are answered.
|
||||||
sub = append(sub, p.inbox())
|
sub = append(sub, p.inbox())
|
||||||
}
|
}
|
||||||
|
if p.Kind == KindNodeTools {
|
||||||
|
// Its reply space, so the answers to what its tools call come back to it. No ack subject
|
||||||
|
// for the same reason a person has none: nothing is delivered to it.
|
||||||
|
sub = append(sub, p.inbox())
|
||||||
|
}
|
||||||
|
|
||||||
if p.Kind == KindModule || p.Kind == KindNode || p.Kind == KindController {
|
if p.Kind == KindModule || p.Kind == KindNode || p.Kind == KindController {
|
||||||
// Its own reply space, and nothing wider.
|
// Its own reply space, and nothing wider.
|
||||||
@@ -403,7 +472,7 @@ func PermissionsFor(p Principal) (Permissions, error) {
|
|||||||
// A module answers what it was asked — a tool call reaches it on its own namespace, so the
|
// 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 and a
|
// authority is bounded by having been asked — and so does the controller. A node and a
|
||||||
// person are never asked anything, and are granted nothing here.
|
// person are never asked anything, and are granted nothing here.
|
||||||
AllowResponses: p.Kind == KindModule || p.Kind == KindController,
|
AllowResponses: p.Kind == KindModule || p.Kind == KindController || p.Kind == KindNodeTools,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -625,6 +694,20 @@ func ComposeAccounts(principals []Principal) (string, error) {
|
|||||||
return b.String(), nil
|
return b.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// unique is a sorted list with each subject once. Two carried modules holding seats with the same
|
||||||
|
// verb, or the runtime module itself carried beside the others, would otherwise write a grant twice
|
||||||
|
// — harmless to the server, and noise in a file that is read as the mesh's authority model.
|
||||||
|
func unique(values []string) []string {
|
||||||
|
sort.Strings(values)
|
||||||
|
out := values[:0]
|
||||||
|
for i, v := range values {
|
||||||
|
if i == 0 || v != values[i-1] {
|
||||||
|
out = append(out, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
func quoted(values []string) string {
|
func quoted(values []string) string {
|
||||||
if len(values) == 0 {
|
if len(values) == 0 {
|
||||||
return ""
|
return ""
|
||||||
|
|||||||
@@ -371,3 +371,73 @@ func TestAModulePullsItsOwnConsumerAndNoOthers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The runtime's authority is the union of what the modules it carries would have been granted for
|
||||||
|
// their tools (novox/hq ADR 0175): every carried module's tool namespace, every held seat's verbs
|
||||||
|
// on this node, every module's membership on this node, and a call to anything. Nothing it
|
||||||
|
// consumes, because it reacts to nothing.
|
||||||
|
func TestTheRuntimeServesTheUnionAndConsumesNothing(t *testing.T) {
|
||||||
|
filter := Seat{Name: "node-packet-filter", Scope: "node", Serves: []string{"rules", "reload"}}
|
||||||
|
p := Principal{Kind: KindNodeTools, Node: "anchor", Module: RuntimeModule, Carries: []Declared{
|
||||||
|
{Module: "nftables", Holds: []Seat{filter}, Serves: []string{"firewall_rules"}},
|
||||||
|
{Module: "zsh", Emits: []string{"shell.opened"}, Consumes: []string{"shop.order.placed"}},
|
||||||
|
{Module: RuntimeModule},
|
||||||
|
}}
|
||||||
|
perms, err := PermissionsFor(p)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
for _, want := range []string{
|
||||||
|
"mesh.mod.nftables.tool.>", "mesh.mod.zsh.tool.>", "mesh.mod." + RuntimeModule + ".tool.>",
|
||||||
|
"mesh.seat.node-packet-filter.tool.rules.anchor", "mesh.seat.node-packet-filter.tool.reload.anchor",
|
||||||
|
"mesh.assignment.anchor.*",
|
||||||
|
"_INBOX.anchor." + RuntimeModule + ".>",
|
||||||
|
} {
|
||||||
|
if !contains(perms.Subscribe, want) {
|
||||||
|
t.Errorf("the runtime may not subscribe %s: %v", want, perms.Subscribe)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, want := range []string{
|
||||||
|
"mesh.mod.*.tool.>", "mesh.seat.*.tool.>",
|
||||||
|
"$JS.API.DIRECT.GET.ASSIGNMENTS.mesh.assignment.anchor.*",
|
||||||
|
"mesh.mod.zsh.event.shell.opened",
|
||||||
|
} {
|
||||||
|
if !contains(perms.Publish, want) {
|
||||||
|
t.Errorf("the runtime may not publish %s: %v", want, perms.Publish)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Nothing of what a carried module consumes, and no consumer of its own to ack.
|
||||||
|
for _, s := range perms.Subscribe {
|
||||||
|
if strings.Contains(s, ".event.") || strings.HasPrefix(s, "_DELIVER.") {
|
||||||
|
t.Errorf("the runtime was granted a delivery it has no consumer for: %s", s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, s := range perms.Publish {
|
||||||
|
if strings.HasPrefix(s, "$JS.ACK.") || strings.Contains(s, "CONSUMER") {
|
||||||
|
t.Errorf("the runtime was granted a consumer's subject and has no consumer: %s", s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !perms.AllowResponses {
|
||||||
|
t.Error("the runtime answers what it is asked, and may not reply")
|
||||||
|
}
|
||||||
|
if _, needed := ConsumerFor(p); needed {
|
||||||
|
t.Error("a consumer would be made for the runtime, which consumes nothing")
|
||||||
|
}
|
||||||
|
// Each subject once: the file is read as the mesh's authority model.
|
||||||
|
seen := map[string]bool{}
|
||||||
|
for _, s := range append(append([]string{}, perms.Subscribe...), perms.Publish...) {
|
||||||
|
if seen[s] {
|
||||||
|
t.Errorf("%s is granted twice", s)
|
||||||
|
}
|
||||||
|
seen[s] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func contains(list []string, want string) bool {
|
||||||
|
for _, s := range list {
|
||||||
|
if s == want {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|||||||
@@ -62,13 +62,33 @@ func Users(r Records) ([]Principal, error) {
|
|||||||
|
|
||||||
for _, node := range sortedCopy(r.Nodes) {
|
for _, node := range sortedCopy(r.Nodes) {
|
||||||
out = append(out, Principal{Kind: KindNode, Node: node})
|
out = append(out, Principal{Kind: KindNode, Node: node})
|
||||||
|
// **Where the runtime is assigned, the machine gets one runtime principal in place of the
|
||||||
|
// runtime module's own** (novox/hq ADR 0175, to-be 38). It carries every module on the
|
||||||
|
// node: its serving grants are the union of theirs. Every other module keeps its own
|
||||||
|
// principal — a module still serving tools from its own container holds its own
|
||||||
|
// credential until it moves, and the two serve side by side in the meantime.
|
||||||
|
runtimeHere := false
|
||||||
for _, d := range r.Assigned[node] {
|
for _, d := range r.Assigned[node] {
|
||||||
|
if d.Module == RuntimeModule {
|
||||||
|
runtimeHere = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, d := range r.Assigned[node] {
|
||||||
|
if runtimeHere && d.Module == RuntimeModule {
|
||||||
|
continue
|
||||||
|
}
|
||||||
out = append(out, Principal{
|
out = append(out, Principal{
|
||||||
Kind: KindModule, Node: node, Module: d.Module,
|
Kind: KindModule, Node: node, Module: d.Module,
|
||||||
Emits: d.Emits, Consumes: d.Consumes, Serves: d.Serves,
|
Emits: d.Emits, Consumes: d.Consumes, Serves: d.Serves,
|
||||||
Holds: d.Holds, Uses: d.Uses, Watches: d.Watches, Invokes: d.Invokes,
|
Holds: d.Holds, Uses: d.Uses, Watches: d.Watches, Invokes: d.Invokes,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if runtimeHere {
|
||||||
|
out = append(out, Principal{
|
||||||
|
Kind: KindNodeTools, Node: node, Module: RuntimeModule,
|
||||||
|
Carries: append([]Declared(nil), r.Assigned[node]...),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for _, node := range sortedCopy(r.Enrolling) {
|
for _, node := range sortedCopy(r.Enrolling) {
|
||||||
out = append(out, Principal{Kind: KindEnrolment, Node: node})
|
out = append(out, Principal{Kind: KindEnrolment, Node: node})
|
||||||
|
|||||||
@@ -245,3 +245,54 @@ func TestAUserListIsComposedBeforeAnythingMovesOntoTheBus(t *testing.T) {
|
|||||||
t.Errorf("the composed list does not contain the machine running the bus")
|
t.Errorf("the composed list does not contain the machine running the bus")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Where the runtime module is assigned, the machine gets one runtime principal in place of the
|
||||||
|
// runtime module's own (novox/hq ADR 0175, to-be 38). Every other module keeps its own: a module
|
||||||
|
// still serving tools from its own container holds its own credential until it moves.
|
||||||
|
func TestTheRuntimeModuleBecomesTheMachinesRuntimePrincipal(t *testing.T) {
|
||||||
|
r := someRecords()
|
||||||
|
r.Assigned["one"] = append(r.Assigned["one"], Declared{Module: RuntimeModule})
|
||||||
|
users, err := Users(r)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
var runtime *Principal
|
||||||
|
for i := range users {
|
||||||
|
p := &users[i]
|
||||||
|
if p.Node == "one" && p.Module == RuntimeModule {
|
||||||
|
if p.Kind == KindModule {
|
||||||
|
t.Fatalf("%s on one was composed as an ordinary module beside the runtime", RuntimeModule)
|
||||||
|
}
|
||||||
|
runtime = p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if runtime == nil || runtime.Kind != KindNodeTools {
|
||||||
|
t.Fatalf("one runs %s and got no runtime principal: %v", RuntimeModule, namesOf(t, r))
|
||||||
|
}
|
||||||
|
if runtime.Username() != "one."+RuntimeModule {
|
||||||
|
t.Errorf("the runtime is named %q; `module issue` names it as the module it stands for", runtime.Username())
|
||||||
|
}
|
||||||
|
carried := map[string]bool{}
|
||||||
|
for _, d := range runtime.Carries {
|
||||||
|
carried[d.Module] = true
|
||||||
|
}
|
||||||
|
if !carried["telegram"] || !carried[RuntimeModule] {
|
||||||
|
t.Errorf("the runtime carries %v; it carries every module on its node", carried)
|
||||||
|
}
|
||||||
|
// And the other node, where the runtime is not assigned, is exactly as before.
|
||||||
|
for _, p := range users {
|
||||||
|
if p.Node == "two" && p.Kind == KindNodeTools {
|
||||||
|
t.Fatal("two runs no runtime and was given a runtime principal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// A module serving its own tools beside the runtime keeps its own principal.
|
||||||
|
found := false
|
||||||
|
for _, p := range users {
|
||||||
|
if p.Kind == KindModule && p.Node == "one" && p.Module == "telegram" {
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
t.Error("telegram lost its own principal when the runtime arrived on its node")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package catalogue
|
||||||
|
|
||||||
|
// The node's tool runtime, as the catalogue knows it (novox/hq ADR 0175, to-be 38).
|
||||||
|
//
|
||||||
|
// **One module is the runtime.** Where it is assigned, one process per machine serves every assigned
|
||||||
|
// module's tools and every held seat's verbs, on the host side, from the bundles each module's build
|
||||||
|
// produced — and no module needs a container to reach the bus with its tools. The name is a constant
|
||||||
|
// rather than a manifest field because a rule turns on it: the composer places the runtime's process
|
||||||
|
// where this module is, and registration refuses the old pattern once this module exists.
|
||||||
|
|
||||||
|
// RuntimeModule is the module that is the node's tool runtime. Mirrored in the broker package,
|
||||||
|
// which composes a principal of its own for it; the agreement test there holds the two to one string.
|
||||||
|
const RuntimeModule = "node-tools"
|
||||||
@@ -42,6 +42,9 @@ const (
|
|||||||
BusModule = "module"
|
BusModule = "module"
|
||||||
BusEnrolment = "enrolment"
|
BusEnrolment = "enrolment"
|
||||||
BusPerson = "person"
|
BusPerson = "person"
|
||||||
|
// BusNodeTools is a machine's tool runtime (novox/hq ADR 0175): named like the module it
|
||||||
|
// stands for, recorded as what it is.
|
||||||
|
BusNodeTools = "node-tools"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MintBusPassword makes a bus password and records its hash under a username, replacing whatever was
|
// MintBusPassword makes a bus password and records its hash under a username, replacing whatever was
|
||||||
|
|||||||
Reference in New Issue
Block a user