A terminal UI for running solo Bitcoin mining as what it really is — a lottery. Wraps
minerd, watches it work, doesn't pretend the odds are anything other than astronomical.
Solo Bitcoin mining at today's network difficulty is, statistically, a lottery. The chance of finding a block on a single CPU in any given minute is so small it's barely a number. But unlike a pool, if you do hit, you take the entire block reward — no per-share split. That's the wager: tiny odds, maximum payout, and a long-running process turning electricity into hashes while you wait.
BitlaForge is the dashboard you watch while your machine buys lottery tickets in compute cycles. It launches minerd, parses what it says, and shows you the pool, wallet, threads, hashrate, uptime, and shares without you having to keep a terminal window staring at raw output. It also runs cleanly over SSH, which is what you actually want for a headless rig.
It is the fourth tool in the Forge suite for KognogOS — alongside grubForge, alacrittyForge, and nogForge. Same Catppuccin Mocha aesthetic, same release discipline, same human + AI co-authorship.
v0.1.1 alpha — it actually mines now. v0.1.0 was the Forge-style skeleton (screens, nav, help, confirm); v0.1.1 wires the four pieces that turn the skeleton into a usable tool:
- Persistence. Pool / wallet / algorithm / thread count are saved to
~/.config/bitlaforge/config.tomland reload at launch. - Real
minerdsubprocess. Pressing M spawnsminerdviaasyncio.create_subprocess_exec, streams its stdout line-by-line into the Log screen, and parses hashrate / accepted / rejected / threads into reactive Dashboard fields. Pressing M again terminates cleanly (SIGTERM with a 3-second grace period, SIGKILL fallback). minerdruntime detection.shutil.which("minerd")runs at launch and on every M press. When the binary is missing, the Dashboard shows a persistent banner and M surfaces friendly install guidance instead of a raw "binary not found" error.- Setup screen. A new fourth screen (4) lists the three AUR providers (
cpuminer,cpuminer-multi,cpuminer-opt) with copy-pasteableyay -Scommands and a T action that runsminerd --versionto verify the install actually works.
This repo used to be
BitLA, a Qt6/Widgets desktop scaffold that landed in November 2025 with simulation-driven UI and no real miner integration. On 2026-05-28 it pivoted to a Textual TUI under the Forge-suite umbrella. The Qt prototype is preserved permanently as thev0.1.0-qt-archivedgit tag;mainis the TUI from day one.
- 🏠 Dashboard — read-only overview driven live by parsed minerd output: pool / port / wallet / algorithm / threads / hashrate / accepted / rejected shares / uptime. Persistent ⚠ minerd not detected banner when the binary isn't on PATH.
- 📜 Log — streaming view of minerd's stdout/stderr line-by-line, with a 5,000-line bounded buffer, / to focus search, C to clear.
- ⚙ Config — pool URL, wallet, algorithm (sha256d / scrypt / yescrypt / x11 / x13 / x15 / x17 / groestl), thread count. Persisted to
~/.config/bitlaforge/config.toml; loads at every launch. - 🛠 Setup — install guide for
minerd(AUR-only) with one-liner commands for each provider, and a T action that runsminerd --versionto verify the install. - ❓ Help modal — toggleable; Esc / q / ? all dismiss; lists every binding.
- 💬 Unified feedback —
StatusMixinfrom the Forge suite: status-line + toast popup,popup=Falsefor passive mount hints so launch is quiet. - 🎯 Focus-on-show — screen bindings fire on the first keypress without a panel click; clickable sidebar nav as an alternative path.
- Linux
- Python 3.11+
python-textual,python-rich,python-tomli-wminerd— optional but required to actually mine. AUR-only; install via one ofcpuminer(recommended, pooler's original),cpuminer-multi, orcpuminer-opt. The Setup screen (press 4) has the install commands and a self-test.
yay -S bitlaforgeThen run bitlaforge. The AUR package's optdepends will prompt for one of the cpuminer* variants to actually mine — install whichever fits your needs (see Setup screen for guidance).
sudo pacman -S python-textual python-rich python-tomli-w
git clone https://github.com/jetomev/bitlaforge.git
cd bitlaforge
python main.pypip install textual rich tomli-w
git clone https://github.com/jetomev/bitlaforge.git
cd bitlaforge
python main.pyThe minerd binary lives only on the AUR — three providers, pick one. Press 4 inside BitlaForge for the full guide; the short version:
yay -S cpuminer # pooler's original (recommended)
# or
yay -S cpuminer-multi # multi-algorithm fork
# or
yay -S cpuminer-opt # heavily optimised variantIf minerd is missing, BitlaForge still runs — the Dashboard shows a banner, the Setup screen has the install commands, and pressing M surfaces install-guidance instead of failing silently.
| Key | Action |
|---|---|
1 |
Dashboard |
2 |
Log |
3 |
Config |
4 |
Setup |
M |
Start / Stop miner |
R |
Refresh current screen |
? |
Toggle help overlay |
q |
Quit |
| Key | Action |
|---|---|
/ |
Focus the search filter |
C |
Clear the log buffer |
| Key | Action |
|---|---|
E |
Focus the first input (begin editing) |
S |
Save the current values |
| Key | Action |
|---|---|
T |
Test minerd binary (minerd --version) |
bitlaforge/
├── main.py # Entry point
├── bitlaforge/
│ ├── app.py # BitlaForgeApp shell + nav + M lifecycle + live tick
│ ├── bitlaforge.css # Catppuccin Mocha stylesheet
│ ├── config_manager.py # TOML read/write at ~/.config/bitlaforge/
│ ├── miner_runner.py # Async minerd subprocess + stdout parsing
│ ├── process_stats.py # /proc/<pid>/ CPU% + RAM readouts
│ ├── system_info.py # /proc/cpuinfo + /proc/meminfo + load avg
│ ├── screens/
│ │ ├── dashboard.py # Live miner overview + missing-minerd banner
│ │ ├── log.py # 5,000-line bounded buffer + filter
│ │ ├── config.py # Pool / wallet / algorithm / threads / name / niceness
│ │ └── setup.py # System info + AUR install guide + minerd self-test
│ └── widgets/
│ ├── status.py # StatusMixin (line + toast)
│ ├── help_screen.py # Toggleable modal help
│ └── confirm_dialog.py # Esc-cancel / Enter-confirm modal
Mining is opt-in. Real CPU load, real electricity, real heat. BitlaForge will never:
- Auto-start
minerdon launch. The miner only runs after you explicitly press M (and, eventually, only if a pool + wallet are configured). - Hide the active state. The Dashboard always shows whether the miner is running.
- Make the Stop path more than one keystroke away. M toggles. Always.
- Pivot from Qt6/Widgets
BitLAto Textual TUI under the Forge suite - Sidebar navigation + 3 screens (Dashboard, Log, Config)
-
StatusMixin,HelpScreen,ConfirmDialogported from the Forge baseline - Catppuccin Mocha styling
- Persist config to
~/.config/bitlaforge/config.toml(TOML) - Wire
minerdviaasyncio.create_subprocess_exec: spawn, parse stdout, stop cleanly - Real Dashboard fields driven by parsed minerd output (hashrate / threads / accepted / rejected / uptime)
- Stream minerd stdout into the Log screen's bounded buffer
- Runtime
which("minerd")check + Dashboard banner + friendly install guidance - Setup screen with AUR provider list +
minerd --versionself-test
- System info on Setup screen (CPU model + logical/physical cores + load avg + memory) from stdlib
- Config gains miner name (default hostname; also used as Stratum worker name) and niceness (0–19, default 19)
- Threads input shows "of N available" hint from
os.cpu_count() -
miner_runnerappendswallet.workernameand wraps withnice -n N - Dashboard live tick (1s
set_intervalwhile mining) — uptime advances smoothly between hashmeter lines - Miner name surfaced in the Dashboard title
- Live
minerdCPU% / RAM from/proc/<pid>/stat+/proc/<pid>/status— makes the niceness setting observable - Hashrate parser hotfix: integer rates, per-thread aggregate, auto-scale to Mh/s / Gh/s
-
testing/RELEASE-CHECKLIST.md+ v0.1.3 Test Matrix - Man page
bitlaforge.1 - PKGBUILD with hardened headless-mount
check()+PYTHONDONTWRITEBYTECODE=1defenses - Published on the AUR — completes the Forge suite (joining grubForge, alacrittyForge, nogForge)
- Wallet format validation (bech32 / legacy address shape check)
- Pool reachability probe (TCP connect with short timeout) + "Test connection" on Config
- Persistent log archive (rotating files in
~/.local/share/bitlaforge/sessions/) - Per-session lifetime totals across restarts (uptime + accepted/rejected accumulated)
- Sparkline (
▁▂▃▄▅▆▇█) of hashrate-over-time under the Dashboard's hashrate value - System CPU load sparkline on Setup
- Per-thread hashrate mini-bars
- Optional
textual-plotextintegration for proper time-series charts (btop-style)
- Pool reachability check on save (not just on start)
- Multi-config profiles (switch between pools / wallets / algorithms with one key)
- Optional auto-restart on minerd crash
- Notification on accepted-share (rare event, worth surfacing prominently)
First AUR release — Forge release machinery.
The version that gets BitlaForge into the AUR alongside its three Forge siblings. With this release, the complete Forge suite (grubForge + alacrittyForge + nogForge + BitlaForge) is now installable on Arch via a single yay -S <name>.
No code-feature changes vs. v0.1.2 — pure release machinery + packaging:
testing/—RELEASE-CHECKLIST.mdmirroring the grubForge / alacrittyForge discipline (pre-dogfood snapshot, async-worker audit,_rendershadowing audit, version sync gate, doc coverage, co-author credit gate, release-day flow). Plus the v0.1.3 Test Matrix covering the full v0.1.0 → v0.1.3 user-facing surface for the first AUR-ship dogfood.- Man page
bitlaforge.1— full keybindings, safety section, dependency listing (cpuminervariants as optdeps), files, authors with co-developer credit. - PKGBUILD —
depends=(python python-textual python-rich python-tomli-w);optdepends=()for the threecpuminer*AUR providers (sinceminerdis itself AUR-only and pacman can't reference AUR packages independs); hardenedcheck()runs the headlessrun_test()mount smoke underPYTHONDONTWRITEBYTECODE=1with defensive__pycache__cleanup inpackage()so we never ship the.pycinstall-conflict class that bit grubForge v1.0.2. - The two hotfixes from v0.1.2 (
cfe1010CPU display +e3e3091hashrate parser) are folded forward into v0.1.3 — first tagged version that includes them.
Now installable as:
yay -S bitlaforgeResource awareness + live Dashboard.
v0.1.1 made BitlaForge mine; v0.1.2 makes it feel alive while doing it. Five per-group commits, all stdlib (no new deps):
- G1 — System info on Setup. New
bitlaforge/system_info.pyreads/proc/cpuinfo,/proc/meminfo,os.getloadavg(), andos.cpu_count()into aSystemInfodataclass. The Setup screen gains a block above the minerd status showing CPU model, logical/physical cores, memory total/available with % used, and 1/5/15-minute load averages color-graded against your core count (green when comfortably idle, yellow as load approaches your ceiling, red over it). Refreshes on R. - G2 — Config gains miner name + niceness + threads hint. Two new fields in
~/.config/bitlaforge/config.toml:miner_name(defaults tosocket.gethostname()) andniceness(0–19, default 19). The Thread count label dynamically shows "of N available" sourced from the sameos.cpu_count(). Forward-compatible: a v0.1.1 TOML loads cleanly with new defaults silently applied. - G3 —
miner_runnerworker name + nice wrapper._build_argsnow joins the sanitizedminer_nameto the wallet aswallet.workername(standard Stratum convention — pools show per-rig stats on their dashboards).start()wraps the spawn withnice -n Nwhen niceness > 0; niceness ≤ 0 spawns directly with no overhead. - G4 — Dashboard live tick + name in header. App installs a 1-second
set_intervaltimer when the miner starts, cancels on stop (and when the parser sees the process end on its own). Each tick re-renders the Dashboard so uptime advances smoothly between minerd hashmeter dumps. The Dashboard title now shows the miner name in mauve:⚡ BitlaForge — Miner Overview — workstation-rig. - G5 — Live
minerdCPU% / RAM from/proc/<pid>/. Newprocess_stats.pyparses/proc/<pid>/stat(utime + stime ticks) and/proc/<pid>/status(VmRSS) — stdlib only, nopsutil. The App tick captures a baseline at start, computes deltas on each tick, and writescpu_pct(htop-style: 100% = one logical core) +mem_mbintoMinerStats. Dashboard shows them under Performance. Makes the niceness setting observable: with niceness=19, you'll see minerd's CPU% drop the moment any other process needs cycles.
No dependency changes. Same python, python-textual, python-rich, python-tomli-w.
Make it actually mine.
v0.1.0 shipped the Forge-style TUI skeleton with sidebar nav, three screens, the help modal, and a confirm dialog — but minerd integration was a stub: pressing M just flipped a state flag and notified. v0.1.1 turns the skeleton into a working tool. Four per-group commits:
- G1 — Config persistence. New
config_manager.py(tomllib for read + tomli_w for write) saves pool / wallet / algorithm / threads to~/.config/bitlaforge/config.toml. The values survive launches; the Config screen loads them on__init__. Schema is intentionally flat so future cycles can add[ui]/[log]sections without breaking the miner section. - G2 — Real minerd subprocess. New
miner_runner.pywith aMinerRunnerclass that usesasyncio.create_subprocess_exec, streams stdout line-by-line via a backgroundasyncio.Task, and parses hashrate / accepted / rejected / threads out of pooler-cpuminer-format lines via regex. Each line is forwarded to the Log screen's bounded buffer; each parsed stat update repaints the Dashboard. Stop isSIGTERMwith a 3-second grace,SIGKILLfallback. App'sMbinding is now async and wraps it all. - G3 — Runtime check + Dashboard banner + friendly guards.
shutil.which("minerd")runs at launch and on every M press. When the binary is missing, the Dashboard shows a persistent red "⚠ minerd not detected" banner with install hint; pressing M produces "install one of: cpuminer / cpuminer-multi / cpuminer-opt from AUR (yay -S cpuminer)" instead of the bare "binary not found" error. - G4 — Setup screen (4th nav). New screen at 4 with the minerd status block (path if installed, or ✗ not installed), an "about minerd" explainer, the three AUR providers with copy-pasteable
yay -Scommands, the config file path, and a T action that runsminerd --versionto verify the install actually works.
Dependency note: python-tomli-w is now required (same dep alacrittyForge already pulled in).
Pivot from Qt to TUI; first Forge-era release.
Repo was originally BitLA — a Qt6/Widgets desktop scaffold pushed in November 2025 with simulation-driven UI and no real minerd integration. Untouched for ~6 months. Pivoted to a Textual TUI on 2026-05-28 because solo mining is fundamentally a streaming-stdout activity on long-running headless rigs, which a TUI fits better. The Qt code (~700 LOC of mostly scaffolding) is preserved permanently as the v0.1.0-qt-archived git tag.
Inaugural v0.1.0 ships the Forge-suite skeleton:
- Three screens (Dashboard, Log, Config) under sidebar navigation via
ContentSwitcher. StatusMixinfor unified status-line + toast feedback, withpopup=Falsefor passive mount-time hints (so the app launches quietly).- Toggleable
HelpScreenmodal (Esc / q / ? dismiss; q shadows app-quit while help is up). ConfirmDialogwithescape→ cancel,enter→ confirm.DEFAULT_FOCUSper screen so bindings fire on entry without a panel click.
minerd subprocess wiring is the v0.1.1 headline work.
Javier (@jetomev) — idea, direction, testing
Claude (Anthropic) — co-developer, architecture, implementation
BitlaForge is built as a real collaboration between a human with an idea and an AI that helps bring it to life — one commit at a time. The co-authorship is preserved in the Forge-suite recognition thesis: public projects that demonstrate AI as a serious software collaborator, not a black-box code generator. Co-author credit appears in commits, README, man pages, PKGBUILD, and release notes.
GPL v3. See LICENSE.
This is alpha software — UI feedback, bug reports, and ideas welcome via GitHub Issues. If you find BitlaForge useful, consider starring the repo. The Forge-suite recognition thesis only works if these projects are visible.