Skip to content

Conversation

@nepet
Copy link
Collaborator

@nepet nepet commented Dec 8, 2025

This PR refactors the LSPS plugin into three clearly separated layers with a strict dependency direction: proto → core → cln_adapters.

  • The proto layer now contains only protocol-related pieces: LSPS0/LSPS2 messages, primitives, JSON-RPC request/response types, errors and feature types. It depends only on external crates (like serde) and has no knowledge of Core Lightning or any internal wiring.
  • core holds the reusable logic: the JSON-RPC router and server, typed transport traits and implementations, LSPS services and handlers, and some feature helpers and shared utilities. core depends on proto and external crates, but not on CLN.
  • cln_adapters is the integration layer for Core Lightning: it implements the provider traits (Lightning, datastore, blockheight, offers) using CLN, wires up CLN-specific transports and hooks, and contains the glue needed to expose LSPS0/LSPS2 as a real plugin.

Previously, these three concerns were mixed together: protocol modeling, core logic and CLN integration lived in the same area of the codebase, which made it harder to see what was generic and what was CLN-specific, to reuse LSPS logic in other environments, and to evolve transports, routing and services without dragging CLN details along for the ride.

As part of this refactor, the error handling story has also been cleaned up. Each layer now has its own focused error types, and we convert between them only at well-defined boundaries (for example, from internal errors to protocol-level RpcErrors at the edge). This avoids leaking CLN-specific failures into protocol types, and keeps core logic independent of backend-specific error shapes.

With this change, protocol types live in proto, reusable logic lives in core, and everything that knows about Core Lightning is confined to cln_adapters. That makes it easier to plug in other backends in the future, reason about dependencies and error flows, and iterate on LSPS core logic without touching CLN-specific code.

Important

25.12 FREEZE October 27th: Non-bugfix PRs not ready by this date will wait for 26.03.

RC1 is scheduled on November 10th

The final release is scheduled for December 1st.

Checklist

Before submitting the PR, ensure the following tasks are completed. If an item is not applicable to your PR, please mark it as checked:

  • The changelog has been updated in the relevant commit(s) according to the guidelines.
  • Tests have been added or modified to reflect the changes.
  • Documentation has been reviewed and updated as needed.
  • Related issues have been listed and linked, including any that this PR closes.

nepet added 30 commits December 8, 2025 03:56
This commit is part of a series that refactor the lsp plugin crate into
a modularized crate with the goal to separate the actual plugin runtime
as much as possible from library code to make it accessible for 3rd
party plugin implementations.

Signed-off-by: Peter Neuroth <[email protected]>
This commit is part of a series that refactor the lsp plugin crate into
a modularized crate with the goal to separate the actual plugin runtime
as much as possible from library code to make it accessible for 3rd
party plugin implementations.

Signed-off-by: Peter Neuroth <[email protected]>
This commit is part of a series that refactor the lsp plugin crate into
a modularized crate with the goal to separate the actual plugin runtime
as much as possible from library code to make it accessible for 3rd
party plugin implementations.

Signed-off-by: Peter Neuroth <[email protected]>
This commit is part of a series that refactor the lsp plugin crate into
a modularized crate with the goal to separate the actual plugin runtime
as much as possible from library code to make it accessible for 3rd
party plugin implementations.

Signed-off-by: Peter Neuroth <[email protected]>
This commit is part of a series that refactor the lsp plugin crate into
a modularized crate with the goal to separate the actual plugin runtime
as much as possible from library code to make it accessible for 3rd
party plugin implementations.

Signed-off-by: Peter Neuroth <[email protected]>
Those primitives are actually defined in lsps0

This commit is part of a series that refactor the lsp plugin crate into
a modularized crate with the goal to separate the actual plugin runtime
as much as possible from library code to make it accessible for 3rd
party plugin implementations.

Signed-off-by: Peter Neuroth <[email protected]>
This commit is part of a series that refactor the lsp plugin crate into
a modularized crate with the goal to separate the actual plugin runtime
as much as possible from library code to make it accessible for 3rd
party plugin implementations.

Signed-off-by: Peter Neuroth <[email protected]>
Split them up how they are actually defined.

This commit is part of a series that refactor the lsp plugin crate into
a modularized crate with the goal to separate the actual plugin runtime
as much as possible from library code to make it accessible for 3rd
party plugin implementations.

Signed-off-by: Peter Neuroth <[email protected]>
This commit is part of a series that refactor the lsp plugin crate into
a modularized crate with the goal to separate the actual plugin runtime
as much as possible from library code to make it accessible for 3rd
party plugin implementations.

Signed-off-by: Peter Neuroth <[email protected]>
The error belongs to the transport definition, not into the
wire/protocol module.

Signed-off-by: Peter Neuroth <[email protected]>
In order to create a sane dependecy structure for this crate, this
commit cleans up the jsonrpc error enum and shifts responsibilities
towards the transport client

Signed-off-by: Peter Neuroth <[email protected]>
We can use an enum as a more idiomatic rust style to create and retreive
json-rpc responses. The actual response data is kept in the body enum
while the surrounding only holds the identifier.

Signed-off-by: Peter Neuroth <[email protected]>
This makes it easier for client implementations to separate transport or
parser related issues from actuall json-rpc error responses. Also this
helps to segregate the individual responsibilities of the crates
further.

