> ## 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.

# RLN Remote Signer

> Keep RGB Lightning Node keys out of the node with a VLS-backed external signer, in-process or as a separate signer daemon.

## Overview

RGB Lightning Node (RLN) can run in **external-signer mode**. In this mode the node holds no seed and no private keys. Every signing operation it needs — channel signing, node cryptography (ECDH, inbound payments, peer storage, offers), and RGB PSBT signing — is answered by a signer built on [Validating Lightning Signer (VLS)](https://gitlab.com/lightning-signer/validating-lightning-signer).

VLS does not sign blindly: it checks every request for protocol correctness and against its policy before producing a signature, and rejects anything that fails.

External-signer mode is part of the Utexo RLN ([#27](https://github.com/UTEXO-Protocol/rgb-lightning-node/pull/27), [#95](https://github.com/UTEXO-Protocol/rgb-lightning-node/pull/95)) and is used by the [Utexo WDK](/sdk/wdk-rgb-lightning) today. Like the rest of RLN it is in beta: on mainnet, RLN supports on-chain RGB operations only.

## Deployment modes

| Mode | Where the seed lives | Use when |
| - | - | - |
| **In-process signer** (`NativeExternalSigner`) | In the host application's secret storage (for example the WDK secret manager, Keystore, or Keychain). VLS runs inside the same process as the node. | Mobile and wallet integrations. This is the mode used by [`@utexo/wdk-rgb-lightning`](/sdk/wdk-rgb-lightning). |
| **Remote signer daemon** (`rln-signer-daemon`) | In a separate process, typically on a separate host. The node is watch-only and connects to the daemon over TCP. | Server deployments that isolate keys from the node host. |

Both modes share one signer contract ([`rln-external-signer`](https://github.com/UTEXO-Protocol/rln-external-signer)) and a VLS core patched for RGB channels.

## Validation flow

```text theme={null}
[RGB Lightning Node] --> proposes a transaction or state update
        |
        v
[VLS signer] --> checks protocol correctness + policy
        |
        +-- valid   --> returns signature
        |
        +-- invalid --> rejects request
```

Because the keys never reach the node:

* **Reduced attack surface:** a compromised node cannot read signing keys.
* **Policy enforcement:** the signer refuses requests that break protocol rules or its policy.
* **Self-custody:** keys stay in the environment you control.

## What changes in external-signer mode

| Operation | Behaviour |
| - | - |
| Asset issuance (NIA, CFA, IFA, UDA) and inflation | Not supported. Returns `UnsupportedInExternalSignerMode`. |
| `POST /burn` (BFA burn, from `v0.15.0-beta.3`) | Not supported. Returns `UnsupportedInExternalSignerMode`. |
| `POST /changepassword`, `POST /restore` | Not supported. Returns `UnsupportedInExternalSignerMode`. |
| `POST /backup` | Not available. File backups are encrypted with the node password, and an external-signer node has none. Use [VSS cloud backup](https://github.com/UTEXO-Protocol/rgb-lightning-node#vss-cloud-backup-optional) instead; it supports external-signer mode. |
| Authentication | Required. The node refuses to configure or unlock external-signer mode when started with `--disable-authentication` (`ExternalSignerRequiresAuthentication`). Use [Biscuit tokens](/rgb-lightning-node/self-hosted-rgb-lightning-node#authentication). |
| Restart | The same signer must be attached. A different signer identity is rejected with `ExternalSignerMismatch` before any channel state is read. |
| Secrets on the node | None. The node stores only public signer metadata (`key_source.json`). |

## Run the remote signer daemon

The daemon and the node's remote-signer support are behind the `remote-signer` cargo feature. It is not enabled in the default build or the Docker image, so build from source:

```sh theme={null}
cargo build --release --locked --features remote-signer
# produces target/release/rgb-lightning-node and target/release/rln-signer-daemon
```

<Steps>
  <Step title="Start the daemon">
    ```sh theme={null}
    rln-signer-daemon \
      --seed-file /secure/signer/seed.hex \
      --network bitcoin \
      --listen-addr 127.0.0.1:9737
    ```

    A new seed is generated (file mode `0600`) if `--seed-file` does not exist. The daemon keeps its VLS state in `--data-dir` (default: a `signer-db` directory next to the seed file). **Persist this directory** — it is what lets a restarted daemon keep signing for existing channels.

    `--network` accepts `bitcoin`, `testnet`, `signet`, or `regtest`.
  </Step>

  <Step title="Print the signer identity">
    ```sh theme={null}
    rln-signer-daemon --seed-file /secure/signer/seed.hex --network bitcoin --print-bootstrap
    ```

    This prints the daemon's bootstrap identity as JSON and exits.
  </Step>

  <Step title="Start the node pointing at the daemon">
    ```sh theme={null}
    rgb-lightning-node dataldk0/ \
      --daemon-listening-port 3001 \
      --ldk-peer-listening-port 9735 \
      --network mainnet \
      --root-public-key <YOUR_PUBLIC_KEY> \
      --remote-signer-addr 127.0.0.1:9737
    ```
  </Step>

  <Step title="Initialise external-signer mode">
    Send the bootstrap JSON from step 2 to the locked node:

    ```sh theme={null}
    curl -X POST http://localhost:3001/initexternalsigner \
      -H "Authorization: Bearer <ADMIN_TOKEN>" \
      -H "Content-Type: application/json" \
      -d @bootstrap.json
    ```

    The node probes the daemon at `--remote-signer-addr` and rejects the request if the submitted identity does not match the live daemon.
  </Step>

  <Step title="Unlock">
    Call `POST /unlock` as usual. In external-signer mode the `password` field is not checked; the Biscuit token is the credential that protects the node.
  </Step>
</Steps>

## Securing the signer link

| Side | Setting |
| - | - |
| Daemon | `--tls-cert` and `--tls-key` enable TLS. The certificate SAN must include `rln-remote-signer`. `--client-ca` enables mTLS and authenticates the node. |
| Daemon | A non-loopback `--listen-addr` is refused unless mTLS is configured. `--allow-unauthenticated-remote-signer` overrides this; use it only when the link is already secured by other means, such as a WireGuard tunnel. |
| Node | Place `ca.pem` in `<data-dir>/remote-signer-tls/` to verify the daemon certificate. Add `client.pem` and `client.key` to the same directory for mTLS. |

<Warning>
  If `remote-signer-tls/ca.pem` is missing, the node connects to the daemon **without TLS**. Always provision the TLS directory when the node and the signer are not on the same host.
</Warning>

<Warning>
  `--permissive` relaxes the VLS policy for development and testing. Never use it with real funds; the daemon refuses it on mainnet.
</Warning>

## Related

* [Self-Hosted RGB Lightning Node](/rgb-lightning-node/self-hosted-rgb-lightning-node)
* [RLN Quick Start](/rgb-lightning-node/quickstart)
* [wdk-rgb-lightning Reference](/sdk/wdk-rgb-lightning)


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