> ## Documentation Index
> Fetch the complete documentation index at: https://docs.utexo.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Self-Hosted RGB Lightning Node

> Run one self-hosted RGB Lightning Node for on-chain RGB operations and RGB-enabled Lightning payments.

The RGB Lightning Node (RLN) is Utexo's self-hosted runtime for both direct, on-chain RGB operations and RGB-enabled Lightning payments. A single RLN daemon and REST API provides both paths, so integrators do not need a second on-chain node service.

<Warning>
  Utexo supports on-chain RGB operations on mainnet. Lightning functionality is beta and testnet-only for now; from `v0.15.0-beta.3`, a mainnet node rejects Lightning API calls with `403 LightningUnsupportedOnMainnet`. Test your deployment on regtest or testnet before handling mainnet funds. The maintainers take no responsibility for loss of funds.
</Warning>

<Info>
  Source repository: [https://github.com/UTEXO-Protocol/rgb-lightning-node](https://github.com/UTEXO-Protocol/rgb-lightning-node)

  Full OpenAPI / Swagger reference: [https://utexo-protocol.github.io/rgb-lightning-node](https://utexo-protocol.github.io/rgb-lightning-node)
</Info>

## One node, two execution paths

| Path | RLN capabilities | Current Utexo support |
| - | - | - |
| **On-chain RGB** | Issue assets, create RGB invoices, transfer assets directly on Bitcoin, and inspect balances, transactions, transfers, and UTXOs | **Mainnet**; also available in test environments |
| **RGB over Lightning** | Connect peers, open RGB channels, create invoices, and send or route payments through Lightning | **Testnet only**; beta |

The `--network` option selects the Bitcoin network for the entire node. It does not change Utexo's support policy: use a mainnet deployment for on-chain RGB operations only. On testnet, you can exercise both the on-chain and Lightning paths through the same integration.

## Prerequisites

Before starting, make sure you have the following available:

| Dependency | Purpose |
| - | - |
| [**bitcoind**](https://github.com/bitcoin/bitcoin/tree/master/doc) (optional) | Chain backend for the `BlockSync` [sync mode](#chain-sync-modes). Not needed with `TransactionSync` |
| [**Electrum**](https://github.com/romanz/electrs) or [Esplora](https://github.com/Blockstream/esplora) indexer | Indexer forwarded to `rgb-lib` for RGB wallet operations |
| **RGB Proxy Server** | Relay for RGB state transitions (see [rgb-proxy-server](https://github.com/RGB-Tools/rgb-proxy-server)) |
| **Rust toolchain** (stable) | Needed to build from source |
| **Docker** (optional) | For the containerized setup and regtest helper scripts |

<Note>
  Chain-backend credentials are supplied in the `/unlock` request, not on the command line. See [Unlocking the node](#unlocking-the-node).
</Note>

## Installation

### Build from Source

Clone the repository with its submodules:

```sh theme={null}
git clone https://github.com/UTEXO-Protocol/rgb-lightning-node \
  --recurse-submodules --shallow-submodules
```

Install the `rgb-lightning-node` binary:

```sh theme={null}
cargo install --locked --path .
```

### Docker Image

Build the Docker image locally:

```sh theme={null}
docker build -t rgb-lightning-node .
```

## Running the Node

Each RLN daemon is started with `rgb-lightning-node` and requires the following arguments:

| Flag | Description |
| - | - |
| `<data-dir>` | Directory where node state is persisted |
| `--daemon-listening-port` | Port for the REST API |
| `--ldk-peer-listening-port` | Port for Lightning peer connections |
| `--network` | One of `regtest`, `signet`, `signetcustom`, `testnet`, `testnet4`, or `mainnet` |
| `--disable-authentication` | Skip token auth (development only) |
| `--root-public-key` | Public key used to verify Biscuit tokens (production) |
| `--config` | Path to a TOML configuration file (default: `<data-dir>/config.toml` if present). CLI options override file values |
| `--remote-signer-addr` | Address of a [remote signer daemon](/security/rln-remote-signer) (`host:port`). Requires a build with the `remote-signer` feature |

### Unlocking the node

After `POST /init`, every start requires `POST /unlock`. The body selects how LDK syncs with the chain through `ldk_chain_sync`, and sets the RGB wallet's indexer and transport:

```json theme={null}
{
  "password": "nodepassword",
  "ldk_chain_sync": {
    "mode": "BlockSync",
    "config": {
      "bitcoind_rpc_username": "user",
      "bitcoind_rpc_password": "password",
      "bitcoind_rpc_host": "localhost",
      "bitcoind_rpc_port": 18443
    }
  },
  "indexer_url": "127.0.0.1:50001",
  "proxy_endpoint": "rpc://127.0.0.1:3000/json-rpc",
  "announce_addresses": []
}
```

#### Chain sync modes

| Mode | `config` | Use when |
| - | - | - |
| `BlockSync` | `bitcoind_rpc_username`, `bitcoind_rpc_password`, `bitcoind_rpc_host`, `bitcoind_rpc_port` | You run a trusted bitcoind. More trust-minimized: the node does not rely on an indexer to tell it which transactions are relevant |
| `TransactionSync` | `indexer_url` (Electrum or Esplora) | You don't run bitcoind. The LDK indexer can differ from the RGB wallet's `indexer_url` |

`indexer_url` (the RGB wallet's indexer) is independent of the sync mode, so any indexer can be paired with either mode. `indexer_url` and `proxy_endpoint` can be omitted from the body when they are set in the `[chain]` section of the config file; if no indexer URL is set anywhere, unlock fails with `MissingIndexerUrl`.

### Regtest (Local Development)

Start the required Docker services (bitcoind, electrs, proxy):

```sh theme={null}
./regtest.sh start
```

Launch three nodes in separate shells:

```sh theme={null}
# Shell 1
rgb-lightning-node dataldk0/ \
  --daemon-listening-port 3001 \
  --ldk-peer-listening-port 9735 \
  --network regtest \
  --disable-authentication

# Shell 2
rgb-lightning-node dataldk1/ \
  --daemon-listening-port 3002 \
  --ldk-peer-listening-port 9736 \
  --network regtest \
  --disable-authentication

# Shell 3
rgb-lightning-node dataldk2/ \
  --daemon-listening-port 3003 \
  --ldk-peer-listening-port 9737 \
  --network regtest \
  --disable-authentication
```

**Regtest unlock values** (`BlockSync` config, `indexer_url`, `proxy_endpoint`):

```text theme={null}
bitcoind_rpc_username: user
bitcoind_rpc_password: password
bitcoind_rpc_host:     localhost
bitcoind_rpc_port:     18443
indexer_url:           127.0.0.1:50001
proxy_endpoint:        rpc://127.0.0.1:3000/json-rpc
```

#### Regtest Helper Commands

```sh theme={null}
# Fund a node — first get an address via POST /address, then:
./regtest.sh sendtoaddress <address> <amount>

# Mine blocks
./regtest.sh mine <blocks>

# Stop services and clean data
./regtest.sh stop

# Full help
./regtest.sh -h
```

### Regtest with Docker

To run a node inside Docker while using the shared regtest network:

```sh theme={null}
docker run \
  --rm -it \
  -p 3001:3001 \
  -v RLNdata1:/RLNdata \
  --network rgb-lightning-node_default \
  rgb-lightning-node \
  --daemon-listening-port 3001 \
  --ldk-peer-listening-port 9735 \
  --network regtest \
  --disable-authentication \
  RLNdata
```

Data is persisted in the `RLNdata1` volume. To start fresh:

```sh theme={null}
docker volume rm RLNdata1
```

When unlocking a node in this mode use:

```text theme={null}
bitcoind_rpc_host: bitcoind
bitcoind_rpc_port: 18443
indexer_url:       electrs:50001
proxy_endpoint:    rpc://proxy:3000/json-rpc
```

### Testnet3

No local Docker services needed — the node uses public infrastructure:

```sh theme={null}
rgb-lightning-node dataldk0/ \
  --daemon-listening-port 3001 \
  --ldk-peer-listening-port 9735 \
  --network testnet \
  --disable-authentication
```

**Testnet3 unlock values** (`BlockSync` config and `indexer_url`; `proxy_endpoint` only when using proxy transport):

```text theme={null}
bitcoind_rpc_username: user
bitcoind_rpc_password: password
bitcoind_rpc_host:     electrum.iriswallet.com
bitcoind_rpc_port:     18332
indexer_url:           ssl://electrum.iriswallet.com:50013
proxy_endpoint:        rpcs://proxy.iriswallet.com/0.2/json-rpc
```

### Testnet4

Same as testnet3, with the following differences:

```text theme={null}
--network testnet4
bitcoind_rpc_port: 18443
indexer_url:       ssl://electrum.iriswallet.com:50053
```

## On-chain RGB operations

The on-chain path uses the same RLN process and authentication model as the Lightning path. Use the following REST endpoints instead of integrating a separate on-chain service:

| Operation | Endpoints |
| - | - |
| Prepare the wallet | `POST /address`, `POST /btcbalance`, `POST /createutxos`, `POST /listunspents` |
| Issue assets | `POST /issueassetnia`, `POST /issueassetcfa`, `POST /issueassetifa`, `POST /issueassetuda` |
| Receive and inspect RGB assets | `POST /rgbinvoice`, `POST /decodergbinvoice`, `POST /listassets`, `POST /assetbalance` |
| Transfer and track RGB assets | `POST /sendrgb`, `POST /listtransfers`, `POST /refreshtransfers`, `POST /failtransfers` |
| Inspect on-chain activity | `POST /listtransactions` |
| Back up or restore node state | `POST /backup`, `POST /restore` |

RLN does not use xPub request headers or a client-side `*begin`/`*end` PSBT flow. Authenticate to RLN with Biscuit tokens as described below. If your custody model requires keys outside the node, run RLN in external-signer mode; see [Remote Signer](/security/rln-remote-signer).

For complete request and response schemas, see the [RGB Lightning Node API](/rgb-lightning-node/rgb-lightning-node-api).

## Authentication

RLN uses [Biscuit tokens](https://www.biscuitsec.org/) for API authentication. For production deployments **never** use `--disable-authentication`.

### One-Time Setup

Install the Biscuit CLI and generate a root keypair:

```sh theme={null}
cargo install biscuit-cli

# Generate a keypair (prints both public and private keys)
biscuit keypair

# Or export only the private key to a file
biscuit keypair --only-private-key > private-key-file

# Derive the public key later
biscuit keypair --from-file private-key-file --only-public-key
```

<Warning>
  Store your private key in a secret manager (e.g. HashiCorp Vault, AWS Secrets Manager). Anyone with the private key can mint valid tokens.
</Warning>

Start the node with the public key:

```sh theme={null}
rgb-lightning-node dataldk0/ \
  --daemon-listening-port 3001 \
  --ldk-peer-listening-port 9735 \
  --network testnet \
  --root-public-key <YOUR_PUBLIC_KEY>
```

### Minting Tokens

Three built-in roles are available:

**Admin** (full access):

```sh theme={null}
echo 'role("admin");' \
  | biscuit generate --private-key-file private-key-file -
```

**Read-only** (GET endpoints only):

```sh theme={null}
echo 'role("read-only");' \
  | biscuit generate --private-key-file private-key-file -
```

**Custom** (specific endpoints only):

```sh theme={null}
echo 'role("custom"); right("api", "/nodeinfo"); right("api", "/networkinfo");' \
  | biscuit generate --private-key-file private-key-file -
```

Add an expiry date to any token. Replace the timestamp with your own — the node rejects a token once its expiry has passed:

```sh theme={null}
echo 'role("admin"); check if time($t), $t <= 2027-12-31T00:00:00Z;' \
  | biscuit generate --private-key-file private-key-file -
```

For short-lived tokens, generate the timestamp at issue time rather than hardcoding it — `date -u -d '+30 days' +%Y-%m-%dT%H:%M:%SZ` with GNU coreutils, or `date -u -v+30d +%Y-%m-%dT%H:%M:%SZ` on macOS.

### Using Tokens

Pass the token in the `Authorization` header:

```sh theme={null}
curl -H "Authorization: Bearer <TOKEN>" \
  http://localhost:3001/nodeinfo
```

In the Swagger UI click the **Authorize** (lock) button, paste the token, and click **Authorize**.

### Revoking Tokens

To revoke a token before its expiry:

```sh theme={null}
curl -X POST \
  -H "Authorization: Bearer <ADMIN_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"token": "<TOKEN_TO_REVOKE>"}' \
  http://localhost:3001/revoketoken
```

## Using the REST API

Once a daemon is running, interact with it via its REST API. Example — issue an RGB asset:

```bash theme={null}
curl -X POST \
  -H "Content-Type: application/json" \
  -d '{"ticker": "USDT", "name": "Tether", "amounts": [666], "precision": 0}' \
  http://localhost:3001/issueassetnia
```

### Key Endpoints

| Endpoint | Method | Description |
| - | - | - |
| `/init` | POST | Initialise the node (creates or imports the mnemonic) |
| `/unlock` | POST | Unlock a locked node |
| `/lock` | POST | Lock the node |
| `/nodeinfo` | GET | Return node identity and status |
| `/networkinfo` | GET | Return network-level info |
| `/btcbalance` | POST | Get on-chain BTC balance |
| `/address` | POST | Generate a new Bitcoin address |
| `/openchannel` | POST | Open an RGB payment channel |
| `/closechannel` | POST | Close a channel |
| `/listchannels` | GET | List all channels |
| `/connectpeer` | POST | Connect to a Lightning peer |
| `/listpeers` | GET | List connected peers |
| `/lninvoice` | POST | Create a Lightning invoice |
| `/sendpayment` | POST | Pay a Lightning invoice |
| `/rgbinvoice` | POST | Create an RGB invoice |
| `/sendrgb` | POST | Send RGB assets on-chain |
| `/issueassetnia` | POST | Issue a new NIA RGB asset |
| `/issueassetcfa` | POST | Issue a CFA RGB asset |
| `/listassets` | POST | List all RGB assets |
| `/assetbalance` | POST | Get balance of a specific asset |
| `/backup` | POST | Create an encrypted backup |
| `/restore` | POST | Restore from an encrypted backup |
| `/shutdown` | POST | Gracefully stop the daemon |

<Info>
  The complete list of endpoints with request/response schemas is available in the interactive Swagger UI at [https://utexo-protocol.github.io/rgb-lightning-node](https://utexo-protocol.github.io/rgb-lightning-node).
</Info>

### Running a Local Swagger UI

You can also expose the OpenAPI spec locally:

```sh theme={null}
docker run -it \
  -p 8246:8080 \
  -e SWAGGER_JSON=/var/specs/openapi.yaml \
  -v $PWD/openapi.yaml:/var/specs/openapi.yaml \
  swaggerapi/swagger-ui
```

Open `http://localhost:8246` in your browser. If a daemon is running on one of the example ports you can call the APIs directly from the UI.

## Running Tests

Integration tests use the same regtest services as `regtest.sh` (they cannot run simultaneously):

```sh theme={null}
cargo test
```

## Production Checklist

<Steps>
  <Step title="Secure your private key">
    Store the Biscuit issuer private key in a dedicated secret manager. Never commit it to version control.
  </Step>

  <Step title="Enable authentication">
    Remove `--disable-authentication` and pass `--root-public-key` when starting the daemon.
  </Step>

  <Step title="Restrict network access">
    Expose only the daemon REST port (default `3001`) to trusted clients. Firewall the LN peer port (`9735`) as appropriate.
  </Step>

  <Step title="Persist data">
    Mount the node data directory on durable storage (e.g. a named Docker volume or a cloud disk) to survive container restarts.
  </Step>

  <Step title="Monitor the daemon">
    Poll `GET /nodeinfo` and `GET /networkinfo` periodically to verify the node is reachable and in sync.
  </Step>

  <Step title="Backup regularly">
    Call `POST /backup` on a schedule and store the encrypted backup off-node.
  </Step>

  <Step title="Rotate tokens">
    Issue short-lived tokens with expiry dates and rotate them regularly. Revoke compromised tokens immediately via `POST /revoketoken`.
  </Step>
</Steps>

## References

* [UTEXO RGB Lightning Node repository](https://github.com/UTEXO-Protocol/rgb-lightning-node)
* [OpenAPI Swagger UI](https://utexo-protocol.github.io/rgb-lightning-node)
* [RGB on the Lightning Network — docs.rgb.info](https://docs.rgb.info/lightning-network-compatibility)
* [RGB Proxy Server](https://github.com/RGB-Tools/rgb-proxy-server)
* [Biscuit token specification](https://www.biscuitsec.org/)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.