@ -52,17 +49,15 @@ parameters have `default` values for initial setup and can be updated after RTL
The environment variable can also be used for all of the above configurations except the UI settings.<br/>
If the environment variables are set, it will take precedence over the parameters in the RTL-Config.json file.<br/>
<br/>
PORT (port number for the rtl node server, default 3000, Optional)<br/>
PORT (port number for the rtl node server, default 3000, Required)<br/>
HOST (host for the rtl node server, default localhost, Optional)<br/>
DB_DIRECTORY_PATH (Path for the folder where rtl database file should be saved, default RTL root directory, Optional)
APP_PASSWORD (Plaintext password to be provided by the parent container, NOT suggested for standalone RTL applications, to be used by Umbrel) (Optional)<br/>
LN_SERVER_URL (LN server URL for LNP REST APIs, default https://127.0.0.1:8080) (Required)<br/>
SWAP_SERVER_URL (Swap server URL for REST APIs, default http://127.0.0.1:8081) (Optional)<br/>
BOLTZ_SERVER_URL (Boltz server URL for REST APIs, default http://127.0.0.1:9003) (Optional)<br/>
CONFIG_PATH (Full path of the LNP .conf file including the file name) (Optional for LND & CLN, Mandatory for ECL if LN_API_PASSWORD is undefined)<br/>
MACAROON_PATH (Path for the folder containing 'admin.macaroon' for LND, Required for LND)<br/>
RUNE_PATH (Complete path for the file containing 'rune' for CLN where the file should define the rune in 'LIGHTNING_RUNE="your-rune"' format, Required for CLN)<br/>
MACAROON_PATH (Path for the folder containing 'admin.macaroon' (LND)/'access.macaroon' (CLN) file, Required for LND & CLN)<br/>
SWAP_MACAROON_PATH (Path for the folder containing Loop's 'loop.macaroon', optional)<br/>
BOLTZ_MACAROON_PATH (Path for the folder containing Boltz's 'admin.macaroon', optional)<br/>
RTL_SSO (1 - single sign on via an external cookie, 0 - stand alone RTL authentication, Required)<br/>
RTL is now enabled to manage lightning nodes running Core Lightning
RTL is now enabled to manage lightning nodes running Core Lightning.
Follow the below steps to install and setup RTL to run on Core Lightning
Follow the below steps to install and setup RTL to run on Core Lightning.
### <aname="prereq"></a>Pre-requisites:
1. Functioning Core Lightning node. Follow install instructions on their [github](https://github.com/ElementsProject/lightning)
2. NodeJS - Can be downloaded [here](https://nodejs.org/en/download)
3. CLNRest - Ensure that core lightning's `CLNRest` API server is configured. Configuration instructions [here](https://docs.corelightning.org/docs/rest#configuration)
4. Create/reuse core-lightning's rune. Check [`createrune`](https://docs.corelightning.org/reference/lightning-createrune) and [`showrunes`](https://docs.corelightning.org/reference/lightning-showrunes) documentation for more details on how to create runes
4. Copy the `rune` and save it in a file which must be accessible to RTL. The content of the file must be `LIGHTNING_RUNE="<your-rune>"`
3. Cl-REST - Ensure that `cl-rest` API server is installed and running. Install instructions [here](https://github.com/Ride-The-Lightning/c-lightning-REST)
4. Copy the `access.macaroon` file from `cl-rest` to the device, on which RTL will be installed
### <aname="arch"></a>Architecture
![](../screenshots/RTL-CLN-Arch-2.png)
@ -33,7 +32,7 @@ To download from master (*not recommended*):
If there is an error with `upstream dependency conflict` message then replace `npm install --omit=dev` with `npm install --omit=dev --legacy-peer-deps`.
### <aname="prep"></a>Prep for Execution
RTL requires its own config file `RTL-Config.json`, to start the server and provide user authentication on the app
* Rename the file `Sample-RTL-Config.json` to `RTL-Config.json` located at`./RTL`
* Locate the complete path of the readable `.commando` file on your node
RTL requires its own config file `RTL-Config.json`, to start the server and provide user authentication on the app.
* Rename the file `Sample-RTL-Config.json` to `RTL-Config.json` located at`./RTL`..
* Locate the complete path of the readable `access.macaroon` from `cl-rest` on your node.
* Modify the RTL conf file per the example file below
Ensure that the follow values are correct per your config:
* `lnImplementation` - This should be `CLN`, indicating that RTL is connecting to a core lightning node
* `runePath` - Path of the folder including **filename** which contains the `rune` for the node. The content of the file must be `LIGHTNING_RUNE="<your-rune>"`
* `lnServerUrl` - complete url with ip address and port of the CLNRest server
* `multiPass` - Specify the password (in plain text) to access RTL. This password will be hashed and not stored as plain text
* `configPath` (optional) - File path of the core lightning config file, if RTL server is local to the core lightning server
* `lnImplementation` - This should be `CLN`, indicating that RTL is connecting to a core lightning node.
* `macaroonPath` - Path of the folder containing `access.macaroon` file from cl-rest server.
* `lnServerUrl` - complete url with ip address and port of the cl-rest server.
* `multiPass` - Specify the password (in plain text) to access RTL. This password will be hashed and not stored as plain text.
* `configPath` (optional) - File path of the core lightning config file, if RTL server is local to the core lightning server.
@ -20,18 +20,16 @@ This step is only required to configure the nodes, which will be remotely connec
2. Set `multiPass` to the preferred password. This password will be used to authenticate the user for RTL. Once authenticated, the user will be able to access all the nodes configured in the json file
3. Set the `port` to the preferred port number over which to run RTL
4. Set the `defaultNodeIndex` to configure the default start up node at server restart
5. `dbDirectoryPath` should be set to the folder where RTL's database will be saved.
6. `SSO` section can be used for single-sign-on from applications like BTCPayserver. If using RTL as a stand-alone app to connect with the nodes, keep the `rtlSSO=0` and ignore the rest of `SSO` section.
7. `nodes` section is a json array, with each element of the array representing the specific parameters for the LND node to connect with. `index` must be a number and start with 1. This number must be unique for each node in the array. For each element, two items need to be configured for each node on the network (`macaroonPath` and `lnServerUrl`).
8. `macaroonPath` should be set to the local path of the folder containing `admin.macaroon` file for each node. Each node must have a different folder for the `admin.macaroon` on the RTL server.
9. `swapMacaroonPath` should be set to the local path of the folder containing `loop.macaroon` file for loop.
10. `boltzMacaroonPath` should be set to the local path of the folder containing `admin.macaroon` file for boltz swaps.
11. `lnServerUrl` must be set to the service url for LND/Core Lightining REST APIs for each node, with the unique ip address of the node hosting LND/Core Lightning e.g. https://192.168.0.1:8080 OR https://192.168.0.1:3001. In this case the ip address of the node hosting LND/Core Lightning is '192.168.0.1'
12. `swapServerUrl` must be set to the swap service url. e.g. https://127.0.0.1:8081.
13. `boltzServerUrl` must be set to the boltz service url. e.g. https://127.0.0.1:9003.
14. `configPath` and `bitcoindConfigPath` are optional parameters which can be set only if the RTL is running locally on the same node. Else it can be set to "" or removed from the conf file all together.
15. `lnApiPassword` is mandatory if the ln implementation is ECL and configPath is missing. It is used to provide password for API authentication. It will be ignored in other ln implementations.
16. `runePath` is mandatory for CLN implementation. It should be set to the local path of the folder including filename containing rune value. This rune value in the file should be saved in `LIGHTNING_RUNE="your-rune"` format.
5. `SSO` section can be used for single-sign-on from applications like BTCPayserver. If using RTL as a stand-alone app to connect with the nodes, keep the `rtlSSO=0` and ignore the rest of `SSO` section.
6. `nodes` section is a json array, with each element of the array representing the specific parameters for the LND node to connect with. `index` must be a number and start with 1. This number must be unique for each node in the array. For each element, two items need to be configured for each node on the network (`macaroonPath` and `lnServerUrl`).
7. `macaroonPath` should be set to the local path of the folder containing `admin.macaroon` file for each node. Each node must have a different folder for the `admin.macaroon` on the RTL server.
8. `swapMacaroonPath` should be set to the local path of the folder containing `loop.macaroon` file for loop.
9. `boltzMacaroonPath` should be set to the local path of the folder containing `admin.macaroon` file for boltz swaps.
10. `lnServerUrl` must be set to the service url for LND/Core Lightining REST APIs for each node, with the unique ip address of the node hosting LND/Core Lightning e.g. https://192.168.0.1:8080 OR https://192.168.0.1:3001. In this case the ip address of the node hosting LND/Core Lightning is '192.168.0.1'
11. `swapServerUrl` must be set to the swap service url. e.g. https://127.0.0.1:8081.
12. `boltzServerUrl` must be set to the boltz service url. e.g. https://127.0.0.1:9003.
13. `configPath` and `bitcoindConfigPath` are optional parameters which can be set only if the RTL is running locally on the same node. Else it can be set to "" or removed from the conf file all together.
14. `lnApiPassword` is mandatory in the ln implementation is ECL and configPath is missing. It is used to provide password for API authentication. It will be ignored in other ln implementations.
logger.log({selectedNode:req.session.selectedNode,level:'INFO',fileName:'Channels',msg:'Peer Channels List With Aliases Received',data:body.channels});
logger.log({selectedNode:req.session.selectedNode,level:'DEBUG',fileName:'Channels',msg:'Forwarding History Received For Status '+req.query.status,data:body});
res.status(200).json(body);
}).catch((errRes)=>{
consterr=common.handleError(errRes,'Channels','Forwarding History Error',req.session.selectedNode);
logger.log({selectedNode:req.session.selectedNode,level:'DEBUG',fileName:'Channels',msg:'Paginated Forwarding History url'+options.url});
request.get(options).then((body)=>{
logger.log({selectedNode:req.session.selectedNode,level:'DEBUG',fileName:'Channels',msg:'Paginated Forwarding History Received For Status '+req.query.status,data:body});
res.status(200).json(body);
}).catch((errRes)=>{
consterr=common.handleError(errRes,'Channels','Paginated Forwarding History Error',req.session.selectedNode);
logger.log({selectedNode:req.session.selectedNode,level:'INFO',fileName:'Network',msg:'Network Fee Rates Received for '+req.params.feeRateStyle,data:body});
this.logger.log({selectedNode:clientExists.selectedNode,level:'INFO',fileName:'CLWebSocket',msg:'Disconnecting from the Core Lightning\'s Websocket Server..'});
logger.log({selectedNode:req.session.selectedNode,level:'INFO',fileName:'Payments',msg:'Payment Information Received for '+req.params.paymentHash,data:body});
logger.log({selectedNode:req.session.selectedNode,level:'ERROR',fileName:'RTLConf',msg:'Node config does not exist!',error:{error:'Node config does not exist.'}});
logger.log({selectedNode:req.session.selectedNode,level:'ERROR',fileName:'RTLConf',msg:'Node config does not exist!',error:{error:'Node config does not exist.'}});
logger.log({selectedNode:req.session.selectedNode,level:'INFO',fileName:'RTLConf',msg:'Selected Node Updated To '+req.session.selectedNode.lnNode||''});
returnthis.handleError({statusCode:401,message:'Session expired after a day\'s inactivity'},'Session Expired','Session Expiry Error',this.selectedNode);
returnthis.handleError({statusCode:401,message:'Session expired after a day\'s inactivity'},'Session Expired','Session Expiry Error',this.initSelectedNode);
this.logger.log({selectedNode:this.selectedNode,level:'INFO',fileName:'Common',msg:'Updated Node Options for '+req.session.selectedNode.lnNode,data:req.session.selectedNode.authentication.options});
this.logger.log({selectedNode:this.initSelectedNode,level:'INFO',fileName:'Common',msg:'Updated Node Options for '+req.session.selectedNode.ln_node,data:req.session.selectedNode.options});
this.logger.log({selectedNode:this.selectedNode,level:'ERROR',fileName:'Common',msg:'Common Set Options Error',error:err});
node.authentication.options ={
this.logger.log({selectedNode:this.initSelectedNode,level:'ERROR',fileName:'Common',msg:'Common Set Options Error',error:err});
node.options ={
url:'',
rejectUnauthorized:false,
json:true,
form:''
};
}
this.logger.log({selectedNode:this.selectedNode,level:'INFO',fileName:'Common',msg:'Set Node Options for '+node.lnNode,data:node.authentication.options});
this.logger.log({selectedNode:this.initSelectedNode,level:'INFO',fileName:'Common',msg:'Set Node Options for '+node.ln_node,data:node.options});
this.logger.log({selectedNode:this.selectedNode,level:'INFO',fileName:'Common',msg:'Please note that, RTL has encrypted the plaintext password into its corresponding hash'});
this.logger.log({selectedNode:this.initSelectedNode,level:'INFO',fileName:'Common',msg:'Please note that, RTL has encrypted the plaintext password into its corresponding hash'});
this.logger.log({selectedNode:this.selectedNode,level:'ERROR',fileName:'Common',msg:'Error in Channel Backup for Node '+node.lnNode,error:err});
if(node.ln_node){
this.logger.log({selectedNode:this.initSelectedNode,level:'ERROR',fileName:'Common',msg:'Error in Channel Backup for Node '+node.ln_node,error:err});
}
else{
this.logger.log({selectedNode:this.selectedNode,level:'ERROR',fileName:'Common',msg:'Error in Channel Backup for File '+channel_backup_file,error:err});
this.logger.log({selectedNode:this.initSelectedNode,level:'ERROR',fileName:'Common',msg:'Error in Channel Backup for File '+channel_backup_file,error:err});
}
}
else{
if(node.lnNode){
this.logger.log({selectedNode:this.selectedNode,level:'INFO',fileName:'Common',msg:'Successful in Channel Backup for Node '+node.lnNode,data:body});
if(node.ln_node){
this.logger.log({selectedNode:this.initSelectedNode,level:'INFO',fileName:'Common',msg:'Successful in Channel Backup for Node '+node.ln_node,data:body});
}
else{
this.logger.log({selectedNode:this.selectedNode,level:'INFO',fileName:'Common',msg:'Successful in Channel Backup for File '+channel_backup_file,data:body});
this.logger.log({selectedNode:this.initSelectedNode,level:'INFO',fileName:'Common',msg:'Successful in Channel Backup for File '+channel_backup_file,data:body});
}
}
});
},(err)=>{
this.logger.log({selectedNode:this.selectedNode,level:'ERROR',fileName:'Common',msg:'Error in Channel Backup for Node '+node.lnNode,error:err});
this.logger.log({selectedNode:this.initSelectedNode,level:'ERROR',fileName:'Common',msg:'Error in Channel Backup for Node '+node.ln_node,error:err});
this.errMsg=this.errMsg+'\nPlease set config path Or api password for node index '+node.index+' in RTL-Config.json! It is mandatory for Eclair authentication!';
this.logger.log({selectedNode:this.common.selectedNode,level:'ERROR',fileName:'Config',msg:'Something went wrong while creating backup file: \n'+err});
this.logger.log({selectedNode:this.common.initSelectedNode,level:'ERROR',fileName:'Config',msg:'Something went wrong while creating backup file: \n'+err});
}
}
}
catch(err){
this.logger.log({selectedNode:this.common.selectedNode,level:'ERROR',fileName:'Config',msg:'Something went wrong while creating the backup directory: \n'+err});
this.logger.log({selectedNode:this.common.initSelectedNode,level:'ERROR',fileName:'Config',msg:'Something went wrong while creating the backup directory: \n'+err});
this.logger.log({selectedNode:this.common.selectedNode,level:'ERROR',fileName:'Config',msg:'Something went wrong while creating log file '+log_file+': \n'+err});
this.logger.log({selectedNode:this.common.initSelectedNode,level:'ERROR',fileName:'Config',msg:'Something went wrong while creating log file '+log_file+': \n'+err});
this.logger.log({selectedNode:this.common.selectedNode,level:'ERROR',fileName:'Config',msg:'Something went wrong while configuring the node server: \n'+err});
this.logger.log({selectedNode:this.common.initSelectedNode,level:'ERROR',fileName:'Config',msg:'Something went wrong while configuring the node server: \n'+err});
this.logger.log({selectedNode:this.common.selectedNode,level:'INFO',fileName:'WebSocketServer',msg:'Disconnected due to '+code+' : '+websocket.clientId+', Total WS clients: '+this.webSocketServer.clients.size});
this.logger.log({selectedNode:this.common.initSelectedNode,level:'INFO',fileName:'WebSocketServer',msg:'Disconnected due to '+code+' : '+websocket.clientId+', Total WS clients: '+this.webSocketServer.clients.size});
this.logger.log({selectedNode:!selectedNode?this.common.selectedNode :selectedNode,level:'ERROR',fileName:'WebSocketServer',msg:'Invalid Node Selection. Previous and current node indices can not be less than zero.'});
this.logger.log({selectedNode:!selectedNode?this.common.initSelectedNode :selectedNode,level:'ERROR',fileName:'WebSocketServer',msg:'Invalid Node Selection. Previous and current node indices can not be less than zero.'});