package runtime import ( "fmt" "strings" "git.novox.be/novox/mesh-sdk/go/approval" "git.novox.be/novox/mesh-sdk/go/seatdef" "github.com/novox/mesh-tools/node-tools/internal/launch" ) // FingerprintWord is the word of a module's composed environment that names the build its bundle serves (design 47 G §424). const FingerprintWord = "MESH_FINGERPRINT" // Gate is the approval gate the runner serves through (design 46 7-approval-mark §4, §20; node-tools/internal/approval). type Gate interface { // Unserved is, of a module's marked tools, those its bundle does not register, saying it as the module's finding. Unserved(module, fingerprint string, registered []string) []string // Run is a call through the gate: run is called only when the call may run now, once. Run(call approval.Call, run func() (any, error)) (any, error) // Approvals is the seat verb `approvals` for a caller: the calls the gate holds, its acts and claims (A-265, A-271). Approvals(caller string) (approval.Approvals, error) } // gatedRun is how a handler calls a tool: through the gate where there is one. type gatedRun func(call approval.Call, t launch.Tool, caller string) (any, error) // gated calls the tool through the gate, or straight where the runner has none. func (c *consumers) gated(call approval.Call, t launch.Tool, caller string) (any, error) { run := func() (any, error) { return t.Run(caller, call.Args) } if c.gate == nil { return run() } return c.gate.Run(call, run) } // approvalsOf is how the runner answers a held seat's verb `approvals`: from its gate's record, nil without a gate. func (r *runner) approvalsOf() func(caller string) (any, error) { if r.events.gate == nil { return nil } return func(caller string) (any, error) { return r.events.gate.Approvals(caller) } } // WithGate serves every call through the approval gate. func WithGate(g Gate) Option { return func(c *consumers) { c.gate = g } } var _ Gate = (*approval.Gate)(nil) // WithSeatDefinitions refuses, where a seat's verb is answered, a caller outside the verb's effective callers, before the // bundle runs (design 47 G §610; lead answers A-165, A-179): defs are the seat definitions the runner's seat list holds. // Without it a verb is answered for every caller, as before. func WithSeatDefinitions(defs func() map[string]seatdef.Definition) Option { return func(c *consumers) { c.definitions = defs } } // admitted says why a seat's verb is refused to a caller, nil when it is answered: an unproven caller is refused, and so is // every caller while the seat's definition is not read, names no such verb, or the verb's effective callers are empty; a // proven caller is among them, since the grants part composes a verb's call subject only for its effective callers (A-179). func (c *consumers) admitted(seat, verb, caller string) error { if c.definitions == nil { return nil } if caller == "" { return fmt.Errorf("Not run: %s.%s takes no unproven caller: call it on its subject that names its caller, "+ "mesh.seat.%s.call.%s.", seat, verb, seat, verb) } def, ok := c.definitions()[seat] if !ok { return fmt.Errorf("Not run: the definition of the seat %s is not yet read by this tool runner's seat list, so no caller "+ "of %s.%s is admitted", seat, seat, verb) } for _, v := range def.Verbs { if v.Name == verb { if len(v.EffectiveCallers()) == 0 { return fmt.Errorf("Not run: %s.%s is open to no caller", seat, verb) } return nil } } return fmt.Errorf("Not run: the seat %s's definition names no verb %s, so no caller is admitted", seat, verb) } // unservedMarks is, of the module's marked tools, those its bundle's registrations under its own name do not register. func (r *runner) unservedMarks(module string, regs []launch.Registration) []string { if r.events.gate == nil { return nil } var registered []string for _, reg := range regs { if reg.Module == module { for _, t := range reg.Tools { registered = append(registered, t.Name) } } } return r.events.gate.Unserved(module, r.fingerprintOf(module), registered) } // chunked says whether key names a seat's verb whose definition says `chunks` (design 47 G A-249): its answer is received in // chunks by the runner, whose call hands the bundle the whole. func (c *consumers) chunked(key string) bool { if c.definitions == nil || !strings.HasPrefix(key, "seat:") { return false } name, _, _ := strings.Cut(key, "@") // a verb asked of the machine that ran the ask is chunked as the verb is (A-254) seat, verb, ok := strings.Cut(strings.TrimPrefix(name, "seat:"), ".") if !ok { return false } for _, v := range c.definitions()[seat].Verbs { if v.Name == verb { return v.Chunks } } return false }