From 9e2350a36837045baee4ad5eda01418a78df96f9 Mon Sep 17 00:00:00 2001 From: archipelago Date: Tue, 14 Jul 2026 10:02:16 -0400 Subject: [PATCH] =?UTF-8?q?fix(bitcoin):=20size=20prune=20to=20the=20data?= =?UTF-8?q?=20volume=20=E2=80=94=20never=20full-archive=20a=20small=20disk?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit write_bitcoin_conf silently produced an unpruned (~810GB and growing) config regardless of disk: the framework node got a full-archive chain on a 205GB volume. Volumes under 1.2TB now get prune = 25% of the volume, clamped to [550MB, 100GB]; bigger volumes keep full archive; unknown df results stay unpruned (don't surprise big iron). Framework node switched live to prune=50000. Co-Authored-By: Claude Fable 5 --- .../src/api/rpc/package/install.rs | 46 ++++++++++++++++++- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/core/archipelago/src/api/rpc/package/install.rs b/core/archipelago/src/api/rpc/package/install.rs index eef2e000..046ee746 100644 --- a/core/archipelago/src/api/rpc/package/install.rs +++ b/core/archipelago/src/api/rpc/package/install.rs @@ -1395,6 +1395,24 @@ impl RpcHandler { // the bridge get connection refused (fresh-install LND crash-loop + // bitcoin-rpc 502, seen on the 1.7.99 ISO). The port publish stays // 127.0.0.1-only on the host, so exposure is unchanged. + // Prune sized to the data volume. A full archive needs ~810 GB and + // grows; silently writing an unpruned config onto a small disk fills + // it mid-IBD (framework node 2026-07-14: unpruned mainnet on a 205 GB + // volume). Volumes with real archival headroom (≥1.2 TB) stay full + // archive; smaller ones get prune = 25% of the volume, clamped to + // [550 MB, 100 GB], leaving room for LND/apps sharing the disk. + let prune_line = match bitcoin_data_volume_gb().await { + Some(total_gb) if total_gb > 0 && total_gb < 1200 => { + let prune_mb = ((total_gb as f64 * 0.25 * 1024.0) as u64).clamp(550, 100_000); + info!( + volume_gb = total_gb, + prune_mb, "Data volume below archival size — enabling sized bitcoin prune" + ); + format!("prune={}\n", prune_mb) + } + _ => String::new(), + }; + let bitcoin_conf = format!( "\ # rpcauth: salted hash only - no plaintext password in config or CLI\n\ @@ -1405,8 +1423,9 @@ rpcallowip=0.0.0.0/0\n\ listen=1\n\ rpcthreads=16\n\ rpcworkqueue=256\n\ -printtoconsole=0\n", - rpcauth_line +printtoconsole=0\n\ +{}", + rpcauth_line, prune_line ); tokio::fs::create_dir_all(bitcoin_dir) .await @@ -2491,6 +2510,29 @@ async fn wait_for_adopted_container(package_id: &str, container_name: &str) -> R )) } +/// Total size (GB) of the filesystem holding the bitcoin data dir, via +/// `df -k`. None when df fails (containers, exotic mounts) — callers treat +/// unknown as "don't prune" to preserve archival defaults on big iron. +async fn bitcoin_data_volume_gb() -> Option { + let target = if std::path::Path::new("/var/lib/archipelago").exists() { + "/var/lib/archipelago" + } else { + "/" + }; + let output = tokio::process::Command::new("df") + .args(["-k", target]) + .output() + .await + .ok()?; + if !output.status.success() { + return None; + } + let stdout = String::from_utf8_lossy(&output.stdout); + let line = stdout.lines().nth(1)?; + let kb: u64 = line.split_whitespace().nth(1)?.parse().ok()?; + Some(kb / 1024 / 1024) +} + /// One-shot probe: does bitcoind answer an authenticated getblockchaininfo? /// Works during IBD (the call answers with progress while syncing). Goes via /// the host-published RPC port, which fails in exactly the same conditions