This further alows us to remove the error enum from proto/jsonrpc
completely as this now only deals as a from/to-wire module

Signed-off-by: Peter Neuroth <[email protected]>
This commit is part of a series of commits to make the lsps plugin crate
layered and modular for efficient reuse in external projects.

The core module is responsible for common business logic and interfaces
and acts as the intermediate layer for an actual implementation

Signed-off-by: Peter Neuroth <[email protected]>
Frankly, transport without a target doesn't make sense, so this commit
adds a PublicKey (from secp256k1) as the target to the Transport trait.
It can easily be replaced by a common PeerId without changing the api
for external implementations if needed in the future

Signed-off-by: Peter Neuroth <[email protected]>
As we pass the peer_id through the transport methods, there is no need
to store the peer_id in the Bolt8Transport struct anymore

Signed-off-by: Peter Neuroth <[email protected]>
instead of using std::error, this is more specific and keeps the errors
"type-safe"

Signed-off-by: Peter Neuroth <[email protected]>
Improves flexibility in transport implementations.

Signed-off-by: Peter Neuroth <[email protected]>
In order to seperate concerns, this adds the basic encoding of an lsps0
frame to the proto module.

Signed-off-by: Peter Neuroth <[email protected]>
There is no need to generate the id in some client when we can actually
generate it on a lower layer as we know by LSPS0 how we have to generate
it.

Signed-off-by: Peter Neuroth <[email protected]>
Introducing "request" to the transport trait that uses the RequestObject
and JsonRpcResponse directly reduces some overhead and unnecessary
conversions. Why do we implement a generic transport over &str when we
already know that we are dealing with JSON-RPC 2.0 here.

Signed-off-by: Peter Neuroth <[email protected]>
There is no need to communicate via a generic JsonRpcClient if all
messages are well defined and we can use the typed transport to remove a
whole lot of overhead and just pass the ResponseObjects directly to the
Transport layer

Signed-off-by: Peter Neuroth <[email protected]>
This removes the jsonrpc module

Signed-off-by: Peter Neuroth <[email protected]>
This router is very lightweight and can handle routing for the lsps
service. It avoids allocations by just borrowing from the request. This
is much cleaner than what we have now and reduces the amount of
serializations by a lot.

Also it supports typed handlers which will remove serialization overhead
from the handlers in future commits

Signed-off-by: Peter Neuroth <[email protected]>
This commit copys and adapts the lsps2 service handlers to match the
slim router handlers

Signed-off-by: Peter Neuroth <[email protected]>
This commit replaces the lsps service. Therefore we add the cln_adapters
module that handles the communication with core-lightning. We add a
lightweight LspsService that wraps the router and allows handlers to
register themselves to the service/router.

Signed-off-by: Peter Neuroth <[email protected]>
cln_adapters is the layer that holds concrete implementations that are
related to core-lightning.

Signed-off-by: Peter Neuroth <[email protected]>
We can separate concerns here by adding a lightweight multiplexing
transport client. This helps us to clean up any direct dependencies on
CLN

Signed-off-by: Peter Neuroth <[email protected]>
Implement the MessageSender trait for Bolt8 transport via core-lightning

Signed-off-by: Peter Neuroth <[email protected]>
We replace the legacy transport by a slim custommsg hook that makes use
of the lightweight multiplexed transport and avoids deserialization and
serialization through this extra layer

Signed-off-by: Peter Neuroth <[email protected]>
nepet added 10 commits December 8, 2025 04:12
We have some shared behavior and can use the MessageSender for the
service hook as well

Signed-off-by: Peter Neuroth <[email protected]>
The cln api trait is really cluttered, we try to clean it up a little
bit.

Signed-off-by: Peter Neuroth <[email protected]>
The ClnApi trait is way overloaded. We break things down to make it more
modular on the service side.

Signed-off-by: Peter Neuroth <[email protected]>
This now only makes it easier to test, but also more modular. It allows
us to use any kind of storage, not only the datastore.

Signed-off-by: Peter Neuroth <[email protected]>
Clean up the CLN interface

Signed-off-by: Peter Neuroth <[email protected]>
This commit separates the business logic from core-lighting behaviour
and moves the core-lightning related implementations into the
cln_adapters module.

Splits up tests as well

Signed-off-by: Peter Neuroth <[email protected]>
Separate concerns of the lsps2 modules into their respective layers:
core for tlv, as it is lightning related, not specifically
core-lightning. cln_adapters for core-lightning related types.

Signed-off-by: Peter Neuroth <[email protected]>
This removes the util.rs module and leaves us with proto, core and
cln_adapters

Signed-off-by: Peter Neuroth <[email protected]>
…service

The core module shouldn't depend on anything from the cln_adapters
module. This ensures that we can switch out interfaces in the future
(e.g a different database than cln's datastore).

This change made it necessary to refactor the service plugin which
allowed us to clean it up on the go

Signed-off-by: Peter Neuroth <[email protected]>
Remove anyhow as a dependency from the tlv module. This allows for a
cleaner error handling

Changelog-None

Signed-off-by: Peter Neuroth <[email protected]>
@nepet nepet force-pushed the 2546-modularize-lsps-crate branch from 2c4ef30 to d6422f1 Compare December 8, 2025 04:09
@madelinevibes madelinevibes added this to the v26.03 milestone Dec 8, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants