clboss/Boss/Mod/SetConfigHandler.hpp
Ken Sedgwick 2632620f93
Add dynamic (setconfig-tunable) plugin option infrastructure
Introduces the mechanism for runtime-mutable plugin options: an option
marked dynamic can be changed via `lightning-cli setconfig <name> <val>`
without restarting clboss or lightningd.  No option opts in yet -- this
is the foundation (the rebalancer mode selector is the first consumer).

  - Boss::Msg::ManifestOption gains a bool dynamic field (default false,
    preserving the existing startup-only contract).
  - Boss::Mod::Manifester emits the per-option dynamic flag in the
    getmanifest response, so lightningd knows to forward setconfig for
    that option.
  - New Boss::Mod::SetConfigHandler module records (name -> dynamic)
    from Msg::ManifestOption events, then handles incoming setconfig
    CommandRequests: it validates the named option is registered and
    dynamic, and re-raises a fresh Msg::Option on the bus, so existing
    option handlers re-apply the new value transparently.

Because Msg::Option is now re-emitted at runtime (not only during init),
subscribers must filter by name and tolerate post-init arrival.
AmountSettingsHandler gains an `if (!settings) return` guard: it moves
`settings` away at EndOfOptions, so a later Msg::Option for an unrelated
name must be dropped -- this also fixes a latent assert(settings) crash
that any post-EndOfOptions Msg::Option would have tripped.

Contract documented in SetConfigHandler.hpp: lightningd delivers
Int/Bool/Flag option values as JSON primitives at startup but as JSON
strings at setconfig time, so dynamic-option handlers must accept both
Jsmn shapes.
2026-07-02 11:57:07 -07:00

53 lines
1.7 KiB
C++

#ifndef BOSS_MOD_SETCONFIGHANDLER_HPP
#define BOSS_MOD_SETCONFIGHANDLER_HPP
#include<map>
#include<string>
namespace S { class Bus; }
namespace Boss { namespace Mod {
/** class Boss::Mod::SetConfigHandler
*
* @brief Dispatches `setconfig` JSON-RPC calls from lightningd
* for options that were registered with `dynamic = true` on their
* Msg::ManifestOption.
*
* Lightningd routes `setconfig <name> <val>` to the plugin that
* owns the option, as a JSON-RPC method call. We turn that into
* a fresh Msg::Option on the bus, so existing option handlers
* re-apply the new value without a plugin restart.
*
* Contract for module authors who mark an option `dynamic = true`:
* at startup lightningd delivers Int / Bool / Flag option values
* as JSON primitives, but at setconfig time lightningd encodes the
* value as a JSON string. Any module that opts in to dynamic
* updates MUST tolerate both shapes in its Msg::Option handler --
* inspect `o.value.is_string()` and parse from the string form
* when appropriate.
*/
class SetConfigHandler {
private:
S::Bus& bus;
/* Name -> dynamic flag, populated from Msg::ManifestOption
* events during the Manifestation phase. Non-dynamic
* options are recorded too so we can return a clearer error
* than "unknown option" if lightningd ever forwards a
* setconfig for a non-dynamic name (which it should not). */
std::map<std::string, bool> options;
void start();
public:
SetConfigHandler() =delete;
SetConfigHandler(SetConfigHandler&&) =delete;
SetConfigHandler(SetConfigHandler const&) =delete;
explicit
SetConfigHandler(S::Bus& bus_) : bus(bus_) { start(); }
};
}}
#endif /* !defined(BOSS_MOD_SETCONFIGHANDLER_HPP) */