fix(umbrel): bundle bitcoin-cli and lncli for mode A/B without Lite fallback

PyBLOCK's mode A (Bitcoin + Lightning) and mode B (Bitcoin only) call
bitcoin-cli and lncli directly via subprocess. The Umbrel image did not
ship those binaries, so the startup validation in PyBlock.py:1898-1909
detected the empty CLI paths and silently redirected to Lite Mode against
public APIs — defeating the point of installing PyBLOCK on a node.

Per nmfretz's review on getumbrel/umbrel-apps#5258, this takes the
"bundle the binaries inside the PyBLOCK image" path (option 2):

- dockerfile: download bitcoin-cli (Bitcoin Core 28.1) and lncli (LND
  v0.20.1-beta, matching what Umbrel ships) for both linux/amd64 and
  linux/arm64. Verifies the Bitcoin Core SHA256SUMS. Real binaries land
  at /usr/local/bin/{bitcoin-cli,lncli}.bin.
- umbrel/{bitcoin-cli,lncli}-wrapper.sh: thin shell wrappers installed
  as /usr/local/bin/{bitcoin-cli,lncli} that exec the real binary with
  -rpcconnect/-rpcuser/-rpcpassword (or --rpcserver/--tlscertpath/
  --macaroonpath for lncli) injected from the BITCOIN_RPC_* / LND_*
  env vars Umbrel provides via APP_BITCOIN_* / APP_LIGHTNING_*. They
  fail loud if those env vars are missing.
- entrypoint.sh: default BITCOIN_CLI_PATH/LND_CLI_PATH to the wrapper
  locations when the relevant RPC host env vars are set and the wrapper
  is executable, so bclock.conf / blndconnect.conf get the right
  bitcoincli / ln paths automatically.
- umbrel/: bump image tag and app version to v4.0.2 with release notes.

Local smoke test on amd64:
  bitcoin-cli.bin --version -> Bitcoin Core RPC client version v28.1.0
  lncli.bin --version       -> lncli version 0.20.1-beta
  /usr/local/bin/bitcoin-cli (no env) -> fails with "BITCOIN_RPC_HOST must be set"
  /usr/local/bin/bitcoin-cli (env set) -> dispatches to the real binary

Image grows ~70MB (mostly the Go-built lncli).

Co-Authored-By: kulvex code <noreply@github.com>
This commit is contained in:
GaltRanch 2026-06-23 18:10:00 -03:00
parent 27a3ddef3a
commit ba5e9606db
6 changed files with 91 additions and 6 deletions

View file

@ -29,6 +29,32 @@ RUN apt-get update \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Install bitcoin-cli and lncli so PyBLOCK's mode A/B can talk to Umbrel's
# Bitcoin Core and LND containers over RPC/gRPC without a degraded Lite Mode
# fallback. The binaries are wrapped by umbrel/{bitcoin-cli,lncli}-wrapper.sh
# (installed below) which inject the connection details Umbrel injects via
# env vars.
ARG TARGETARCH
ARG BITCOIN_VERSION=28.1
ARG LND_VERSION=v0.20.1-beta
RUN set -eux; \
case "${TARGETARCH}" in \
amd64) BTC_ARCH=x86_64-linux-gnu; LND_ARCH=amd64 ;; \
arm64) BTC_ARCH=aarch64-linux-gnu; LND_ARCH=arm64 ;; \
*) echo "Unsupported TARGETARCH: ${TARGETARCH}" >&2; exit 1 ;; \
esac; \
cd /tmp; \
wget -q "https://bitcoincore.org/bin/bitcoin-core-${BITCOIN_VERSION}/bitcoin-${BITCOIN_VERSION}-${BTC_ARCH}.tar.gz"; \
wget -q "https://bitcoincore.org/bin/bitcoin-core-${BITCOIN_VERSION}/SHA256SUMS"; \
grep "bitcoin-${BITCOIN_VERSION}-${BTC_ARCH}.tar.gz" SHA256SUMS | sha256sum -c -; \
tar -xzf "bitcoin-${BITCOIN_VERSION}-${BTC_ARCH}.tar.gz" "bitcoin-${BITCOIN_VERSION}/bin/bitcoin-cli"; \
install -m 0755 "bitcoin-${BITCOIN_VERSION}/bin/bitcoin-cli" /usr/local/bin/bitcoin-cli.bin; \
rm -rf "bitcoin-${BITCOIN_VERSION}" "bitcoin-${BITCOIN_VERSION}-${BTC_ARCH}.tar.gz" SHA256SUMS; \
wget -q "https://github.com/lightningnetwork/lnd/releases/download/${LND_VERSION}/lnd-linux-${LND_ARCH}-${LND_VERSION}.tar.gz"; \
tar -xzf "lnd-linux-${LND_ARCH}-${LND_VERSION}.tar.gz" --strip-components=1 "lnd-linux-${LND_ARCH}-${LND_VERSION}/lncli"; \
install -m 0755 lncli /usr/local/bin/lncli.bin; \
rm -f lncli "lnd-linux-${LND_ARCH}-${LND_VERSION}.tar.gz"
RUN python3 -m venv /app/venv
ENV PATH="/app/venv/bin:$PATH"
@ -39,6 +65,13 @@ RUN pip install --no-cache-dir --upgrade pip \
COPY . /app/pyblock/
# Install the bitcoin-cli / lncli wrappers as the default CLI paths so any
# subprocess call to bitcoin-cli / lncli (including PyBLOCK's mode A/B menus)
# is transparently routed through RPC/gRPC against the Umbrel dependency
# containers. The real binaries live at /usr/local/bin/{bitcoin-cli,lncli}.bin.
RUN install -m 0755 /app/pyblock/umbrel/bitcoin-cli-wrapper.sh /usr/local/bin/bitcoin-cli \
&& install -m 0755 /app/pyblock/umbrel/lncli-wrapper.sh /usr/local/bin/lncli
# Entrypoint for auto-configuration
COPY entrypoint.sh /app/entrypoint.sh
RUN chmod +x /app/entrypoint.sh

View file

@ -16,6 +16,17 @@ if ! touch "$CONFIG_DIR/.writetest" 2>/dev/null; then
fi
rm -f "$CONFIG_DIR/.writetest"
# Default to the bundled bitcoin-cli / lncli wrappers when the caller hasn't
# overridden them. The wrappers route every CLI invocation through RPC/gRPC
# against the Umbrel Bitcoin Core and LND containers, so PyBLOCK's mode A/B
# work without a real local node binary on disk.
if [ -n "$BITCOIN_RPC_HOST" ] && [ -x /usr/local/bin/bitcoin-cli ]; then
export BITCOIN_CLI_PATH="${BITCOIN_CLI_PATH:-/usr/local/bin/bitcoin-cli}"
fi
if [ -n "$LND_HOST" ] && [ -x /usr/local/bin/lncli ]; then
export LND_CLI_PATH="${LND_CLI_PATH:-/usr/local/bin/lncli}"
fi
# Auto-generate Bitcoin config from env vars if set
if [ -n "$BITCOIN_RPC_HOST" ] && [ -n "$BITCOIN_RPC_USER" ]; then
BITCOIN_RPC_PORT="${BITCOIN_RPC_PORT:-8332}"

View file

@ -0,0 +1,21 @@
#!/bin/sh
# bitcoin-cli wrapper for Umbrel/Docker deployments.
#
# PyBLOCK's modes A/B call bitcoin-cli directly via subprocess. Inside the
# Umbrel container we connect to the host's Bitcoin Core (or Knots) over the
# Docker network using the credentials Umbrel injects through APP_BITCOIN_*
# env vars (re-exported by the entrypoint as BITCOIN_RPC_*). This wrapper
# turns every `bitcoin-cli` call into a properly-authenticated remote RPC
# call against that node.
set -e
: "${BITCOIN_RPC_HOST:?BITCOIN_RPC_HOST must be set}"
: "${BITCOIN_RPC_USER:?BITCOIN_RPC_USER must be set}"
: "${BITCOIN_RPC_PASS:?BITCOIN_RPC_PASS must be set}"
exec /usr/local/bin/bitcoin-cli.bin \
-rpcconnect="${BITCOIN_RPC_HOST}" \
-rpcport="${BITCOIN_RPC_PORT:-8332}" \
-rpcuser="${BITCOIN_RPC_USER}" \
-rpcpassword="${BITCOIN_RPC_PASS}" \
"$@"

View file

@ -7,7 +7,7 @@ services:
APP_PORT: 6969
web:
image: curly60e/pyblock:v4.0.1
image: curly60e/pyblock:v4.0.2
restart: on-failure
stop_grace_period: 1m
user: "1000:1000"

19
umbrel/lncli-wrapper.sh Normal file
View file

@ -0,0 +1,19 @@
#!/bin/sh
# lncli wrapper for Umbrel/Docker deployments.
#
# Mirrors umbrel/bitcoin-cli-wrapper.sh: PyBLOCK shells out to lncli for
# Lightning operations, so we turn every `lncli` call into one against the
# Umbrel LND container using the gRPC endpoint, TLS cert, and macaroon
# Umbrel injects through APP_LIGHTNING_* env vars (re-exported by the
# entrypoint as LND_*).
set -e
: "${LND_HOST:?LND_HOST must be set}"
: "${LND_TLS_CERT_PATH:?LND_TLS_CERT_PATH must be set}"
: "${LND_MACAROON_PATH:?LND_MACAROON_PATH must be set}"
exec /usr/local/bin/lncli.bin \
--rpcserver="${LND_HOST}:${LND_GRPC_PORT:-10009}" \
--tlscertpath="${LND_TLS_CERT_PATH}" \
--macaroonpath="${LND_MACAROON_PATH}" \
"$@"

View file

@ -2,7 +2,7 @@ manifestVersion: 1
id: pyblock
category: bitcoin
name: PyBLOCK
version: "4.0.1"
version: "4.0.2"
tagline: Terminal-based Bitcoin & Lightning node dashboard
description: >-
PyBLOCK is a cyberpunk-aesthetic Bitcoin dashboard that runs in your
@ -40,9 +40,10 @@ defaultUsername: ""
defaultPassword: ""
deterministicPassword: false
releaseNotes: >-
v4.0.1: Fix permission errors on Umbrel by pinning the container user
to UID/GID 1000, matching the user enforced by docker-compose. Adds a
startup writability check that fails fast with a clear message when
the bind-mounted config directory is not writable.
v4.0.2: Bundle bitcoin-cli and lncli inside the image, wrapped to inject
the RPC/gRPC connection details Umbrel provides via APP_BITCOIN_* and
APP_LIGHTNING_* env vars. Modes A (Bitcoin + Lightning) and B (Bitcoin
only) now connect to the Umbrel dependency containers directly instead
of falling back to Lite Mode at startup.
submitter: curly60e
submission: ""