The part binds its own durable on the node-engine's kept stream (HearKept, tried at every wake until the pass has made it), the seat's holders are read from the manifest of the digest each node says it runs, and a node-engine's account is granted its own node at the part its definition names (running.<node>.*) and its durable on the kept stream by name. Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ Builds: design 47 G §199, §249, §309
120 lines
6.7 KiB
Go
120 lines
6.7 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-delivery/modules/mesh-grants/busgrants"
|
|
"maps"
|
|
"slices"
|
|
)
|
|
|
|
// 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 = busgrants.EngineModule
|
|
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)))
|
|
// And an event whose subject carries parts says its machine at the part the definition names `<node>` (design 47:
|
|
// `running.<node>.<module>`): its own node's alone, any module, never another node's.
|
|
seat := Seat{Name: EngineSeat, Scope: "node", HeldByTheEngine: true, EventSubjects: p.EngineEvents}
|
|
for _, e := range slices.Sorted(maps.Keys(p.EngineEvents)) {
|
|
if pattern, ok := machineEvent(seat, e); ok {
|
|
pub = append(pub, seatSubject(seat, "event", onMachine(pattern, p.Node)))
|
|
}
|
|
}
|
|
// And the durable it makes on each of the seat's events kept in a stream of its own (the engine is no module, so no
|
|
// Heard): its own, named as its consumer on the shared stream is, asked about, pulled and acknowledged, and the seat's
|
|
// events of every node heard there, since a seat's pass is decided from what the other nodes run.
|
|
if safeSubject.MatchString(p.Node) {
|
|
for _, k := range p.EngineKept {
|
|
if !safeSubject.MatchString(k.Stream) {
|
|
continue
|
|
}
|
|
pub = append(pub,
|
|
"$JS.API.CONSUMER.CREATE."+k.Stream+"."+p.Node, "$JS.API.CONSUMER.CREATE."+k.Stream+"."+p.Node+".>",
|
|
"$JS.API.CONSUMER.INFO."+k.Stream+"."+p.Node, "$JS.API.CONSUMER.MSG.NEXT."+k.Stream+"."+p.Node,
|
|
"$JS.ACK."+k.Stream+"."+p.Node+".>")
|
|
sub = append(sub, k.Filter)
|
|
}
|
|
}
|
|
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(ProvisionerProvisionsBucket, 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...)
|
|
}
|