lsp_plugin: remove redundant config option
What changed, and why it matters
This commit removes a redundant developer-only configuration flag from an experimental Lightning Service Provider (LSP) plugin. Previously, users had to enable both a general 'LSPS service' flag and a separate 'LSPS2 service' flag. The change makes the LSPS2 flag alone control whether the plugin runs. It also adds a clear error if LSPS2 is enabled but its required secret is missing. There is no direct security vulnerability here; it is a code cleanup that slightly reduces configuration confusion.
No immediate security action required. Treat as routine cleanup. Operators using the experimental lsps-plugin should note that dev-lsps-service-enabled is removed and only lsps2-service-enabled is now required. Review whether the new hard error on missing promise-secret affects any dev/test deployments.
Security signals we found
Removal of redundant experimental/dev flag reduces misconfiguration surface
Added explicit bail! when required lsps2 promise secret is missing, turning a silent no-op into a hard failure
No input validation, cryptographic, memory-safety, or authorization changes observed
No CVE, advisory, or vendor security disclosure referenced in commit
Evidence from the diff
The patch deletes the dev-lsps-service-enabled flag and its associated option registration and early-disable logic. The plugin now starts only when lsps2::OPTION_ENABLED is true, and bails with an explicit error if the lsps2 promise secret is unset. The JsonRpcServer builder and handler registration are moved inside the lsps2-enabled branch, so the plugin no longer constructs a partial service with only the LSPS0 protocol-list handler. The Lsps0ListProtocolsHandler still reports lsps2_enabled based on the same option.
Changed components
plugins/lsps-plugin/src/service.rscln_lsps::lsps0 protocol-list handler registrationLSPS2 service startup pathInspect captured patch +27 / −39
diff --git a/plugins/lsps-plugin/src/service.rs b/plugins/lsps-plugin/src/service.rs
index b17c8e83..60607754 100644
--- a/plugins/lsps-plugin/src/service.rs
+++ b/plugins/lsps-plugin/src/service.rs
@@ -1,4 +1,4 @@
-use anyhow::anyhow;
+use anyhow::{anyhow, bail};
use async_trait::async_trait;
use cln_lsps::jsonrpc::server::JsonRpcResponseWriter;
use cln_lsps::jsonrpc::TransportError;
@@ -11,8 +11,7 @@ use cln_lsps::lsps2::handler::{ClnApiRpc, HtlcAcceptedHookHandler};
use cln_lsps::lsps2::model::{Lsps2BuyRequest, Lsps2GetInfoRequest};
use cln_lsps::util::wrap_payload_with_peer_id;
use cln_lsps::{lsps0, lsps2, util, LSP_FEATURE_BIT};
-use cln_plugin::options::ConfigOption;
-use cln_plugin::{options, Plugin};
+use cln_plugin::Plugin;
use cln_rpc::notifications::CustomMsgNotification;
use cln_rpc::primitives::PublicKey;
use log::debug;
@@ -20,12 +19,6 @@ use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::sync::Arc;
-/// An option to enable this service.
-const OPTION_ENABLED: options::FlagConfigOption = ConfigOption::new_flag(
- "dev-lsps-service-enabled",
- "Enables an LSPS service on the node.",
-);
-
#[derive(Clone)]
struct State {
lsps_service: JsonRpcServer,
@@ -35,7 +28,6 @@ struct State {
#[tokio::main]
async fn main() -> Result<(), anyhow::Error> {
if let Some(plugin) = cln_plugin::Builder::new(tokio::io::stdin(), tokio::io::stdout())
- .option(OPTION_ENABLED)
.option(lsps2::OPTION_ENABLED)
.option(lsps2::OPTION_PROMISE_SECRET)
.featurebits(
@@ -54,23 +46,9 @@ async fn main() -> Result<(), anyhow::Error> {
let rpc_path =
Path::new(&plugin.configuration().lightning_dir).join(&plugin.configuration().rpc_file);
- if !plugin.option(&OPTION_ENABLED)? {
- return plugin
- .disable(&format!("`{}` not enabled", OPTION_ENABLED.name))
- .await;
- }
-
- let mut lsps_builder = JsonRpcServer::builder().with_handler(
- Lsps0listProtocolsRequest::METHOD.to_string(),
- Arc::new(Lsps0ListProtocolsHandler {
- lsps2_enabled: plugin.option(&lsps2::OPTION_ENABLED)?,
- }),
- );
-
- let lsps2_enabled = if plugin.option(&lsps2::OPTION_ENABLED)? {
- log::debug!("lsps2 enabled");
- let secret_hex = plugin.option(&lsps2::OPTION_PROMISE_SECRET)?;
- if let Some(secret_hex) = secret_hex {
+ if plugin.option(&lsps2::OPTION_ENABLED)? {
+ log::debug!("lsps2-service enabled");
+ if let Some(secret_hex) = plugin.option(&lsps2::OPTION_PROMISE_SECRET)? {
let secret_hex = secret_hex.trim().to_lowercase();
let decoded_bytes = match hex::decode(&secret_hex) {
@@ -97,6 +75,13 @@ async fn main() -> Result<(), anyhow::Error> {
}
};
+ let mut lsps_builder = JsonRpcServer::builder().with_handler(
+ Lsps0listProtocolsRequest::METHOD.to_string(),
+ Arc::new(Lsps0ListProtocolsHandler {
+ lsps2_enabled: plugin.option(&lsps2::OPTION_ENABLED)?,
+ }),
+ );
+
let cln_api_rpc = lsps2::handler::ClnApiRpc::new(rpc_path);
let getinfo_handler =
lsps2::handler::Lsps2GetInfoHandler::new(cln_api_rpc.clone(), secret);
@@ -107,20 +92,23 @@ async fn main() -> Result<(), anyhow::Error> {
Arc::new(getinfo_handler),
)
.with_handler(Lsps2BuyRequest::METHOD.to_string(), Arc::new(buy_handler));
- }
- true
- } else {
- false
- };
- let lsps_service = lsps_builder.build();
+ let lsps_service = lsps_builder.build();
- let state = State {
- lsps_service,
- lsps2_enabled,
- };
- let plugin = plugin.start(state).await?;
- plugin.join().await
+ let state = State {
+ lsps_service,
+ lsps2_enabled: true,
+ };
+ let plugin = plugin.start(state).await?;
+ plugin.join().await
+ } else {
+ bail!("lsps2 enabled but no promise-secret set.");
+ }
+ } else {
+ return plugin
+ .disable(&format!("`{}` not enabled", &lsps2::OPTION_ENABLED.name))
+ .await;
+ }
} else {
Ok(())
}
Why this scored 18/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.