Files
mesh-delivery/modules/mesh-grants/internal/buscompose/engine.go
T

95 lines
5.5 KiB
Go

package buscompose
import (
nodeengineseat "git.novox.be/novox/mesh-contracts/go/seats/nodeengine"
occ "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/clients/operatorchannelclient"
"git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient"
)
// The node seat node-engine (novox/hq ADR 0563 §1): every node's engine holds it on its own bus account, and no module
// claims it. Its verbs and events are the catalogue's (catalogue.EngineVerbs), read here rather than copied.
// EngineSeat is the seat.
const EngineSeat = nodeengineseat.Seat
// EngineModule is the node-engine's manifest's module name, and EngineActsState its state the acts asked through the
// node seat and its own asks are kept in (ADR 0563 §2, §3: "the engine's own state"): the bucket mesh-host_acts, keys
// `<node>.act.<id>`, `<node>.open.<act>.<digest>` and `<node>.ask.<id>`, each written by that node's engine alone.
const (
EngineModule = "mesh-host"
EngineActsState = "acts"
)
// EngineActsBucket is that state's bucket.
var EngineActsBucket = BucketName(EngineModule, EngineActsState)
// EnginePackagesBucket is the engine's state of its node's declared packages (novox/hq ADR 0565 clause 6): one key, the
// node's name, written by that node's engine alone.
var EnginePackagesBucket = BucketName(EngineModule, EnginePackagesState)
// EngineFindingsBucket is the engine's state of the findings it raises about its own node (novox/hq ADR 0564 §2, ADR 0636 §1):
// keys `machine.<node>.<kind>`, written by that node's engine alone and cleared by it; the condition keeper raises each as a
// condition.
var EngineFindingsBucket = BucketName(EngineModule, "findings")
// EngineFindingsGrants is what a node's engine publishes to write and clear its node's findings: its own node's keys alone,
// and their read. Without it the finding that the node waits for the seat list is refused on the bus and never raised.
func EngineFindingsGrants(node string) []string {
return append([]string{"$KV." + EngineFindingsBucket + ".machine." + node + ".>"}, readGrants(EngineFindingsBucket, "machine."+node+".>")...)
}
// EnginePackagesGrants is what a node's engine publishes to keep its node's key of the packages bucket: the key alone,
// and its read.
func EnginePackagesGrants(node string) []string {
return append([]string{"$KV." + EnginePackagesBucket + "." + node}, readGrants(EnginePackagesBucket, node)...)
}
// engineGrants is what a node's engine is granted to hold its node seat and to ask the operator (ADR 0563 §1, §3): it
// answers its own node's verbs (the caller-named subjects are added beside them with every principal's), says the
// seat's events about its node, answers discovery for the seat, asks who answers what (the login shell's execute, ADR
// 0563 §6), and asks the operator under occ.EngineAsker(node) on the router's seat as its holder declares it, as any
// user of that seat is granted.
//
// Its verbs and events are granted as any verb and any event of the seat on its own node, not as the contract's list:
// the list is compiled into whichever mesh-sdk the grants part pins, so a verb the engine gained before the grants part
// was released was refused on every node and the engine reopened its link every few minutes (2026-10-31, the optional
// verb composition of mesh-host #184). As a module answers `mesh.mod.<module>.tool.>`, nothing is given away: no other
// principal may subscribe this seat's subjects of this node, and a caller is still granted each verb by name.
func engineGrants(p Principal) (pub, sub []string) {
sub = append(sub, "mesh.seat."+EngineSeat+".tool.*."+p.Node)
// Any event of the seat about its own node, by the one rule a watcher of it is composed by (machineEvent).
every, _ := machineEvent(Seat{Name: EngineSeat, Scope: "node", HeldByTheEngine: true}, "*")
pub = append(pub, seatSubject(Seat{Name: EngineSeat}, "event", onMachine(every, p.Node)))
sub = append(sub, announcing(EngineSeat)...)
pub = append(pub, discovering()...)
tp, ts := seatTrafficGrants(SeatTrafficOf(occ.EngineAsker(p.Node), nil, p.Uses, nil))
// And its acts and asks, under its own node's keys alone; never the bucket's creation, which is the grants part's.
if own, err := NodeKeysGrants(EngineActsBucket, p.Node); err == nil {
pub = append(pub, own...)
}
// And the provisions its node's modules consume, its own node's keys alone, read (novox/hq ADR 0570 clause 6: the
// consuming engine checks the vault's name and generation of what it is handed).
if safeSubject.MatchString(p.Node) {
pub = append(pub, readGrants(meshprovisionerclient.ProvisionsBucket, p.Node+".>")...)
}
// And its node's own health report, its key `<node>` alone (issue 774, the operator's alert table): what the watchman
// reads for the machine's alerts.
if safeSubject.MatchString(p.Node) {
pub = append(pub, append([]string{"$KV." + NodeHealthBucket + "." + p.Node},
readGrants(NodeHealthBucket, p.Node)...)...)
}
// And its node's placed modules' data items, its own node's keys alone (ADR 0577 clause 1).
if own, err := NodeKeysGrants(EngineDataBucket, p.Node); err == nil {
pub = append(pub, own...)
}
// And its node's declared packages, its own key alone (ADR 0565 clause 6).
if safeSubject.MatchString(p.Node) {
pub = append(pub, EnginePackagesGrants(p.Node)...)
}
// And the findings it raises about its own node, its own node's keys alone (ADR 0564 §2, ADR 0636 §1).
if safeSubject.MatchString(p.Node) {
pub = append(pub, EngineFindingsGrants(p.Node)...)
}
return append(pub, tp...), append(sub, ts...)
}