Getting started with CLNRest plugin

LIVE #13September 14, 2023

In this live we see how to start and use the new builtin plugin CLNRest that transforms RPC calls into a REST service. We learn how to use the Swagger interface to generate/send http requests. Then we connect to the websocket offered by CLNRest that sends lightningd notification. Finally, we take a look at CLNRest implementation to show how plugin developers can take advantage of the new JSON RPC method checkrune.

CLNRest changes from v23.08.1 to v23.11

This live was recorded the day after v23.08.1 point release. Since then, Core Lightning v23.11 has been released with some changes. Some parts of the video if reproduced as is with v23.11 won't work. We list here what they are and what should be done instead if you are running Core Lightning v23.11 and maybe also things that I forgot to mentioned during the live.

CLNRest Python dependencies

The clnrest Python plugin depends on the following packages that can be installed like this on Ubuntu:

$ sudo apt-get install python3-json5 python3-flask python3-gunicorn
$ pip3 install --user flask-cors flask_restx pyln-client flask-socketio gevent gevent-websocket

See lightning:doc/getting-started/getting-started/installation.md.

CLNRest config options prefixed by cln

The config options rest-port, rest-protocol, rest-host and rest-certs have been renamed respectively to clnrest-port, clnrest-protocol, clnrest-host and clnrest-certs by adding cln prefix.

So for instance, if we want clnrest web server to listen to on the port 3010, we must run the following command:

lightningd --clnrest-port=3010

See lightningd: fix up deprecated rest-port, rest-protocol, rest-host and rest-certs option if we would otherwise fail. #6876.

Self-signed certificates

During the live we used curl flag --insecure to skip the TLS verification.

Starting with 23.11, the certificate authority file is also provided, so we can also use --cacert flag with the certificate authority file to issue the curl request like this (instead of --insecure):

$ curl --cacert /tmp/l2-regtest/regtest/ca.pem \  -X 'POST' \  'https://localhost:3010/v1/getinfo' \  -H 'accept: application/json' \  -H 'Rune: FPD_6O81E8UdSkmpy2qtjPcuPVbxVi0lIQ-OMtVic_U9MA==' \  -H 'Content-Type: application/json' \  -d '{}'

We assume the same setup as in the live session (the lightning directory of l2 node is /tmp/l2-regtest/).

Websocket requires a readonly rune

Starting with Core Lightning version v23.11, connecting to clnrest websocket requires a readonly rune.

Let's look at an example where we create a NodeJS websocket client that connects to clnrest plugin of a node l1 running on regtest using a readonly rune. Then we create an invoice with that node and observe an invoice_creation notification being forwarded to that websocket client.

Assuming the lightning repository is cloned and that we are at the top of it, we start 2 nodes running on regtest using start_ln command from contrib/startup_regtest.sh script and restart l1 with clnrest enabled by setting clnrest-port option to 3010:

◉ tony@tony:~/clnlive/lightning:[git»(HEAD detached at v23.11)]
$ source contrib/startup_regtest.sh
...
◉ tony@tony:~/clnlive/lightning:[git»(HEAD detached at v23.11)]
$ start_ln
...
◉ tony@tony:~/clnlive/lightning:[git»(HEAD detached at v23.11)]
$ alias l1-cli
alias l1-cli='lightning-cli --lightning-dir=/tmp/l1-regtest'
◉ tony@tony:~/clnlive/lightning:[git»(HEAD detached at v23.11)]
$ l1-cli stop
"Shutdown complete"
◉ tony@tony:~/clnlive/lightning:[git»(HEAD detached at v23.11)]
$ lightningd --lightning-dir=/tmp/l1-regtest --clnrest-port=3010 --daemon

clnrest web server is now running at https://127.0.0.1:3010 with /tmp/l1-regtest/regtest/ca.pem certificate authority.

Thus, we can connect to its websocket available at https://127.0.0.1:3010.

To connect to that websocket we must provide a readonly rune that we create like this:

◉ tony@tony:~/clnlive/lightning:[git»(HEAD detached at v23.11)]
$ l1-cli createrune null readonly
{
   "rune": "kYjRUZvgkKOFO3angTlIovd6TGPO5zSnKM90WZQDszc9MCZtZXRob2RebGlzdHxtZXRob2ReZ2V0fG1ldGhvZD1zdW1tYXJ5Jm1ldGhvZC9saXN0ZGF0YXN0b3Jl",
   "unique_id": "0"
}

Now in a file named clnrest-websocket.js we implement the following Node JS websocket client that will be able to connect to our websocket server running at https://127.0.0.1:3010:

const https = require("https");
const os = require("os")
const rootCas = require("ssl-root-cas").create();
rootCas.addFile("/tmp/l1-regtest/regtest/ca.pem");
const io = require("socket.io-client");

const socket = io.connect("https://127.0.0.1:3010", {
  extraHeaders: {
    Rune:
      "kYjRUZvgkKOFO3angTlIovd6TGPO5zSnKM90WZQDszc9MCZtZXRob2RebGlzdHxtZXRob2ReZ2V0fG1ldGhvZD1zdW1tYXJ5Jm1ldGhvZC9saXN0ZGF0YXN0b3Jl",
  },
  agent: https.globalAgent,
});

socket.on("connect", function () {
  console.log("Websocket connection established at https://127.0.0.1:3010");
});

socket.on("message", function (data) {
  console.log("Notification: ", data);
});

In another terminal, we install clnrest-websocket.js dependencies and run it which establishes a connection with clnrest websocket with the terminal hanging waiting for incoming messages (forwarded Core Lightning notifications) from clnrest:

# TERMINAL 2

◉ tony@tony:~/clnlive/lightning:[git»(HEAD detached at v23.11)]
$ npm install ssl-root-cas socket.io-client
◉ tony@tony:~/clnlive/lightning:[git»(HEAD detached at v23.11)]
$ node clnrest-websocket.js
Websocket connection established at https://127.0.0.1:3010

Back in the first terminal, we create an invoice like this:

◉ tony@tony:~/clnlive/lightning:[git»(HEAD detached at v23.11)]
$ l1-cli invoice 10000 label description
{
   "payment_hash": "fe3874d285a20521f2a4d41ab1329c2d3cd16801263c3969447c9a848a4430ca",
   "expires_at": 1702224390,
   "bolt11": "lnbcrt100n1pjkefuxsp5rpa4kweh4aykywqfgzmw56ct0dd40ldmmak5qv2l0mfkpmsqpu3spp5lcu8f5595gzjru4y6sdtzv5u957dz6qpyc7rj62y0jdgfzjyxr9qdqjv3jhxcmjd9c8g6t0dcxqyjw5qcqp2fp4p3x3kug5mt0aqdpryf9n58ddn339lgt24rkcgc78uq6n3kv8aarqq9qx3qysgq8ps05vdfetc8w5x3xzud7fckklvyxnw5n3jzq9asys7ee7e4y0g3sjjjvhvvmsc4xj6e0qg9f9qt7aat9jltvptxvk8dkmyvxdwcmdcp6xlhjp",
   "payment_secret": "187b5b3b37af4962380940b6ea6b0b7b5b57fdbbdf6d40315f7ed360ee000f23",
   "created_index": 1,
   "warning_capacity": "Insufficient incoming channel capacity to pay invoice"
}

