DLV-G2: the bus module's keeper is the process whose bus declares keeps, and only it is composed the create, update, snapshot and delete of every module's bus state (ADR 0626 §1, §3)
A process of the bus module without keeps, or keeps on any other module, is composed nothing of it. mesh-sdk's ProcessBus.Keeps is asked of A1 (a5-sdk-keeps.patch). Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
This commit is contained in:
@@ -1252,7 +1252,9 @@ func TestGrants(t *testing.T) {
|
||||
n := newFakeBus()
|
||||
// 1952/S5 (ADR 0564 Limits 4): the bus module's tools bundle declares its own account and what it may remove.
|
||||
n.manifestOf("nats", strings.Replace(strings.TrimSuffix(natsManifest, "}"), `"broker":"/var/lib/nats/broker"`, `"broker":"/var/lib/nats/broker","broker-nats-tools":"${dir:tools-secret}/broker-nats-tools"`, 1)+
|
||||
`,"resources":[{"id":"tools-secret","type":"directory","path":"/var/lib/mesh/nats-tools","mode":"0711"}],"build":{"artifacts":[{"name":"tools","kind":"bundle","language":"go",`+
|
||||
`,"resources":[{"id":"tools-secret","type":"directory","path":"/var/lib/mesh/nats-tools","mode":"0711"},`+
|
||||
// ADR 0626 §1: the keeper's process declares `keeps`, which its principal carries.
|
||||
`{"id":"keeper","type":"process","name":"nats-keeper","artifact":"tools","run":["./nats-tools","keep"],"bus":{"state":["asked"],"keeps":true}}],"build":{"artifacts":[{"name":"tools","kind":"bundle","language":"go",`+
|
||||
`"system":"arch","from":"cmd/nats-tools","binary":"nats-tools","loads":["nats-tools"],"bus":{"removes":["controller-leftovers","mesh-host_acts"]}}]}}`)
|
||||
n.manifestOf("claude-licence-manager", string(licence))
|
||||
n.assign("novox", "nats")
|
||||
@@ -1272,8 +1274,9 @@ func TestGrants(t *testing.T) {
|
||||
tools = d.Processes
|
||||
}
|
||||
}
|
||||
if len(tools) != 1 || tools[0].Process != "nats-tools" || !slices.Equal(tools[0].Removes, []string{"controller-leftovers", "mesh-host_acts"}) {
|
||||
t.Errorf("the bus module's principals = %+v, want nats-tools carrying both removals", tools)
|
||||
if len(tools) != 2 || tools[0].Process != "keeper" || !tools[0].Keeps || tools[1].Process != "nats-tools" || tools[1].Keeps ||
|
||||
!slices.Equal(tools[1].Removes, []string{"controller-leftovers", "mesh-host_acts"}) {
|
||||
t.Errorf("the bus module's principals = %+v, want the keeper keeping and nats-tools carrying both removals", tools)
|
||||
}
|
||||
if m := grants.Memberships(recs)["novox.claude-licence-manager"]; len(m.Membership.Seats) == 0 {
|
||||
t.Errorf("the licence manager's membership under the seeded placement carries no seats: %+v", m.Membership)
|
||||
|
||||
@@ -1072,9 +1072,9 @@ func theBusModulesProcessKeepsBusState(t *testing.T) {
|
||||
{Module: "nats", SnapshotsTheBus: true, Holds: []Seat{{Name: catalogue.BrokerSeat, Serves: BrokerVerbs()}},
|
||||
Consumes: []string{"mesh-delivery-indexer.manifest-accepted"}, Uses: []Seat{{Name: "operator-channel", Accepts: []string{"condition"}, ByCaller: []string{"condition"}}},
|
||||
Streams: []ModuleStream{{Module: "nats", Name: "BROKER_EVENTS", Events: []string{"created", "refused"}, MaxAgeDays: 30, MaxMiB: 256}},
|
||||
Processes: []ProcessPrincipal{{Process: "keeper", State: []string{"asked"}}, {Process: "nats-tools", Tools: true,
|
||||
Removes: []string{catalogue.RemovesEngineActs}}}},
|
||||
{Module: "mod-x"},
|
||||
Processes: []ProcessPrincipal{{Process: "keeper", State: []string{"asked"}, Keeps: true}, {Process: "other"},
|
||||
{Process: "nats-tools", Tools: true, Removes: []string{catalogue.RemovesEngineActs}}}},
|
||||
{Module: "mod-x", Processes: []ProcessPrincipal{{Process: "sneak", Keeps: true}}},
|
||||
{Module: "admin-ish", Invokes: []string{"*"}},
|
||||
}},
|
||||
BusState: map[string][]catalogue.BusStateEntry{"mod-x": {
|
||||
@@ -1098,10 +1098,16 @@ func theBusModulesProcessKeepsBusState(t *testing.T) {
|
||||
keeper := perms["a.nats.keeper"]
|
||||
for _, s := range []string{"KV_mod-x_records", "mod-x_audit", "mod-x_audit_snapshot", "KV_mod-x_audit_state",
|
||||
"KV_mod-x_audit_index-by-status", "OBJ_mod-x_parts", "KV_gone_kept"} {
|
||||
for _, op := range []string{"CREATE", "UPDATE", "DELETE"} {
|
||||
for _, op := range []string{"CREATE", "UPDATE", "DELETE", "SNAPSHOT"} {
|
||||
if !MayPublish(keeper, "$JS.API.STREAM."+op+"."+s) {
|
||||
t.Errorf("the bus module's process may not %s %s", op, s)
|
||||
}
|
||||
// Only the process whose bus declares `keeps`, and only on the module holding mesh-broker (ADR 0626 §1, §3).
|
||||
for _, other := range []string{"a.nats.other", "a.mod-x.sneak"} {
|
||||
if MayPublish(perms[other], "$JS.API.STREAM."+op+"."+s) {
|
||||
t.Errorf("%s, which is no keeper of the bus, may %s %s", other, op, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, s := range []string{"EVENTS", "KV_mesh-grants_memberships", "KV_mod-x_other"} {
|
||||
|
||||
@@ -133,6 +133,9 @@ type ProcessPrincipal struct {
|
||||
Invokes []string
|
||||
// Tools says it is a tools bundle's account, run by the node's tool runtime, not a process the node runs.
|
||||
Tools bool
|
||||
// Keeps says its bus declares `keeps`: the bus module's keeper (novox/hq ADR 0626 §1), composed the bus's create,
|
||||
// update, snapshot and delete subjects of every module's bus state, and only on the module holding mesh-broker.
|
||||
Keeps bool
|
||||
}
|
||||
|
||||
// Records is what composing a user list needs to know about the mesh, and nothing more.
|
||||
@@ -239,9 +242,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 process of the module holding mesh-broker keeps every module's bus state (busstate.go, ADR 0595 §4),
|
||||
// and its tools bundle reads its module's own stream back.
|
||||
if d.SnapshotsTheBus && !pr.Tools && len(pr.Removes) == 0 {
|
||||
// The process of the module holding mesh-broker whose bus declares `keeps` keeps every module's bus state
|
||||
// (busstate.go, ADR 0595 §4, ADR 0626 §1); `keeps` on any other module composes nothing (the judge refuses it,
|
||||
// ADR 0626 §3). Its tools bundle reads its module's own stream back.
|
||||
if d.SnapshotsTheBus && pr.Keeps && !pr.Tools {
|
||||
k := &out[len(out)-1]
|
||||
k.KeepsBus, k.KeepsBusState = true, BusStateNames(r.BusState, r.Bound)
|
||||
k.Consumes, k.Streams = d.Consumes, d.Streams
|
||||
|
||||
@@ -377,7 +377,7 @@ func processesOf(m catalogue.Manifest) []buscompose.ProcessPrincipal {
|
||||
// what it may remove from the bus (ADR 0564 Limits 4, issue 1952 S5).
|
||||
for _, a := range accounts {
|
||||
pr := buscompose.ProcessPrincipal{Process: a.ID, Emits: a.Bus.Emits, State: a.Bus.State, Asks: a.Bus.Asks,
|
||||
Invokes: a.Bus.Invokes, Removes: a.Bus.Removes, Tools: a.Tools}
|
||||
Invokes: a.Bus.Invokes, Removes: a.Bus.Removes, Tools: a.Tools, Keeps: a.Bus.Keeps}
|
||||
for _, q := range queues {
|
||||
if q.Process == a.ID {
|
||||
pr.Queues = append(pr.Queues, q)
|
||||
|
||||
Reference in New Issue
Block a user