2023-09-12 15:20:02 +09:30
|
|
|
---
|
|
|
|
title: "REST APIs"
|
|
|
|
slug: "rest"
|
|
|
|
hidden: false
|
|
|
|
createdAt: "2023-09-05T09:54:01.784Z"
|
2023-10-05 09:19:14 +02:00
|
|
|
updatedAt: "2023-10-13T09:54:01.784Z"
|
2023-09-12 15:20:02 +09:30
|
|
|
---
|
|
|
|
|
|
|
|
# CLNRest
|
|
|
|
|
2023-10-05 09:19:14 +02:00
|
|
|
CLNRest is a lightweight Python-based built-in Core Lightning plugin (from v23.08) that transforms RPC calls into a REST service.
|
|
|
|
It also broadcasts Core Lightning notifications to listeners connected to its websocket server. By generating REST API endpoints,
|
|
|
|
it enables the execution of Core Lightning's RPC methods behind the scenes and provides responses in JSON format.
|
2023-09-12 15:20:02 +09:30
|
|
|
|
2023-10-05 09:19:14 +02:00
|
|
|
An online demo for the REST interface is available at [REST API REFERENCE](ref:get_list_methods_resource).
|
2023-09-12 15:20:02 +09:30
|
|
|
|
|
|
|
> 📘 Pro-tip
|
|
|
|
>
|
2023-09-15 12:00:59 +04:00
|
|
|
> [REST API REFERENCE](ref:get_list_methods_resource) can also be tested with your own server.
|
2023-09-12 15:20:02 +09:30
|
|
|
>
|
|
|
|
> By default, the base URL is set to connect with the Blockstream-hosted regtest node.
|
|
|
|
>
|
|
|
|
> However, it can be configured to connect to your own cln node as described below:
|
|
|
|
>
|
|
|
|
> - Select `{protocol}://{ip}:{port}/` from Base URL dropdown on the right section of the page.
|
|
|
|
>
|
|
|
|
> - Click on the right side of the dropdown and configure `protocol`, `ip` and `port` values according to your setup.
|
|
|
|
>
|
|
|
|
> - The `ip` should be configured with your system's public IP address.
|
|
|
|
>
|
2023-11-09 22:16:39 -08:00
|
|
|
> - Default `clnrest-host` is `127.0.0.1` but this testing will require it to be `0.0.0.0`.
|
2023-09-12 15:20:02 +09:30
|
|
|
>
|
|
|
|
> Note: This setup is for **testing only**. It is **highly recommended** to test with _non-mainnet_ (regtest/testnet) setup only.
|
|
|
|
|
|
|
|
|
|
|
|
## Installation
|
|
|
|
|
2023-10-05 09:19:14 +02:00
|
|
|
The plugin is built-in with Core Lightning but its python dependencies are not, and must be installed separately.
|
|
|
|
Install required packages with `pip install -r plugins/clnrest/requirements.txt`.
|
|
|
|
|
2023-11-09 22:16:39 -08:00
|
|
|
Note: if you have the older c-lightning-REST plugin, you can configure Core Lightning with `disable-plugin=clnrest.py`
|
|
|
|
option to avoid confusion with this one. You can also run both plugins simultaneously till all your applications
|
|
|
|
are not migrated to `clnrest`.
|
2023-09-12 15:20:02 +09:30
|
|
|
|
|
|
|
|
|
|
|
## Configuration
|
|
|
|
|
2023-11-09 22:16:39 -08:00
|
|
|
If `clnrest-port` is not specified, the plugin will disable itself.
|
2023-09-12 15:20:02 +09:30
|
|
|
|
2023-11-09 22:16:39 -08:00
|
|
|
- --clnrest-port: Sets the REST server port to listen to (3010 is common)
|
2023-10-05 09:19:14 +02:00
|
|
|
|
2023-11-09 22:16:39 -08:00
|
|
|
- --clnrest-protocol: Specifies the REST server protocol. Default is HTTPS.
|
2023-10-05 09:19:14 +02:00
|
|
|
|
2023-11-09 22:16:39 -08:00
|
|
|
- --clnrest-host: Defines the REST server host. Default is 127.0.0.1.
|
2023-10-05 09:19:14 +02:00
|
|
|
|
2023-11-09 22:16:39 -08:00
|
|
|
- --clnrest-certs: Defines the path for HTTPS cert & key. Default path is same as RPC file path to utilize gRPC's client certificate.
|
2023-10-05 09:19:14 +02:00
|
|
|
If it is missing at the configured location, new identity will be generated.
|
|
|
|
|
2023-11-09 22:16:39 -08:00
|
|
|
- --clnrest-csp: Creates a whitelist of trusted content sources that can run on a webpage and helps mitigate the risk of attacks.
|
2023-10-05 09:19:14 +02:00
|
|
|
Default CSP:
|
|
|
|
`default-src 'self'; font-src 'self'; img-src 'self' data:; frame-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline';`
|
|
|
|
Example CSP:
|
2023-11-09 22:16:39 -08:00
|
|
|
`clnrest-csp=default-src 'self'; font-src 'self'; img-src 'self'; frame-src 'self'; style-src 'self'; script-src 'self';`.
|
2023-10-05 09:19:14 +02:00
|
|
|
|
2023-11-09 22:16:39 -08:00
|
|
|
- --clnrest-cors-origins: Define multiple origins which are allowed to share resources on web pages to a domain different from the
|
2023-10-05 09:19:14 +02:00
|
|
|
one that served the web page. Default is `*` which allows all origins. Example to define multiple origins:
|
2023-09-11 21:29:22 -07:00
|
|
|
|
|
|
|
```
|
2023-11-09 22:16:39 -08:00
|
|
|
clnrest-cors-origins=https://localhost:5500
|
|
|
|
clnrest-cors-origins=http://192.168.1.50:3030
|
|
|
|
clnrest-cors-origins=https?://127.0.0.1:([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])
|
2023-09-11 21:29:22 -07:00
|
|
|
|
|
|
|
```
|
2023-09-12 15:20:02 +09:30
|
|
|
|
|
|
|
## Server
|
|
|
|
|
2023-10-05 09:19:14 +02:00
|
|
|
With the default configurations, the Swagger user interface will be available at https://127.0.0.1:3010/.
|
|
|
|
The POST method requires `rune` header for authorization.
|
2023-09-12 15:20:02 +09:30
|
|
|
|
2023-10-05 09:19:14 +02:00
|
|
|
- A new `rune` can be created via [createrune](https://docs.corelightning.org/reference/lightning-createrune) or the list of
|
2024-01-02 20:07:56 +08:00
|
|
|
existing runes can be retrieved with [listrunes](https://docs.corelightning.org/reference/lightning-commando-listrunes) command.
|
2023-09-12 15:20:02 +09:30
|
|
|
|
2023-10-05 09:19:14 +02:00
|
|
|
Note: in version v23.08, a parameter `Nodeid` was required to be the id of the node we're talking to (see `id (pubkey)` received
|
|
|
|
from [getinfo](https://docs.corelightning.org/reference/lightning-getinfo)). You can still send this for backwards compatiblity,
|
|
|
|
but it is completely ignored.
|
2023-09-12 15:20:02 +09:30
|
|
|
|
|
|
|
### cURL
|
|
|
|
Example curl command for POST will also require a `rune` header like below:
|
2023-10-05 09:19:14 +02:00
|
|
|
`curl -k -X POST 'https://localhost:3010/v1/getinfo' -H 'Rune: <node-rune>'`
|
2023-09-12 15:20:02 +09:30
|
|
|
|
|
|
|
With `-k` or `--insecure` option curl proceeds with the connection even if the SSL certificate cannot be verified.
|
|
|
|
This option should be used only when testing with self signed certificate.
|
|
|
|
|
|
|
|
## Websocket Server
|
2023-10-05 09:19:14 +02:00
|
|
|
Websocket server is available at `https://127.0.0.1:3010`. clnrest queues up notifications received for a second
|
|
|
|
then broadcasts them to all listeners.
|
|
|
|
|
|
|
|
This websocket server requires a `rune` with at least `readonly` access for authorization. The default method used
|
|
|
|
for current validation is `listclnrest-notifications`. User can either provided a rune with minimum `readonly`
|
|
|
|
access or can create a new special purpose rune, only for websocket validation, with restrictions='[["method=listclnrest-notifications"]]'.
|
|
|
|
The client will only receive notifications if `rune`, provided in headers, allows it.
|
2023-09-12 15:20:02 +09:30
|
|
|
|
|
|
|
### Websocket client examples
|
|
|
|
|
|
|
|
#### Python
|
|
|
|
|
|
|
|
```python
|
|
|
|
import socketio
|
|
|
|
import requests
|
|
|
|
|
|
|
|
http_session = requests.Session()
|
2023-09-08 19:49:25 -07:00
|
|
|
http_session.verify = True
|
|
|
|
http_session.headers.update({
|
|
|
|
"rune": "your-generated-rune"
|
|
|
|
})
|
2023-09-12 15:20:02 +09:30
|
|
|
sio = socketio.Client(http_session=http_session)
|
|
|
|
|
|
|
|
@sio.event
|
|
|
|
def connect():
|
2023-09-08 19:49:25 -07:00
|
|
|
print("Client Connected")
|
2023-09-12 15:20:02 +09:30
|
|
|
|
|
|
|
@sio.event
|
|
|
|
def disconnect():
|
2023-09-08 19:49:25 -07:00
|
|
|
print(f"Server connection closed.\nCheck CLN logs for errors if unexpected")
|
|
|
|
|
|
|
|
@sio.event
|
|
|
|
def message(data):
|
|
|
|
print(f"Message from server: {data}")
|
|
|
|
|
|
|
|
@sio.event
|
|
|
|
def error(err):
|
|
|
|
print(f"Error from server: {err}")
|
|
|
|
|
|
|
|
sio.connect('http://127.0.0.1:3010')
|
2023-09-12 15:20:02 +09:30
|
|
|
|
|
|
|
sio.wait()
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
#### NodeJS
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
const io = require('socket.io-client');
|
|
|
|
|
2023-09-08 19:49:25 -07:00
|
|
|
const socket = io.connect('http://127.0.0.1:3010', {extraHeaders: {rune: "your-generated-rune"}});
|
2023-09-12 15:20:02 +09:30
|
|
|
|
|
|
|
socket.on('connect', function() {
|
2023-09-08 19:49:25 -07:00
|
|
|
console.log('Client Connected');
|
|
|
|
});
|
|
|
|
|
|
|
|
socket.on('disconnect', function(reason) {
|
|
|
|
console.log('Server connection closed: ', reason, '\nCheck CLN logs for errors if unexpected');
|
2023-09-12 15:20:02 +09:30
|
|
|
});
|
|
|
|
|
|
|
|
socket.on('message', function(data) {
|
2023-09-08 19:49:25 -07:00
|
|
|
console.log('Message from server: ', data);
|
2023-09-12 15:20:02 +09:30
|
|
|
});
|
|
|
|
|
2023-09-08 19:49:25 -07:00
|
|
|
socket.on('error', function(err) {
|
|
|
|
console.error('Error from server: ', err);
|
2023-09-12 15:20:02 +09:30
|
|
|
});
|
|
|
|
|
|
|
|
```
|
2023-09-08 19:49:25 -07:00
|
|
|
|
|
|
|
#### HTML
|
|
|
|
|
|
|
|
```html
|
|
|
|
<!DOCTYPE html>
|
|
|
|
<html>
|
|
|
|
<head>
|
|
|
|
<title>Socket.IO Client Example</title>
|
|
|
|
<script src="https://cdn.socket.io/4.0.1/socket.io.min.js"></script>
|
|
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<h1>Socket.IO Client Example</h1>
|
|
|
|
<hr>
|
|
|
|
<h3>Status:</h3>
|
|
|
|
<div id="status">Not connected</div>
|
|
|
|
<hr>
|
|
|
|
<h3>Send Message:</h3>
|
|
|
|
<input type="text" id="messageInput" placeholder="Type your message here">
|
|
|
|
<button onclick="sendMessage()">Send</button>
|
|
|
|
<hr>
|
|
|
|
<h3>Received Messages:</h3>
|
|
|
|
<div id="messages"></div>
|
|
|
|
<script>
|
|
|
|
const statusElement = document.getElementById('status');
|
|
|
|
const messagesElement = document.getElementById('messages');
|
|
|
|
|
|
|
|
const socket = io('http://127.0.0.1:3010', {extraHeaders: {rune: "your-generated-rune"}});
|
|
|
|
|
|
|
|
socket.on('connect', () => {
|
|
|
|
statusElement.textContent = 'Client Connected';
|
|
|
|
});
|
|
|
|
|
|
|
|
socket.on('disconnect', (reason) => {
|
|
|
|
statusElement.textContent = 'Server connection closed: ' + reason + '\n Check CLN logs for errors if unexpected';
|
|
|
|
});
|
|
|
|
|
|
|
|
socket.on('message', (data) => {
|
|
|
|
const item = document.createElement('li');
|
|
|
|
item.textContent = JSON.stringify(data);
|
|
|
|
messagesElement.appendChild(item);
|
|
|
|
console.log('Message from server: ', data);
|
|
|
|
});
|
|
|
|
|
|
|
|
socket.on('error', (err) => {
|
|
|
|
const item = document.createElement('li');
|
|
|
|
item.textContent = JSON.stringify(err);
|
|
|
|
messagesElement.appendChild(item);
|
|
|
|
console.error('Error from server: ', err);
|
|
|
|
});
|
|
|
|
|
|
|
|
function sendMessage() {
|
|
|
|
const message = messageInput.value;
|
|
|
|
if (message) {
|
|
|
|
socket.emit('message', message);
|
|
|
|
messageInput.value = '';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</script>
|
|
|
|
</body>
|
|
|
|
</html>
|
|
|
|
|
|
|
|
```
|