Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions docs/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ nav:
- Testserver: reference/testserver.md
- Certificates: reference/certs.md
- Security: reference/security.md
- Signer Backups: reference/signer-backups.md
- LSP Integration: reference/lsp.md
- Webhooks: reference/webhooks.md
- Pairing: reference/pairing.md
Expand Down
116 changes: 116 additions & 0 deletions docs/src/reference/signer-backups.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# Signer Backups

Greenlight signers can keep a local copy of the VLS signer state for disaster recovery purposes. This backup is opt-in and disabled by default. The backups can be converted into CLN `recoverchannel` input, which can be used to restore channels in CLN if the Greenlight node is lost.

## Enable backups

Start the signer with a backup path:

```bash
glcli signer run --backup-path backup.json
```

By default, the signer writes a snapshot when a new recoverable channel appears.
For periodic snapshots, use:

```bash
glcli signer run \
--backup-path backup.json \
--backup-strategy periodic \
--backup-periodic-updates 10
```

The backup file is not created immediately at process startup. It is created
after a snapshot trigger, such as a new recoverable channel or the configured
periodic update threshold.

## Backup strategies

`never` disables automatic backups. Use this when you want to create backups

`new-channels-only` writes a snapshot when a channel
first becomes recoverable, which keeps disk writes low while still capturing the
data needed to recover that channel later.

`periodic` writes the initial snapshot for new recoverable channels and then
writes again after the configured number of recoverable channel updates. Use it
when you want the local file to track ongoing signer-state changes more closely,
at the cost of more frequent disk writes.

If a backup write fails, the signer logs the error and continues. Check signer
logs when relying on local backups.

## Inspect a backup

Use `inspect-backup` to verify that the file can be read and to list the
recoverable channel inventory:

```bash
glcli signer inspect-backup --path backup.json
```

For human-readable output:

```bash
glcli signer inspect-backup --path backup.json --format text
```

Channels with missing `peers/{peer_id}` signer-state entries are marked
incomplete. Incomplete channels remain visible, but they cannot be converted
into complete CLN recovery entries until an address is available.

## Convert for Core Lightning

!!!important
Use CLN recovery only as the last resort when the Greenlight node is lost. Running it in parallel with an active Greenlight node risks loss of funds.

Convert the signer backup to Core Lightning recovery input:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PLease add a warning that this is only ever to be done if the service goes down, and the signer MUST NEVER connect to Greenlight's hosted node ever, otherwise loss of funds may be inevitable.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sort of, actually the backup we are building here is the SCB equivalent, not a real resumable backup. That'd involve storing the shachain secrets, and other related secrets in lockstep wtih the node, which this PR does not implement.

So technically, could be safe, but only because CLN will have to immediately close the channels in the backup to recover the funds, which makes concurrent operations less risky, but still quite risky should the GL node not have been immobilized.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a warning


```bash
glcli signer convert-backup --path backup.json --format cln
```

To write the converted recovery request to a file:

```bash
glcli signer convert-backup \
--path backup.json \
--format cln \
--output cln-recoverchannel.json
```

The output is a CLN `recoverchannel` request body containing `scb` entries:

```json
{
"scb": ["<hex-encoded-static-channel-backup>"]
}
```

When VLS counterparty revocation secrets are present in the backup, the
converted CLN SCB entries include the shachain TLV. If that signer state is
absent, conversion still emits CLN recovery input without the shachain TLV.
Comment on lines +90 to +92
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very good, I think there isn't a whole lot missing if we have the shachain to just be able to resume the channels in their state without closing them.


Pass the generated `scb` array to CLN's `recoverchannel` RPC. The exact command
depends on the CLN RPC client you are using. Greenlight does not execute
recovery; `convert-backup` only prepares CLN recovery input.

If the backup contains incomplete channels and you still want to export the
complete ones, use:

```bash
glcli signer convert-backup \
--path backup.json \
--format cln \
--skip-incomplete
```

## Current limitations

- CLN conversion assumes current v1 channels where the channel id is derived
from the funding outpoint.
- The CLN shachain TLV is included only when VLS counterparty revocation
secrets are present in the backup.
- Missing `peers/{peer_id}` signer-state entries make affected channels
incomplete. This issue will gradually resolve as the signer receives peer
addresses during normal operation.
7 changes: 6 additions & 1 deletion libs/gl-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,16 @@ clap = { version = "4.5", features = ["derive"] }
dirs = "6.0"
env_logger = "0.11"
futures = "0.3"
gl-client = { version = "0.4", path = "../gl-client" }
gl-client = { version = "0.4", path = "../gl-client", features = ["backup"] }
hex = "0.4"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
thiserror = "2.0.11"
tokio = "1.43.0"
vls-core.workspace = true

[dev-dependencies]
tempfile = "3.10.1"

[badges]
maintenance = { status = "actively-developed" }
Loading
Loading