Files
mesh-delivery/modules/mesh-grants/internal/buscompose/gated.go
T
jschoubben cdc6e1f521 Item 5, area 8 (join 11): the grants part lets a caller of a marked verb hear its own calls' ends
A holder says a held call's end on its seat's gated-ended, kept last per subject for a day in SEAT_<seat>_gated-ended,
which the grants part makes from the definition seatdef.Resolve gives (A-264). The caller is granted hearing
gated-ended.<caller>.> under its own escaped account name, and an ordered consumer on that stream filtered to it, from its
invokes of the marked verb by name; a module or process that answers a marked verb is granted saying the event to any
caller. No process of mesh-delivery holds a marked verb, so no holder here takes NewGateChannel.

Builds: design 47 G §677, §700, A-264, A-273

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 12:24:48 +01:00

55 lines
2.3 KiB
Go

package buscompose
import (
"slices"
"strings"
"git.novox.be/novox/mesh-sdk/go/keys"
"git.novox.be/novox/mesh-sdk/go/seatdef"
)
// **A held call's end is heard by its caller alone** (design 47 G §677; lead answer A-264): a holder says the end of a call of
// a marked verb on its seat's event `gated-ended`, subject `mesh.seat.<seat>.event.gated-ended.<caller>.<call>`, `<caller>` the
// account the bus proved escaped as a key's name is, kept last per subject for a day in the stream SEAT_<seat>_gated-ended
// (KeptEventsOf, from the definition seatdef.Resolve gives). The caller is granted hearing `gated-ended.<caller>.>` and no other
// caller's, from its invokes of the marked verb by name; it reads the stream through an ordered consumer filtered to its own
// subjects (mesh-sdk process awaitGated), made for the read. A process that answers a marked verb says the event to any caller.
// gatedEndedGrants are the grants of the principal user from the marked verbs (`seat:<seat>.<verb>`) it invokes by name and
// those it answers.
func gatedEndedGrants(user string, marked, invokes, answers []string) (pub, sub []string) {
caller := keys.EscapeName(user)
if user == "" {
return nil, nil
}
var heard, said []string
for _, v := range marked {
seat, _, ok := strings.Cut(strings.TrimPrefix(v, "seat:"), ".")
if !ok || !safeSubject.MatchString(seat) {
continue
}
if slices.Contains(invokes, v) {
heard = append(heard, seat)
}
if slices.Contains(answers, v) {
said = append(said, seat)
}
}
for _, seat := range unique(heard) {
stream := seatdef.KeptStream(seat, seatdef.Message{Name: seatdef.GatedEndedEvent})
own := GatedEndedSubject(seat, caller) + ".>"
sub = append(sub, own)
pub = append(pub, "$JS.API.STREAM.INFO."+stream, "$JS.API.CONSUMER.CREATE."+stream+".*."+own,
"$JS.API.CONSUMER.MSG.NEXT."+stream+".*", "$JS.API.CONSUMER.INFO."+stream+".*", "$JS.API.CONSUMER.DELETE."+stream+".*")
}
for _, seat := range unique(said) {
pub = append(pub, GatedEndedSubject(seat, "*")+".*")
}
return pub, sub
}
// GatedEndedSubject is the subject of the seat's gated-ended for one caller, its escaped name (keys.EscapeName) or `*`.
func GatedEndedSubject(seat, caller string) string {
return seatdef.EventSubject(seat, seatdef.GatedEndedEvent) + "." + caller
}