feat(appliance): the built-in RigForge miner — 'Both' on the current image (#797 R2) - #812
Merged
VijitSingh97 merged 2 commits intoAug 1, 2026
Conversation
…image The appliance now honours "Mine on this machine too?" itself instead of telling a shell-less machine's operator to install RigForge by hand: - image bake: rigforge's Debian toolchain (trixie: linux-cpupower), a pinned rigforge tree (ARG RIGFORGE_REF, recorded in the image) and a prebuilt XMRig at rigforge's own pin — a Tor-only box can never run rigforge's github clone, so the clone happens at build time; - pithead-sync: rigforge program tree replaced wholesale on /data, config.json + workspace preserved, the prebuilt seeded only when the pinned commit changed (a same-pin native rebuild is left alone); - render: rigforge's config.json becomes a derived file — loopback pool url, stratum password when set, and the stack's HugePages budget declared as hugepages_reserve_extra_mb so rigforge's grow-only sysctl sizes the shared pool as the single writer; pithead's own vm.nr_hugepages write is now grow-only for the same reason; - boot path: pithead-boot runs `pithead local-miner` LAST, after the health-gated slot commit — the miner needs the stratum listening and must never delay or block the commit; setup and apply run the same leg so first provisioning and dashboard toggles converge without a reboot; - wizard: the Yes answer promises the built-in worker, with no install-it-yourself instruction anywhere. Coverage: stack tests for the render/boot/sync legs + grow-only sysctl (1878 pass), a frontend probe pinning the wizard promise (292 pass), verify-image checks for everything baked, and the KVM provision phase gains the enable -> xmrig up -> share accepted -> back-after-reboot leg (needs the KVM host to run). Closes #796 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adversarial review: pithead-boot runs with TimeoutStartSec=infinity, so '|| true' alone cannot save the unit from a HUNG miner setup — a stalled native rebuild would leave the boot unit activating forever. The leg now runs under timeout 1800 (clears a worst-case first native recompile; the prebuilt path finishes in seconds). Tests pin the timeout's presence beside the ordering assert, and the provision leg gains the second-boot re-run case. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This was referenced Aug 1, 2026
Open
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase R2 of #797: the appliance honours "Mine on this machine too?" itself. The image bakes a pinned RigForge tree and a prebuilt XMRig,
pithead-syncdelivers them to/data,renderderives the miner's config, and the boot path runs RigForge's appliance mode (rigforge#330,RIGFORGE_APPLIANCE=1) after the stack is up. The wizard's Yes answer becomes true — no more "install RigForge on it afterwards" on a machine with no shell.Closes #796.
What's baked at image build vs synced at boot
Baked (
os/rootfs/Containerfile):git build-essential cmake libuv1-dev libssl-dev libhwloc-dev gettext-base python3 linux-cpupower msr-tools(trixie ships cpupower aslinux-cpupower, not Ubuntu'slinux-tools-common— rigforge#327;jqwas already baked). R0 proved a post-ship install can never work: apt fails on the RO root, and even a rw-remount install leaves alternatives/ld.so state on the volatile/etcoverlay./opt/rigforge, fetched as a GitHub tarball atARG RIGFORGE_REFand recorded in/opt/rigforge/RIGFORGE_REF. The pin is a commit on rigforgedevelop(32be3a4, the merge of rigforge#330 — the first ref with appliance mode); no tagged rigforge release carries it yet (latest is v1.12.0, 2026-07-19). Move the default to the next rigforge tag when it cuts.XMRIG_VERSION/XMRIG_COMMITpin (read from the bakedrigforge.shvia sed, so they cannot drift), staged under/opt/rigforge/prebuilt/xmrigwith the exact.rigforge-commit/.rigforge-sha256markersrigforge.shwrites. This answers R0 finding 8: RigForge's build step is a github clone over clearnet, unreachable from a Tor-only appliance — so the clone happens on the build host, and the appliance never compiles on first enable. The donate-level source patch is skipped (default 1% == XMRig's own default; the runtime config carries the value regardless). On-boxupgrade/recompile against a changed pin would still need clearnet, but a new pin only ever arrives with a new image that carries its new prebuilt.Synced every boot (
pithead-sync): the rigforge program tree is replaced wholesale into/data/rigforge(RigForge'sDYNAMIC_HOMEroots its workspace under the script dir — R0 finding 6 — so tree placement alone puts the build on writable storage);config.jsonand thedata/workspace are state and survive by construction. The prebuilt is seeded intodata/worker/xmrigonly when the commit marker differs — a native recompile of the same pin is left alone, a new pin's prebuilt replaces the stale cache.Boot order (the ordering decision)
pithead-bootruns the miner leg last, after the health-gatedrauc mark-good:The stack serving is the product's health and must gate the A/B commit; the miner is a passenger that needs the stratum listening and must never delay or block the commit (first-enable work, a wedged setup).
|| true— a miner that cannot start does not fail the boot unit. The sameprovision_local_mineralso runs at the end ofsetup(first provisioning — the miner starts without waiting for a reboot) and at the end ofapply(a dashboard toggle converges immediately: on → setup, off →systemctl stop xmrig).HugePages ownership (as implemented)
Per the #797 follow-up measurements (sum, not max — both sides hold pages concurrently):
renderwriteshugepages_reserve_extra_mb: 6144(pithead's own 3072 × 2 MiB budget) into the miner's config — rigforge#305's designed hand-off. RigForge's grow-only sysctl (rigforge#328) then sizes the whole pool as miner need + declared headroom.optimize_kernel): it reads the live pool and only writesvm.nr_hugepages=3072when the pool is smaller — it never shrinks the merged budget back. The budget now has ONE definition (PITHEAD_HUGEPAGES), feeding the sysctl, the GRUB params, and the declared headroom.sysctl.d3072 is unchanged: it lands on a fresh pool before anything holds pages, and RigForge grows from there.The render leg
RigForge's
config.jsonis a derived file on the appliance, rebuilt byrender/apply/setuplike the Caddyfile:pools[0].url = 127.0.0.1:<stratum port>,passonly when a stratum password is set, plus the headroom above. Disabled ⇒ the file is removed (derived means derived; no stale secret on/data). DIY hosts are untouched everywhere —announce_local_minerstill hands DIY operators the two values, and gets an appliance variant that promises the built-in worker instead of instructing an install.Tests
tests/stack/run.sh— 1878 passed, 0 failed locally (macOS). New: render leg (loopback url, pass only-when-set, 6144 headroom, disabled-removes, DIY untouched), boot leg against a stubrigforge.sh(RIGFORGE_APPLIANCE=1, run from the tree, config rendered first, disabled → stop, failure contained),pithead-syncrigforge leg run for real against a sandbox (wholesale program replace, state preserved, seed-on-pin-change, same-pin native rebuild kept), grow-onlyoptimize_kernel, appliance announce text, and the pithead-boot ordering guard (miner leg after mark-good).node --test— 292 passed incl. a new probe pinning "Yes" to the built-in-miner promise (no "install RigForge" instruction).make lintgreen (shellcheck, shfmt, ruff, biome, markdownlint, docs voice, operator strings).tests/os/verify-image.sh— new checks: rigforge tree + recorded ref, prebuilt xmrig + commit-marker-matches-pin + sha record, compiler chain, envsubst/cpupower/rdmsr.tests/os/run.sh --phase provision— the submit now carrieslocal_miner=true, and the phase gains the Master installer image: one stick provisions a Pithead coordinator, a RigForge rig, or both #797 leg: enable → xmrig unit active + process up → a share loggedacceptedby xmrig-proxy (i.e. taken by p2pool) → after the unaided reboot, the miner is back (its/rununit died with the boot; only pithead-boot's leg can have re-created it). I could not run the KVM battery from this environment — it needs the KVM build host.XMRIG_VERSION/XMRIG_COMMITsed extraction was proven against the real rigforge.sh at the pinned ref, the marker formats are copied from whatrigforge.shitself writes, andlinux-cpupoweris the trixie package name rigforge#327 established. An isolated docker build of just these layers was still running (network-bound) when this PR was opened — the real amd64 image build is item 1 of the bench list below and is the authoritative check.What the bench session must verify (operator's HP box, over SSH)
tests/os/verify-image.shgreen (the new built-in-miner section in particular).tests/os/run.sh --phase provisionend to end (miner up, share accepted, miner back after reboot) plus the untouched phases.local_miner— first boot must start the miner from the prebuilt (no compile, no clearnet),rdmsrconfirms the MSR preset, and the hugepage pool must equal the merged budget (miner need + 6144 MB) with neither side shrinking it across a stack restart or apithead apply.local_miner.enabledoff via the dashboard → miner stops without a reboot; on again → returns.systemctl --failedclean.enable --runtimeend-to-end) — first real-hardware exercise of that mode.Follow-ups (deliberately out of scope)
RIGFORGE_REFmoves to a tagged rigforge release when the next one cuts.🤖 Generated with Claude Code