This triggered an invoice_creation notification that has been forwarded to clnrest plugin which queued it up for a second and then broadcasted it to our Node JS listener. We can see in the second terminal running clnrest-websocket.js script that the invoice_creation message has been received:

# TERMINAL 2

◉ tony@tony:~/clnlive/lightning:[git»(HEAD detached at v23.11)]
$ node clnrest-websocket.js
Websocket connection established at https://127.0.0.1:3010
Notification:  {
  invoice_creation: {
    msat: 10000,
    preimage: 'b173d0ccc614cc81e65c478e77ecfde420ca74936eee9e4ac1be85e7653d66b7',
    label: 'label'
  }
}
^C

See clnrest: refactoring #6749.

Transcript with corrections and improvements

Hi everybody, let's jump in into that session. I'm happy to see you and spend some time talking with you about about clnrest new builtin plugin in v23.08 implemented by Shahana.

This plugin lets us do HTTP requests to our node and run JSON RPC command in our node and also offer a websocket connection to receive Core Lightning notifications.

We are going to see how to get this working.

Note that I'm running Core Lightning 23.08.1.

Start 2 Lightning nodes running on regtest

Let's start two Lightning nodes running on the Bitcoin regtest chain by sourcing the script lightning/contrib/startup_regtest.sh provided by CLN repository and running the command start_ln:

◉ tony@tony:~/clnlive:
$ source lightning/contrib/startup_regtest.sh
lightning-cli is /usr/local/bin/lightning-cli
lightningd is /usr/local/bin/lightningd
Useful commands:
  start_ln 3: start three nodes, l1, l2, l3
  connect 1 2: connect l1 and l2
  fund_nodes: connect all nodes with channels, in a row
  stop_ln: shutdown
  destroy_ln: remove ln directories
◉ tony@tony:~/clnlive:
$ start_ln
Bitcoin Core starting
awaiting bitcoind...
Making "default" bitcoind wallet.
error code: -35
error message:
Wallet "default" is already loaded.
[1] 1646572
[2] 1646618
WARNING: eatmydata not found: install it for faster testing
Commands:
        l1-cli, l1-log,
        l2-cli, l2-log,
        bt-cli, stop_ln, fund_nodes

We can check that l2-cli is just an alias for lightning-cli with the base directory being /tmp/l2-regtest:

◉ tony@tony:~/clnlive:
$ alias l2-cli
alias l2-cli='lightning-cli --lightning-dir=/tmp/l2-regtest'

To be sure that we have at least a lightning node running on regtest, we can call the subcommand getinfo like this:

◉ tony@tony:~/clnlive:
$ l2-cli getinfo | jq .id
"03fd22c90bc2aaa17e5a1928eeef20944783d57de7c36a712fd733b63b8149a00e"

/tmp/l2-regtest/log

I want in that session to show where we can get information and play with the node.

So, let's jump into /tmp/l2-regtest/log log file of l2 node.

We can see that just after creating the configuration directory and openning the log file, the plugin manager started the builtin plugins including clnrest:

2023-09-14T14:10:07.019Z INFO    lightningd: Creating configuration directory /tmp/l2-regtest/regtest
2023-09-14T14:10:07.019Z DEBUG   lightningd: Opened log file /tmp/l2-regtest/log
2023-09-14T14:10:07.023Z DEBUG   plugin-manager: started(1646626) /usr/local/libexec/c-lightning/plugins/autoclean
2023-09-14T14:10:07.034Z DEBUG   plugin-manager: started(1646627) /usr/local/libexec/c-lightning/plugins/chanbackup
2023-09-14T14:10:07.038Z DEBUG   plugin-manager: started(1646628) /usr/local/libexec/c-lightning/plugins/bcli
2023-09-14T14:10:07.045Z DEBUG   plugin-manager: started(1646629) /usr/local/libexec/c-lightning/plugins/commando
2023-09-14T14:10:07.049Z DEBUG   plugin-manager: started(1646630) /usr/local/libexec/c-lightning/plugins/fetchinvoice
2023-09-14T14:10:07.052Z DEBUG   plugin-manager: started(1646631) /usr/local/libexec/c-lightning/plugins/funder
2023-09-14T14:10:07.056Z DEBUG   plugin-manager: started(1646632) /usr/local/libexec/c-lightning/plugins/topology
2023-09-14T14:10:07.059Z DEBUG   plugin-manager: started(1646633) /usr/local/libexec/c-lightning/plugins/keysend
2023-09-14T14:10:07.062Z DEBUG   plugin-manager: started(1646634) /usr/local/libexec/c-lightning/plugins/offers
2023-09-14T14:10:07.066Z DEBUG   plugin-manager: started(1646635) /usr/local/libexec/c-lightning/plugins/pay
2023-09-14T14:10:07.069Z DEBUG   plugin-manager: started(1646636) /usr/local/libexec/c-lightning/plugins/txprepare
2023-09-14T14:10:07.072Z DEBUG   plugin-manager: started(1646637) /usr/local/libexec/c-lightning/plugins/cln-renepay
2023-09-14T14:10:07.075Z DEBUG   plugin-manager: started(1646638) /usr/local/libexec/c-lightning/plugins/spenderp
2023-09-14T14:10:07.078Z DEBUG   plugin-manager: started(1646639) /usr/local/libexec/c-lightning/plugins/sql
2023-09-14T14:10:07.081Z DEBUG   plugin-manager: started(1646640) /usr/local/libexec/c-lightning/plugins/bookkeeper
2023-09-14T14:10:07.087Z DEBUG   plugin-manager: started(1646641) /usr/local/libexec/c-lightning/plugins/clnrest/clnrest.py

To get the plugin started we must specify a port to rest-port config option. And by default the start_ln command that we use to start l2 node on regest doesn't specify it. So we see in the log that after the plugin manager tried to start clnrest it disabled it:

2023-09-14T14:10:08.292Z DEBUG   plugin-clnrest.py: Killing plugin: disabled itself at init: `rest-port` option is not configured

Note that starting with v23.11 rest-port option has been replaced by clnrest-port option (see CLNRest changes from v23.08.1 to v23.11).

We'll start that plugin in the next section.

