Merge b4/registry (9fafbcfd): the renamed seats (issue 1966)

This commit is contained in:
2026-11-05 14:22:14 +01:00
29 changed files with 77 additions and 77 deletions
+1 -1
View File
@@ -34,7 +34,7 @@ puts the call on the bus, as every agent session on the machine does. The consol
`/etc/mesh-cli/console`, or `MESH_CONSOLE` when set, or node-tools' declared port when the file is not there yet.
- **Who judges.** The seat that owns the job: a change of placement is a proposal the operator approves
(`mesh-placement.propose`), a config value is set with its why (`mesh-config.config`), a silence or a hand act is
(`mesh-module-assigner.propose`), a config value is set with its why (`mesh-config.config`), a silence or a hand act is
recorded as one. Until novox/hq issue 1126 the controller ran these lines and judged the account (ADR 0272); with
the controller unassigned, that judgement went with it.
- **The map.** Every line the controller ran, and the seat verb or tool that does its job now, or none, is
+17 -17
View File
@@ -111,17 +111,17 @@ var commands = []command{
// What runs where.
{words: []string{"status"}, does: "what is wrong, what is quiet, what is moving (the delivery view)",
build: fixed("mesh-delivery-view.view_status")},
{words: []string{"nodes"}, does: "the nodes placement holds", build: fixed("mesh-placement.placement"),
{words: []string{"nodes"}, does: "the nodes placement holds", build: fixed("mesh-module-assigner.placement"),
render: renderNodes},
{words: []string{"node", "list"}, does: "the nodes placement holds", build: fixed("mesh-placement.placement"),
{words: []string{"node", "list"}, does: "the nodes placement holds", build: fixed("mesh-module-assigner.placement"),
render: renderNodes},
{words: []string{"node", "show"}, usage: "<node>", does: "one node's placement: what it runs and its decided fields",
build: nodeArg("mesh-placement.placement")},
build: nodeArg("mesh-module-assigner.placement")},
{words: []string{"node", "public-domain"}, usage: "<node> [<domain> | --clear] [--why <text>]",
does: "the domain a node composes its routed names under; with a domain or --clear, proposed to placement",
allowed: []string{"clear", "why"}, build: publicDomain},
{words: []string{"node", "add"}, instead: "a new machine joins through the seat mesh-enrolment: nox enrol issue <node> --why <text>"},
{words: []string{"node", "networks"}, instead: "no seat keeps a node's routed networks yet; mesh-placement.placement shows what a node decides"},
{words: []string{"node", "add"}, instead: "a new machine joins through the seat mesh-node-enroller: nox enrol issue <node> --why <text>"},
{words: []string{"node", "networks"}, instead: "no seat keeps a node's routed networks yet; mesh-module-assigner.placement shows what a node decides"},
{words: []string{"plan"}, usage: "<node>", does: "what the assigner wants the node to run", build: optNode("mesh-delivery-view.assigner_wants")},
{words: []string{"haves"}, usage: "[<node>]", does: "what each node runs, as its node-engine said", build: optNode("mesh-delivery-view.node_haves")},
{words: []string{"switch"}, does: "what each node runs, as its node-engine said", build: fixed("mesh-delivery-view.node_haves")},
@@ -133,9 +133,9 @@ var commands = []command{
return call{l.pos[0] + "/node-engine.composition", map[string]any{"module": l.pos[1]}}, nil
}},
{words: []string{"seats"}, does: "who holds each seat, and where", build: func(line) (call, error) {
return call{"mesh-placement.placement", map[string]any{"seats": "true"}}, nil
return call{"mesh-module-assigner.placement", map[string]any{"seats": "true"}}, nil
}},
{words: []string{"seat"}, instead: "a seat is held by placing its module: nox assign <node> <module> --why <text> (mesh-placement.propose)"},
{words: []string{"seat"}, instead: "a seat is held by placing its module: nox assign <node> <module> --why <text> (mesh-module-assigner.propose)"},
{words: []string{"assign"}, usage: "<node> <module>... --why <text>", does: "propose placing modules on a node",
allowed: []string{"why"}, build: placeModules("place")},
{words: []string{"unassign"}, usage: "<node> <module>... --why <text>", does: "propose taking modules off a node",
@@ -155,9 +155,9 @@ var commands = []command{
}
return propose(l, map[string]any{"op": "unpin", "node": l.pos[0], "provision": l.pos[1]})
}},
{words: []string{"proposals"}, usage: "[<id>]", does: "the open proposals of placement", build: optArg("mesh-placement.proposals", "id")},
{words: []string{"overlay", "show"}, does: "every node's placement, with its tunnel", build: fixed("mesh-placement.placement")},
{words: []string{"overlay"}, instead: "a node's tunnel is a decided field of its placement: mesh-placement.propose with op decide"},
{words: []string{"proposals"}, usage: "[<id>]", does: "the open proposals of placement", build: optArg("mesh-module-assigner.proposals", "id")},
{words: []string{"overlay", "show"}, does: "every node's placement, with its tunnel", build: fixed("mesh-module-assigner.placement")},
{words: []string{"overlay"}, instead: "a node's tunnel is a decided field of its placement: mesh-module-assigner.propose with op decide"},
// Deliveries and builds.
{words: []string{"deliveries"}, usage: "[--state s] [--repository owner/repo] [--group g] [--all]",
@@ -458,7 +458,7 @@ var commands = []command{
return call{"knowledge-base.progress", a}, nil
}},
// Enrolment (the seat mesh-enrolment; `enrol open` is local, in main.go).
// Enrolment (the seat mesh-node-enroller; `enrol open` is local, in main.go).
{words: []string{"enrol", "issue"}, usage: "<node> --why <text> [--expires 1h]", does: "a one-time token for a new machine, on the operator's Approve",
allowed: []string{"why", "expires"}, build: enrolIssue},
{words: []string{"token", "issue"}, usage: "--new <node> --why <text>", does: "the same as nox enrol issue",
@@ -472,15 +472,15 @@ var commands = []command{
}
return enrolIssue(l)
}},
{words: []string{"enrol", "tokens"}, usage: "[<node>]", does: "every open enrolment, or one", build: optArg("mesh-enrolment.tokens", "node")},
{words: []string{"enrol", "tokens"}, usage: "[<node>]", does: "every open enrolment, or one", build: optArg("mesh-node-enroller.tokens", "node")},
{words: []string{"enrol", "revoke"}, usage: "<node> --why <text>", does: "end an enrolment at any stage", allowed: []string{"why"},
build: func(l line) (call, error) {
if len(l.pos) != 1 || !l.has("why") {
return call{}, usageErr("give the node and --why")
}
return call{"mesh-enrolment.revoke", map[string]any{"node": l.pos[0], "why": l.flags["why"]}}, nil
return call{"mesh-node-enroller.revoke", map[string]any{"node": l.pos[0], "why": l.flags["why"]}}, nil
}},
{words: []string{"enrol", "again"}, usage: "<node>", does: "raise an enrolment's ask again", build: needArg("mesh-enrolment.again", "node")},
{words: []string{"enrol", "again"}, usage: "<node>", does: "raise an enrolment's ask again", build: needArg("mesh-node-enroller.again", "node")},
// Any address.
{words: []string{"call"}, usage: "<address> [<json arguments>]", does: "call any seat verb or module tool, e.g. ace/node-service-manager.status",
@@ -630,7 +630,7 @@ func propose(l line, changes ...map[string]any) (call, error) {
for i, c := range changes {
list[i] = c
}
return call{"mesh-placement.propose", map[string]any{"why": l.flags["why"], "changes": list}}, nil
return call{"mesh-module-assigner.propose", map[string]any{"why": l.flags["why"], "changes": list}}, nil
}
func placeModules(op string) func(line) (call, error) {
@@ -649,7 +649,7 @@ func placeModules(op string) func(line) (call, error) {
func publicDomain(l line) (call, error) {
switch {
case len(l.pos) == 1 && !l.has("clear"):
return call{"mesh-placement.placement", map[string]any{"node": l.pos[0]}}, nil
return call{"mesh-module-assigner.placement", map[string]any{"node": l.pos[0]}}, nil
case len(l.pos) == 1:
return propose(l, map[string]any{"op": "decide", "node": l.pos[0], "field": "public-domain", "value": nil})
case len(l.pos) == 2 && !l.has("clear"):
@@ -705,7 +705,7 @@ func enrolIssue(l line) (call, error) {
}
a := pick(l, "why", "expires")
a["node"] = l.pos[0]
return call{"mesh-enrolment.issue", a}, nil
return call{"mesh-node-enroller.issue", a}, nil
}
func addressCall(address string, rest []string) (call, error) {
+2 -2
View File
@@ -2,7 +2,7 @@ package main
// `nox enrol open`: the operator opens an enrolment token at their own terminal (novox/hq ADR 0587 §6, §8).
//
// The seat mesh-enrolment's `tokens` answers the token sealed to the operator key, which only the operator's private key
// The seat mesh-node-enroller's `tokens` answers the token sealed to the operator key, which only the operator's private key
// opens; the mesh never holds it. This subcommand is local: it asks nothing of the node-engine's socket and nothing of
// the controller or the bus. It reads the sealed token (the base64 of the sealed box) on standard input and the private
// key from a file (`--key`, the raw X25519 key in base64 that `operator key make` wrote), opens the box, and prints the
@@ -22,7 +22,7 @@ import (
const enrolUsage = `nox enrol open --key <operator private key file>
Opens an enrolment token sealed to the operator key (the answer of the seat mesh-enrolment's tokens) and prints the token
Opens an enrolment token sealed to the operator key (the answer of the seat mesh-node-enroller's tokens) and prints the token
to type on the new machine. The sealed token is read on standard input; this runs here and asks nothing of the mesh.
`
+13 -13
View File
@@ -87,17 +87,17 @@ func TestEachLineCallsItsOwner(t *testing.T) {
address string
args string
}{
{"node show ace", "mesh-placement.placement", `{"node":"ace"}`},
{"node public-domain ace", "mesh-placement.placement", `{"node":"ace"}`},
{"node public-domain ace example.org --why moved", "mesh-placement.propose", `{"changes":[{"field":"public-domain","node":"ace","op":"decide","value":"example.org"}],"why":"moved"}`},
{"node show ace", "mesh-module-assigner.placement", `{"node":"ace"}`},
{"node public-domain ace", "mesh-module-assigner.placement", `{"node":"ace"}`},
{"node public-domain ace example.org --why moved", "mesh-module-assigner.propose", `{"changes":[{"field":"public-domain","node":"ace","op":"decide","value":"example.org"}],"why":"moved"}`},
{"plan ace", "mesh-delivery-view.assigner_wants", `{"node":"ace"}`},
{"haves", "mesh-delivery-view.node_haves", `{}`},
{"composition ace plex", "ace/node-engine.composition", `{"module":"plex"}`},
{"seats", "mesh-placement.placement", `{"seats":"true"}`},
{"assign ace plex sonarr --why=media", "mesh-placement.propose", `{"changes":[{"module":"plex","node":"ace","op":"place"},{"module":"sonarr","node":"ace","op":"place"}],"why":"media"}`},
{"unassign ace plex --why gone", "mesh-placement.propose", `{"changes":[{"module":"plex","node":"ace","op":"take-off"}],"why":"gone"}`},
{"pin ace postgres-database novox postgres --why one", "mesh-placement.propose", `{"changes":[{"node":"ace","op":"pin","provider":{"module":"postgres","node":"novox"},"provision":"postgres-database"}],"why":"one"}`},
{"unpin ace postgres-database --why back", "mesh-placement.propose", `{"changes":[{"node":"ace","op":"unpin","provision":"postgres-database"}],"why":"back"}`},
{"seats", "mesh-module-assigner.placement", `{"seats":"true"}`},
{"assign ace plex sonarr --why=media", "mesh-module-assigner.propose", `{"changes":[{"module":"plex","node":"ace","op":"place"},{"module":"sonarr","node":"ace","op":"place"}],"why":"media"}`},
{"unassign ace plex --why gone", "mesh-module-assigner.propose", `{"changes":[{"module":"plex","node":"ace","op":"take-off"}],"why":"gone"}`},
{"pin ace postgres-database novox postgres --why one", "mesh-module-assigner.propose", `{"changes":[{"node":"ace","op":"pin","provider":{"module":"postgres","node":"novox"},"provision":"postgres-database"}],"why":"one"}`},
{"unpin ace postgres-database --why back", "mesh-module-assigner.propose", `{"changes":[{"node":"ace","op":"unpin","provision":"postgres-database"}],"why":"back"}`},
{"deliveries --state merged --json", "mesh-delivery.deliveries", `{"state":"merged"}`},
{"delivery novox/mesh-sdk@dff0ce2abb21", "mesh-delivery.show", `{"id":"novox/mesh-sdk@dff0ce2abb21"}`},
{"checks novox/mesh-catalog --number 591", "mesh-delivery.checks", `{"number":"591","repository":"novox/mesh-catalog"}`},
@@ -132,9 +132,9 @@ func TestEachLineCallsItsOwner(t *testing.T) {
{"kb ask what holds about the merge window", "knowledge-base.ask", `{"question":"what holds about the merge window"}`},
{"kb get 587 --reasoning", "knowledge-base.get", `{"number":587,"reasoning":true}`},
{"progress 47", "knowledge-base.progress", `{"of":"design 47"}`},
{"enrol issue new1 --why laptop --expires 2h", "mesh-enrolment.issue", `{"expires":"2h","node":"new1","why":"laptop"}`},
{"token issue --new new1 --why laptop", "mesh-enrolment.issue", `{"node":"new1","why":"laptop"}`},
{"enrol tokens", "mesh-enrolment.tokens", `{}`},
{"enrol issue new1 --why laptop --expires 2h", "mesh-node-enroller.issue", `{"expires":"2h","node":"new1","why":"laptop"}`},
{"token issue --new new1 --why laptop", "mesh-node-enroller.issue", `{"node":"new1","why":"laptop"}`},
{"enrol tokens", "mesh-node-enroller.tokens", `{}`},
{"call ace/node-service-manager.status {\"unit\":\"plex\"}", "ace/node-service-manager.status", `{"unit":"plex"}`},
{"ask mesh-catalog catalog_modules", "mesh-catalog.catalog_modules", `{}`},
} {
@@ -190,13 +190,13 @@ func TestALineWithNoOwnerOrNotReadCallsNothingAndSaysWhy(t *testing.T) {
// What the console says is printed as said: an answer it marks an error on standard error with exit 1, a console that
// does not answer as nothing called, a verb's process refused before anything is called; nodes are one line each.
func TestAnswersRefusalsAndSilenceAreSaid(t *testing.T) {
got := console(t, map[string]string{"mesh-enrolment.tokens": "!nothing serves seat:mesh-enrolment.tokens"})
got := console(t, map[string]string{"mesh-node-enroller.tokens": "!nothing serves seat:mesh-node-enroller.tokens"})
if code, out, errOut := runLine("enrol", "tokens"); code != 1 || out != "" || !strings.Contains(errOut, "nothing serves") {
t.Errorf("an error answer: exit %d, stdout %q, stderr %q", code, out, errOut)
}
placement := `{"keys":{"novox":{"placement":{"decided":{"address":"10.10.0.1","site":"hub"},"modules":[{},{}]}},` +
`"ace":{"placement":{"decided":{"address":"10.10.0.2"},"modules":[{}]}}}}`
console(t, map[string]string{"mesh-placement.placement": placement})
console(t, map[string]string{"mesh-module-assigner.placement": placement})
if code, out, _ := runLine("nodes"); code != 0 || !strings.HasPrefix(out, "ace ") ||
!strings.Contains(out, "10.10.0.1") || !strings.Contains(out, "2 modules") || !strings.HasSuffix(out, "answered by ace\n") {
t.Errorf("nodes: exit %d, %q", code, out)
@@ -94,7 +94,7 @@ func TestADeliveryHoldsItsOwnFindingsUntilItEnds(t *testing.T) {
}
}
// A seat's finding is about the module the placement says holds the seat on that node (the lead, 2026-10-29): held
// while the assigner is delivered on ace, though the seat is named mesh-placement.
// while the assigner is delivered on ace, though the seat is named mesh-module-assigner.
r := newRig()
// Research 072 row 37: the placement in its keys per module and node; a stale legacy key beside them is not read.
stale, err := delivery.EncodePlacement(delivery.Placement{Node: "ace"})
@@ -103,7 +103,7 @@ func TestADeliveryHoldsItsOwnFindingsUntilItEnds(t *testing.T) {
}
r.k.Placement("placement.ace", stale, false)
placed, err := delivery.PlacementValues(delivery.Placement{Node: "ace", Modules: []delivery.PlacedModule{{Module: "mesh-delivery-assigner"}},
Holds: []delivery.Holding{{Claim: "mesh-placement", Scope: "mesh", Module: "mesh-delivery-assigner"}}})
Holds: []delivery.Holding{{Claim: "mesh-module-assigner", Scope: "mesh", Module: "mesh-delivery-assigner"}}})
if err != nil {
t.Fatal(err)
}
@@ -111,7 +111,7 @@ func TestADeliveryHoldsItsOwnFindingsUntilItEnds(t *testing.T) {
r.k.Placement("ace.node", placed["ace.node"], false)
_ = r.k.Want("want.ace.mesh-delivery-assigner", want(ctrlNext, ctrlNextComp), false)
_ = r.k.Have("ace.mesh-delivery-assigner", have(ctrlRunning, ctrlComposed, delivery.PhaseRunning, delivery.HealthOK), false)
seat, seatRaw := finding(t, selfCheck, health.ScopeSeat, "mesh-placement.ace", "holder-silent", "D3", health.Warning, r.now)
seat, seatRaw := finding(t, selfCheck, health.ScopeSeat, "mesh-module-assigner.ace", "holder-silent", "D3", health.Warning, r.now)
_ = r.k.Finding(selfCheck, seat, seatRaw, false)
if got := r.changes(); got != "" || len(r.k.Held()) != 1 || r.k.Held()[0].Module != "mesh-delivery-assigner" {
t.Errorf("a seat's finding while its holder is delivered: said %q, held %+v", got, r.k.Held())
@@ -54,7 +54,7 @@ var Observers = []string{health.WatchmanModule, health.SelfCheckModule, health.H
const (
AttemptsRead = health.HealersModule + "." + health.AttemptsState
WantsRead = delivery.AssignerModule + ".wants"
// PlacementRead is the mesh-placement seat's placement, read for which module holds a seat on a node (hold.go; the
// PlacementRead is the mesh-module-assigner seat's placement, read for which module holds a seat on a node (hold.go; the
// lead, 2026-10-29, for ADR 0564 clause 2's inputs).
PlacementRead = delivery.PlacementRead
HavesRead = "node-engine.have"
+1 -1
View File
@@ -21,7 +21,7 @@ account alone writes its buckets.
## How a value resolves
For every module the seat mesh-placement places on a node (`mesh-delivery-assigner.placement`), from the module's
For every module the seat mesh-module-assigner places on a node (`mesh-delivery-assigner.placement`), from the module's
own manifests as the delivery built them, never from the controller (issue 1953): the trunk's, which the indexer's
`modules.<module>` names in its manifests by digest (`mesh-delivery-indexer.modules`, `.manifests`), with the build the
node's want names (`mesh-delivery-assigner.wants`, the builder's `mesh-delivery-builder.builds` naming its manifest)
@@ -146,7 +146,7 @@ func (m *memStore) mirror(t *testing.T, read, key, raw string) {
m.states[read][key] = []byte(raw)
}
// assign places a module on a node, in the seat mesh-placement's key for the node.
// assign places a module on a node, in the seat mesh-module-assigner's key for the node.
func (m *memStore) assign(t *testing.T, node, module string) {
t.Helper()
m.place(t, node, module, true)
@@ -148,7 +148,7 @@ type Published struct {
Commit string `json:"commit"`
}
// Assignment is a module placed on a node, as the seat mesh-placement's key `placement.<node>` names it.
// Assignment is a module placed on a node, as the seat mesh-module-assigner's key `placement.<node>` names it.
type Assignment struct {
Node string `json:"node"`
Module string `json:"module"`
+1 -1
View File
@@ -4,7 +4,7 @@
// node), mesh-config_resolved (each module's value on each node, with its source), mesh-config_predefined (the
// published list of predefined keys) and mesh-config_proposals (each value proposed for the operator's Approve,
// asked under this module's own name, design 57 K4).
// It resolves from where the seat mesh-placement places each module, and each module's declarations as its own
// It resolves from where the seat mesh-module-assigner places each module, and each module's declarations as its own
// manifests declare them, the trunk's and its wanted build's (the indexer's and the builder's state, issue 1953); the
// list of predefined keys comes from the indexer's (ADR 0565 §2); the controller's records are no longer read (issue 774). stdout is the MCP channel; what this module says, it says on stderr.
package main
@@ -22,7 +22,7 @@ const (
PredefinedState = "predefined"
ProposalsState = "proposals"
// PlacementRead is the seat mesh-placement's state, held by the assigner (novox/hq ADR 0567 §1, §8): each
// PlacementRead is the seat mesh-module-assigner's state, held by the assigner (novox/hq ADR 0567 §1, §8): each
// node's key `placement.<node>` with the modules placed there. It is the one source of which module runs where
// (issue 1953): the controller's assignments are no longer read.
PlacementRead = delivery.PlacementRead
@@ -13,14 +13,14 @@ import (
// Reading the sources, writing the resolved values, and publishing the list of predefined keys.
// sources reads everything a resolution reads, from the bus as it is now, from the source the switch names
// (takeover.go): which modules are placed where from the seat mesh-placement, and each placed module's declarations
// (takeover.go): which modules are placed where from the seat mesh-module-assigner, and each placed module's declarations
// from its own manifests as built (issue 1953, ADR 0565 §3); the controller's records are no longer read.
func (c *Config) sources() (Sources, error) {
s := Sources{Declared: map[string]ModuleDeclarations{}, OnNode: map[string]ModuleDeclarations{},
Predefined: map[string]Published{}, Values: map[string]Value{}, Renames: map[string]Rename{}}
var problems []string
if err := c.readPlacement(&s); err != nil {
return s, fmt.Errorf("the seat mesh-placement's placement cannot be read: %w", err)
return s, fmt.Errorf("the seat mesh-module-assigner's placement cannot be read: %w", err)
}
if err := c.readDeclarations(&s); err != nil {
return s, fmt.Errorf("the modules' declarations cannot be read: %w", err)
@@ -63,7 +63,7 @@ func (c *Config) sources() (Sources, error) {
return s, nil
}
// readPlacement is every module the seat mesh-placement places on a node (ADR 0567 §1, §9), from the state's keys in
// readPlacement is every module the seat mesh-module-assigner places on a node (ADR 0567 §1, §9), from the state's keys in
// either form: a node key and its module keys, or the form before, `placement.<node>` (delivery.PlacementsOf;
// research 072 row 37). By node in name order, each node's modules in the order its placement names them.
func (c *Config) readPlacement(s *Sources) error {
+2 -2
View File
@@ -5,12 +5,12 @@ P5). Go, one binary (`cmd/mesh-provisioner-client`), run as a service of its own
`mesh-provisioner-client`, outside the tool runner, on a bus account of its own, `provisioner.<node>`, delivered as
its own secret `provisioner-broker` (mesh-sdk `buscompose/provisioner.go`). The bus grants that account its own
machine's keys and, to read alone, the seat mesh-provisioner's whole `plans`, where a provider's consumers on any
machine are bound to it (ADR 0373), and the seat mesh-placement's whole `placement`; nothing else.
machine are bound to it (ADR 0373), and the seat mesh-module-assigner's whole `placement`; nothing else.
## What it does
For each module placed on its node — a plan in the seat mesh-provisioner's `plans` (`mesh-provisioner_plans`,
`<node>.<module>`, novox/hq ADR 0570 clause 2); a module the seat mesh-placement still places here stays while the
`<node>.<module>`, novox/hq ADR 0570 clause 2); a module the seat mesh-module-assigner still places here stays while the
seat holds no plan of it, filled from its last plan, and leaves only when neither names it (issue 1126): the templates archive
by reference and digest, the files it fills (path, template, `owner-at-runtime`, how its program takes a change)
and the placement values its templates name — the client:
@@ -37,7 +37,7 @@ import (
// The owners it fills from are placement (in the module's plan), config (mesh-config_resolved) and the vault, asked for
// each secret the module's plan names (secrets.go), and for each consumer's credential a provider here lists
// (consumers.go); and each provider's entries for a consumer here. Every key it writes is its own machine's; it reads
// the plans whole, for who binds a provider here (ADR 0373), and the seat mesh-placement's placement, for which
// the plans whole, for who binds a provider here (ADR 0373), and the seat mesh-module-assigner's placement, for which
// modules stay here: its bus account is granted no other.
// The buckets the client reads and writes, and the subject it reports on.
@@ -45,7 +45,7 @@ const (
// PlansBucket is where each module's provisioning plan on each node is read: the seat mesh-provisioner's
// `plans` (novox/hq ADR 0570 clause 2), read as an assignment's value carried it (asAssignment).
PlansBucket = provisions.PlansBucket
// PlacementBucket is the seat mesh-placement's placement (novox/hq ADR 0567): a module it names on this node stays
// PlacementBucket is the seat mesh-module-assigner's placement (novox/hq ADR 0567): a module it names on this node stays
// here while the seat mesh-provisioner holds no plan of it (nats on novox runs without a want), filled from its
// last plan, and leaves only when neither names it (issue 1126 B12; the controller's assignments mirror, read for
// this before, is frozen since the controller's end, ADR 0590).
@@ -155,7 +155,7 @@ type state struct {
plans map[string]Plan
// seatPlans is the seat's plan by key, as an assignment's value reads.
seatPlans map[string][]byte
// placement is the seat mesh-placement's keys as read, and placed the modules it names on this node.
// placement is the seat mesh-module-assigner's keys as read, and placed the modules it names on this node.
placement *delivery.PlacementKeys
placed map[string]bool
refused map[string]string // a module whose plan cannot be acted on, and why
@@ -1161,7 +1161,7 @@ func (c *Client) forgetItems(ctx context.Context, kvs map[string]jetstream.KeyVa
return removed
}
// takePlacement folds one key of the seat mesh-placement's placement in, and answers the modules of this node it moved:
// takePlacement folds one key of the seat mesh-module-assigner's placement in, and answers the modules of this node it moved:
// one it names now stays here without a plan; one it no longer names, with no plan of the seat, leaves.
func (c *Client) takePlacement(s *state, key string, value []byte, revision uint64, gone bool) []string {
node, err := s.placement.Put(key, value, revision, gone)
@@ -97,7 +97,7 @@ func startBus(t *testing.T) *aBus {
`"bucket":"mesh-provisioner_collect","key":"<node>.<collection>.<contributor>"}`)
}
for _, kind := range placementKinds {
b.put(t, RegistryBucket, kind, `{"kind":"`+kind+`","domain":"mesh-placement","from":"bucket",`+
b.put(t, RegistryBucket, kind, `{"kind":"`+kind+`","domain":"mesh-module-assigner","from":"bucket",`+
`"bucket":"mesh-provisioner_plans","key":"<node>.<module>"}`)
}
return b
@@ -122,7 +122,7 @@ func (b *aBus) put(t *testing.T, bucket, key, value string) {
}
}
// place writes this node's placement as the seat mesh-placement keeps it, naming exactly these modules.
// place writes this node's placement as the seat mesh-module-assigner keeps it, naming exactly these modules.
func (b *aBus) place(t *testing.T, modules ...string) {
t.Helper()
p := delivery.Placement{V: 1, Node: node}
@@ -659,7 +659,7 @@ func TestNatsAModuleTakenOffTheNodeLosesItsReport(t *testing.T) {
}
// Issue 1126 B12 (ADR 0590): the controller's assignments are frozen and go, so what places a module here is the seat
// mesh-placement's placement alone. A module stays while placement names it, even with no plan of the seat
// mesh-module-assigner's placement alone. A module stays while placement names it, even with no plan of the seat
// mesh-provisioner (nats on novox runs without a want): its last plan keeps being filled, and it leaves only when
// neither its plan nor its placement is there.
func TestNatsAModuleStaysWhilePlacementNamesItAndLeavesOnlyWhenNeitherPlacesIt(t *testing.T) {
@@ -1,7 +1,7 @@
package main
// The probes of the mesh's machines (novox/hq ADR 0564 §6, issue 1952): each reads its facts where they live on the
// bus — placement (the seat mesh-placement's state), who holds which seat (its seat-holders), whether a node is heard
// bus — placement (the seat mesh-module-assigner's state), who holds which seat (its seat-holders), whether a node is heard
// from (the watchman's presence) — and asks each node's seat holder what only that node knows. Its assertion, kind,
// key and words are the controller's (mesh-controller probes.go), so the condition the keeper raises from it is the one
// the controller raised.
@@ -2,14 +2,14 @@ package main
// The vault's part of enrolment (novox/hq ADR 0587 §3, §5, §8; ADR 0561 §4, §5; issue 774).
//
// A machine joins with a token a person types. The seat mesh-enrolment decides who may join; the vault holds the secrets.
// A machine joins with a token a person types. The seat mesh-node-enroller decides who may join; the vault holds the secrets.
// Its verb `enrolment-token` makes the token's one-time secret, keeps it with its operator copy as `enrol/<node>/token`,
// writes its bcrypt hash under `bus-accounts` as the user `enrol.<node>`, and answers at issue the public fields alone
// (never the token). Asked to show it, it answers the whole token sealed to the operator key, which the operator opens at
// their own terminal (`nox enrol open`); asked to drop it, it takes the token and its hash away. Its verb `node-account`
// makes the account `node.<node>` of a joining machine at the redeem: once for a node and a set of keys, sealed to the key
// the machine presented, with the node's key in `node-logins` naming that login in use and nothing rotating. Only the
// seat mesh-enrolment may call either: the runtime proves who asks.
// seat mesh-node-enroller may call either: the runtime proves who asks.
import (
"crypto/ecdh"
@@ -32,7 +32,7 @@ import (
"golang.org/x/crypto/bcrypt"
)
// The names enrolment gives: the module that holds the seat mesh-enrolment, the prefix of a token's bus user, and the
// The names enrolment gives: the module that holds the seat mesh-node-enroller, the prefix of a token's bus user, and the
// vault's state of each node's logins.
const (
EnrolmentModule = delivery.EnrolmentModule
@@ -115,7 +115,7 @@ func (v *Vault) loginKeyRefusal(caller, publicKey string) string {
return ""
}
// fromEnrolment refuses every caller but the seat mesh-enrolment, as the runtime proved it.
// fromEnrolment refuses every caller but the seat mesh-node-enroller, as the runtime proved it.
func (v *Vault) fromEnrolment(caller, verb string) error {
callers := v.EnrolCallers
if callers == nil {
@@ -199,7 +199,7 @@ func TestTheVaultsEnrolmentAnswersAreMeshSDKsWireShapes(t *testing.T) {
filled []string // the json fields the holder reads, which must not be empty
refused string // a part of the refusal, when refused
}{
// Only the seat mesh-enrolment is answered: not the controller, a node, a person or a caller not proven.
// Only the seat mesh-node-enroller is answered: not the controller, a node, a person or a caller not proven.
{"a node asks a token", "enrolment-token", "node.g14", map[string]any{"node": "new1", "tunnel": tunnel}, nil, nil, "mesh-enrolment"},
{"the controller asks a token", "enrolment-token", "controller", map[string]any{"node": "new1", "tunnel": tunnel}, nil, nil, "mesh-enrolment"},
{"a person asks an account", "node-account", "person.jochen", map[string]any{"node": "new1"}, nil, nil, "mesh-enrolment"},
@@ -55,7 +55,7 @@ func enrolmentTools(env *Env) []toolDef {
node := str("the node's name")
return []toolDef{
{Name: Seat + ".enrolment-token",
Description: "The enrolment token of a joining machine, for the seat mesh-enrolment alone (novox/hq ADR 0587 §3, §5, " +
Description: "The enrolment token of a joining machine, for the seat mesh-node-enroller alone (novox/hq ADR 0587 §3, §5, " +
"§8). Issue (the default): takes the machine's first tunnel, makes the one-time secret, keeps both with the operator " +
"copy, writes the secret's hash as the bus user enrol.<node>, and answers the bus's address, its certificate's " +
"fingerprint and the vault's signing key, never the token. sealed: the whole token sealed to the operator key, " +
@@ -78,7 +78,7 @@ func enrolmentTools(env *Env) []toolDef {
Tunnel: tunnel, Sealed: isTrue(a["sealed"]), Drop: isTrue(a["drop"])})
}},
{Name: Seat + ".node-account",
Description: "A joining node's account node.<node>, for the seat mesh-enrolment alone at the redeem (novox/hq ADR 0587 " +
Description: "A joining node's account node.<node>, for the seat mesh-node-enroller alone at the redeem (novox/hq ADR 0587 " +
"§3): made once for a node and the keys it presented, answered sealed to the sealing key presented, with the " +
"node's key in node-logins naming the login in use. Asked again for the same keys it answers the same account. " +
"retire: the account, its value and its key taken away at a revoke.",
+1 -1
View File
@@ -238,7 +238,7 @@ func watchCredentialAsks(env *Env, kick chan<- struct{}) bool {
return true
}
// placement reads the seat mesh-placement's placement through its owner's tool and events (placementview.go), never the
// placement reads the seat mesh-module-assigner's placement through its owner's tool and events (placementview.go), never the
// owner's bucket.
var placement = viewPlacement(func() *views.Follower { return placementView })
+2 -2
View File
@@ -1,6 +1,6 @@
package main
// Where modules stand, from the placement (novox/hq ADR 0549 §3, §5; ADR 0561; issue 774), the seat mesh-placement's
// Where modules stand, from the placement (novox/hq ADR 0549 §3, §5; ADR 0561; issue 774), the seat mesh-module-assigner's
// state held by the assigner (ADR 0567). The controller's assignments mirror goes with its lease, so the vault reads which
// module stands on which node from the seat's placement first, and the assignments beside it while both stand: a module either names stands. The provisioner's plan,
// which only an assignment's value carries, is read from the assignments still: its home after the lease is a subtask of
@@ -16,7 +16,7 @@ import (
"git.novox.be/novox/mesh-sdk/go/delivery"
)
// PlacementRead is the seat mesh-placement's placement, as the vault's manifest reads it (mesh-sdk's contract).
// PlacementRead is the seat mesh-module-assigner's placement, as the vault's manifest reads it (mesh-sdk's contract).
const PlacementRead = delivery.PlacementRead
// PlacementReader reads the seat's placement: every key, and one key's value (nil when there is none).
@@ -7,7 +7,7 @@ import (
"git.novox.be/novox/mesh-sdk/go/delivery"
)
// placementsOf is the seat mesh-placement's placement of each node, as the vault reads it.
// placementsOf is the seat mesh-module-assigner's placement of each node, as the vault reads it.
func placementsOf(t *testing.T, nodes map[string][]string) PlacementReader {
t.Helper()
values := map[string][]byte{}
@@ -1,6 +1,6 @@
package main
// The vault's reading of the seat mesh-placement's placement, through its owner (novox/hq ADR 0615 §1, §4 to §6, ADR
// The vault's reading of the seat mesh-module-assigner's placement, through its owner (novox/hq ADR 0615 §1, §4 to §6, ADR
// 0596): the assigner's tool `assigner_read_placement` asked for the whole state at start, the owner's `views.placement`
// events after it, and the tool once more only on a gap. Started while the assigner is down it keeps asking and says so;
// it never reads the assigner's bucket instead. (The vault's other reads, the node-engines' haves, the grants part's
+3 -3
View File
@@ -277,7 +277,7 @@ func (v *Vault) Sign(caller string, a SignArgs) (meshvault.SignAnswer, error) {
return meshvault.SignAnswer{}, fmt.Errorf("%q is not a kind the vault signs (%s)", a.Kind, strings.Join(delivery.SignKinds, ", "))
}
// redeeming says node has an enrolment token held: a machine joining, which the seat mesh-enrolment redeems.
// redeeming says node has an enrolment token held: a machine joining, which the seat mesh-node-enroller redeems.
func (v *Vault) redeeming(node string) error {
if err := enrolNode(node); err != nil {
return err
@@ -290,7 +290,7 @@ func (v *Vault) redeeming(node string) error {
return nil
}
// signRedeemingCertificate signs the certificate of a machine that is joining, for the seat mesh-enrolment at the redeem
// signRedeemingCertificate signs the certificate of a machine that is joining, for the seat mesh-node-enroller at the redeem
// (ADR 0587 §6). The machine has written no record yet, so the serving key is the one it presented at the redeem, which
// counts only when the four keys presented have the digest node-account recorded for its account; the name is its
// placement's, which mesh-enrolment writes before it asks.
@@ -410,7 +410,7 @@ func (v *Vault) internalName(node string) (string, error) {
return "", fmt.Errorf(notYetPrefix+"the placement of %s could not be read: %w", node, err)
}
if !found {
return "", fmt.Errorf("%s has no placement of the seat mesh-placement, so it has no name inside the mesh", node)
return "", fmt.Errorf("%s has no placement of the seat mesh-module-assigner, so it has no name inside the mesh", node)
}
if decided.Address == "" {
return "", fmt.Errorf("%s is not on the private network (its placement names no address), so it has no name inside the mesh to be certified for", node)
+1 -1
View File
@@ -78,7 +78,7 @@ type Vault struct {
// none.
Signers SignersStore
// EnrolCallers are the callers of the enrolment verbs `enrolment-token` and `node-account` and of `sign` for an
// enrolment: the seat mesh-enrolment (enrolment.go). Nil: that module alone.
// enrolment: the seat mesh-node-enroller (enrolment.go). Nil: that module alone.
EnrolCallers stdio.Callers
// RetireCallers are the callers of the seat's verb `retire`: the vault's trusted config value `retire-callers`, the
// holder of the seat mesh-config alone (ownretire.go). Nil: nobody.