From 1a97539a9544055b07e74e1e72f6238e835a32ad Mon Sep 17 00:00:00 2001 From: "pithead-developer[bot]" <4933481+pithead-developer[bot]@users.noreply.github.com> Date: Wed, 30 Sep 2026 07:55:51 -0500 Subject: [PATCH 1/4] fix: surface onion SOCKS setup and advisory proxy diagnostics --- README.md | 2 +- docs/configuration.md | 7 ++- docs/faq.md | 5 ++- docs/getting-started.md | 12 +++--- docs/hardware.md | 2 +- docs/operations.md | 4 +- docs/pithead-integration.md | 5 ++- rigforge.sh | 73 ++++++++++++++----------------- tests/onion-proxy.sh | 86 +++++++++++++++++++++++++++++++++++++ tests/run.sh | 26 +++++------ 10 files changed, 153 insertions(+), 69 deletions(-) create mode 100644 tests/onion-proxy.sh diff --git a/README.md b/README.md index 726f9c5..8bd0536 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ you like to your stack's single endpoint. for hardware-prefetcher control, and `hugetlbfs` mounts + memlock limits. - Runs XMRig as a `systemd` service on Linux, with a `cpupower` performance governor and log rotation. -- Asks for your pool URL on first run if no config exists. +- Asks for your pool URL and optional password on first run; an `.onion` pool also offers a Tor SOCKS proxy override. - Is idempotent: re-running skips the recompile when the pinned XMRig is already built and never double-applies system tuning. diff --git a/docs/configuration.md b/docs/configuration.md index 0de53fe..7555610 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -4,7 +4,10 @@ RigForge reads a small `config.json` in the repo root. It holds only what the sc rest (CPU profile, thread count, HugePage sizing) is detected and applied for you. On first run, if there's no `config.json`, `setup` creates a minimal one interactively (it asks for -your pool URL). You can also pre-create one from +your pool URL and optional stratum password). For an `.onion` host it explains that you must +install and run Tor yourself, defaults `socks5` to `127.0.0.1:9050`, and offers a validated +interactive override (Enter or EOF keeps the default). LAN pools get no proxy prompt or key. +You can also pre-create one from [`config.minimal.json`](../config.minimal.json). Setup validates every field when it parses the config. A malformed pool URL, an out-of-range port, a @@ -27,7 +30,7 @@ its `url` (a `host:port`). Everything else falls back to a default: ``` That's a complete config. Replace `:3333` with your pool's host and port (Pithead's -proxy listens on `3333`). The interactive first-run setup writes exactly this minimal shape. +proxy listens on `3333`). The interactive first-run setup writes this minimal shape for a LAN pool without a password. > Mining to a public pool like [SupportXMR](https://www.supportxmr.com)? A `url` alone isn't enough: > public pools also need your Monero wallet as the pool `user` (and usually a TLS port). See diff --git a/docs/faq.md b/docs/faq.md index 2ab7f32..7b3ca1d 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -113,8 +113,9 @@ Tuning is CPU-specific, so only reuse it between identical CPUs. See ## Does the worker need Tor? -No. Workers talk to the pool/stack over plain Stratum on your local network. Tor (for privacy and no -port-forwarding) is a stack-host concern, handled by Pithead, not the miner. +Not for a LAN pool, the normal case. An `.onion` pool needs a Tor SOCKS proxy: install and run +Tor yourself, then set the pool’s `socks5` address. First-run setup offers `127.0.0.1:9050` for an +onion pool and lets you override it. RigForge does not install, start, or supervise Tor. --- diff --git a/docs/getting-started.md b/docs/getting-started.md index dd3b2e8..1273b8b 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -12,7 +12,7 @@ whole process, and most of it is automated. > sudo ./rigforge.sh > ``` > -> Answer one prompt (your pool URL — plus an optional stratum password if your stack uses one), let it build, and on Linux reboot once to apply the +> Enter your pool URL and optional stratum password (an `.onion` pool also offers a SOCKS proxy override), let it build, and on Linux reboot once to apply the > kernel tuning. The `xmrig` service starts automatically after the reboot. --- @@ -25,7 +25,7 @@ whole process, and most of it is automated. | **CPU** | 64-bit x86 with **AVX2** is strongly recommended for RandomX performance. More and faster cores mean more hashrate. | | **RAM** | **~2.3 GB free** for RandomX fast mode (a 2080 MB dataset + 256 MB cache), plus ~2 MB of L3 cache per mining thread. **4 GB+** recommended. | | **Privileges** | `root` for `setup` (it installs packages and tunes the kernel, so run it with `sudo`). Looking is free: `status`, `logs`, and `backup` never need root. | -| **Network** | The worker must reach your pool / stack host on its Stratum port (Pithead uses **3333**). Workers run on a trusted LAN and do **not** need Tor. | +| **Network** | The worker must reach your pool / stack host on its Stratum port (Pithead uses **3333**). LAN pools need no Tor; an `.onion` pool needs a Tor SOCKS proxy that you install and run yourself. | > 📐 Full sizing guidance, including minimum vs. recommended specs and the per-CPU tuning profiles, > is in **[Hardware Requirements](hardware.md)**. The **stack host** these workers connect to is sized @@ -57,8 +57,7 @@ and its proxy port `3333` (e.g. `stack.lan:3333`); with Pithead you do **not** n stack handles payouts centrally. > Mining to a public pool (SupportXMR, etc.) instead of Pithead? Public pools pay **you**, so they -> expect your Monero wallet address as the login (and usually a TLS port). The first-run prompt only -> asks for the pool URL, so afterwards set `pools[].user` to your wallet (and `tls`) and run +> expect your Monero wallet address as the login (and usually a TLS port). First-run setup leaves the wallet and TLS fields for you, so afterwards set `pools[].user` to your wallet (and `tls`) and run > `sudo ./rigforge.sh apply`. There's a copy-paste example in > [Configuration › Connecting to a public pool](configuration.md#connecting-to-a-public-pool-supportxmr-etc). > Otherwise your hashes credit the rig's hostname instead of you. @@ -74,8 +73,9 @@ sudo ./rigforge.sh `setup` is the default command and is safe to re-run. On a fresh machine it walks through: 1. **Dependencies.** Installs the build toolchain and runtime libraries for your OS. -2. **First-run config.** If there's no `config.json`, it asks for the one thing it needs, your - **pool URL** (and, optionally, the stack's stratum password — Enter skips it), and writes a minimal config. (You can also pre-create one; see +2. **First-run config.** If there's no `config.json`, it asks for your **pool URL** and optional + stratum password (Enter skips it). An `.onion` host also offers a SOCKS proxy override, defaulting + to `127.0.0.1:9050`; install and run Tor yourself. It writes a minimal config. (You can also pre-create one; see [Configuration](configuration.md).) 3. **Build.** Clones and compiles XMRig from source, pinned to a known version/commit and patched to your `DONATION` level. Build output is captured to a logfile. diff --git a/docs/hardware.md b/docs/hardware.md index 1230c81..66d4d73 100644 --- a/docs/hardware.md +++ b/docs/hardware.md @@ -17,7 +17,7 @@ are modest; most of the performance comes from tuning, which RigForge applies fo | RAM | ~2.3 GB free for RandomX fast mode (a 2080 MB dataset + 256 MB cache), plus ~2 MB of L3 cache per mining thread | 4 GB+; budget more on high-core-count CPUs. | | HugePages | Optional, but a significant speedup | RigForge configures 2 MB and 1 GB HugePages (plus MSR access) for you. Linux only, and it needs a reboot to take effect. | | OS | Ubuntu 22.04+ or Debian 12 | Ubuntu is the supported target. macOS is deprecated (unsupported, untested since 2026-09-14). | -| Network | Reach your pool / stack host on its Stratum port (Pithead uses 3333) | Local network; workers do not need Tor. | +| Network | Reach your pool / stack host on its Stratum port (Pithead uses 3333) | A LAN pool needs no Tor. An `.onion` pool needs an operator-managed Tor SOCKS proxy. | > RandomX light mode needs only 256 MB of RAM but is far slower; fast mode (the default) is what you > want for real hashrate. These figures are from XMRig's own diff --git a/docs/operations.md b/docs/operations.md index 0dcfd30..61e2f04 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -56,7 +56,7 @@ Not every verb needs root — the design, in four lines: | `upgrade` | Redeploy this RigForge release onto the rig: regenerate the live XMRig config, reinstall the systemd units, restart so both are in effect, re-tune if periodic autotune is enabled (the fastest knobs can shift between versions), and hand the files back to you. The **compile** is the conditional part — XMRig is rebuilt only when the pinned version/commit changed. A miner you had deliberately stopped is left stopped, and the regenerated config and unit take effect at your next `start` (#413). `--check` just reports whether a newer RigForge release exists (on-demand GitHub query, always exits 0). | | `apply` | Re-read `config.json`, regenerate the live XMRig config, and restart, without recompiling. A miner that is deliberately stopped — a watchdog thermal hold, or your own `stop` — is left stopped, and picks up the new config at its next start. The fast path after editing `config.json`. On Linux it also reconciles the periodic-autotune timer with config (so changing the `autotune` target takes effect) and reports it (efficiency / performance / disabled), then asks the running miner whether a live pool connection came up — and warns when none did (a bad `pools[0].url` otherwise mines nothing while looking applied, #343). Warn only, never a failure: the pool may be legitimately down at apply time. `apply` does exit non-zero if the watchdog units could not be re-rendered (#395) — that one is a real failure, because the cadence the rig is running would otherwise diverge from `config.json` silently. | | `uninstall` | Remove the service and revert all system changes (fstab, limits, modules, GRUB) and the worker build/logs. Leaves `config.json`. Prompts first; add `--yes` to skip. | -| `doctor` | Read-only health check (run with `sudo` for the deepest checks). Critical findings (counted as issues): the service is active, the miner has a live pool connection (read from the miner's own local API — a running miner with no connection is not mining, #343; with the service stopped, one TCP dial of `pools[0]` stands in), HugePages are reserved, kernel lockdown isn't blocking MSR writes (read from `/sys/kernel/security/lockdown` — active lockdown means the MSR mod cannot apply, and `msr.allow_writes=on` can't override it), the `msr` module is loaded, and the MSR mod actually applied, confirmed from XMRig's log and, as root, an `rdmsr` register read-back (see [MSR mod verification](#msr-mod-verification)). Advisory findings (hints, not failures): CPU governor, 1 GB HugePages, HugePages 100%-backed (from the XMRig log), hashrate-capping hardware RigForge can't fix but you can (single-channel or slow RAM via `dmidecode`, and a power/boost-capped CPU clock), and BIOS/firmware recommendations (board/BIOS context, plus enable XMP/EXPO/DOCP or SMT when they're off; manual BIOS changes RigForge can't make from the OS). Prints an actionable hint for anything off. Also binary tamper evidence (#141): the on-disk `xmrig` is compared against the SHA-256 recorded at compile time — a deliberate rebuild refreshes the record, anything else warns and counts as an issue. When the [writable control path](#writable-control-path-opt-in) is enabled, it's also a counted issue: `doctor` checks `rigforge-control` is active and its `/status` endpoint answers (#278), staying silent when control is disabled. Exits non-zero when critical issues are found (cron-friendly, matching Pithead's `status`). | +| `doctor` | Read-only health check (run with `sudo` for the deepest checks). Critical findings (counted as issues): the service is active, the miner has a live pool connection (read from the miner's own local API — a running miner with no connection is not mining, #343; with the service stopped, one TCP dial of `pools[0]` stands in for a direct pool; a proxied connection cannot be verified until mining starts), HugePages are reserved, kernel lockdown isn't blocking MSR writes (read from `/sys/kernel/security/lockdown` — active lockdown means the MSR mod cannot apply, and `msr.allow_writes=on` can't override it), the `msr` module is loaded, and the MSR mod actually applied, confirmed from XMRig's log and, as root, an `rdmsr` register read-back (see [MSR mod verification](#msr-mod-verification)). Advisory findings (hints, not failures): an unreachable configured SOCKS proxy for an enabled pool (a successful TCP dial stays quiet and does not prove SOCKS/Tor health), CPU governor, 1 GB HugePages, HugePages 100%-backed (from the XMRig log), hashrate-capping hardware RigForge can't fix but you can (single-channel or slow RAM via `dmidecode`, and a power/boost-capped CPU clock), and BIOS/firmware recommendations (board/BIOS context, plus enable XMP/EXPO/DOCP or SMT when they're off; manual BIOS changes RigForge can't make from the OS). Prints an actionable hint for anything off. Also binary tamper evidence (#141): the on-disk `xmrig` is compared against the SHA-256 recorded at compile time — a deliberate rebuild refreshes the record, anything else warns and counts as an issue. When the [writable control path](#writable-control-path-opt-in) is enabled, it's also a counted issue: `doctor` checks `rigforge-control` is active and its `/status` endpoint answers (#278), staying silent when control is disabled. Exits non-zero when critical issues are found (cron-friendly, matching Pithead's `status`). | | `bench` | Run a one-off `xmrig --bench` and report the hashrate (a quick perf/health check; set `BENCH=10M` for a longer run). | | `tune` | The single command for tuning. A bare `tune` measures the fastest CPU-specific knobs (prefetch, `cpu.yield`, thread count) offline and keeps them, an optional, one-time step. Live variants: `--now` / `--short` (a quick prefetch re-tune against the running miner, the *run a live tune now* path), `--now --long` (a full live search of every knob, = `--live`), `--confirm` (A/B-check the winner live). Plus `--efficiency` / `--perf`, `--history`, `--clear`. See [Tuning](#tuning). | | `bios` | Guided, resumable walk-through of the BIOS/UEFI changes for your hardware — the settings `tune` can't reach from the OS (Secure Boot when kernel lockdown is blocking the MSR mod, memory profile XMP/EXPO/DOCP, SMT, PBO/Eco-Mode, NUMA-per-socket NPS on EPYC; `--efficiency` picks the low-power set). Detects the current firmware state via the same probes `doctor` uses, hands you a board-specific checklist one item at a time, saves the pending items, and on the next run re-verifies which changes actually took. RigForge never writes BIOS itself; plan for console access (keyboard/KVM) for the reboot-into-BIOS step. Linux-only. See [Guided BIOS tuning](#guided-bios-tuning). | @@ -542,7 +542,7 @@ If you see MSR errors, see Troubleshooting below. | Low hashrate / few threads | RandomX is L3-bound (~2 MB per thread). A CPU with little L3 runs fewer effective threads; this is expected. See [Hardware › L3 cache](hardware.md#a-note-on-l3-cache). | | No AVX2 | RandomX still runs but slower. AVX2 is strongly recommended; there's no fix beyond different hardware. | | Dashboard can't read the worker | The API is open (no token) by default, so first check the worker is reachable from the stack host on `:8080`. If you set an `ACCESS_TOKEN`, the dashboard must send the same token (`workers.api_auth: token` + `workers.api_token`, or `name` if the token is the rig name). See [Pithead Integration › Troubleshooting](pithead-integration.md#troubleshooting). | -| Pool unreachable | Confirm the worker can reach its pool URL (firewall, DHCP/static IP). Workers use plain Stratum on the LAN, no Tor. | +| Pool unreachable | Confirm the worker can reach its pool URL (firewall, DHCP/static IP). LAN pools use plain Stratum without Tor. For an `.onion` pool, check `pools[].socks5` and that your Tor SOCKS proxy is running; `doctor` warns when the configured proxy cannot accept a TCP connection. | --- diff --git a/docs/pithead-integration.md b/docs/pithead-integration.md index 459257e..7fa932a 100644 --- a/docs/pithead-integration.md +++ b/docs/pithead-integration.md @@ -21,7 +21,8 @@ split centrally, so the worker config stays minimal and you never put a wallet a - The XMRig pool `user` field is just a label for the rig. It defaults to the hostname (set `pools[].user` to name it) so you can tell workers apart on the dashboard. - Point as many workers as you like at the same stack endpoint; the stack aggregates them. -- Workers talk to the pool over plain Stratum on your local network; they do not need Tor. +- LAN workers talk to the pool over plain Stratum and need no Tor. An `.onion` pool needs a Tor SOCKS + proxy that the DIY operator installs and runs; RigForge only configures its address. - The endpoint must be reachable from the worker; if the stack host has a firewall, allow the Stratum port (3333) on the LAN. @@ -172,7 +173,7 @@ stack-side: | Auth token | none (open) by default; set `ACCESS_TOKEN` to require a Bearer token | Pithead's stock probe is no-auth, so an open, read-only API works without extra config. Setting `ACCESS_TOKEN` turns auth on; see below. This port takes **only** the master `ACCESS_TOKEN` — XMRig allows exactly one `http.access-token`, so named `ACCESS_TOKENS` entries are not accepted here. | Pithead discovers workers from the stratum proxy's connection list (the pool `user` label, which is the -rig name), so there's nothing to register stack-side. Workers run on a trusted LAN and need no Tor. +rig name), so there's nothing to register stack-side. Workers on a trusted LAN need no Tor; an `.onion` pool requires an operator-managed Tor SOCKS proxy. --- diff --git a/rigforge.sh b/rigforge.sh index 9992ae6..7ca1e5e 100755 --- a/rigforge.sh +++ b/rigforge.sh @@ -275,14 +275,7 @@ ensure_config_exists() { if [[ "$CREATE_CONF" =~ ^[Yy] ]]; then log "Starting interactive setup..." - # We only need the pool URL — every other key has a sensible default (see - # config.reference.json for the full list). The URL is host:port (Pithead's proxy - # listens on 3333). - # #344: re-prompt on an invalid entry instead of exiting the whole script — nothing is - # persisted at this point (the write is below, after validation), so a typo should cost - # three lines, not a restart. Bounded, not until-valid: a non-interactive/EOF stdin (tests, - # a piped install) must still terminate instead of spinning. SETUP_URL_TRIES follows this - # file's existing env-overridable-retry-count idiom (APPLY_POOL_TRIES, CONTROL_LIVE_TRIES). + # Validate before writing; bounded retries also terminate on piped stdin/EOF. url_tries="${SETUP_URL_TRIES:-3}" IN_URL="" for i in $(seq 1 "$url_tries"); do @@ -318,22 +311,26 @@ ensure_config_exists() { error "A valid pool URL (host:port) is required — giving up after $url_tries attempt(s)." fi - # Pithead stratum auth (#113): if the stack sets p2pool.stratum_password, every rig's pool - # `pass` must match or the proxy rejects the login. The secret is shown by `pithead status`. - # Enter skips it (open stack / non-Pithead pool) — parse_config then defaults pass to "x". - # Pre-validate with parse_config's exact pass rule (same reasoning as the host check above: - # fail before the write so a bad value doesn't leave a prompt-suppressing config on disk). + # Optional stratum password; validate before creating the config. IN_PASS="" read -r -p "Stratum password, if your stack requires one (shown by 'pithead status'; Enter for none): " IN_PASS || true if [ -n "$IN_PASS" ] && ! [[ "$IN_PASS" =~ ^[[:graph:]]+$ ]]; then error "Stratum password must have no spaces or control characters." fi - # Minimal config: just the native pools array. jq writes it so the URL (and pass, when - # given) are safely quoted; an empty pass writes no key at all, keeping the no-auth - # minimal config byte-identical to before. - (umask 077 && jq -n --arg url "$IN_URL" --arg pass "$IN_PASS" \ - '{pools: [({url: $url} + (if $pass == "" then {} else {pass: $pass} end))]}' >"$CONFIG_JSON") + IN_SOCKS5="" + case "$_host" in + *.[Oo][Nn][Ii][Oo][Nn]) + log "An onion pool needs Tor. Using SOCKS proxy 127.0.0.1:9050; install and run Tor yourself." + read -r -p "SOCKS proxy (host:port; Enter for 127.0.0.1:9050): " IN_SOCKS5 || true + IN_SOCKS5="${IN_SOCKS5:-127.0.0.1:9050}" + (_validate_host_port "$IN_SOCKS5" "SOCKS proxy" 9050) >/dev/null 2>&1 || error "SOCKS proxy must be a valid host:port." + ;; + esac + + # Omit unset fields to preserve the LAN config's minimal shape. + (umask 077 && jq -n --arg url "$IN_URL" --arg pass "$IN_PASS" --arg socks5 "$IN_SOCKS5" \ + '{pools: [({url: $url} + (if $pass == "" then {} else {pass: $pass} end) + (if $socks5 == "" then {} else {socks5: $socks5} end))]}' >"$CONFIG_JSON") # The operator is told (below) to hand-edit this file to add a wallet / ACCESS_TOKEN, and the # first `apply` may be a long way off — chmod now so those secrets are never world-readable # in the interim (generate_xmrig_config's chmod 600 only runs on setup/apply). (#131) @@ -394,15 +391,7 @@ _validate_host_port() { #