If you've never tried to debug or to check the log, this is a good place to look at for information and also to report issues if you encounter some.

Start clnrest

Let's restart l2 node with clnrest plugin enabled.

First we stop it:

◉ tony@tony:~/clnlive:
$ l2-cli stop
"Shutdown complete"

Then we restart lightningd setting --rest-port config option to 3010:

◉ tony@tony:~/clnlive:
$ lightningd --lightning-dir=/tmp/l2-regtest --rest-port=3010 --daemon

Note that starting with v23.11 --rest-port must be replaced by --clnrest-port (see CLNRest changes from v23.08.1 to v23.11).

We can check using nc utility that clnrest is now listening on port 3010 at localhost:

◉ tony@tony:~/clnlive:
$ nc -z -v localhost 3010
Connection to localhost (127.0.0.1) 3010 port [tcp/*] succeeded!

Note that:

  • 127.0.0.1 is the default value for --rest-host (which is from v23.11 --clnrest-host) and

  • HTTPS is the default value for --rest-protocol (which is from v23.11 --clnrest-protocol).

See CLNRest changes from v23.08.1 to v23.11.

GET /v1/list-methods

Let's jump into our browser and visit https://localhost:3010 address which serves the Swagger interface we can use to query our node

/img/2023-09-14-live-0013-swagger-interface-01.png

and do a GET /v1/list-methods request, which lists all the methods we can run on l2 node using clnrest plugin, like this:

/img/2023-09-14-live-0013-swagger-interface-02.png

This also gives use the curl command to run to do the exact same request to which we add --insecure flag to skip the TLS verification.

the self-signed certificates:

◉ tony@tony:~/clnlive:
$ curl --insecure -X 'GET' \  'https://localhost:3010/v1/list-methods' \  -H 'accept: application/json'
Command: addgossip message
Category: utility
Description: Inject gossip {message} into gossipd
Verbose: HELP! Please contribute a description for this json_command!

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Command: autoclean-once subsystem age
Category: plugin
Description: Perform a single run of autocleaning on one subsystem
Verbose: Requires {subsystem} and {age}

...

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Command: waitsendpay payment_hash [timeout] [partid] [groupid]
Category: payment
Description: Wait for payment attempt on {payment_hash} to succeed or fail, but only up to {timeout} seconds.
Verbose: HELP! Please contribute a description for this json_command!

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Command: withdraw destination satoshi [feerate] [minconf] [utxos]
Category: plugin
Description: Send funds to {destination} address
Verbose: Send to {destination} {satoshi} (or 'all') at optional {feerate} using utxos from {minconf} or {utxos}.

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Note that for that request /v1/list-methods we don't need to provide a rune in the header of the request.

The other endpoints need a rune and this is what we are going to see in the next section.

POST /v1/getinfo

Let's do a HTTP request that runs the getinfo command in l2 node.

To that we need to provide in the header of POST request a rune (authorization token) that let us run the getinfo command.

If you don't know about Core Lightning runes you can check Introduction to commando and commando-rune.

We can generate an unrestricted rune on l2 node using createrune command like this:

◉ tony@tony:~/clnlive:
$ l2-cli createrune
{
   "rune": "FPD_6O81E8UdSkmpy2qtjPcuPVbxVi0lIQ-OMtVic_U9MA==",
   "unique_id": "0",
   "warning_unrestricted_rune": "WARNING: This rune has no restrictions! Anyone who has access to this rune could drain funds from your node. Be careful when giving this to apps that you don't trust. Consider using the restrictions parameter to only allow access to specific rpc methods."
}

We use that rune in the Swagger interface

/img/2023-09-14-live-0013-swagger-interface-03.png

and do a POST /v1/getinfo request, which runs getinfo on l2 node and gives us back the response using clnrest plugin, like this:

/img/2023-09-14-live-0013-swagger-interface-04.png

We get the same JSON object as if we run the following command:

◉ tony@tony:~/clnlive:
$ l2-cli getinfo
{
   "id": "03fd22c90bc2aaa17e5a1928eeef20944783d57de7c36a712fd733b63b8149a00e",
   "alias": "LOUDPHOTO",
   "color": "03fd22",
   "num_peers": 0,
   "num_pending_channels": 0,
   "num_active_channels": 0,
   "num_inactive_channels": 0,
   "address": [],
   "binding": [
      {
         "type": "ipv4",
         "address": "127.0.0.1",
         "port": 7272
      }
   ],
   "version": "v23.08.1",
   "blockheight": 1,
   "network": "regtest",
   "fees_collected_msat": 0,
   "lightning-dir": "/tmp/l2-regtest/regtest",
   "our_features": {
      "init": "08a0000a0269a2",
      "node": "88a0000a0269a2",
      "channel": "",
      "invoice": "02000002024100"
   }
}

The Swagger interface also gives use the curl command to run to do the exact same request to which we add --insecure flag to skip the TLS verification:

◉ tony@tony:~/clnlive:
$ curl --insecure -X 'POST' \  'https://localhost:3010/v1/getinfo' \  -H 'accept: application/json' \  -H 'Rune: FPD_6O81E8UdSkmpy2qtjPcuPVbxVi0lIQ-OMtVic_U9MA==' \  -H 'Content-Type: application/json' \  -d '{}'
{"id": "03fd22c90bc2aaa17e5a1928eeef20944783d57de7c36a712fd733b63b8149a00e", "alias": "LOUDPHOTO", "color": "03fd22", "num_peers": 0, "num_pending_channels": 0, "num_active_channels": 0, "num_inactive_channels": 0, "address": [], "binding": [{"type": "ipv4", "address": "127.0.0.1", "port": 7272}], "version": "v23.08.1", "blockheight": 1, "network": "regtest", "fees_collected_msat": "0msat", "lightning-dir": "/tmp/l2-regtest/regtest", "our_features": {"init": "08a0000a0269a2", "node": "88a0000a0269a2", "channel": "", "invoice": "02000002024100"}}

Note that in Core Lightning v23.08.1, it is no longer needed to pass the node id in the header of the requests.

POST requests with a rune restricted to the invoice command

Let's create a rune that authorizes to run only the invoice command:

◉ tony@tony:~/clnlive:
$ l2-cli createrune null '[["method=invoice"]]'
{
   "rune": "SfE5g35cmd6pDQEb7PbVUHyz2e9Q_QOcjd1t2ZgxmW09MSZtZXRob2Q9aW52b2ljZQ==",
   "unique_id": "1"
}

Now in the Swagger interface we use that new rune instead of the previous unrestricted one and we try to do a POST /v1/getinfo request as we did before. As this rune no longer authorizes to call methods different for invoice on l2 node we get the following error:

/img/2023-09-14-live-0013-swagger-interface-05.png

Now let's generate an invoice by doing a POST /v1/invoice request with the data part being

{
  "amount_msat": "10000",
  "label": "inv",
  "description": "pizza"
}

like this

/img/2023-09-14-live-0013-swagger-interface-06.png

To complete that example, we are going to pay that invoice with l1 node.

First we fund the nodes and create a channel from l1 node to l2 node using fund_nodes command provided by lightning/contrib/startup_regtest.sh script

◉ tony@tony:~/clnlive:
$ fund_nodes
Mining into address bcrt1qcqm8vdlk7xpgd69fp5994gextw3qtvf8zaqxj5... done.
bitcoind balance: 50.00000000
Waiting for lightning node funds... found.
Funding channel from node 1 to node 2. Waiting for confirmation... done.

and we check that l2 has its previously generated invoice unpaid:

◉ tony@tony:~/clnlive:
$ l2-cli listinvoices
{
   "invoices": [
      {
         "label": "inv",
         "bolt11": "lnbcrt100n1pjsxx36sp57zyg0h5s3s0knz2626p9vkm9vdavddavelq03rmxw633e50e6fkqpp5smt5dp0sfyxre7sux0qy28srpq5heajnfrewd4jz4qdxmcr8y8dsdqgwp5h57npxqyjw5qcqp29qxpqysgqaeqhg5t3s754954hg56xucza2rp0u674gk5rp5nt6wsvsut59lnrq5zwkd55lhe9j25ptyrngj3ftcrgps52e5epu0lfnnjf6mqxsqcphcw9la",
         "payment_hash": "86d74685f0490c3cfa1c33c0451e0308297cf65348f2e6d642a81a6de06721db",
         "amount_msat": 10000,
         "status": "unpaid",
         "description": "pizza",
         "expires_at": 1695306938,
         "created_index": 1
      }
   ]
}

And finally we pay that invoice:

◉ tony@tony:~/clnlive:
$ l1-cli pay lnbcrt100n1pjsxx36sp57zyg0h5s3s0knz2626p9vkm9vdavddavelq03rmxw633e50e6fkqpp5smt5dp0sfyxre7sux0qy28srpq5heajnfrewd4jz4qdxmcr8y8dsdqgwp5h57npxqyjw5qcqp29qxpqysgqaeqhg5t3s754954hg56xucza2rp0u674gk5rp5nt6wsvsut59lnrq5zwkd55lhe9j25ptyrngj3ftcrgps52e5epu0lfnnjf6mqxsqcphcw9la
{
   "destination": "03fd22c90bc2aaa17e5a1928eeef20944783d57de7c36a712fd733b63b8149a00e",
   "payment_hash": "86d74685f0490c3cfa1c33c0451e0308297cf65348f2e6d642a81a6de06721db",
   "created_at": 1694702327.026,
   "parts": 1,
   "amount_msat": 10000,
   "amount_sent_msat": 10000,
   "payment_preimage": "15dec389fb7135f807ff9fe0915f08d00e81d959fc7f54c8dc06e647ed01d2be",
   "status": "complete"
}

CLNRest websocket and CLN notifications

The clnrest plugin once started exposes a websocket endpoint that forward Core Lightning notifications.

Note that starting with Core Lightning v23.11, we need to provide a readonly rune to the websocket client to connect to clnrest plugin (see CLNRest changes from v23.08.1 to v23.11).

Let's consider clnrest-websocket.js websocket client which connect clnrest plugin on l2 node:

const io = require('socket.io-client');

const socket = io.connect('https://127.0.0.1:3010', {rejectUnauthorized: false});

socket.on('connect', function() {
  console.log("I'm connected!");
});

socket.on('message', function(data) {
  console.log('I received a message: ', data);
});

socket.on('disconnect', function() {
  console.log("I'm disconnected!");
});

In another terminal we install socket.io-client dependency and connect to the websocket:

TERMINAL #2

◉ tony@tony:~/clnlive:
$ npm i socket.io-client

added 9 packages in 1s
◉ tony@tony:~/clnlive:
$ node clnrest-websocket.js
I'm connected!

Then we generate an invoice in l2 node using the Swagger interface in we see in the terminal 2 almost at the same time an invoice_creation notification being forwarded to the websocket client clnrest-websocket.js:

◉ tony@tony:~/clnlive:
$ node clnrest-websocket.js
I'm connected!
I received a message:  {'invoice_creation': {'msat': '10000msat', 'preimage': '3bee0d00e7ea124111e4ad1649e0878eed26826b20b93eb04e5d8fb949d31670', 'label': 'inv-1'}}

Finally we pay that invoice we l1 node

◉ tony@tony:~/clnlive:
$ l1-cli pay lnbcrt100n1pjsx8gpsp5ynak3sff5dme03qhn5waccxw47q964fua4r9ufv64eunydfnrqvspp5ynp2fdkh48wywwpy65kesepulppatky9t865vhqlky8gtnkkahfsdqgwp5h57npxqyjw5qcqp29qxpqysgqqa380yun5a7g6npmvnmyh37xszezv8raqhtyp7uernv0eztcpch4755d4y2yjptmwa5mnsfum4g38cdsj56xyt4fpckcpkhh582k2xsqr0t3jz
{
   "destination": "03fd22c90bc2aaa17e5a1928eeef20944783d57de7c36a712fd733b63b8149a00e",
   "payment_hash": "24c2a4b6d7a9dc473824d52d98643cf843d5d88559f5465c1fb10e85ced6edd3",
   "created_at": 1694702917.711,
   "parts": 1,
   "amount_msat": 10000,
   "amount_sent_msat": 10000,
   "payment_preimage": "3bee0d00e7ea124111e4ad1649e0878eed26826b20b93eb04e5d8fb949d31670",
   "status": "complete"
}

which led to two notifications invoice_payment and coin_movement to be forwarded to clnrest-websocket.js websocket client:

◉ tony@tony:~/clnlive:
$ node clnrest-websocket.js
I'm connected!
I received a message:  {'invoice_creation': {'msat': '10000msat', 'preimage': '3bee0d00e7ea124111e4ad1649e0878eed26826b20b93eb04e5d8fb949d31670', 'label': 'inv-1'}}
I received a message:  {'invoice_payment': {'msat': '10000msat', 'preimage': '3bee0d00e7ea124111e4ad1649e0878eed26826b20b93eb04e5d8fb949d31670', 'label': 'inv-1'}}
I received a message:  {'coin_movement': {'version': 2, 'node_id': '03fd22c90bc2aaa17e5a1928eeef20944783d57de7c36a712fd733b63b8149a00e', 'type': 'channel_mvt', 'account_id': '06167ce9ab31a2836321c4c91983ac291f369e1221b78d4f0abea1c1d0e48cac', 'payment_hash': '24c2a4b6d7a9dc473824d52d98643cf843d5d88559f5465c1fb10e85ced6edd3', 'credit_msat': 10000, 'debit_msat': 0, 'fees_msat': 0, 'tags': ['invoice'], 'timestamp': 1694702919, 'coin_type': 'bcrt'}}
^C

checkrune

As we can in checkrune man page, The checkrune RPC command checks the validity/authorization rights of specified rune for the given nodeid, method, and params.

Let's use it with the rune generated above that authorizes only to run invoice method.

If we check that rune against the invoice method we get a valid response

◉ tony@tony:~/clnlive:
$ l2-cli -k checkrune rune=SfE5g35cmd6pDQEb7PbVUHyz2e9Q_QOcjd1t2ZgxmW09MSZtZXRob2Q9aW52b2ljZQ== \method=invoice
{
   "valid": true
}

and if check it against the getinfo method we get a 1502 error:

◉ tony@tony:~/clnlive:
$ l2-cli -k checkrune rune=SfE5g35cmd6pDQEb7PbVUHyz2e9Q_QOcjd1t2ZgxmW09MSZtZXRob2Q9aW52b2ljZQ== method=getinfo
{
   "code": 1502,
   "message": "Not permitted: method is not equal to invoice"
}

The checkrune method is used by clnrest plugin and can be used by any other plugin since the last release v23.08.

Terminal session

We ran the following commands in this order:

$ source lightning/contrib/startup_regtest.sh
$ start_ln
$ alias l2-cli
$ l2-cli getinfo | jq .id
$ l2-cli stop
$ lightningd --lightning-dir=/tmp/l2-regtest --rest-port=3010 --daemon
$ nc -z -v localhost 3010
$ curl --insecure -X 'GET' \  'https://localhost:3010/v1/list-methods' \  -H 'accept: application/json'
$ l2-cli createrune
$ l2-cli getinfo
$ curl --insecure -X 'POST' \  'https://localhost:3010/v1/getinfo' \  -H 'accept: application/json' \  -H 'Rune: FPD_6O81E8UdSkmpy2qtjPcuPVbxVi0lIQ-OMtVic_U9MA==' \  -H 'Content-Type: application/json' \  -d '{}'
$ l2-cli createrune null '[["method=invoice"]]'
$ fund_nodes
$ l2-cli listinvoices
$ l1-cli pay lnbcrt100n1pjsxx36sp57zyg0h5s3s0knz2626p9vkm9vdavddavelq03rmxw633e50e6fkqpp5smt5dp0sfyxre7sux0qy28srpq5heajnfrewd4jz4qdxmcr8y8dsdqgwp5h57npxqyjw5qcqp29qxpqysgqaeqhg5t3s754954hg56xucza2rp0u674gk5rp5nt6wsvsut59lnrq5zwkd55lhe9j25ptyrngj3ftcrgps52e5epu0lfnnjf6mqxsqcphcw9la
$ l1-cli pay lnbcrt100n1pjsx8gpsp5ynak3sff5dme03qhn5waccxw47q964fua4r9ufv64eunydfnrqvspp5ynp2fdkh48wywwpy65kesepulppatky9t865vhqlky8gtnkkahfsdqgwp5h57npxqyjw5qcqp29qxpqysgqqa380yun5a7g6npmvnmyh37xszezv8raqhtyp7uernv0eztcpch4755d4y2yjptmwa5mnsfum4g38cdsj56xyt4fpckcpkhh582k2xsqr0t3jz
$ l2-cli -k checkrune rune=SfE5g35cmd6pDQEb7PbVUHyz2e9Q_QOcjd1t2ZgxmW09MSZtZXRob2Q9aW52b2ljZQ== \method=invoice
$ l2-cli -k checkrune rune=SfE5g35cmd6pDQEb7PbVUHyz2e9Q_QOcjd1t2ZgxmW09MSZtZXRob2Q9aW52b2ljZQ== method=getinfo

And below you can read the terminal session (command lines and outputs):

◉ tony@tony:~/clnlive:
$ source lightning/contrib/startup_regtest.sh
lightning-cli is /usr/local/bin/lightning-cli
lightningd is /usr/local/bin/lightningd
Useful commands:
  start_ln 3: start three nodes, l1, l2, l3
  connect 1 2: connect l1 and l2
  fund_nodes: connect all nodes with channels, in a row
  stop_ln: shutdown
  destroy_ln: remove ln directories
◉ tony@tony:~/clnlive:
$ start_ln
Bitcoin Core starting
awaiting bitcoind...
Making "default" bitcoind wallet.
error code: -35
error message:
Wallet "default" is already loaded.
[1] 1646572
[2] 1646618
WARNING: eatmydata not found: install it for faster testing
Commands:
        l1-cli, l1-log,
        l2-cli, l2-log,
        bt-cli, stop_ln, fund_nodes
◉ tony@tony:~/clnlive:
$ alias l2-cli
alias l2-cli='lightning-cli --lightning-dir=/tmp/l2-regtest'
◉ tony@tony:~/clnlive:
$ l2-cli getinfo | jq .id
"03fd22c90bc2aaa17e5a1928eeef20944783d57de7c36a712fd733b63b8149a00e"
◉ tony@tony:~/clnlive:
$ l2-cli stop
"Shutdown complete"
[2]+  Done                    test -f "/tmp/l$i-$network/lightningd-$network.pid" || $EATMYDATA "$LIGHTNINGD" "--network=$network" "--lightning-dir=/tmp/l$i-$network" "--bitcoin-datadir=$PATH_TO_BITCOIN" "--database-upgrade=true"
◉ tony@tony:~/clnlive:
$ lightningd --lightning-dir=/tmp/l2-regtest --rest-port=3010 --daemon
◉ tony@tony:~/clnlive:
$ nc -z -v localhost 3010
Connection to localhost (127.0.0.1) 3010 port [tcp/*] succeeded!
◉ tony@tony:~/clnlive:
$ curl --insecure -X 'GET' \  'https://localhost:3010/v1/list-methods' \  -H 'accept: application/json'
Command: addgossip message
Category: utility
Description: Inject gossip {message} into gossipd
Verbose: HELP! Please contribute a description for this json_command!

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Command: autoclean-once subsystem age
Category: plugin
Description: Perform a single run of autocleaning on one subsystem
Verbose: Requires {subsystem} and {age}

...

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Command: waitsendpay payment_hash [timeout] [partid] [groupid]
Category: payment
Description: Wait for payment attempt on {payment_hash} to succeed or fail, but only up to {timeout} seconds.
Verbose: HELP! Please contribute a description for this json_command!

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Command: withdraw destination satoshi [feerate] [minconf] [utxos]
Category: plugin
Description: Send funds to {destination} address
Verbose: Send to {destination} {satoshi} (or 'all') at optional {feerate} using utxos from {minconf} or {utxos}.

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------

◉ tony@tony:~/clnlive:
$ l2-cli createrune
{
   "rune": "FPD_6O81E8UdSkmpy2qtjPcuPVbxVi0lIQ-OMtVic_U9MA==",
   "unique_id": "0",
   "warning_unrestricted_rune": "WARNING: This rune has no restrictions! Anyone who has access to this rune could drain funds from your node. Be careful when giving this to apps that you don't trust. Consider using the restrictions parameter to only allow access to specific rpc methods."
}
◉ tony@tony:~/clnlive:
$ l2-cli getinfo
{
   "id": "03fd22c90bc2aaa17e5a1928eeef20944783d57de7c36a712fd733b63b8149a00e",
   "alias": "LOUDPHOTO",
   "color": "03fd22",
   "num_peers": 0,
   "num_pending_channels": 0,
   "num_active_channels": 0,
   "num_inactive_channels": 0,
   "address": [],
   "binding": [
      {
         "type": "ipv4",
         "address": "127.0.0.1",
         "port": 7272
      }
   ],
   "version": "v23.08.1",
   "blockheight": 1,
   "network": "regtest",
   "fees_collected_msat": 0,
   "lightning-dir": "/tmp/l2-regtest/regtest",
   "our_features": {
      "init": "08a0000a0269a2",
      "node": "88a0000a0269a2",
      "channel": "",
      "invoice": "02000002024100"
   }
}
◉ tony@tony:~/clnlive:
$ curl --insecure -X 'POST' \  'https://localhost:3010/v1/getinfo' \  -H 'accept: application/json' \  -H 'Rune: FPD_6O81E8UdSkmpy2qtjPcuPVbxVi0lIQ-OMtVic_U9MA==' \  -H 'Content-Type: application/json' \  -d '{}'
{"id": "03fd22c90bc2aaa17e5a1928eeef20944783d57de7c36a712fd733b63b8149a00e", "alias": "LOUDPHOTO", "color": "03fd22", "num_peers": 0, "num_pending_channels": 0, "num_active_channels": 0, "num_inactive_channels": 0, "address": [], "binding": [{"type": "ipv4", "address": "127.0.0.1", "port": 7272}], "version": "v23.08.1", "blockheight": 1, "network": "regtest", "fees_collected_msat": "0msat", "lightning-dir": "/tmp/l2-regtest/regtest", "our_features": {"init": "08a0000a0269a2", "node": "88a0000a0269a2", "channel": "", "invoice": "02000002024100"}}
◉ tony@tony:~/clnlive:
$ l2-cli createrune null '[["method=invoice"]]'
{
   "rune": "SfE5g35cmd6pDQEb7PbVUHyz2e9Q_QOcjd1t2ZgxmW09MSZtZXRob2Q9aW52b2ljZQ==",
   "unique_id": "1"
}
◉ tony@tony:~/clnlive:
$ fund_nodes
Mining into address bcrt1qcqm8vdlk7xpgd69fp5994gextw3qtvf8zaqxj5... done.
bitcoind balance: 50.00000000
Waiting for lightning node funds... found.
Funding channel from node 1 to node 2. Waiting for confirmation... done.
◉ tony@tony:~/clnlive:
$ l2-cli listinvoices
{
   "invoices": [
      {
         "label": "inv",
         "bolt11": "lnbcrt100n1pjsxx36sp57zyg0h5s3s0knz2626p9vkm9vdavddavelq03rmxw633e50e6fkqpp5smt5dp0sfyxre7sux0qy28srpq5heajnfrewd4jz4qdxmcr8y8dsdqgwp5h57npxqyjw5qcqp29qxpqysgqaeqhg5t3s754954hg56xucza2rp0u674gk5rp5nt6wsvsut59lnrq5zwkd55lhe9j25ptyrngj3ftcrgps52e5epu0lfnnjf6mqxsqcphcw9la",
         "payment_hash": "86d74685f0490c3cfa1c33c0451e0308297cf65348f2e6d642a81a6de06721db",
         "amount_msat": 10000,
         "status": "unpaid",
         "description": "pizza",
         "expires_at": 1695306938,
         "created_index": 1
      }
   ]
}
◉ tony@tony:~/clnlive:
$ l1-cli pay lnbcrt100n1pjsxx36sp57zyg0h5s3s0knz2626p9vkm9vdavddavelq03rmxw633e50e6fkqpp5smt5dp0sfyxre7sux0qy28srpq5heajnfrewd4jz4qdxmcr8y8dsdqgwp5h57npxqyjw5qcqp29qxpqysgqaeqhg5t3s754954hg56xucza2rp0u674gk5rp5nt6wsvsut59lnrq5zwkd55lhe9j25ptyrngj3ftcrgps52e5epu0lfnnjf6mqxsqcphcw9la
{
   "destination": "03fd22c90bc2aaa17e5a1928eeef20944783d57de7c36a712fd733b63b8149a00e",
   "payment_hash": "86d74685f0490c3cfa1c33c0451e0308297cf65348f2e6d642a81a6de06721db",
   "created_at": 1694702327.026,
   "parts": 1,
   "amount_msat": 10000,
   "amount_sent_msat": 10000,
   "payment_preimage": "15dec389fb7135f807ff9fe0915f08d00e81d959fc7f54c8dc06e647ed01d2be",
   "status": "complete"
}
◉ tony@tony:~/clnlive:
$ l1-cli pay lnbcrt100n1pjsx8gpsp5ynak3sff5dme03qhn5waccxw47q964fua4r9ufv64eunydfnrqvspp5ynp2fdkh48wywwpy65kesepulppatky9t865vhqlky8gtnkkahfsdqgwp5h57npxqyjw5qcqp29qxpqysgqqa380yun5a7g6npmvnmyh37xszezv8raqhtyp7uernv0eztcpch4755d4y2yjptmwa5mnsfum4g38cdsj56xyt4fpckcpkhh582k2xsqr0t3jz
{
   "destination": "03fd22c90bc2aaa17e5a1928eeef20944783d57de7c36a712fd733b63b8149a00e",
   "payment_hash": "24c2a4b6d7a9dc473824d52d98643cf843d5d88559f5465c1fb10e85ced6edd3",
   "created_at": 1694702917.711,
   "parts": 1,
   "amount_msat": 10000,
   "amount_sent_msat": 10000,
   "payment_preimage": "3bee0d00e7ea124111e4ad1649e0878eed26826b20b93eb04e5d8fb949d31670",
   "status": "complete"
}
◉ tony@tony:~/clnlive:
$ l2-cli -k checkrune rune=SfE5g35cmd6pDQEb7PbVUHyz2e9Q_QOcjd1t2ZgxmW09MSZtZXRob2Q9aW52b2ljZQ== \method=invoice
{
   "valid": true
}
◉ tony@tony:~/clnlive:
$ l2-cli -k checkrune rune=SfE5g35cmd6pDQEb7PbVUHyz2e9Q_QOcjd1t2ZgxmW09MSZtZXRob2Q9aW52b2ljZQ== method=getinfo
{
   "code": 1502,
   "message": "Not permitted: method is not equal to invoice"
}
TERMINAL #2

◉ tony@tony:~/clnlive:
$ npm i socket.io-client

added 9 packages in 1s
◉ tony@tony:~/clnlive:
$ node clnrest-websocket.js
I'm connected!
I received a message:  {'invoice_creation': {'msat': '10000msat', 'preimage': '3bee0d00e7ea124111e4ad1649e0878eed26826b20b93eb04e5d8fb949d31670', 'label': 'inv-1'}}
I received a message:  {'invoice_payment': {'msat': '10000msat', 'preimage': '3bee0d00e7ea124111e4ad1649e0878eed26826b20b93eb04e5d8fb949d31670', 'label': 'inv-1'}}
I received a message:  {'coin_movement': {'version': 2, 'node_id': '03fd22c90bc2aaa17e5a1928eeef20944783d57de7c36a712fd733b63b8149a00e', 'type': 'channel_mvt', 'account_id': '06167ce9ab31a2836321c4c91983ac291f369e1221b78d4f0abea1c1d0e48cac', 'payment_hash': '24c2a4b6d7a9dc473824d52d98643cf843d5d88559f5465c1fb10e85ced6edd3', 'credit_msat': 10000, 'debit_msat': 0, 'fees_msat': 0, 'tags': ['invoice'], 'timestamp': 1694702919, 'coin_type': 'bcrt'}}
^C

Core Lightning source code

@rpcns.route("/")

@rpcns.route("/<rpc_method>") in lightning:plugins/clnrest/utilities/rpc_routes.py

@rpcns.route("/<rpc_method>")
class RpcMethodResource(Resource):
    @rpcns.doc(security=[{"rune": []}])
    @rpcns.doc(params={"rpc_method": (f"Name of the RPC method to be called")})
    @rpcns.expect(payload_model, validate=False)
    @rpcns.response(201, "Success")
    @rpcns.response(500, "Server error")
    def post(self, rpc_method):
        """Call any valid core lightning method (check list-methods response)"""
        try:
            is_valid_rune = verify_rune(plugin, request)

            if "error" in is_valid_rune:
                plugin.log(f"Error: {is_valid_rune}", "error")
                raise Exception(is_valid_rune)

        except Exception as err:
            return json5.loads(str(err)), 403

        try:
            if request.is_json:
                if len(request.data) != 0:
                    payload = request.get_json()
                else:
                    payload = {}
            else:
                payload = request.form.to_dict()
            return call_rpc_method(plugin, rpc_method, payload), 201

        except Exception as err:
            plugin.log(f"Error: {err}", "error")
            return json5.loads(str(err)), 500

call_rpc_method

call_rpc_method in lightning:plugins/clnrest/utilities/shared.py

def call_rpc_method(plugin, rpc_method, payload):
    try:
        response = plugin.rpc.call(rpc_method, payload)
        if '"error":' in str(response).lower():
            raise Exception(response)
        else:
            plugin.log(f"{response}", "debug")
            if '"result":' in str(response).lower():
                # Use json5.loads ONLY when necessary, as it increases processing time significantly
                return json.loads(response)["result"]
            else:
                return response

    except Exception as err:
        plugin.log(f"Error: {err}", "error")
        if "error" in str(err).lower():
            match_err_obj = re.search(r'"error":\{.*?\}', str(err))
            if match_err_obj is not None:
                err = "{" + match_err_obj.group() + "}"
            else:
                match_err_str = re.search(r"error: \{.*?\}", str(err))
                if match_err_str is not None:
                    err = "{" + match_err_str.group() + "}"
        raise Exception(err)

verify_rune

verify_rune in lightning:plugins/clnrest/utilities/shared.py

def verify_rune(plugin, request):
    rune = request.headers.get("rune", None)

    if rune is None:
        raise Exception('{ "error": {"code": 403, "message": "Not authorized: Missing rune"} }')

    if request.is_json:
        if len(request.data) != 0:
            rpc_params = request.get_json()
        else:
            rpc_params = {}
    else:
        rpc_params = request.form.to_dict()

    return call_rpc_method(plugin, "checkrune",
                           {"rune": rune,
                            "method": request.view_args["rpc_method"],
                            "params": rpc_params})

Source code

clnrest-websocket.js

Note that starting with Core Lightning v23.11, we need to provide a readonly rune to the websocket client to connect to clnrest plugin (see CLNRest changes from v23.08.1 to v23.11).

const io = require('socket.io-client');

const socket = io.connect('https://127.0.0.1:3010', {rejectUnauthorized: false});

socket.on('connect', function() {
  console.log("I'm connected!");
});

socket.on('message', function(data) {
  console.log('I received a message: ', data);
});

socket.on('disconnect', function() {
  console.log("I'm disconnected!");
});

Logs

/tmp/l2-regtest/log

2023-09-14T14:10:07.019Z INFO    lightningd: Creating configuration directory /tmp/l2-regtest/regtest
2023-09-14T14:10:07.019Z DEBUG   lightningd: Opened log file /tmp/l2-regtest/log
2023-09-14T14:10:07.023Z DEBUG   plugin-manager: started(1646626) /usr/local/libexec/c-lightning/plugins/autoclean
2023-09-14T14:10:07.034Z DEBUG   plugin-manager: started(1646627) /usr/local/libexec/c-lightning/plugins/chanbackup
2023-09-14T14:10:07.038Z DEBUG   plugin-manager: started(1646628) /usr/local/libexec/c-lightning/plugins/bcli
2023-09-14T14:10:07.045Z DEBUG   plugin-manager: started(1646629) /usr/local/libexec/c-lightning/plugins/commando
2023-09-14T14:10:07.049Z DEBUG   plugin-manager: started(1646630) /usr/local/libexec/c-lightning/plugins/fetchinvoice
2023-09-14T14:10:07.052Z DEBUG   plugin-manager: started(1646631) /usr/local/libexec/c-lightning/plugins/funder
2023-09-14T14:10:07.056Z DEBUG   plugin-manager: started(1646632) /usr/local/libexec/c-lightning/plugins/topology
2023-09-14T14:10:07.059Z DEBUG   plugin-manager: started(1646633) /usr/local/libexec/c-lightning/plugins/keysend
2023-09-14T14:10:07.062Z DEBUG   plugin-manager: started(1646634) /usr/local/libexec/c-lightning/plugins/offers
2023-09-14T14:10:07.066Z DEBUG   plugin-manager: started(1646635) /usr/local/libexec/c-lightning/plugins/pay
2023-09-14T14:10:07.069Z DEBUG   plugin-manager: started(1646636) /usr/local/libexec/c-lightning/plugins/txprepare
2023-09-14T14:10:07.072Z DEBUG   plugin-manager: started(1646637) /usr/local/libexec/c-lightning/plugins/cln-renepay
2023-09-14T14:10:07.075Z DEBUG   plugin-manager: started(1646638) /usr/local/libexec/c-lightning/plugins/spenderp
2023-09-14T14:10:07.078Z DEBUG   plugin-manager: started(1646639) /usr/local/libexec/c-lightning/plugins/sql
2023-09-14T14:10:07.081Z DEBUG   plugin-manager: started(1646640) /usr/local/libexec/c-lightning/plugins/bookkeeper
2023-09-14T14:10:07.087Z DEBUG   plugin-manager: started(1646641) /usr/local/libexec/c-lightning/plugins/clnrest/clnrest.py
2023-09-14T14:10:07.809Z DEBUG   lightningd: io_break: check_plugins_manifests
2023-09-14T14:10:07.809Z DEBUG   lightningd: io_loop_with_timers: plugins_init
2023-09-14T14:10:07.809Z DEBUG   lightningd: testing /usr/local/libexec/c-lightning/lightning_channeld
2023-09-14T14:10:07.811Z DEBUG   lightningd: testing /usr/local/libexec/c-lightning/lightning_closingd
2023-09-14T14:10:07.812Z DEBUG   lightningd: testing /usr/local/libexec/c-lightning/lightning_connectd
2023-09-14T14:10:07.813Z DEBUG   lightningd: testing /usr/local/libexec/c-lightning/lightning_gossipd
2023-09-14T14:10:07.814Z DEBUG   lightningd: testing /usr/local/libexec/c-lightning/lightning_hsmd
2023-09-14T14:10:07.815Z DEBUG   lightningd: testing /usr/local/libexec/c-lightning/lightning_onchaind
2023-09-14T14:10:07.817Z DEBUG   lightningd: testing /usr/local/libexec/c-lightning/lightning_openingd
2023-09-14T14:10:07.818Z DEBUG   hsmd: pid 1646664, msgfd 50
2023-09-14T14:10:07.845Z DEBUG   hsmd: capability +WIRE_HSMD_CHECK_PUBKEY
2023-09-14T14:10:07.845Z DEBUG   hsmd: capability +WIRE_HSMD_SIGN_ANY_DELAYED_PAYMENT_TO_US
2023-09-14T14:10:07.845Z DEBUG   hsmd: capability +WIRE_HSMD_SIGN_ANCHORSPEND
2023-09-14T14:10:07.845Z DEBUG   hsmd: capability +WIRE_HSMD_SIGN_HTLC_TX_MINGLE
2023-09-14T14:10:07.845Z DEBUG   hsmd: capability +WIRE_HSMD_SIGN_SPLICE_TX
2023-09-14T14:10:07.846Z INFO    lightningd: Creating database
2023-09-14T14:10:08.104Z DEBUG   connectd: pid 1646665, msgfd 54
2023-09-14T14:10:08.104Z UNUSUAL hsmd: HSM: created new hsm_secret file
2023-09-14T14:10:08.104Z DEBUG   hsmd: Client: Received message 27 from client
2023-09-14T14:10:08.104Z DEBUG   hsmd: Client: Received message 27 from client
2023-09-14T14:10:08.104Z DEBUG   hsmd: new_client: 0
2023-09-14T14:10:08.105Z DEBUG   plugin-clnrest.py: Notification: {'warning': {'level': 'warn', 'time': '1694700608.104590967', 'timestamp': '2023-09-14T14:10:08.104Z', 'source': 'hsmd', 'log': 'HSM: created new hsm_secret file'}}
2023-09-14T14:10:08.123Z DEBUG   connectd: Created listener on 127.0.0.1:7272
2023-09-14T14:10:08.123Z DEBUG   connectd: REPLY WIRE_CONNECTD_INIT_REPLY with 0 fds
2023-09-14T14:10:08.123Z DEBUG   lightningd: io_break: connect_init_done
2023-09-14T14:10:08.123Z DEBUG   lightningd: io_loop: connectd_init
2023-09-14T14:10:08.188Z INFO    plugin-bcli: bitcoin-cli initialized and connected to bitcoind.
2023-09-14T14:10:08.188Z DEBUG   lightningd: io_break: plugin_config_cb
2023-09-14T14:10:08.188Z DEBUG   lightningd: io_loop_with_timers: config_plugin
2023-09-14T14:10:08.188Z DEBUG   lightningd: All Bitcoin plugin commands registered
2023-09-14T14:10:08.241Z DEBUG   lightningd: Adding block 0: 0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206
2023-09-14T14:10:08.241Z DEBUG   lightningd: io_break: maybe_completed_init
2023-09-14T14:10:08.287Z DEBUG   lightningd: io_loop_with_timers: setup_topology
2023-09-14T14:10:08.287Z DEBUG   wallet: Loaded 0 channels from DB
2023-09-14T14:10:08.288Z DEBUG   gossipd: pid 1646676, msgfd 53
2023-09-14T14:10:08.288Z DEBUG   hsmd: new_client: 0
2023-09-14T14:10:08.289Z DEBUG   gossipd: total store load time: 0 msec
2023-09-14T14:10:08.289Z DEBUG   gossipd: gossip_store: Read 0/0/0/0 cannounce/cupdate/nannounce/cdelete from store (0 deleted) in 1 bytes
2023-09-14T14:10:08.289Z DEBUG   gossipd: seeker: state = STARTING_UP New seeker
2023-09-14T14:10:08.289Z DEBUG   gossipd: REPLY WIRE_GOSSIPD_INIT_REPLY with 0 fds
2023-09-14T14:10:08.289Z DEBUG   lightningd: io_break: gossipd_init_done
2023-09-14T14:10:08.289Z DEBUG   lightningd: io_loop: gossip_init
2023-09-14T14:10:08.290Z DEBUG   lightningd: Looking for [autoclean,succeededforwards,num]
2023-09-14T14:10:08.291Z DEBUG   lightningd: Looking for [autoclean,failedforwards,num]
2023-09-14T14:10:08.291Z DEBUG   hsmd: Client: Received message 27 from client
2023-09-14T14:10:08.292Z DEBUG   plugin-clnrest.py: Killing plugin: disabled itself at init: `rest-port` option is not configured
2023-09-14T14:10:08.293Z DEBUG   lightningd: Looking for [autoclean,succeededpays,num]

Resources