Replace all dynamic .split() patterns in subprocess calls with safe
alternatives: shlex.split(), explicit list args, and _run_btc/_run_ln
helpers in PyBlock.py. Covers PyBlock, block_visualizer, clockscript,
lastblockdetail, mempoolclock, nodeconnection, and ai/context.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. Shell injection in readHexBlock/readHexTx (PyBlock.py):
- Validate user input with hex-only regex before use
- Replace shell=True pipe chain with subprocess list + piped stdin
- Same fix for OP_RETURN loop TX decoding
2. Shell injection in weather commands (ppi.py):
- Replace curl shell commands with requests.get()
- User input (city, lang, unit) no longer touches shell
- Upgraded from HTTP to HTTPS
3. Runtime crash in SPV/spvblock.py:
- os.path.isfile() called with 2 args (TypeError)
- Fixed to use 'and' for two separate checks
4. Config files added to .gitignore:
- pybitblock/config/*.conf (RPC creds, API keys, tokens)
- pybitblock/SPV/config/*.conf
- *.log files
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Centralize bitcoin-cli subprocess calls into a single _run_cli()
function with nosemgrep annotation. The cli path is already
sanitized via shlex.split() before reaching this function.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add cyan separator lines (────) before and after AI responses
- Change prompt to 'pyblock>' in yellow to distinguish from AI text
- Balance shown below the closing separator in dim
- Add UTF-8 env vars to entrypoint.sh for ttyd/Docker contexts
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rich Console renders to a StringIO buffer, then the result is
encoded as UTF-8 bytes and written directly to sys.stdout.buffer,
bypassing Python's stdout encoding which may not be UTF-8 in all
launch contexts (ttyd, Docker, pipes).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reconfigure sys.stdout to UTF-8 when the ai module loads, ensuring
accented characters (á, é, ñ, ¡, ¿) render correctly regardless
of how PyBLOCK was launched.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Force Rich Console to use UTF-8 output encoding so Spanish
accented characters render correctly in the terminal.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use shlex.split() to safely parse bitcoincli path before passing
to subprocess.run(), same pattern as clock/data.py fix.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The status line after 'U' (usage) showed the old balance instead
of refreshing it from the API. Now calls get_balance() before
displaying the status line.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Major UX improvements inspired by KCode's terminal rendering:
- Continuous chat flow — no screen clearing between messages,
conversation scrolls naturally like a real chat
- Rich Markdown rendering for AI responses — proper tables,
code blocks with syntax highlighting, headers, bold, bullets
- Remove "Press Enter to continue" interruption from chat loop
- Compact status line showing balance + commands inline
- Ctrl+C returns to main menu cleanly
- Balance updates shown inline after each response
- Context refreshed on each query for up-to-date node data
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Client tries the public URL first (api.astrolexis.space), and
falls back to localhost:10400 on 404 or connection errors. This
handles CDN cache issues and provides resilience when the gateway
runs on the same machine as PyBLOCK.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
A function with yield is always a generator in Python, so
chat() with stream=False was returning a generator instead
of a dict. Split into chat() for non-streaming (returns dict)
and _stream_chat() for streaming (yields SSE chunks).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Document the Stripe checkout flow for token generation:
astrolexis.space/pyblock → Stripe → webhook → token on success page.
Remove token generation from open questions (now resolved).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New pybitblock/ai/ package integrating with Astrolexis AI Gateway
at https://api.astrolexis.space:
- client.py: API client for auth, top-up (Lightning), chat (SSE
streaming), and usage tracking
- context.py: Gathers Bitcoin/Lightning node data (via CLI, RPC,
or mempool.space API) for AI context injection
- ui.py: Terminal chat interface with conversation history,
Lightning top-up flow with QR codes, usage stats display,
and first-time token setup
Accessible from Main Menu as "I - AI Assistant". All queries go
through Astrolexis gateway — user pays in sats via Lightning.
Token stored in pyblocksettings.conf.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove 1000-sat Lightning invoice paywalls from LNBits, LNPay, and
OpenNode API integrations. All three now go directly to config setup
(same flow as TippinMe/TallyCoin which were already free).
Changes:
- Replace aaccPPiLNBits/LNPay/OpenNode() payment loops with direct
config-or-setup logic in both PyBlock.py and SPV/spvblock.py
- Change all menu labels from PAID/PREMIUM/LOCKED to FREE
- Remove LNURL file existence checks (lnbitSN.conf gates)
- Remove ~400 lines of payment invoice generation, QR display,
and payment polling code
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ColdCore was non-functional due to literal '$HOME' paths that never
expanded, making all file checks always fail. The upstream project
(jamesob/coldcore) is experimental/alpha and requires Coldcard
hardware, limiting its audience.
Removed: callColdCore() function, menu entry "I" (ColdCore), and
handlers from PyBlock.py, SPV/spvblock.py, and umbrel-app.yml.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The clock commit (8e27372) inadvertently reverted Rich Columns menus
(Bitcoin submenu, Lightning, API menus) back to plain ANSI format.
Restored from commit 3062a34 and reapplied clock changes on top:
- artist()/design() delegation to clock.run_clock()
- mainmenuControl unified clock launch
- menuSelection()/menuSelectionLN() using cfg singleton
- Settings D option with clockDisplaySettings()
- TUI startup using cfg.intro_mode
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use shlex.split() instead of str.split() for bitcoin-cli commands
to prevent command injection via crafted config values
- Remove partial threading.Lock usage that only guarded writes but
not reads, relying on Python's GIL for atomic attribute assignment
- Remove unused threading import (Lock)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The hash_art() function was hardcoding pad based on 80 columns.
Now accepts term_width parameter and the renderer passes the
real terminal width for proper centering.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Miner pool tag: shows who mined the last block (coinbase decode for local/remote, API for lite)
- Block weight meter: colored fullness bar (green/yellow/red)
- Block time histogram: sparkline of last 14 block intervals with color-coded speed + streak detection
- Peer count: network connections indicator with health coloring
- Moon phase: current lunar phase emoji + name
All toggleable via Settings → D (Clock Display Settings).
Also fixes negative countdown timer (clamp to 0) and countdown row tracking bug.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New clock/ package replaces the old design() polling loop with a
flicker-free ANSI cursor-positioned renderer. Features include:
countdown timer, epoch/halving progress bar, fee rate indicator,
hashrate sparkline, matrix mining animation, odometer digit transition,
heartbeat pulse, zen mode, UTC time display, fireworks on milestone
blocks, generative hash art, and configurable sound modes.
All features are toggleable via Settings → D (Clock Display Settings).
Also fixes hardcoded config paths in menuSelection(), menuSelectionLN(),
and TUI startup to use the cfg singleton instead.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Install python3-dev, libgmp-dev, libffi-dev needed to compile
psutil and fastecdsa on ARM64 (Raspberry Pi / Umbrel).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- entrypoint.sh: PYBLOCK_MODE env var now always overwrites intro.conf
(previously skipped if file existed from a previous run)
- requirements.txt: Comment out vanity-address (not available on all
platforms, blocks Docker build)
Tested Docker build with simulated Umbrel env vars:
- Bitcoin RPC auto-config: OK
- LND auto-config: OK
- Mode detection: OK
- Health check (port 6969): HTTP 200
- Config file generation: verified inside container
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When only LND_TLS_CERT_PATH was set (without LND_HOST), the entrypoint
produced ":10009" as ip_port, which is syntactically valid but unusable.
Now matches Python _env_lnd_config behavior: empty string when host is
missing, only builds host:port when LND_HOST is explicitly provided.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- icon.svg: 256x256 cyberpunk Bitcoin icon with animated cursor,
grid lines, Bitcoin B symbol gradient, and PyBLOCK text
- README.md: Testing instructions, submission steps, env var reference,
and multi-arch Docker build commands
Gallery screenshots (1.jpg, 2.jpg, 3.jpg) to be added after
capturing from a running instance.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dockerfile changes:
- Use COPY instead of git clone (build from local source)
- Add entrypoint.sh for auto-configuration from env vars
- EXPOSE 6969 and configurable via PYBLOCK_PORT env var
- Create config directory as volume mount point
- ENTRYPOINT replaces CMD for pre-launch setup
New .github/workflows/docker-build.yml:
- Multi-arch build: linux/amd64 + linux/arm64 (Raspberry Pi)
- Triggers on version tags (v*.*.*)
- Pushes to Docker Hub as curly60e/pyblock:version and :latest
- Uses QEMU + Buildx for cross-compilation
- GitHub Actions cache for faster builds
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
umbrel/docker-compose.yml:
- app_proxy service for Umbrel authentication
- Web service with ttyd on port 6969
- Volume mounts: persistent config + read-only LND data
- All Bitcoin RPC and LND env vars from Umbrel's injection
- Auto-mode set to A (Bitcoin + Lightning)
umbrel/umbrel-app.yml:
- App manifest with full description and feature list
- Category: bitcoin
- Dependencies: bitcoin, lightning
- Port: 6969
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Shell script that runs before PyBLOCK to:
- Auto-generate bclock.conf from BITCOIN_RPC_* env vars
- Auto-generate blndconnect.conf from LND_* env vars
- Auto-detect mode (A/B/C) from available services
- Create default settings files if missing
- Launch ttyd with configurable port (PYBLOCK_PORT)
- Support optional ttyd auth (PYBLOCK_TTYD_AUTH)
Enables zero-config deployment: set env vars → configs generated → app starts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Config now reads these env vars (with priority over config files):
Bitcoin Core:
BITCOIN_RPC_HOST, BITCOIN_RPC_PORT, BITCOIN_RPC_USER, BITCOIN_RPC_PASS
BITCOIN_CLI_PATH
Lightning (LND):
LND_HOST, LND_GRPC_PORT, LND_TLS_CERT_PATH, LND_MACAROON_PATH
LND_CLI_PATH
Mode:
PYBLOCK_MODE (A=Bitcoin+Lightning, B=Bitcoin, C=Lite)
When env vars are set, config files are auto-generated for consistency.
This enables zero-config deployment on Umbrel where credentials are
injected via docker-compose environment.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bring back the Panel borders for sysinfo, status bar, header, and menu
but use style='on default' which inherits the terminal's background
color instead of Rich's default dark gray. border_style='dim' keeps
the borders subtle.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tables pad columns with spaces that show a different background color.
Switch sysinfo and menu to console.print() with markup strings instead,
which render with the terminal's native background color.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace Panel() wrappers with plain text output for status_bar and
header — Panels create a visible background box that clashes with
dark terminal themes. Error/warning panels kept as-is since they
should visually stand out.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use transparent backgrounds instead of forced dark grays in panels
- Replace ░ block chars with ─ dashes for progress bar empty space
- Set console highlight=False to prevent unwanted auto-styling
- Remove pyblock.dim style from panels (used default dim instead)
Rich elements now blend seamlessly with the terminal's own background.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- artist(): catch KeyboardInterrupt explicitly so Ctrl+C breaks the
block display loop and returns to caller
- main(): change KeyboardInterrupt handler from sys.exit(0) to
continue, which loops back to menuSelection()
Users can now press Ctrl+C to exit any screen and return to the menu.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When bitcoincli is empty, go straight to Lite Mode instead of trying
remote mode (which also fails without tls/macaroon). Also validate
remote mode has tls configured before attempting connection.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
codecs is used in 10+ places for macaroon encoding but was previously
available only via star imports that were removed in the security audit.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace mount/remove_children pattern (async issues, duplicate IDs)
with a single persistent #section-view Static widget that gets its
content updated via .update(). All sections now render correctly:
- Dashboard: summary table + latest blocks (fetched async from API)
- Bitcoin/Lightning/Platforms/Settings: info panels
- M key: return to main menu
Also added fetch_latest_blocks() and fetch_hashrate() data workers.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Each menu key now loads real content:
- A (Dashboard): block height, price, hashrate, difficulty, mempool
stats, and latest 5 blocks table — all from mempool.space API
- B (Bitcoin): submenu with blockchain features
- L (Lightning): channel management overview
- P (Platforms): API integrations list
- S (Settings): current mode and config info
- M: return to main menu from any section
New data fetchers: fetch_latest_blocks(), fetch_hashrate(),
fetch_mempool_info() expanded.
Dashboard loads async via run_worker(thread=True) with live
content replacement in the center panel.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
MainMenuScreen was a Textual Screen mounted inside a Vertical container,
which doesn't render. Create MainMenu as a Static widget instead:
- New tui/widgets/main_menu.py with Panel+Table menu
- Move keybindings (A/B/L/P/S/Q) to the App level
- Menu now renders correctly in the content area
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
intro.conf can contain either a plain string ("A"/"B"/"C") or a dict
with keys like fullbtclnd/fullbtc. Handle both formats when detecting
the mode for TUI launch.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move SPV.spvblock import inside the functions that call
MainMenuCROPPED() instead of top-level, preventing circular
dependency issues when SPV modules load before PyBlock globals.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This function was previously available via the removed star import
'from SPV.spvblock import *'. Add explicit import so Lite Mode
fallback and mode C selection work correctly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When bitcoincli is empty in bclock.conf, instead of crashing with
PermissionError, MainMenu now:
1. Falls back to RPC remote mode if ip_port/rpcuser are configured
2. Redirects to Lite Mode (MainMenuCROPPED) if nothing is configured
Also simplified the fatal error handler in main() to show the error
message visibly before exiting.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>