Core Lightning with BLAKE2b block header and unified signature support, packaged for StartOS. Unofficial, not audited.
  • TypeScript 97.9%
  • Dockerfile 2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-10-22 17:28:16 -04:00
.github/workflows build both arch s9pks in CI with pinned tools and drop the inherited release workflows 2026-10-20 20:58:14 -04:00
assets chore: adopt syncNext workflow and .gitkeep convention 2026-08-16 22:26:21 -06:00
clboss@49ffd76ad0 chore: bump CLBOSS to 0.16.3, drop the unused plugins submodule; 26.6.6:11 → 26.6.6:12 (#189) 2026-08-24 10:14:34 -06:00
rust-teos@be344ecc52 chore: update Core Lightning to 26.06.7, lift offline mode, bump TEOS watchtower; 26.6.6:13 → 26.6.7:0 (#192) 2026-08-28 12:17:49 -06:00
startos release #blake:26.6.9:3 2026-10-22 17:16:38 -04:00
.dockerignore build: filter the docker build context [skip ci] 2026-08-24 09:43:37 -06:00
.gitignore initial commit 2025-07-17 12:20:49 -06:00
.gitmodules chore: bump CLBOSS to 0.16.3, drop the unused plugins submodule; 26.6.6:11 → 26.6.6:12 (#189) 2026-08-24 10:14:34 -06:00
.prettierignore chore: bump CLBOSS to 0.16.3, drop the unused plugins submodule; 26.6.6:11 → 26.6.6:12 (#189) 2026-08-24 10:14:34 -06:00
AGENTS.md Say services have not adopted BLAKE2b rather than naming a SHA256d chain 2026-10-21 23:45:46 -04:00
CLAUDE.md docs: standardise package doc set and expand UPDATING.md 2026-05-19 19:06:40 -06:00
Dockerfile release #blake:26.6.9:3 2026-10-22 17:16:38 -04:00
Dockerfile.ui Say services have not adopted BLAKE2b rather than naming a SHA256d chain 2026-10-21 23:45:46 -04:00
icon.svg svg icon 2026-03-03 13:02:55 -07:00
instructions.md Describe BLAKE2b as Bitcoin's proof of work, not a separate chain 2026-10-21 23:36:10 -04:00
LICENSE add LICENSE in place of symlink 2025-09-22 14:23:38 -06:00
Makefile chore: migrate to start-sdk 2.0 and bump Core Lightning to 26.06.6 (#173) 2026-07-22 18:20:39 -06:00
package-lock.json chore: update Core Lightning to 26.06.7, lift offline mode, bump TEOS watchtower; 26.6.6:13 → 26.6.7:0 (#192) 2026-08-28 12:17:49 -06:00
package.json chore: dedupe start-sdk via npm overrides; 26.6.6:2 → 26.6.6:3 2026-07-25 00:50:37 -06:00
README.md release #blake:26.6.9:3 2026-10-22 17:16:38 -04:00
tsconfig.json chore: migrate to start-sdk 2.0 and bump Core Lightning to 26.06.6 (#173) 2026-07-22 18:20:39 -06:00
UPDATING.md Describe BLAKE2b as Bitcoin's proof of work, not a separate chain 2026-10-21 23:36:10 -04:00

Core Lightning Logo

Core Lightning on StartOS

Everything not listed in this document should behave the same as upstream Core Lightning. If a feature, setting, or behavior is not mentioned here, the upstream documentation is accurate and fully applicable — see the Documentation section of instructions.md for links.

Core Lightning is a Lightning Network node implementation. This package builds it with the Sling plugin built into the image and runs a web UI alongside it. It does not include a watchtower: The Eye of Satoshi's tower cannot read BLAKE2b block headers, so the watchtower returns once it supports BLAKE2b.

This is the #blake flavor of the package. It builds Core Lightning v26.06.9 with support for the 164-byte BLAKE2b block header, which the stock build cannot parse: a stock node stops at the activation block. It also signs wallet transactions and new channels with the opt-in SIGHASH_UNIFIED digest, so a channel funded past activation from coins received past activation is signed in a way a node that has not upgraded rejects.

See BLAKE2b Hard Fork Support for what that changes, including the peers you can connect to and the coins you should fund channels from.


Table of Contents


Image and Container Runtime

Two images. The node's is built here: a signed release tarball is unpacked onto a slim Debian base and the Sling plugin is added; the web UI's is pulled as published. lightningd comes from the v26.06.9-blake2b.9 release tarball of privkeyio/lightning, with its checksum pinned in the lightningd-dist stage and taken from a GPG-verified manifest. bitcoin-cli is pinned and checksummed the same way in the bitcoin-cli stage: plugin-bcli and the check-synced health check both exec it, and the image it used to come with no longer supplies it.

Property Value
Images Built from Dockerfile on debian:bookworm-slim, plus ghcr.io/elementsproject/cln-application
Architectures x86_64, aarch64 — both images declare emulateMissingAs: 'aarch64'
Entrypoint lightningd with an explicit config path; the UI runs its own server

One plugin is dropped into the plugin directory at build time: sling (rebalancing), an upstream release binary pinned by SLING_VERSION and its sha256 in the Dockerfile. Nothing is fetched at runtime, so the image is self-contained.

Subcontainer Purpose
lightning-sub lightningd and every oneshot, the one to attach to
cln-application-sub The web UI, which talks to the node over its RPC and rune

Volume and Data Layout

One volume, holding everything.

Volume Mount Point Purpose
main /root/.lightning The lightning directory: config, the node's hsm_secret and channel database, the UI's own data, and store.json

Bitcoin's data directory is mounted read-only at /mnt/bitcoin, which is how lightningd reads its RPC cookie without a password ever being stored.

File Models

Three models. One is the node's own configuration, one is the web UI's, and one is StartOS-side state.

File Format Modelled Written by
/config INI Yes — FileHelper.raw Every init, the config actions, and watchHosts on address changes
/store.json JSON Yes — FileHelper.json Every init, the rescan action, main, and restore
/data/app/config.json JSON Yes — FileHelper.json Every init, and the Reset UI Password action

config

Enforced — rewritten whenever the package writes the file: network, bitcoin-datadir, bind-addr, grpc-port, grpc-host, and clnrest-protocol. bitcoin-rpcuser and bitcoin-rpcpassword are modelled as "must be absent" and deleted if present, because authentication is by the cookie read through the mount.

clnrest-protocol is the one enforced value that is an override rather than a constant: upstream defaults CLNrest to HTTPS, and this package forces plaintext. A Tor onion address already encrypts, and wallets reaching the node that way cannot validate a StartOS-issued certificate; LAN and clearnet callers still get TLS, terminated by StartOS at the edge. See Network Access and Interfaces.

Derived, and rewritten whenever the underlying address changes: proxy (Tor's SOCKS address), announce-addr (the onion and public addresses published on the peer interface, or your custom external host in place of the IPs), and bitcoin-rpcconnect / bitcoin-rpcport. Editing any of these by hand does not stick.

Everything else — alias, color, fee policy, channel minimums, the plugin selection, the experimental flags — is yours, through the config actions. The only override install makes is switching clnrest on.

Two interactions are worth knowing because they produce a state neither setting explains alone. A custom external host is dropped rather than written while Tor Only is enabled: always-use-proxy disables lightningd's DNS resolution, and an announce-addr it cannot resolve is a fatal startup error rather than a warning — so the package omits it and raises a health check saying so. And enabling the Clams websocket adds a second ws:: bind address rather than replacing the first.

store.json

customExternalHosts is the announced address override, and rescan and restore are one-shot request flags.

Those two flags are deliberately not cleared when main reads them. A session where lightningd never comes up must not consume a request, or it vanishes silently — which is how a rescan requested during a crash loop used to be lost. A oneshot clears them only once the node answers RPC, and main ignores that clearing write so it does not bounce the service.

config.json

The web UI's config.json carries its display preferences and its password hash.

Dependencies

One, and it is required.

Dependency Kind Health checks Mount Why
Bitcoin running bitcoind, sync-progress /mnt/bitcoin, read-only Chain data, and the RPC cookie

Both health checks are required, not just "running": a node that is up but still syncing cannot serve a Lightning node correctly, and the sync state is surfaced again in this package's own check-synced.

Bitcoin's RPC address is resolved from its own binding over the service bridge, so nothing is configured by hand and a Bitcoin update does not move it. When Bitcoin is absent the address keys are cleared rather than left stale, and lightningd fails to connect until it returns.

The node additionally restarts when Bitcoin writes a replacement RPC cookie, but not when the cookie merely disappears — an absent cookie means Bitcoin is down, and stopping lightningd at that moment hangs its shutdown.

Network Access and Interfaces

Four interfaces always, and two more depending on what is enabled.

Interface Id Type Port Present
Web UI ui ui 4500 always
RPC rpc api 8080 always
Peer peer p2p 9735 always
gRPC grpc api 2106 always
CLNrest clnrest api 3010 when CLNrest is enabled (it is, at install)
Clams Websocket websocket api 7272 when the Clams remote websocket is enabled

gRPC is passed through, not terminated. The plugin performs its own mutual TLS, so StartOS must not terminate at the edge — doing so would present the device certificate and strip the client's. The binding is configured for raw passthrough deliberately; a conventional HTTPS binding would look correct and silently break client authentication.

CLNrest carries its own credential in the address. The interface's URL includes the rune the package generated, and its scheme is overridden to clnrest+https or clnrest+http so that a wallet reading the scheme knows which transport to use — a bare clnrest:// is assumed to be TLS, which would be wrong for the Tor address.

Installation and First-Run Flow

Install seeds the three models, switches CLNrest on, and starts the node — there is no wizard, and no credential is asked for. Wallet creation is lightningd's own: it generates hsm_secret on first start.

The one piece of setup the package performs is the web UI's credential. A oneshot creates a rune scoped to the application and records it alongside the node's public key, regenerating it only if the node's identity changes or the rune is missing. The UI cannot start until that has happened.

The ordering that matters is Bitcoin's: the node starts, but check-synced reports Bitcoin's progress and then its own until both are caught up, which on a fresh Bitcoin node is the length of an initial block download.

Actions

Ten actions. Three configure the node, and the rest are recovery and information.

Configuration — General Settings, Plugins, Experimental Features

Three actions writing /config, grouped together. Each writes only the fields it presents, costs seconds plus a restart, and is safe to re-run — the forms are pre-filled from the current file.

  • General Settings carries node identity, fee policy, channel minimums, Tor Only, the custom external host, and the CLNrest and Clams toggles. Two combinations produce a visible consequence rather than an error: Tor Only with a custom external host drops the host and raises a health check, and the Clams toggle changes the bind addresses.
  • Plugins toggles CLNrest and selects whether the built-in Sling plugin loads.
  • Experimental Features exposes upstream's experimental flags, which are not standardized across implementations and may break between releases.

Create Rune, Revoke All Runes

Create Rune mints an access credential for an external application, with the restrictions you specify. Available only while running; each run produces a new rune and does not affect existing ones.

Revoke All Runes invalidates every rune this node has issued including the web UI's, which is regenerated automatically on the next start. Run it when a credential may have been exposed. It is not selective — that is the point of it — so anything you have connected must be re-authorized afterwards.

Display BIP-39 Seed

Shows the seed words backing the on-chain wallet, for disaster recovery. Note what it is not: the seed alone cannot recover channel funds.

  • Visibility: hidden entirely when no wallet exists yet, and shown as disabled with an explanation on a node whose wallet predates BIP-39 seeds — such a wallet cannot be given one, and moving the funds to a fresh install is the only route.
  • Repeat safety: read-only.

Rescan Blockchain

Re-scans the chain for wallet outputs. Run it after a restore, or when an on-chain balance is missing.

  • Input: a depth from the tip, or an absolute block height written with a leading hyphen.
  • What it changes: sets the rescan request flag, which the next start turns into a lightningd argument and then clears.
  • Cost: hours. check-synced stays red for the duration; leave the node and Bitcoin running.
  • Repeat safety: safe to re-run. Because the flag is only consumed once the node answers RPC, a request survives a failed start rather than being silently dropped.

Reset UI Password

Sets a new password for the web UI, writing its config.json. It does not touch the node, its runes, or any external application's access.

Delete Gossip Store

Deletes the network gossip database, which the node rebuilds from peers. Run it if gossip is suspected corrupt.

  • Availability: only while stopped, since the file is open in use.
  • Cost: the node re-learns the network graph after starting, which takes time and affects routing until it does.
  • Repeat safety: idempotent.

Node Info

Read-only, running only: the node's identity and current state.

Tasks

One task, raised by a restore rather than at install.

Task Severity Raised when Cleared when
Rescan Blockchain important Immediately after a backup restore The action runs

The reason is that a restored node reports an on-chain balance of zero until the chain is rescanned, and nothing else in the interface explains why. important rather than critical: the node should keep running — indeed it must, for the rescan to proceed.

Health Checks

Three or four checks, plus one that appears only after a restore.

Check Displayed Method Present
lightningd "RPC Interface" lightning-cli getinfo succeeds always
cln-application "Web Interface" The UI's port is listening always
check-synced "Synced" getinfo's sync warnings, and Bitcoin's block count always
custom-external-host "Custom External Host" Always fails, with an explanation while Tor Only and a custom host conflict
restored "Backup Restoration Detected" Always fails, with an explanation after an emergency recovery

check-synced distinguishes three states, which is what makes it worth reading: Bitcoin not yet synced, the node catching up to Bitcoin (reported as a block count against Bitcoin's own), and synced. It fails only when lightning-cli itself errors, so a red check here is the node, not the chain.

Two checks are deliberate permanent failures, used as a way to say something the interface has nowhere else to put. custom-external-host reports that an announced address is being suppressed by Tor Only, and names both settings to change. restored reports that an emergency recovery has happened and that the node should be drained and reinstalled rather than kept in service — a state that is not a fault in the running software but is a serious one for the operator.

Backups and Restore

The main volume is copied wholesale — sdk.Backups.ofVolumes('main') — but the exclusions are the substance, because the channel database is deliberately not backed up.

  • Excluded: lightningd.sqlite3 and its write-ahead sidecars, the RPC socket, the gossip store, and the application log.
  • Included: hsm_secret, config, store.json, the emergency-recovery file, the UI's settings, and any watchtower data left by an earlier version, which holds its keys.

Restoring a Lightning node's channel database is dangerous — a stale copy claims a channel state the network has moved past — so this package does not restore one. What comes back is the node's identity and enough to recover funds, not a resumable node.

What a restore therefore does, automatically:

  1. The emergency-recovery file is copied aside before anything runs. Upstream's own plugin rewrites that file to describe the current channel set, so the restored copy is the last record able to reconstruct the pre-backup channels, and the copy is never touched again.
  2. emergencyrecover runs, and a permanently-failing health check appears saying what that means: all channels will be force-closed, funds swept on-chain, and the node should be drained and reinstalled afterwards rather than kept.
  3. Ten thousand wallet addresses are pre-generated. A restored database restarts the address counter at zero, and the node only recognises addresses within a fixed window past the highest known-used index — so without this, a rescan silently misses outputs beyond the first gap. The window this widens applies to every later rescan too.
  4. The Rescan Blockchain task is raised, because until it runs the on-chain balance reads zero.

BLAKE2b Hard Fork Support

Stock Core Lightning assumes every block header is 80 bytes and hashes it with SHA256d. The BLAKE2b hard fork changes the proof of work at an activation height: from that block on, headers are 164 bytes and hashed with BLAKE2b, signalled by the top bit of the version field.

This is one chain with continuous history, not a second chain. Every block below the activation height keeps its original 80-byte SHA256d header permanently, so both forms coexist and each must be hashed with its own algorithm. Core Lightning computes block ids itself while following the chain, so a stock node cannot parse the activation block and stops there rather than following the wrong chain.

This build reads both forms. It also signs wallet transactions and new channels with the fork's opt-in SIGHASH_UNIFIED digest, so a channel funded after the fork, from coins that are themselves post-fork, cannot be replayed onto the old rules.

Four consequences worth knowing:

  • The node will not connect to a peer that is still on the SHA256d rules. It advertises a required feature bit. That is deliberate: it stops you opening a channel with a peer that cannot follow the chain past activation. It also means you cannot cooperatively close a channel opened before activation with a counterparty still on the SHA256d rules.
  • The feature numbers are provisional. They are not registered BOLT allocations and are expected to move. Channels opened under the current numbering may have to be closed and reopened once they are settled.
  • Fund channels only from coins received past activation. A channel funded from a pre-activation UTXO has a funding transaction valid under both rule sets, which reopens the exposure unified signing exists to close.
  • Downgrading is refused. A build without unified signing computes a different signature hash and could not close the channels this one opens, so the package declares the downgrade impossible rather than letting you strand a channel.

Limitations and Differences

  1. A restore is a recovery, not a resumption. Channels are force-closed by design; plan to sweep the funds and reinstall.
  2. The channel database is excluded from backups, deliberately.
  3. CLNrest is served as plaintext by the node, with TLS added at the edge for LAN and clearnet only.
  4. gRPC cannot be reached through a TLS-terminating path, because the plugin authenticates clients with their own certificates.
  5. A custom external host is incompatible with Tor Only and is dropped while both are set.
  6. No watchtower. The Eye of Satoshi's tower cannot read BLAKE2b block headers, so neither the tower nor its client is shipped.
  7. Plugins are those built into the image. Adding another means changing the image, not dropping a file on the volume.
  8. No riscv64 build, and on hardware without a native image the aarch64 build runs emulated.

Quick Reference for AI Consumers

package_id: c-lightning
image: ./Dockerfile # on debian:bookworm-slim; plus ghcr.io/elementsproject/cln-application
architectures:
  - x86_64
  - aarch64
subcontainers:
  - lightning-sub # lightningd and every oneshot; the one to attach to
  - cln-application-sub # the web UI
volumes:
  main: /root/.lightning
file_models:
  - /root/.lightning/config
  - /root/.lightning/store.json
  - /root/.lightning/data/app/config.json
startos_managed_env_vars:
  - BITCOIN_NETWORK # web UI
  - LIGHTNING_DATA_DIR # web UI
  - APP_PROTOCOL # web UI
  - APP_HOST # web UI
  - APP_PORT # web UI
  - APP_CONFIG_FILE # web UI
  - APP_LOG_FILE # web UI
  - LIGHTNING_VARS_FILE # web UI
  - LIGHTNING_WS_PORT # web UI
  - LIGHTNING_REST_PORT # web UI
  - LIGHTNING_REST_PROTOCOL # web UI
  - LIGHTNING_GRPC_PORT # web UI
dependencies:
  - bitcoind # required; mounted read-only at /mnt/bitcoin
interfaces:
  ui: { type: ui, port: 4500 }
  rpc: { type: api, port: 8080 }
  peer: { type: p2p, port: 9735 }
  grpc: { type: api, port: 2106 } # TLS passthrough, not terminated
  clnrest: { type: api, port: 3010 } # when enabled; URL carries the rune
  websocket: { type: api, port: 7272 } # when the Clams websocket is enabled
actions:
  - config
  - plugins
  - experimental
  - createrune
  - revoke-runes
  - display-seed # hidden with no wallet; disabled on a pre-BIP-39 wallet
  - rescan-blockchain
  - reset-password
  - delete-gossip-store # only-stopped
  - node-info
tasks:
  - { action: rescan-blockchain, severity: important } # raised after a restore
health_checks:
  - lightningd # displayed "RPC Interface"
  - cln-application # displayed "Web Interface"
  - check-synced # displayed "Synced"
  - custom-external-host # only while Tor Only conflicts with a custom host
  - restored # only after an emergency recovery