Grants: the node-engine's records keep its consumes, so each node account hears the backup holder's word and settled
Dropped since the hear was removed (ADR 0625 §4); the golden user list and the grants case now hold the node user's subscriptions. The indexer's open-tries comment says it refuses an answer without the key. Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
This commit is contained in:
@@ -302,7 +302,7 @@ func openTriesVia(call toolCaller) func(ctx context.Context) ([]checkermsg.Open,
|
||||
return nil, err
|
||||
}
|
||||
// No try open is an answer: the checker's list is empty, or null in an older checker (issue 1921). An answer without the
|
||||
// key is none.
|
||||
// key is refused below, never read as none.
|
||||
var o map[string]json.RawMessage
|
||||
if err := json.Unmarshal(raw, &o); err != nil {
|
||||
return nil, fmt.Errorf("the answer of %s is not {tries}: %v", openTriesTool, err)
|
||||
|
||||
@@ -1388,6 +1388,17 @@ func TestGrants(t *testing.T) {
|
||||
if slices.ContainsFunc(runtime.Publish, func(s string) bool { return strings.HasPrefix(s, "$KV.mesh-host_") }) {
|
||||
t.Errorf("novox.node-tools is granted a write of the node-engine's state: %v", runtime.Publish)
|
||||
}
|
||||
// The node's account hears what the node-engine's manifest consumes (ADR 0625 §4: the manifest alone gives it, ADR
|
||||
// 0539 clause 16): dropping `consumes` from the engine's records left every node deaf to the backup holder's word.
|
||||
var node grants.User
|
||||
for _, u := range shadowOf(t, n) {
|
||||
if u.User == "node.novox" {
|
||||
node = u
|
||||
}
|
||||
}
|
||||
if !slices.Contains(node.Subscribe, "mesh.mod.restic.event.backed-up") {
|
||||
t.Errorf("node.novox does not hear restic's backed-up, which the node-engine's manifest consumes: %v", node.Subscribe)
|
||||
}
|
||||
for _, s := range live.Users[0].Subscribe {
|
||||
if strings.Contains(s, ".call.") && !slices.Contains(runtime.Subscribe, s) {
|
||||
t.Errorf("under the seeded assignments novox.node-tools is not granted %s, which the bus grants it today", s)
|
||||
|
||||
@@ -73,6 +73,14 @@ func UserListFixtures() []UserListFixture {
|
||||
},
|
||||
RootFree: map[string]bool{"ace": true},
|
||||
}},
|
||||
// The node-engine's manifest as it ships places on every node: its account hears what the manifest consumes (the
|
||||
// backup holder's word, the status view's settled, the seat-definition events), and nothing else names them.
|
||||
{Name: "a node-engine that consumes", Records: Records{
|
||||
Nodes: []string{"ace"},
|
||||
Assigned: map[string][]Declared{"ace": {{Module: "web"}}},
|
||||
Engine: &Declared{Module: EngineModule, Consumes: []string{"mesh-catalog.seat-definition-changed",
|
||||
"mesh-catalog.seat-definition-removed", "mesh-delivery-view.settled", "restic.backed-up"}},
|
||||
}},
|
||||
// ADR 0618 §4: an event of the node-engine's seat names the engine's machine, so its watcher hears it from every
|
||||
// machine; a node seat held by a module says its other events flat, and is heard flat.
|
||||
{Name: "a node seat's events, by machine and flat", Records: Records{
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -111,7 +111,7 @@ func records(in Inputs, union bool) (buscompose.Records, []Gap) {
|
||||
// The node-engine's own manifest, placed on every node (ADR 0563): its state, logs and streams alone, created by this
|
||||
// part; it adds no principal, since each node's engine speaks as the node's account.
|
||||
if m, ok := declared[buscompose.EngineModule]; ok {
|
||||
out.Engine = &buscompose.Declared{Module: m.Module, State: bucketsFor(m, in.Nodes), Logs: logsOf(m), Streams: streamsOf(m)}
|
||||
out.Engine = &buscompose.Declared{Module: m.Module, Consumes: m.Consumes, State: bucketsFor(m, in.Nodes), Logs: logsOf(m), Streams: streamsOf(m)}
|
||||
}
|
||||
return out, gaps
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user