Carry a bundle's seat traffic under its own name and kind, and start a module as its own account (hq ADR 0259)
This commit is contained in:
@@ -60,6 +60,10 @@ type Bus interface {
|
||||
Subscribe(deliver func(envelope json.RawMessage) error) error
|
||||
State(verb string, params json.RawMessage) (json.RawMessage, error)
|
||||
Watch(params json.RawMessage, deliver func(change json.RawMessage) error) (stop func(), err error)
|
||||
// Seat answers a bundle's seat traffic (novox/hq ADR 0259 §3): `publish`, `prove` and `record` at once;
|
||||
// `take` and `answer` bind once and hand each message or proof on through handOn, to whichever child
|
||||
// of the module asked last — a restarted child asks again as its code runs again.
|
||||
Seat(verb string, params json.RawMessage, handOn func(method string, params any) (json.RawMessage, error)) (json.RawMessage, error)
|
||||
}
|
||||
|
||||
// EventTimeout bounds how long a bundle has to handle one event before it is offered again.
|
||||
@@ -147,6 +151,17 @@ func Start(module, entry string, env []string, mesh Bus, logf func(string, ...an
|
||||
// The child that last subscribed is the one events are handed to: a restarted child subscribes
|
||||
// again as its code is imported, and from then on the module's events are its.
|
||||
var subscriber *child
|
||||
// The child that last asked to take a seat's work or answer its proofs is the one they are handed to.
|
||||
var seatTaker *child
|
||||
handOn := func(method string, params any) (json.RawMessage, error) {
|
||||
mu.Lock()
|
||||
c := seatTaker
|
||||
mu.Unlock()
|
||||
if c == nil {
|
||||
return nil, errors.New(module + "'s bundle is not running to take it")
|
||||
}
|
||||
return c.ask(module, method, params, EventTimeout)
|
||||
}
|
||||
stopped := false
|
||||
deliver := func(envelope json.RawMessage) error {
|
||||
mu.Lock()
|
||||
@@ -166,6 +181,12 @@ func Start(module, entry string, env []string, mesh Bus, logf func(string, ...an
|
||||
start := func() (*child, error) {
|
||||
cmd := exec.Command(entry)
|
||||
cmd.Env = env
|
||||
// **A module that names an account of its own runs as it** (novox/hq ADR 0259 §8): its secrets and
|
||||
// state are that account's, and the operator's account — which every agent runs as — reaches
|
||||
// neither. Never root, never the operator's.
|
||||
if err := runAs(cmd, env); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stdin, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -237,7 +258,21 @@ func Start(module, entry string, env []string, mesh Bus, logf func(string, ...an
|
||||
subscriber = c
|
||||
mu.Unlock()
|
||||
err = mesh.Subscribe(deliver)
|
||||
case "mesh/state.get", "mesh/state.put", "mesh/state.delete", "mesh/state.keys":
|
||||
case "mesh/seat.publish", "mesh/seat.prove", "mesh/seat.record":
|
||||
var answered json.RawMessage
|
||||
if answered, err = mesh.Seat(strings.TrimPrefix(m.Method, "mesh/seat."), m.Params, nil); err == nil {
|
||||
result = answered
|
||||
}
|
||||
case "mesh/seat.take", "mesh/seat.answer":
|
||||
mu.Lock()
|
||||
seatTaker = c
|
||||
mu.Unlock()
|
||||
var answered json.RawMessage
|
||||
if answered, err = mesh.Seat(strings.TrimPrefix(m.Method, "mesh/seat."), m.Params, handOn); err == nil {
|
||||
result = answered
|
||||
}
|
||||
case "mesh/state.get", "mesh/state.put", "mesh/state.delete", "mesh/state.keys",
|
||||
"mesh/state.create", "mesh/state.update":
|
||||
var answered json.RawMessage
|
||||
if answered, err = mesh.State(strings.TrimPrefix(m.Method, "mesh/state."), m.Params); err == nil {
|
||||
result = answered
|
||||
@@ -329,6 +364,9 @@ func Start(module, entry string, env []string, mesh Bus, logf func(string, ...an
|
||||
if subscriber == c {
|
||||
subscriber = nil
|
||||
}
|
||||
if seatTaker == c {
|
||||
seatTaker = nil
|
||||
}
|
||||
wasStopped := stopped
|
||||
// Started again at once if it ran a while; after a growing pause while it keeps exiting.
|
||||
wait := RestartFirst
|
||||
|
||||
Reference in New Issue
Block a user