Files
mesh-catalog/modules/nats/cmd/nats-tools/requests.go
T
jschoubben 3b61a05532 CAT-N8: a person's create, apply, recreate and drop are handed to the keeper as the caller the tool runner proved
The bus module's tools hold no grant of the seat's acting verbs (design 25
§ 12.3): nats_create, nats_apply, nats_recreate and nats_drop write a request
into the keeper's own state requests, stamped with stdio.CallerOf (a name in
the arguments is never the caller), and read its end back. recreate and drop
raise the operator's ask first, its option binding exactly that act; the
keeper acts only on the router's warrant for it (Warrant.For, Option.Performs),
after a snapshot of the entry's streams (bussnap, moved from the snapshot
command with a streams filter), says recreated or dropped, and records every
act as a hand act through mesh-hand-acts. A drop removes an orphan alone.

Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
2026-11-04 12:34:59 +01:00

132 lines
6.2 KiB
Go

package main
import (
"crypto/rand"
"encoding/hex"
"encoding/json"
"fmt"
"strings"
"time"
stdio "git.novox.be/novox/mesh-sdk/go"
"git.novox.be/novox/mesh-sdk/go/asks"
"nats-tools/internal/keeper"
)
// A person's acting tools (design 25 § 12.3; CAT-N8): nats_create and nats_apply (acts), nats_recreate and nats_drop
// (dangerous, granted only by name). None holds a grant of the seat's acting verbs: each hands its request to the
// keeper through the keeper's own state requests, stamped with the caller the tool runner proved, and reads its end
// back. recreate and drop first raise the operator's ask, binding exactly that act; the keeper acts only on its warrant,
// after a snapshot.
// requestStore is the keeper's state requests, as this bundle reaches it.
type requestStore struct {
Put func(key string, r keeper.Request) error
Get func(key string) (*keeper.Request, error)
Ask func(a asks.Ask) error
Now func() time.Time
Wait func(d time.Duration)
}
// RequestsInState is the store through the runtime that launched this bundle.
func RequestsInState() requestStore {
s := stdio.State(keeper.StateRequests)
return requestStore{
Put: func(key string, r keeper.Request) error { _, err := s.Put(key, r); return err },
Get: func(key string) (*keeper.Request, error) {
e, err := s.Get(key)
if err != nil || e == nil {
return nil, err
}
var r keeper.Request
return &r, json.Unmarshal(e.Value, &r)
},
Ask: func(a asks.Ask) error { return stdio.SeatPublish(asks.AskSubject(keeper.Module), a, a.ID) },
Now: func() time.Time { return time.Now().UTC() },
Wait: time.Sleep,
}
}
func requestID(now time.Time) string {
var b [4]byte
_, _ = rand.Read(b[:])
return now.Format("20060102t150405") + "-" + hex.EncodeToString(b[:])
}
// askFor is the operator's ask for a recreate or a drop of one entry, its approve option binding exactly that act.
func askFor(r keeper.Request, now time.Time) (asks.Ask, error) {
binds, err := asks.ActDigest(r.Act())
if err != nil {
return asks.Ask{}, err
}
what := map[string]string{keeper.VerbDrop: "remove", keeper.VerbRecreate: "rebuild"}[r.Verb]
name := keeper.BusName(r.Module, r.Local)
return asks.Ask{ID: "bus-" + r.ID, Who: asks.Operator, Expires: now.Add(24 * time.Hour),
OnExpiry: "nothing is removed and the entry stays as it is",
About: keeper.ConditionKey(map[string]string{keeper.VerbDrop: "orphaned", keeper.VerbRecreate: "drifted"}[r.Verb], r.Module, r.Local),
Headline: fmt.Sprintf("%s's kept data %s: %s it?", r.Module, r.Local, what),
Explanation: fmt.Sprintf("%s asks to %s %s on the bus, %s. A snapshot of it is taken first; its data is lost from the bus.",
r.Caller, what, name, r.Why),
Options: []asks.Option{
{ID: "go", Label: strings.ToUpper(what[:1]) + what[1:], Does: fmt.Sprintf("%s %s after a snapshot", what, name), Level: asks.Destroy, Binds: binds},
{ID: "keep", Label: "Keep it", Does: "nothing is removed", Level: asks.Acknowledge},
}}, nil
}
func requestTools(rs requestStore) []stdio.Tool {
entry := map[string]any{"module": str("the module, e.g. mesh-knowledge"), "local": str("its local name, e.g. records"),
"why": str("why, in plain words: kept with the act"), "request": str("a request this tool made before: its end is read, nothing new is asked")}
tool := func(verb, description string) stdio.Tool {
return stdio.Tool{Name: "nats_" + verb, Description: description, Input: entry, Run: func(a map[string]any) (any, error) {
if id := arg(a, "request"); id != "" {
r, err := rs.Get(id)
if err != nil || r == nil {
return nil, fmt.Errorf("no request %s is held (%v)", id, err)
}
return r, nil
}
r := keeper.Request{Verb: verb, Module: arg(a, "module"), Local: arg(a, "local"), Why: strings.TrimSpace(arg(a, "why")),
// The caller the tool runner proved, never a name the arguments give.
Caller: stdio.CallerOf(a), At: rs.Now()}
if r.Module == "" || r.Local == "" || r.Why == "" {
return nil, fmt.Errorf("nats_%s names one entry, a module and its local name, and says why", verb)
}
r.ID = requestID(r.At)
if verb == keeper.VerbRecreate || verb == keeper.VerbDrop {
ask, err := askFor(r, r.At)
if err != nil {
return nil, err
}
r.Ask = ask.ID
if err := rs.Put(r.ID, r); err != nil {
return nil, fmt.Errorf("the request cannot be handed to the keeper: %w", err)
}
if err := rs.Ask(ask); err != nil {
return nil, fmt.Errorf("the operator's ask cannot be raised, so nothing will be removed: %w", err)
}
return map[string]any{"request": r.ID, "ask": ask.ID, "state": "the operator is asked; the keeper acts once they approve, after a snapshot",
"read": fmt.Sprintf("nats_%s with request %s", verb, r.ID)}, nil
}
if err := rs.Put(r.ID, r); err != nil {
return nil, fmt.Errorf("the request cannot be handed to the keeper: %w", err)
}
// The keeper acts on its watch of requests at once; its end is read back for at most half a minute.
for wait := 100 * time.Millisecond; wait < 20*time.Second; wait *= 2 {
rs.Wait(wait)
if got, err := rs.Get(r.ID); err == nil && got != nil && !got.Done.IsZero() {
return got, nil
}
}
return map[string]any{"request": r.ID, "state": "handed to the keeper, not ended yet",
"read": fmt.Sprintf("nats_%s with request %s", verb, r.ID)}, nil
}}
}
return []stdio.Tool{
tool(keeper.VerbCreate, "Create one entry of a module's kept data on the bus now, as its accepted manifest asks for it (design 25 § 12.3): handed to the bus module's keeper, which alone may create, and recorded as a hand act."),
tool(keeper.VerbApply, "Apply what a module's manifest asks of one entry where the bus holds less (a larger cap, a longer age): only a difference that grows; handed to the keeper and recorded as a hand act."),
tool(keeper.VerbRecreate, "Rebuild one entry whose settings drifted in a way that does not grow: the operator is asked first, and the keeper acts only on their warrant, after a snapshot of the entry. Its data is lost from the bus (the snapshot keeps it). Dangerous."),
tool(keeper.VerbDrop, "Remove one orphan, an entry no accepted manifest asks for: the operator is asked first, and the keeper acts only on their warrant, after a snapshot of the entry. Dangerous."),
}
}