package stdio import ( "encoding/json" "errors" "time" ) // A module's code reads its values at runtime (novox/hq ADR 0349 clause 14, ADR 0370, design 57 S1): its config // values and its bindings' values that are not secret, for what its manifest declares, and watches them. The // runtime that launched this bundle asks its machine's provisioner client **as this bundle's module** — the module it // launched, never a name this code passes — and the client answers only what that module declares, only while the // mesh places it on that machine. A secret is refused while the module's code runs in the tool runner: the vault // hands a secret only to a caller the bus proves, and the bus proves only the tool runner there, so a module's // secrets reach it through its files until it runs under a bus account of its own (novox/hq issue 569). // ProvisionChange is what a watch hands over: the value now, or — once it is no longer answered, the module taken // off the machine or the key no longer declared — Refused, why. type ProvisionChange struct { Kind string `json:"kind"` Key string `json:"key"` Value json.RawMessage `json:"value,omitempty"` Refused string `json:"refused,omitempty"` } const provisionTimeout = 30 * time.Second func provisionGet(kind, key string) (json.RawMessage, error) { c, err := live() if err != nil { return nil, err } return c.ask("mesh/provision.get", map[string]any{"kind": kind, "key": key}, provisionTimeout) } func provisionWatch(kind, key string, handle func(ProvisionChange) error) error { if handle == nil { return errors.New("a watch needs a handler") } c, err := live() if err != nil { return err } id := c.newID() c.mu.Lock() c.provisioned[id] = handle c.mu.Unlock() if _, err := c.askAs(id, "mesh/provision.watch", map[string]any{"kind": kind, "key": key}, provisionTimeout); err != nil { c.mu.Lock() delete(c.provisioned, id) c.mu.Unlock() return err } return nil } // Config is one of this module's config values as mesh-config resolved it for this machine, as JSON: the key as // its manifest declares it under `config`. A key it does not declare is refused, with the reason. func Config(key string) (json.RawMessage, error) { return provisionGet("config", key) } // WatchConfig hands handle the config value now and then every change, in order, and returns once the value now is // handled. The watch lasts as long as the process. func WatchConfig(key string, handle func(ProvisionChange) error) error { return provisionWatch("config", key, handle) } // Bound is one value, not a secret, of what this module is bound to: the provision as its manifest requires it, // and the key its provider answers (`${bound::}`). func Bound(provision, key string) (json.RawMessage, error) { return provisionGet("bound", provision+":"+key) } // WatchBound is WatchConfig for a bound value. func WatchBound(provision, key string, handle func(ProvisionChange) error) error { return provisionWatch("bound", provision+":"+key, handle) } // Secret is one of this module's secrets. Refused, with the reason, while the module's code runs in the tool // runner (novox/hq ADR 0349 §11, ADR 0370 §2): its secrets reach it through its files. func Secret(name string) ([]byte, error) { raw, err := provisionGet("secret", name) if err != nil { return nil, err } var s string if err := json.Unmarshal(raw, &s); err != nil { return nil, errors.New("the runtime answered a secret that is not a string") } return []byte(s), nil }