diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index df5f73d..595823f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,24 +1,23 @@ # Contributing to RigForge -Thanks for your interest in improving RigForge. Whether it's a bug fix, a new -CPU tuning profile, or a docs tweak, contributions are welcome. - RigForge is the companion miner for the -[Pithead](https://github.com/p2pool-starter-stack/pithead) P2Pool stack. If your -idea is really about the stack as a whole rather than the miner, that repo may be -the better home for it. +[Pithead](https://github.com/p2pool-starter-stack/pithead) P2Pool stack. Bug +fixes, CPU tuning profiles, and docs changes are all welcome. + +If your idea is about the stack as a whole rather than the miner, the Pithead +repo may be the better home for it. ## Before you start -- For anything beyond a small fix, **open an issue first** so we can agree on the +- For anything beyond a small fix, open an issue first so we can agree on the approach before you spend time on it. This avoids duplicated work. - Check the existing issues to see if someone is already on it. ## Making changes -RigForge is portable Bash that has to run on Ubuntu/Debian and macOS, so: +RigForge is portable Bash that has to run on Ubuntu/Debian and macOS: -- Keep it **portable bash**. Avoid GNU-only flags and other Linux-isms where a +- Keep it portable. Avoid GNU-only flags and other Linux-isms where a POSIX-friendly alternative exists, and guard platform-specific code paths. - Run `make lint` before you push and fix any warnings. It runs ShellCheck and `shfmt` over the script, utilities, and the test scripts, exactly as CI does: @@ -63,8 +62,8 @@ newline) so most editors match these checks automatically. RigForge uses a two-branch model (same as [Pithead](https://github.com/p2pool-starter-stack/pithead)): -- **`develop`** is the default, integration branch. All PRs target `develop`. -- **`main`** is the release branch. `develop` is merged into `main` at each release, and version tags +- `develop` is the default, integration branch. All PRs target `develop`. +- `main` is the release branch. `develop` is merged into `main` at each release, and version tags are cut from `main`. ## Submitting a pull request @@ -72,12 +71,10 @@ RigForge uses a two-branch model (same as [Pithead](https://github.com/p2pool-st 1. Fork the repo and create a topic branch off `develop`. 2. Make your change and confirm `shellcheck` passes. 3. Open a PR against `develop` and fill out the template. -4. **All PRs require review** before merging; a code owner will take a look. +4. All PRs require review before merging; a code owner will take a look. Keep PRs focused and the description clear about what changed and why. Small, reviewable changes get merged faster. By contributing, you agree that your contributions are licensed under the project's [MIT License](LICENSE). - -Thanks again for contributing! πŸ”₯ diff --git a/README.md b/README.md index 90405c4..99960dd 100644 --- a/README.md +++ b/README.md @@ -12,40 +12,41 @@ [![Miner: XMRig](https://img.shields.io/badge/Miner-XMRig-F26822?logo=monero&logoColor=white)](https://github.com/xmrig/xmrig) [![Companion: Pithead](https://img.shields.io/badge/Companion-Pithead-F26822)](https://github.com/p2pool-starter-stack/pithead) -RigForge turns a fresh Ubuntu/Debian (or macOS) machine into a tuned [XMRig](https://github.com/xmrig/xmrig) -mining worker. It installs the toolchain, compiles XMRig from source, applies kernel- and CPU-level -tuning for RandomX hashrate, and runs it as a managed service. Point it at a pool and you're done. +RigForge provisions a tuned [XMRig](https://github.com/xmrig/xmrig) mining worker on a fresh +Ubuntu/Debian (or macOS) machine. It installs the toolchain, compiles XMRig from source, applies +kernel- and CPU-level tuning for RandomX hashrate, and runs it as a managed service. Point it at a +pool to start mining. It works against any RandomX Stratum pool, and it's the companion miner for -[Pithead](https://github.com/p2pool-starter-stack/pithead): connect as many RigForge workers as +[Pithead](https://github.com/p2pool-starter-stack/pithead). Connect as many RigForge workers as you like to your stack's single endpoint. -> **RigForge is not a custom miner.** It compiles stock, upstream XMRig and wraps it in the setup, +> RigForge is not a custom miner. It compiles stock, upstream XMRig and wraps it in the setup, > hardware tuning, and service management that are otherwise fiddly to get right by hand. --- ## ✨ What it does -- **Automated setup** β€” installs build dependencies (`cmake`, `libuv`, `hwloc`, …) and compiles a - pinned, commit-verified XMRig from source. -- **Hardware-aware tuning** β€” leans on XMRig's cache-aware auto-detection (thread count, assembly - path, MSR preset, NUMA) and layers on dedicated-miner defaults for maximum hashrate. -- **Kernel & system tuning (Linux)** β€” topology-aware HugePages (1 GB and 2 MB), MSR access for - hardware-prefetcher control, and `hugetlbfs` mounts + memlock limits. -- **Service management (Linux)** β€” runs XMRig as a `systemd` service with a `cpupower` performance - governor and automatic log rotation. -- **Interactive config** β€” if no config exists, it asks for the one thing it needs: your pool URL. -- **Idempotent** β€” re-running skips the recompile when the pinned XMRig is already built and never +- Installs build dependencies (`cmake`, `libuv`, `hwloc`, …) and compiles a pinned, commit-verified + XMRig from source. +- Tunes for the detected CPU: XMRig's cache-aware auto-detection (thread count, assembly path, MSR + preset, NUMA), plus dedicated-miner defaults. +- Applies kernel and system tuning on Linux: topology-aware HugePages (1 GB and 2 MB), MSR access + 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. +- Is idempotent: re-running skips the recompile when the pinned XMRig is already built and never double-applies system tuning. --- ## πŸ“Š Does it actually help? -Measured on a Ryzen **7800X3D**, **mining live** to a real pool (not a synthetic `--bench`): +Measured on a Ryzen **7800X3D**, mining live to a real pool (not a synthetic `--bench`): | | Stock XMRig | RigForge | | |---|--:|--:|:--| @@ -54,9 +55,9 @@ Measured on a Ryzen **7800X3D**, **mining live** to a real pool (not a synthetic | **Efficiency** | 120.1 H/s/W | **129.2 H/s/W** | **+7.6%** | Without HugePages the CPU stalls on memory, so stock XMRig draws more power for less work. RigForge is -faster and runs cooler, in one command. On a 48-core EPYC the gap is bigger (+6.6%), and RigForge -matched an expert's hand-tuned config while avoiding a prefetch setting that halves RandomX on that -chip. Full method, both CPUs, and the caveats: [Benchmarks](docs/benchmarks.md). +faster and runs cooler. On a 48-core EPYC the gap is +6.6%, and RigForge matched an expert's +hand-tuned config while avoiding a prefetch setting that halves RandomX on that chip. Full method, +both CPUs, and the caveats: [Benchmarks](docs/benchmarks.md). --- @@ -75,10 +76,10 @@ The script needs root to install packages and tune the system. On first run it a and writes a minimal `config.json`. On Linux, reboot once afterward to apply the HugePages tuning; the `xmrig` service then starts automatically. -> **Mining to a public pool like SupportXMR?** Point `url` at the pool and set your **Monero wallet** -> as the pool `user` (most pools also want their TLS port) β€” see +> Mining to a public pool like SupportXMR? Point `url` at the pool and set your Monero wallet +> as the pool `user` (most pools also want their TLS port). See > [Configuration β€Ί Connecting to a public pool](docs/configuration.md#connecting-to-a-public-pool-supportxmr-etc). -> With a [Pithead](https://github.com/p2pool-starter-stack/pithead) stack you need no wallet β€” just the +> With a [Pithead](https://github.com/p2pool-starter-stack/pithead) stack you need no wallet, just the > stack's `host:3333`. ➑️ **Full walkthrough:** [docs/getting-started.md](docs/getting-started.md) @@ -94,7 +95,7 @@ the `xmrig` service then starts automatically. | **[Benchmarks](docs/benchmarks.md)** | Measured stock-vs-tuned hashrate and efficiency on real hardware, with the method and caveats. | | **[Configuration](docs/configuration.md)** | Every `config.json` key and default, and how the XMRig config is generated. | | **[Operations & Maintenance](docs/operations.md)** | The full command reference, service management, logs, upgrades, and troubleshooting. | -| **[How It Works](docs/how-it-works.md)** | What the script actually does β€” compile, HugePages, MSR, NUMA, governor, service. | +| **[How It Works](docs/how-it-works.md)** | What the script actually does: compile, HugePages, MSR, NUMA, governor, service. | | **[Pithead Integration](docs/pithead-integration.md)** | The worker ↔ dashboard contract: discovery via `:3333`, the read-only API on `:8080`, and the token rules. | | **[FAQ](docs/faq.md)** | Common questions, plus why RigForge vs. doing it by hand. | @@ -105,7 +106,7 @@ Browse the full index at **[docs/](docs/README.md)**. ## πŸ› οΈ Common commands The everyday tasks, each a single command. There's a task-by-task cheat sheet in -[Operations β€Ί Common tasks](docs/operations.md#common-tasks): +[Operations β€Ί Common tasks](docs/operations.md#common-tasks). ```bash # Change a setting β€” edit config.json first, then: @@ -128,17 +129,17 @@ See [Operations β€Ί Commands](docs/operations.md#commands) for the full referenc RigForge runs as root, so it's worth being explicit about what it does and doesn't do: -- **No telemetry, ever.** No analytics, no version ping, no usage beacon. The only outbound traffic is - to *your* pool, to the pinned XMRig source on GitHub (cloned and **commit-verified** before building), +- No telemetry. No analytics, no version ping, no usage beacon. The only outbound traffic is + to *your* pool, to the pinned XMRig source on GitHub (cloned and commit-verified before building), and to your distro's package mirrors. -- **Honest dev fee.** The XMRig donation defaults to **1%** β€” XMRig's own upstream default, not a - RigForge markup β€” and goes to the XMRig project. Set `"DONATION": 0` to turn it off. -- **Read-only stats API.** Each worker exposes XMRig's HTTP API on `:8080` for the +- The XMRig donation defaults to **1%**. That's XMRig's own upstream default, not a RigForge markup, + and it goes to the XMRig project. Set `"DONATION": 0` to turn it off. +- Each worker exposes XMRig's HTTP API on `:8080` for the [Pithead](https://github.com/p2pool-starter-stack/pithead) dashboard. It's `restricted` (read-only, can't control the miner) and token-gated. It binds the LAN by default; if you mine solo or to a public pool you don't need it at all and can firewall the port off. -Full detail β€” and the exact `ufw` commands to lock down `:8080` β€” are in [SECURITY.md](./SECURITY.md). +Full detail, and the exact `ufw` commands to lock down `:8080`, are in [SECURITY.md](./SECURITY.md). --- @@ -157,21 +158,21 @@ make smoke # release pre-tag gate (quick): real xmrig --bench proves the b make e2e-real # release pre-tag gate (full): real build+tune+bench+doctor+uninstall on a rig (root) ``` -**What `make test` covers** β€” it sources `rigforge.sh` and exercises its functions in isolation, with +**What `make test` covers.** It sources `rigforge.sh` and exercises its functions in isolation, with every external/privileged command (`git`, `make`, `cmake`, `sudo`, `systemctl`, `modprobe`, `apt-get`, …) and all hardware detection (`uname`, `lscpu`, `sysctl`, `nproc`, `hostname`) replaced by -fakes on `PATH`. Because the hardware is faked, **one run on any machine simulates every supported -platform** β€” it asserts the generated XMRig config for EPYC / Ryzen X3D / generic-Linux inputs and the +fakes on `PATH`. Because the hardware is faked, one run on any machine simulates every supported +platform. It asserts the generated XMRig config for EPYC / Ryzen X3D / generic-Linux inputs and the macOS path, plus config parsing, `DONATION` validation, host resolution, and a full stubbed deployment run (executed twice to prove idempotency). -**What `make test-e2e` adds** β€” it runs the *real* `rigforge.sh` end-to-end inside a throwaway +**What `make test-e2e` adds.** It runs the *real* `rigforge.sh` end-to-end inside a throwaway `ubuntu` container (RigForge's documented Linux target, `linux/amd64`), against a real, disposable -`/etc`. This validates the Linux-only deploy path with genuine tools β€” GNU `sed`, `envsubst`, the -`fstab`/`limits`/GRUB edits and their idempotency β€” which can't run natively on a macOS host. Only the +`/etc`. This validates the Linux-only deploy path with genuine tools (GNU `sed`, `envsubst`, the +`fstab`/`limits`/GRUB edits and their idempotency) which can't run natively on a macOS host. Only the heavy XMRig compile and the package install are stubbed. It skips cleanly if Docker isn't available. -No XMRig binary is compiled by the tests β€” the heavy native build is stubbed; the suite asserts the +No XMRig binary is compiled by the tests; the heavy native build is stubbed. The suite asserts the *orchestration* (clone β†’ patch `donate.h` β†’ cmake β†’ make) and the generated configuration instead. **`make coverage`** measures line coverage of `rigforge.sh` + `util/proposed-grub.sh` by running the @@ -179,13 +180,13 @@ suite under [kcov](https://github.com/SimonKagstrom/kcov) (in a digest-pinned co Linux/ptrace based). The black-box tests run the *real* script against a sandbox via `RIGFORGE_HOME`, so both the sourced functions and the command-dispatch paths are credited. CI enforces two gates: a committed **total floor** ([`tests/coverage-floor.txt`](tests/coverage-floor.txt), ratcheted up over -time) and, the important lever, **patch coverage** β€” new/changed lines must be tested (`diff-cover` -against `main`). Neither needs an external service. +time) and, the important lever, **patch coverage** (`diff-cover` against `main`) so new/changed lines +must be tested. Neither needs an external service. **`make smoke`** closes that gap at release time. Because the suites never compile or run XMRig, they can't prove the shipped binary actually starts and hashes. `make smoke` benches a real worker (`xmrig --bench`, fully offline) on a real rig and passes only if a hashrate is reported and the run is -clean β€” it's a manual, Linux-only-for-full-effect pre-tag gate, not a CI job. See +clean. It's a manual, Linux-only-for-full-effect pre-tag gate, not a CI job. See [RELEASING.md](./RELEASING.md). For how RigForge is versioned and released, see [RELEASING.md](./RELEASING.md) and diff --git a/RELEASING.md b/RELEASING.md index 82ffd5b..448fd0c 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -5,23 +5,23 @@ tracked in [`VERSION`](./VERSION) and the history in [`CHANGELOG.md`](./CHANGELO ## Versioning -- **MAJOR** β€” incompatible `config.json` / CLI / behavior changes. -- **MINOR** β€” new, backwards-compatible functionality. -- **PATCH** β€” backwards-compatible fixes. +- MAJOR: incompatible `config.json` / CLI / behavior changes. +- MINOR: new, backwards-compatible functionality. +- PATCH: backwards-compatible fixes. -From `1.0.0` on, the `config.json` and CLI surface is stable, so a breaking change bumps **MAJOR**. (Pre-1.0 +From `1.0.0` on, the `config.json` and CLI surface is stable, so a breaking change bumps MAJOR. (Pre-1.0 `0.x` releases could break the interface between minor versions while it settled.) ## Cutting a release -Work lands on **`develop`** (the integration branch); a release is the point where `develop` is +Work lands on `develop` (the integration branch); a release is the point where `develop` is promoted to `main` and tagged. The steps below build the release commit on `develop`, merge it to `main`, and tag from `main`. 1. Ensure `develop` is green: `make test` (and `make test-e2e` if Docker is available). -2. **Full real-hardware e2e (the release gate).** CI exercises everything it can (lint, the +2. Full real-hardware e2e (the release gate). CI exercises everything it can (lint, the dependency-free suite, the Docker `/etc` e2e, the coverage gate), but it can't compile XMRig, - reserve HugePages, write MSRs, set the governor, or actually hash. So on a **real Linux rig**, run + reserve HugePages, write MSRs, set the governor, or actually hash. So on a real Linux rig, run the genuine deploy end to end and assert each step: ```bash @@ -31,16 +31,16 @@ promoted to `main` and tagged. The steps below build the release commit on `deve sudo bash tests/e2e-real.sh teardown # uninstall + assert a clean revert ``` - Each phase must report `E2E-REAL (): PASS`. This is what proves a release bundle actually + Each phase must report `E2E-REAL (): PASS`. This proves a release bundle actually builds, tunes, and hashes on real hardware, which the suites can't since they all stub XMRig. - - **Put a real, reachable pool in `config.json` first.** Without one, `setup` writes an unroutable - placeholder and `verify` **fails** the connect + share-submission round-trip. That round-trip is + - Put a real, reachable pool in `config.json` first. Without one, `setup` writes an unroutable + placeholder and `verify` fails the connect + share-submission round-trip. That round-trip is mandatory, since proving the rig really mines is the whole point of the gate. Point `pools[0].url` at a real low-difficulty pool you control (e.g. the stack's test pool). For a deliberate offline smoke run with no pool on hand, set `E2E_ALLOW_OFFLINE_POOL=1` to downgrade it to an explicit skip. - - **Quick subset:** `make smoke` (bench-only) is the fast version when you just need to confirm a + - Quick subset: `make smoke` (bench-only) is the fast version when you just need to confirm a built worker still hashes; the full `e2e-real` flow above supersedes it for a real release. - - Kept **out of CI** on purpose (a real build + HugePages + mining are flaky by nature and against + - Kept out of CI on purpose (a real build + HugePages + mining are flaky by nature and against Actions' ToS); it's a manual pre-tag gate the releaser runs. 3. In [`CHANGELOG.md`](./CHANGELOG.md), move the `## [Unreleased]` entries under a new `## [X.Y.Z] - YYYY-MM-DD` heading, then leave a fresh empty `## [Unreleased]` above it. @@ -58,28 +58,28 @@ promoted to `main` and tagged. The steps below build the release commit on `deve git checkout main && git merge --ff-only develop && git push origin main ``` -7. Tag and push from `main` (annotated tag, **matching `VERSION`**): +7. Tag and push from `main` (annotated tag, matching `VERSION`): ```bash git tag -a vX.Y.Z -m "RigForge vX.Y.Z" git push origin main --follow-tags ``` -Pushing the tag triggers the **release pipeline** +Pushing the tag triggers the release pipeline ([`.github/workflows/release.yml`](./.github/workflows/release.yml)), which: -- **verifies** the tag matches `VERSION` (the build fails otherwise), +- verifies the tag matches `VERSION` (the build fails otherwise), - packages the deploy bundle (`rigforge.sh`, `util/`, `systemd/`, `config.json.template`, `config.advanced.example.json`, `README.md`, `docs/`, `images/`, `LICENSE`, `VERSION`) as `rigforge-vX.Y.Z.zip` and `.tar.gz` (`tests/`, `.github/`, and other dev files are excluded), - generates `SHA256SUMS` for the artifacts, - pulls that version's section from [`CHANGELOG.md`](./CHANGELOG.md) as the release notes, -- creates the GitHub Release as a **draft** β€” review the generated notes and bundles, then click - **Publish** (pre-1.0 `0.x` tags are marked pre-release; `1.0.0`+ are full releases). +- creates the GitHub Release as a draft. Review the generated notes and bundles, then click + Publish (pre-1.0 `0.x` tags are marked pre-release; `1.0.0`+ are full releases). To verify a downloaded bundle: `sha256sum -c SHA256SUMS`. -> The release is created as a **draft** so a human reviews it before it goes public, a deliberate gate +> The release is created as a draft so a human reviews it before it goes public, a deliberate gate > for a tool that installs a root miner. Drop `--draft` from `release.yml` to auto-publish on tag instead. ## Notes diff --git a/SECURITY.md b/SECURITY.md index c4c00e8..63e2067 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,33 +2,33 @@ RigForge compiles XMRig from upstream source and applies privileged system tuning. It runs as root, configures kernel HugePages and MSR access, and -installs a `systemd` service. Because of that footprint, we take security -reports seriously and appreciate responsible disclosure. +installs a `systemd` service. Given that footprint, we take security reports +seriously and appreciate responsible disclosure. ## What RigForge exposes (and what it doesn't) -**No telemetry, ever.** RigForge never phones home. There is no analytics, no +No telemetry, ever. RigForge never phones home. There is no analytics, no version ping, and no usage beacon. The only outbound connections it makes are to *your* pool, to the pinned XMRig source on GitHub (a shallow clone whose commit is verified against a hardcoded hash before it's built), and to your distro's package -mirrors. The XMRig developer donation defaults to **1%**, XMRig's own upstream +mirrors. The XMRig developer donation defaults to 1%, XMRig's own upstream default rather than a RigForge markup. It goes to the XMRig project's address (RigForge -substitutes no wallet of its own into the mining path), and is set to **0** with +substitutes no wallet of its own into the mining path), and is set to 0 with `"DONATION": 0` in `config.json`. -**The worker stats API (`:8080`).** Each worker runs XMRig's HTTP API so a +The worker stats API (`:8080`). Each worker runs XMRig's HTTP API so a [Pithead](https://github.com/p2pool-starter-stack/pithead) dashboard can read per-rig stats over the LAN. Know exactly what it is: -- **Read-only.** It's configured `restricted: true`, so the API can be read but +- Read-only. It's configured `restricted: true`, so the API can be read but never used to control the miner (no remote pause, config change, or shutdown). -- **Token-gated.** Reads require a bearer token (the rig name by default), so it +- Token-gated. Reads require a bearer token (the rig name by default), so it isn't an open endpoint. -- **LAN-bound by default.** It binds `0.0.0.0:8080` because the Pithead dashboard +- LAN-bound by default. It binds `0.0.0.0:8080` because the Pithead dashboard polls each worker from the stack host. The data it can return is mining stats: hashrate, the configured pool URL, the worker label, and the CPU model. -**Not running Pithead?** Nothing else needs the port; `tune` and `doctor` read +Not running Pithead? Nothing else needs the port; `tune` and `doctor` read the API over `127.0.0.1`. So if you mine solo or to a public pool, you can firewall `:8080` off entirely without losing anything: @@ -41,12 +41,12 @@ sudo ufw allow from to any port 8080 proto tcp # … or scope i RigForge is built to be reproducible and tamper-evident: -- **Pinned, verified inputs.** XMRig is cloned at a pinned commit and verified against a hardcoded +- Pinned, verified inputs. XMRig is cloned at a pinned commit and verified against a hardcoded hash before it builds; GitHub Actions are SHA-pinned; CI tool installs (shellcheck, shfmt, gitleaks) are version- and checksum-verified. Dependabot keeps the action pins current and flags advisories. -- **Secret scanning.** [gitleaks](https://github.com/gitleaks/gitleaks) scans the full git history on +- Secret scanning. [gitleaks](https://github.com/gitleaks/gitleaks) scans the full git history on every push and PR, and runs as a pre-commit hook, so credentials can't slip into the repo. -- **Workflow auditing.** [zizmor](https://github.com/zizmorcore/zizmor) static-audits the CI workflows +- Workflow auditing. [zizmor](https://github.com/zizmorcore/zizmor) static-audits the CI workflows for template injection, over-broad token scopes, and credential persistence; jobs run with a least-privilege, read-only `GITHUB_TOKEN` by default. @@ -62,7 +62,7 @@ before reporting. ## Reporting a vulnerability -**Please do not open a public issue for security problems.** +Please do not open a public issue for security problems. Instead, use GitHub's private vulnerability reporting on this repository: diff --git a/docs/README.md b/docs/README.md index 1a8a9f5..5b6e486 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,6 +1,6 @@ # Documentation -Everything you need to provision, configure, and operate a **RigForge** mining worker. +Reference for provisioning, configuring, and operating a RigForge mining worker. New here? Start with the [Getting Started](getting-started.md) guide. It takes you from a fresh Ubuntu machine to a tuned, running XMRig worker in one command. The other guides go deeper on @@ -15,7 +15,7 @@ individual topics once you're up and running. | [Benchmarks](benchmarks.md) | Measured stock-vs-tuned hashrate and efficiency (H/s per watt) on real hardware, mining live, with the method and caveats. | | [Configuration](configuration.md) | Every `config.json` key and default, minimal vs. advanced setups, and how the XMRig config is generated. | | [Operations & Maintenance](operations.md) | The full command reference, service management, logs, upgrades, and troubleshooting. | -| [How It Works](how-it-works.md) | What the script actually does β€” dependencies, compile-from-source, HugePages, MSR, NUMA, the governor, and the systemd service. | +| [How It Works](how-it-works.md) | What the script actually does: dependencies, compile-from-source, HugePages, MSR, NUMA, the governor, and the systemd service. | | [Pithead Integration](pithead-integration.md) | The worker ↔ dashboard contract: discovery via `:3333`, the read-only HTTP API on `:8080`, and the token rules. | | [FAQ](faq.md) | Common questions, plus why RigForge vs. setting XMRig up by hand. | diff --git a/docs/benchmarks.md b/docs/benchmarks.md index c8a1f6c..7c1124d 100644 --- a/docs/benchmarks.md +++ b/docs/benchmarks.md @@ -1,29 +1,27 @@ -# Benchmarks β€” what the tuning actually buys you +# Benchmarks -RigForge's pitch is "stock XMRig, but with the setup and tuning that are fiddly to get right by -hand." So how much does that tuning actually move the needle? Every number here is **measured on real -hardware, mining live**, not synthetic `--bench` runs, so it reflects what you'd see in the wild. +What RigForge's tuning buys you over stock XMRig. Every number here is measured on real hardware, mining +live, not synthetic `--bench` runs. -> **TL;DR** β€” across two very different CPUs, RigForge's one-command tuning beats stock XMRig by -> **+3.5%** (desktop Ryzen 7800X3D) to **+6.6%** (48-core EPYC 7642) in hashrate, and **+7.6% / +6.0%** -> in efficiency (H/s per watt). On the EPYC it also **matched an expert's hand-tuned config**, and -> auto-dodged a CPU-specific landmine: a prefetch setting that halves RandomX there but wins on the -> X3D. All in one command, with the nuance kept in. +> TL;DR: across two very different CPUs, RigForge's one-command tuning beats stock XMRig by +3.5% +> (desktop Ryzen 7800X3D) to +6.6% (48-core EPYC 7642) in hashrate, and +7.6% / +6.0% in efficiency (H/s +> per watt). On the EPYC it also matched an expert's hand-tuned config, and auto-dodged a CPU-specific +> landmine: a prefetch setting that halves RandomX there but wins on the X3D. ## How it's measured -Each configuration runs as its own XMRig process **mining to the live pool**. After a warm-up to steady -state, we sample the **hashrate** (XMRig's HTTP API, 60-second average) and **CPU-package power** (RAPL -energy counter) over several-minute windows, repeated across rounds. RandomX is low-variance and both -rigs were thermally steady, so the means are solid (hashrate within ~0.1%). +Each configuration runs as its own XMRig process mining to the live pool. After a warm-up to steady +state, we sample the hashrate (XMRig's HTTP API, 60-second average) and CPU-package power (RAPL energy +counter) over several-minute windows, repeated across rounds. RandomX is low-variance and both rigs were +thermally steady, so the means are solid (hashrate within ~0.1%). The baselines: -- **Stock XMRig** β€” upstream `./xmrig` on a fresh box: no explicit HugePages, prefetcher MSRs at - firmware default, default governor. (Transparent HugePages stay at Ubuntu's `madvise` default, as - a real user would have.) -- **RigForge** β€” `setup`'s kernel work (2 MB + 1 GB **HugePages**, the per-family **MSR prefetcher** - preset, **`performance`** governor) plus the winning knobs from a full live `tune`. +- Stock XMRig: upstream `./xmrig` on a fresh box: no explicit HugePages, prefetcher MSRs at firmware + default, default governor. Transparent HugePages stay at Ubuntu's `madvise` default, as a real user + would have. +- RigForge: `setup`'s kernel work (2 MB + 1 GB HugePages, the per-family MSR prefetcher preset, + `performance` governor) plus the winning knobs from a full live `tune`. ## Rig 1 β€” Ryzen 7800X3D (desktop) @@ -42,15 +40,15 @@ The baselines: | **Stock β†’ tuned** | **+3.5%** | **βˆ’3.8%** | **+7.6%** | Stock XMRig here burns more watts for less work. Without HugePages the CPU stalls on memory, drawing -~87 W to produce fewer hashes; tuned is faster and cooler. Performance and efficiency tuning landed -on the **same** config: RigForge measured the power and found this chip pins ~84 W in any all-core setup, -so there's no hashrate-for-watts trade-off to make, and it correctly didn't invent one. +~87 W to produce fewer hashes; tuned is faster and cooler. Performance and efficiency tuning landed on the +same config: RigForge measured the power and found this chip pins ~84 W in any all-core setup, so there's +no hashrate-for-watts trade-off to make, and it didn't invent one. ## Rig 2 β€” EPYC 7642 (48-core server) Β· RigForge vs an expert hand-tune -This box is the interesting one: it was already running a **hand-tuned** miner (the worker an operator had -configured for this EPYC by hand) at 36,860 H/s. So it's not just "RigForge vs naive XMRig"; it's -RigForge's one-command auto-tune against a human who tuned it themselves. +This box was already running a hand-tuned miner (the worker an operator had configured for this EPYC by +hand) at 36,860 H/s. So it's not just "RigForge vs naive XMRig"; it's RigForge's one-command auto-tune +against a human who tuned it themselves. | | | |---|---| @@ -64,28 +62,27 @@ RigForge's one-command auto-tune against a human who tuned it themselves. | **RigForge** β€” tuned (XMRig 6.26) | **36,866** | 229.5 | **160.6** | | **Stock β†’ RigForge** | **+6.6%** | ~flat | **+6.0%** | -- **RigForge matched the human expert** (within 0.02%), and did it with a newer XMRig (6.26 vs 6.25), - `cpu.yield` off (the expert left it on), and **5Γ— fewer HugePages** (266 vs 1,280 reserved, both hitting - 100%) β€” i.e. the same result with ~2 GB less RAM tied up. -- **The auto-tune dodged a landmine.** On this EPYC, **prefetch mode 2 halves the hashrate** (~17,900 - H/s), the exact opposite of the 7800X3D, where mode 2 is the winner. A fixed "golden profile" would - get one of these two chips badly wrong; the per-CPU live tune measured it and stayed on the right mode - for each. -- The HugePages win is **bigger here** (+6.6% vs the X3D's +3.5%): a 4-NUMA EPYC with a per-node dataset - leans much harder on huge pages than a single-die desktop chip. Efficiency and performance tuning again +- RigForge matched the human expert (within 0.02%), and did it with a newer XMRig (6.26 vs 6.25), + `cpu.yield` off (the expert left it on), and 5Γ— fewer HugePages (266 vs 1,280 reserved, both hitting + 100%): the same result with ~2 GB less RAM tied up. +- The auto-tune dodged a landmine. On this EPYC, prefetch mode 2 halves the hashrate (~17,900 H/s), the + exact opposite of the 7800X3D, where mode 2 is the winner. A fixed "golden profile" would get one of + these two chips badly wrong; the per-CPU live tune measured it and stayed on the right mode for each. +- The HugePages win is bigger here (+6.6% vs the X3D's +3.5%): a 4-NUMA EPYC with a per-node dataset leans + much harder on huge pages than a single-die desktop chip. Efficiency and performance tuning again converged (power ~230 W in any config). ## Caveats (read before quoting a number) -- **Two CPUs, two systems, and RandomX gains vary a lot.** A desktop X3D and a 48-core EPYC already differ by +- Two CPUs, two systems, and RandomX gains vary a lot. A desktop X3D and a 48-core EPYC already differ by more than 3Γ— in raw hashrate; your CPU, RAM speed, NUMA layout, and kernel all matter. Treat the percentages as illustrative, not a guarantee. -- **Modern kernels narrow the stock gap.** Ubuntu 24.04's Transparent HugePages (`madvise`) back some of - even the "stock" allocation with 2 MB pages, so the stock baseline is closer to tuned than on an older - kernel or with THP off. Don't expect the 20–30% some older write-ups quote. -- **`setup` does the heavy lifting; `tune` refines.** Most of the win is the system tuning; the knob - search confirms you're at the optimum (and, as the EPYC shows, keeps you off the landmines) rather than - adding a big jump on top. +- Modern kernels narrow the stock gap. Ubuntu 24.04's Transparent HugePages (`madvise`) back some of even + the "stock" allocation with 2 MB pages, so the stock baseline is closer to tuned than on an older kernel + or with THP off. Don't expect the 20–30% some older write-ups quote. +- `setup` does the heavy lifting; `tune` refines. Most of the win is the system tuning; the knob search + confirms you're at the optimum (and, as the EPYC shows, keeps you off the landmines) rather than adding + a big jump on top. ## Reproduce it diff --git a/docs/configuration.md b/docs/configuration.md index 001e30f..63ce4ad 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1,23 +1,22 @@ # Configuration -RigForge is driven by a small `config.json` in the repo root. It holds only the handful of things the -script can't infer β€” everything else (CPU profile, thread count, HugePage sizing) is detected and -applied for you. +RigForge reads a small `config.json` in the repo root. It holds only what the script can't infer. The +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 [`config.json.template`](../config.json.template). -Every field is **validated** when setup parses the config. A malformed pool URL, an out-of-range port, -a bad hostname, a non-boolean flag, or an unsafe `HOME_DIR` stops setup with a clear message rather than +Setup validates every field when it parses the config. A malformed pool URL, an out-of-range port, a +bad hostname, a non-boolean flag, or an unsafe `HOME_DIR` stops setup with a message rather than producing a config the miner would reject. --- ## Minimal config -The only thing you must set is the **pool**. RigForge uses XMRig's native `pools` array, and a pool -only needs its `url` (a `host:port`). Everything else falls back to a sensible default: +The only required field is the pool. RigForge uses XMRig's native `pools` array, and a pool only needs +its `url` (a `host:port`). Everything else falls back to a default: ```json { @@ -30,15 +29,14 @@ only needs its `url` (a `host:port`). Everything else falls back to a sensible d 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. -> **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). -> Jump to [Connecting to a public pool](#connecting-to-a-public-pool-supportxmr-etc) for a copy-paste -> example. +> 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 +> [Connecting to a public pool](#connecting-to-a-public-pool-supportxmr-etc) for a copy-paste example. -> **Two-tier config (like Pithead).** Keep `config.json` minimal and only add the keys you actually -> want to change. [`config.advanced.example.json`](../config.advanced.example.json) is a reference that -> lists every key with its default. Copy in only what you need; anything you omit keeps the default. -> The reference table below documents each key. +> Two-tier config (like Pithead): keep `config.json` minimal and add only the keys you want to change. +> [`config.advanced.example.json`](../config.advanced.example.json) lists every key with its default. +> Copy in what you need; anything you omit keeps the default. The reference table below documents each +> key. --- @@ -47,64 +45,64 @@ proxy listens on `3333`). The interactive first-run setup writes exactly this mi | Key | Default | What it does | |---|---|---| | `pools` | *(required)* | XMRig's native pools array β€” the pool(s) to mine to. Each entry needs a `url` (`host:port`); every other field falls back to a Pithead default. A pool's `user` is the rig's dashboard label (defaults to the hostname). List multiple entries for failover. See [Pools](#pools-full-control). | -| `ACCESS_TOKEN` | `""` *(open)* | Optional bearer token for the XMRig HTTP API. **Unset (default) leaves the read-only API open** β€” which matches Pithead's default no-auth probe, so it just works. Set a value to require a `Bearer` token; then match it on the dashboard (`workers.api_auth: token` + `workers.api_token`, or `name` if you set it to the rig name). See [Pithead Integration](pithead-integration.md). | -| `DONATION` | `1` | XMRig donate level, an integer **0–100** (percent). Patched into the build (`donate.h`) **and** written to the generated config, so it must be a valid integer or setup fails fast. | +| `ACCESS_TOKEN` | `""` *(open)* | Optional bearer token for the XMRig HTTP API. Unset (default) leaves the read-only API open, which matches Pithead's default no-auth probe. Set a value to require a `Bearer` token, then match it on the dashboard (`workers.api_auth: token` + `workers.api_token`, or `name` if you set it to the rig name). See [Pithead Integration](pithead-integration.md). | +| `DONATION` | `1` | XMRig donate level, an integer 0–100 (percent). Patched into the build (`donate.h`) and written to the generated config, so it must be a valid integer or setup fails fast. | | `HOME_DIR` | `DYNAMIC_HOME` | Where worker files live. `DYNAMIC_HOME` puts them in `data/worker` inside the repo; set an absolute path to use `/worker` instead. | -| `autotune` | `"disabled"` | Periodic live tuning, as a target: `"disabled"` (default) installs no timer; `"performance"` schedules a periodic tune for **raw hashrate**; `"efficiency"` schedules one for **hashrate-per-watt** (needs a power source β€” built-in RAPL or `TUNE_POWER_CMD` β€” else it falls back to `performance` with a warning). Legacy booleans still parse (`true` β†’ `performance`, `false` β†’ `disabled`). This key controls the *schedule*; to run one live pass by hand, use `tune --now` (or `tune --now --long` for a full all-knob sweep). See [Operations β€Ί Live auto-tuning](operations.md#live-auto-tuning-opt-in). | -| `add_to_path` | `false` | When `true`, setup installs a `rigforge` command on your PATH (a symlink in `/usr/local/bin`) so you can run `sudo rigforge ` from any directory. Off by default β€” setup makes no system-wide convenience change you didn't ask for. `uninstall` removes it. | +| `autotune` | `"disabled"` | Periodic live tuning, as a target: `"disabled"` (default) installs no timer; `"performance"` schedules a periodic tune for raw hashrate; `"efficiency"` schedules one for hashrate-per-watt (needs a power source, built-in RAPL or `TUNE_POWER_CMD`, else it falls back to `performance` with a warning). Legacy booleans still parse (`true` β†’ `performance`, `false` β†’ `disabled`). This key controls the schedule; to run one live pass by hand, use `tune --now` (or `tune --now --long` for a full all-knob sweep). See [Operations β€Ί Live auto-tuning](operations.md#live-auto-tuning-opt-in). | +| `add_to_path` | `false` | When `true`, setup installs a `rigforge` command on your PATH (a symlink in `/usr/local/bin`) so you can run `sudo rigforge ` from any directory. Off by default. `uninstall` removes it. | --- ## How the generated XMRig config is built -You don't write XMRig's config. RigForge generates the whole thing in-script and writes it into the -worker root as the live `config.json` the service runs from. There's no template file to keep in sync -and no config key for it. Every run (re-runs included) rebuilds the config from four sources: +You don't write XMRig's config. RigForge generates it in-script and writes it into the worker root as +the live `config.json` the service runs from. There's no template file to keep in sync and no config +key for it. Every run (re-runs included) rebuilds the config from four sources: -1. **Your `config.json`** β€” the `pools` array (with `user`/`pass`/`keepalive`/`tls` and failover +1. Your `config.json`: the `pools` array (with `user`/`pass`/`keepalive`/`tls` and failover defaults filled in), the `donate-level`, and the `http` API block (bound to the LAN, read-only, - open by default β€” set `ACCESS_TOKEN` for a bearer token). These are the keys documented in the + open by default; set `ACCESS_TOKEN` for a bearer token). These are the keys documented in the [reference table](#configuration-reference). -2. **Detected hardware** β€” the per-CPU `cpu`/`randomx` tuning (thread count, `asm`, MSR, NUMA, - HugePages). RigForge leans on XMRig's own cache-aware auto-detection rather than a CPU-model table, +2. Detected hardware: the per-CPU `cpu`/`randomx` tuning (thread count, `asm`, MSR, NUMA, + HugePages). RigForge uses XMRig's own cache-aware auto-detection rather than a CPU-model table, so it stays correct for CPUs it's never seen. See [Hardware Requirements](hardware.md). -3. **Static defaults** β€” the fixed knobs every worker shares, emitted directly: `autosave`, +3. Static defaults: the fixed knobs every worker shares, emitted directly: `autosave`, `randomx.mode: fast`, `randomx.init`, `opencl`/`cuda` off, and the `http` port `8080`. -4. **Tuned overrides** *(if present)* β€” if you've run [`tune`](operations.md#tuning), its winning +4. Tuned overrides *(if present)*: if you've run [`tune`](operations.md#tuning), its winning knobs in `tune-overrides.json` are merged on top as the final step, so tuning wins for just the keys it sets and your `config.json` is never edited. Because the config is rebuilt from these sources every time, editing the generated `config.json` by -hand is pointless. Change your repo-root `config.json` (or `tune`) and re-run instead. +hand has no effect. Change your repo-root `config.json` (or `tune`) and re-run instead. -> ⚠️ **Don't put a wallet address in the worker `user` when using Pithead.** The stack handles -> payouts centrally; the pool `user` is just a rig **label** (it defaults to the hostname so you can -> tell workers apart on the dashboard). +> Don't put a wallet address in the worker `user` when using Pithead. The stack handles payouts +> centrally; the pool `user` is just a rig label (it defaults to the hostname so you can tell workers +> apart on the dashboard). --- ## Pools (full control) -The pool target is XMRig's native **`pools`** array, passed straight through to XMRig, so you can use any -field XMRig supports. Only `url` is **required**; every other field has a default, so you specify only -what you care about: +The pool target is XMRig's native `pools` array, passed straight through to XMRig, so you can use any +field XMRig supports. Only `url` is required; every other field has a default, so you specify only what +you care about: | Field | Default if blank/omitted | |---|---| | `url` | *(required)* β€” `host:port` (e.g. `pool.supportxmr.com:443` or `your-stack:3333`). For an IPv6 literal, use the bracketed `[2001:db8::1]:3333` form. | -| `user` | the machine hostname. For **Pithead** this is just the rig's dashboard **label**; for a **public pool** set it to your **Monero wallet address** (see below). | -| `pass` | `"x"` β€” the stratum password / worker name. For an **open** Pithead stack the default works; if the operator enabled the stack's `p2pool.stratum_password`, set this to that secret or the proxy rejects the rig. See [Pithead Integration β€Ί Stratum authentication](pithead-integration.md#stratum-authentication-optional). | +| `user` | the machine hostname. For Pithead this is the rig's dashboard label; for a public pool set it to your Monero wallet address (see below). | +| `pass` | `"x"` β€” the stratum password / worker name. For an open Pithead stack the default works; if the operator enabled the stack's `p2pool.stratum_password`, set this to that secret or the proxy rejects the rig. See [Pithead Integration β€Ί Stratum authentication](pithead-integration.md#stratum-authentication-optional). | | `keepalive` | `true` | | `tls` | `false` β€” set `true` when you connect on the pool's TLS/SSL port. | | `enabled` | `true` | -There are two common setups; pick the one that matches where you're mining. +Two common setups follow; pick the one that matches where you're mining. ### Connecting to a Pithead stack [Pithead](https://github.com/p2pool-starter-stack/pithead) handles pool selection, payouts, and the P2Pool/XvB split centrally, so the worker only needs the stack host and its proxy port (`3333`). The -`user` is just a **label** for the dashboard, so **don't put a wallet address here**: +`user` is just a label for the dashboard, so don't put a wallet address here: ```json { @@ -119,9 +117,9 @@ P2Pool/XvB split centrally, so the worker only needs the stack host and its prox ### Connecting to a public pool (SupportXMR, etc.) -A public pool pays **you**, so it needs your **Monero wallet address** as the login (`user`) and almost -always a **TLS port**. RigForge builds stock upstream XMRig, so it speaks standard Stratum to any -RandomX pool. Fill in the pool's endpoint and your wallet: +A public pool pays you, so it needs your Monero wallet address as the login (`user`) and almost always a +TLS port. RigForge builds stock upstream XMRig, so it speaks standard Stratum to any RandomX pool. Fill +in the pool's endpoint and your wallet: ```json { @@ -136,11 +134,11 @@ RandomX pool. Fill in the pool's endpoint and your wallet: } ``` -- **`user` = your Monero wallet address.** This is who gets paid. Many pools also accept +- `user` is your Monero wallet address. This is who gets paid. Many pools also accept `WALLET.workername` here to label the rig in their dashboard. -- **`pass` = a worker name** (or just `"x"`; most public pools ignore the password). -- **`url` + `tls` = the pool's stratum endpoint.** Use the pool's **TLS/SSL port** (often `:443` or - `:5555`) with `"tls": true`; a plain, unencrypted port needs no `tls`. Your pool's *Getting started* / +- `pass` is a worker name (or just `"x"`; most public pools ignore the password). +- `url` + `tls` is the pool's stratum endpoint. Use the pool's TLS/SSL port (often `:443` or `:5555`) + with `"tls": true`; a plain, unencrypted port needs no `tls`. Your pool's *Getting started* / *Connect* page lists its exact host, ports, and whether it wants `wallet` or `wallet.worker`. Save that as `config.json`, then `sudo ./rigforge.sh apply` (a fresh `setup` picks it up too). @@ -149,7 +147,7 @@ The pool host must be an IP or DNS-resolvable hostname; allow its Stratum port t ### Backup pools (failover) -List multiple entries. XMRig tries them **in order** and fails over to the next if one is unreachable, +List multiple entries. XMRig tries them in order and fails over to the next if one is unreachable, handy for a primary stack with a public-pool fallback: ```json @@ -174,22 +172,21 @@ Edit `config.json`, then apply it in one step: sudo ./rigforge.sh apply ``` -`apply` re-reads `config.json`, regenerates the live XMRig config, and **restarts the service**, with no -recompile. It's the fast, purpose-built path for a `pools` change, a new rig label, TLS, failover -pools, and the like. (On macOS there's no service, so `apply` regenerates the config and you restart -the miner yourself; see [Operations β€Ί Running on macOS](operations.md#running-on-macos).) - -You can also re-run full setup (`sudo ./rigforge.sh`), but that's meant for **re-provisioning** the -whole worker (dependencies, build, kernel tuning, service). To avoid interrupting a running miner, a -setup re-run on an already-built worker regenerates the config **without restarting**, so the new -config only takes effect on the next restart. When you just want to apply an edit, reach for `apply`; -it does the restart for you. Both are idempotent and skip the recompile when the pinned XMRig is -already built. - -> **Note on `DONATION`:** the donate level is also compiled into the XMRig binary at build time, so on -> an already-built worker neither `apply` nor a setup re-run changes it; both update only the runtime -> config. To re-patch the binary, force a rebuild: remove `/xmrig` (or bump the pinned -> XMRig) and run setup, or run [`upgrade`](operations.md#upgrading-xmrig-redeploy-after-a-git-pull) after bumping the pin. +`apply` re-reads `config.json`, regenerates the live XMRig config, and restarts the service, with no +recompile. It's the path for a `pools` change, a new rig label, TLS, failover pools, and the like. (On +macOS there's no service, so `apply` regenerates the config and you restart the miner yourself; see +[Operations β€Ί Running on macOS](operations.md#running-on-macos).) + +You can also re-run full setup (`sudo ./rigforge.sh`), but that re-provisions the whole worker +(dependencies, build, kernel tuning, service). To avoid interrupting a running miner, a setup re-run on +an already-built worker regenerates the config without restarting, so the new config only takes effect +on the next restart. To apply an edit, use `apply`; it does the restart for you. Both are idempotent and +skip the recompile when the pinned XMRig is already built. + +> NOTE: `DONATION` is also compiled into the XMRig binary at build time, so on an already-built worker +> neither `apply` nor a setup re-run changes it; both update only the runtime config. To re-patch the +> binary, force a rebuild: remove `/xmrig` (or bump the pinned XMRig) and run setup, or run +> [`upgrade`](operations.md#upgrading-xmrig-redeploy-after-a-git-pull) after bumping the pin. --- diff --git a/docs/faq.md b/docs/faq.md index 997cf33..b848a74 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -1,71 +1,66 @@ # FAQ -Common questions about what RigForge is, what it does for you, and how it compares to setting XMRig up -by hand. New here? Start with [Getting Started](getting-started.md). The deeper "how" lives in -[How It Works](how-it-works.md). +Common questions about RigForge and how it compares to setting XMRig up by hand. New here? Start with +[Getting Started](getting-started.md). The mechanics are in [How It Works](how-it-works.md). --- ## Why RigForge vs. doing it by hand? -You can build, tune, and run XMRig yourself β€” it's an excellent, well-documented miner. Doing -it by hand means: +You can build, tune, and run XMRig yourself; it's a well-documented miner. By hand that means: - Installing the build toolchain and compiling XMRig from source. - Reading the [RandomX optimization guide](https://xmrig.com/docs/miner/randomx-optimization-guide) and - hand-configuring HugePages (1 GB + 2 MB), MSR registers, NUMA, and thread layout **for your specific - CPU**. -- Editing GRUB for persistent HugePages β€” without clobbering your existing kernel parameters. + hand-configuring HugePages (1 GB + 2 MB), MSR registers, NUMA, and thread layout for your specific CPU. +- Editing GRUB for persistent HugePages, without clobbering your existing kernel parameters. - Wiring up a systemd service, a performance governor, and log rotation. - Redoing the CPU-specific parts every time you deploy a different machine. -RigForge does all of that in one command, with tuning auto-detected from your CPU, idempotent -re-runs, and a build pinned to an audited XMRig version. It's the difference between a one-off -afternoon of tuning and `sudo ./rigforge.sh`. If you enjoy hand-wiring it, the manual route is a good -learning exercise; RigForge compiles **stock upstream XMRig**, so you're never locked into a -custom fork. +RigForge does that in one command: tuning auto-detected from your CPU, idempotent re-runs, and a build +pinned to an audited XMRig version. The manual route is a fine learning exercise. RigForge compiles stock +upstream XMRig, so you're never locked into a custom fork. --- ## Is RigForge a custom miner? -No. RigForge compiles **stock, upstream [XMRig](https://github.com/xmrig/xmrig)**; it doesn't fork or -modify the miner itself. All it changes at build time is the donate level, so your configured -`DONATION` is honored. Everything else is standard XMRig plus the setup/tuning/service wrapping. +No. RigForge compiles stock, upstream [XMRig](https://github.com/xmrig/xmrig); it doesn't fork or modify +the miner. The only build-time change is the donate level, so your configured `DONATION` is honored. +Everything else is standard XMRig plus the setup, tuning, and service wrapping. --- ## Do I need a specific XMRig version? -No. RigForge always builds a pinned, recent upstream XMRig, and any RandomX-capable XMRig (5.0+, 2019) -speaks the standard Stratum protocol that pools and Pithead's proxy accept. There's no version coupling -between the miner and the stack. +No. RigForge builds a pinned, recent upstream XMRig. Any RandomX-capable XMRig (5.0+, 2019) speaks the +standard Stratum protocol that pools and Pithead's proxy accept. There's no version coupling between the +miner and the stack. --- ## What hardware do I need? -A 64-bit x86 CPU with **AVX2**, ~2.3 GB of free RAM for RandomX fast mode (4 GB+ recommended), and, -for the HugePages/MSR speedups, a Linux box you can reboot once. Full sizing and how the tuning is chosen -are in [Hardware Requirements](hardware.md). Hashrate scales with cores **and L3 cache** (RandomX wants -~2 MB of L3 per thread). +A 64-bit x86 CPU with AVX2, ~2.3 GB of free RAM for RandomX fast mode (4 GB+ recommended), and, for the +HugePages/MSR speedups, a Linux box you can reboot once. Sizing and how the tuning is chosen are in +[Hardware Requirements](hardware.md). Hashrate scales with cores and L3 cache (RandomX wants ~2 MB of L3 +per thread). --- ## Do I have to use Pithead? No. RigForge points XMRig at any RandomX Stratum pool: set that pool's endpoint as a `pools[].url`. -Pithead is the flagship integration (the API and discovery contract is wired up out of the box), -but it's not required. See [Configuration β€Ί Pools](configuration.md#pools-full-control). +Pithead is the flagship integration (the API and discovery contract is wired up by default), but it's not +required. See [Configuration β€Ί Pools](configuration.md#pools-full-control). --- ## Do I put my wallet address in the worker? -**It depends on the pool.** With **Pithead** the stack handles payouts centrally, so you don't: the -worker only needs the stack host, and the `user` field is just a rig label. With a **public pool** -(SupportXMR and the like) you do. The pool pays whoever logs in, so set `pools[].user` to your **Monero -wallet address**. There's a copy-paste example in +It depends on the pool. With Pithead the stack handles payouts centrally, so you don't: the worker only +needs the stack host, and the `user` field is just a rig label. With a public pool (SupportXMR and the +like) you do. The pool pays whoever logs in, so set `pools[].user` to your Monero wallet address. There's +a copy-paste example in [Configuration β€Ί Connecting to a public pool](configuration.md#connecting-to-a-public-pool-supportxmr-etc). --- @@ -75,29 +70,30 @@ wallet address**. There's a copy-paste example in Edit `config.json`, then run `sudo ./rigforge.sh apply`. That regenerates the live XMRig config and restarts the worker, with no rebuild. `apply` is the everyday command for config edits; `setup` is for (re-)provisioning, and `upgrade` is for moving to a newer pinned XMRig. See -[Configuration β€Ί Changing settings later](configuration.md#changing-settings-later). (On macOS, `apply` -regenerates the config; run `./rigforge.sh restart` to pick it up β€” see [Operations β€Ί Running on macOS](operations.md#running-on-macos).) +[Configuration β€Ί Changing settings later](configuration.md#changing-settings-later). On macOS, `apply` +regenerates the config; run `./rigforge.sh restart` to pick it up. See +[Operations β€Ί Running on macOS](operations.md#running-on-macos). --- ## Why does it need a reboot? -On Linux, persistent **HugePages** are configured via GRUB, which only takes effect after a reboot. -That's the single biggest RandomX performance lever. macOS doesn't expose HugePages, so it needs no -reboot. See [How It Works β€Ί Kernel tuning](how-it-works.md#kernel--system-tuning-linux-only). +On Linux, persistent HugePages are configured via GRUB, which only takes effect after a reboot. That's +the single biggest RandomX performance lever. macOS doesn't expose HugePages, so it needs no reboot. See +[How It Works β€Ί Kernel tuning](how-it-works.md#kernel--system-tuning-linux-only). --- ## I see MSR errors in the log. What's wrong? -Almost always **Secure Boot** blocking the `msr` kernel module. Disable Secure Boot in your BIOS/UEFI -and reboot. See [Operations β€Ί Troubleshooting](operations.md#troubleshooting). +Almost always Secure Boot blocking the `msr` kernel module. Disable Secure Boot in your BIOS/UEFI and +reboot. See [Operations β€Ί Troubleshooting](operations.md#troubleshooting). --- ## Is it safe to re-run the script? -Yes β€” `setup` is idempotent. It skips the recompile when the pinned XMRig is already built, never +Yes. `setup` is idempotent. It skips the recompile when the pinned XMRig is already built, never duplicates system-file edits (`fstab`, `limits.conf`, `/etc/modules`), merges (not overwrites) GRUB parameters, and archives a prior install rather than clobbering it. To rebuild only when the pinned version changes, use [`upgrade`](operations.md#upgrading-xmrig-redeploy-after-a-git-pull). @@ -107,30 +103,29 @@ version changes, use [`upgrade`](operations.md#upgrading-xmrig-redeploy-after-a- ## If I lose the disk (or have many machines), do I have to set up and tune each one again? No, that's what `backup`/`restore` are for. `sudo ./rigforge.sh backup` snapshots the only -expensive-to-recreate state (your `config.json` and the tuning result) into `./backups`. After data -loss, `restore` it and re-run `setup` instead of re-tuning from scratch. For a **fleet**, tune one -machine, back it up, and `restore` the archive on each identical machine so they all share the same -config and tuning. Tuning is CPU-specific, so only reuse it between **identical** CPUs. See +expensive-to-recreate state (your `config.json` and the tuning result) into `./backups`. After data loss, +`restore` it and re-run `setup` instead of re-tuning from scratch. For a fleet, tune one machine, back it +up, and `restore` the archive on each identical machine so they all share the same config and tuning. +Tuning is CPU-specific, so only reuse it between identical CPUs. See [Operations β€Ί Backup & restore](operations.md#backup--restore). --- ## 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. +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. --- ## Is macOS supported? -macOS works for development and light use (RigForge builds and configures XMRig there), but **Ubuntu -is the supported deployment target**. The Linux-only tuning (HugePages, MSR, systemd, governor) doesn't -apply on macOS, which the macOS CPU profile accounts for, so the hashrate is lower than a tuned Linux -box. There's no systemd service either, so the miner doesn't auto-start; launch it with -`./rigforge.sh start` (the same `start`/`stop`/`restart`/`status`/`logs` verbs work on macOS). Full -details on what differs, how to run it, and which commands are Linux-only are in -[Operations β€Ί Running on macOS](operations.md#running-on-macos). +macOS works for development and light use (RigForge builds and configures XMRig there), but Ubuntu is the +supported deployment target. The Linux-only tuning (HugePages, MSR, systemd, governor) doesn't apply on +macOS, which the macOS CPU profile accounts for, so the hashrate is lower than a tuned Linux box. There's +no systemd service either, so the miner doesn't auto-start; launch it with `./rigforge.sh start` (the same +`start`/`stop`/`restart`/`status`/`logs` verbs work on macOS). What differs, how to run it, and which +commands are Linux-only are in [Operations β€Ί Running on macOS](operations.md#running-on-macos). --- diff --git a/docs/getting-started.md b/docs/getting-started.md index 1aad35f..c251d45 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -1,7 +1,7 @@ # Getting Started -This guide takes you from a fresh machine to a tuned, running XMRig worker. A single script, -`rigforge.sh`, drives the whole process, and most of it is automated. +Install and tune an XMRig worker from a fresh machine. A single script, `rigforge.sh`, drives the +whole process, and most of it is automated. > **TL;DR** > @@ -27,11 +27,11 @@ This guide takes you from a fresh machine to a tuned, running XMRig worker. A si | **Privileges** | `root`. The script installs packages and tunes the kernel, so run it with `sudo`. | | **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. | -> πŸ“ **Full sizing guidance** β€” 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 +> πŸ“ 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 > separately in [Pithead's hardware guide](https://github.com/p2pool-starter-stack/pithead/blob/main/docs/hardware.md). -You don't need to install build dependencies yourself β€” RigForge installs the toolchain (`cmake`, +You don't need to install build dependencies yourself. RigForge installs the toolchain (`cmake`, `libuv`, `hwloc`, …) for you on first run. You only need `git` to clone the repo. --- @@ -44,12 +44,12 @@ cd rigforge chmod +x rigforge.sh ``` -Have your **pool URL** ready β€” a `host:port`. For a Pithead stack that's the stack machine's address -and its proxy port `3333` (e.g. `stack.lan:3333`); with Pithead you do **not** need a wallet β€” the stack -handles payouts centrally. +Have your **pool URL** ready, a `host:port`. For a Pithead stack that's the stack machine's address +and its proxy port `3333` (e.g. `stack.lan:3333`); with Pithead you do **not** need a wallet, since the +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 +> 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 > `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). @@ -129,6 +129,6 @@ grep -i msr data/worker/xmrig.log # MSR mod applied (no errors) ## Next steps -- [Configuration](configuration.md) β€” every config key, and how the XMRig config is generated. -- [Operations & Maintenance](operations.md) β€” the command reference, logs, upgrades, troubleshooting. -- [Pithead Integration](pithead-integration.md) β€” how the dashboard discovers and reads each worker. +- [Configuration](configuration.md): every config key, and how the XMRig config is generated. +- [Operations & Maintenance](operations.md): the command reference, logs, upgrades, troubleshooting. +- [Pithead Integration](pithead-integration.md): how the dashboard discovers and reads each worker. diff --git a/docs/hardware.md b/docs/hardware.md index f88ee22..0f4bf39 100644 --- a/docs/hardware.md +++ b/docs/hardware.md @@ -1,11 +1,10 @@ # Hardware Requirements -A worker is where the actual RandomX hashing happens, so its **CPU is what determines your -hashrate**. The requirements themselves are modest; most of the performance comes from tuning, -which RigForge applies for you. +A worker is where the RandomX hashing happens, so its CPU determines your hashrate. The requirements +are modest; most of the performance comes from tuning, which RigForge applies for you. -> This page sizes the **miner**. The **stack host** your workers connect to (Monero node, P2Pool, -> proxy, dashboard) is sized separately; see Pithead's +> This page sizes the miner. The stack host your workers connect to (Monero node, P2Pool, proxy, +> dashboard) is sized separately; see Pithead's > [Hardware Requirements](https://github.com/p2pool-starter-stack/pithead/blob/main/docs/hardware.md). --- @@ -14,35 +13,35 @@ which RigForge applies for you. | Resource | Requirement | Recommended | |---|---|---| -| **CPU** | 64-bit x86 with **AVX2** support | A high-core-count CPU (e.g. AMD Ryzen / EPYC) β€” more and faster cores mean more hashrate. XMRig auto-detects the CPU and sizes the tuning to it. | -| **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+, Debian 12, or macOS | Ubuntu is the supported target. | -| **Network** | Reach your pool / stack host on its Stratum port (Pithead uses **3333**) | Local network; workers do **not** need Tor. | - -> 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 +| CPU | 64-bit x86 with AVX2 support | A high-core-count CPU (e.g. AMD Ryzen / EPYC); more and faster cores mean more hashrate. XMRig auto-detects the CPU and sizes the tuning to it. | +| 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+, Debian 12, or macOS | Ubuntu is the supported target. | +| Network | Reach your pool / stack host on its Stratum port (Pithead uses 3333) | Local network; workers do not need Tor. | + +> 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 > [RandomX optimization guide](https://xmrig.com/docs/miner/randomx-optimization-guide). ### A note on L3 cache -RandomX is bottlenecked by **L3 cache**, not core count alone: each mining thread wants ~2 MB of L3. -A CPU with lots of cores but little L3 can't feed every core, so the effective thread count is -roughly `L3 size Γ· 2 MB`. This is the math XMRig's auto thread-sizing uses (and the +RandomX is bottlenecked by L3 cache, not core count alone: each mining thread wants ~2 MB of L3. A CPU +with lots of cores but little L3 can't feed every core, so the effective thread count is roughly +`L3 size Γ· 2 MB`. This is the math XMRig's auto thread-sizing uses (and the `util/proposed-grub.sh` HugePage sizing helper). --- ## What "tuning" actually does -The bulk of a worker's performance comes from configuration RigForge applies automatically, not from -the raw silicon: +Most of a worker's performance comes from configuration RigForge applies automatically, not from the +raw silicon: -- **HugePages (1 GB + 2 MB)** β€” reduces TLB misses on the 2 GB RandomX dataset. Biggest single win. -- **MSR registers** β€” `randomx.wrmsr` tells XMRig to disable the hardware prefetchers (they hurt +- HugePages (1 GB + 2 MB): reduces TLB misses on the 2 GB RandomX dataset. Biggest single win. +- MSR registers: `randomx.wrmsr` tells XMRig to disable the hardware prefetchers (they hurt RandomX's random access pattern). XMRig auto-detects your CPU family and applies the right preset. -- **Thread count, ASM, NUMA** β€” XMRig auto-detects these from the CPU topology (see below). -- **Performance governor** β€” `cpupower` pins the CPU to its performance frequency under load. +- Thread count, ASM, NUMA: XMRig auto-detects these from the CPU topology (see below). +- Performance governor: `cpupower` pins the CPU to its performance frequency under load. The full mechanics are in [How It Works](how-it-works.md). @@ -50,12 +49,12 @@ The full mechanics are in [How It Works](how-it-works.md). ## How RigForge tunes -RigForge does not keep a table of CPU models. Instead it relies on XMRig's own cache-aware -auto-detection and layers on a few defaults that make sense because the box is a **dedicated** miner: +RigForge does not keep a table of CPU models. It relies on XMRig's own cache-aware auto-detection and +adds a few defaults that make sense because the box is a dedicated miner: | Setting | Value | Why | |---|---|---| -| `cpu.rx` | `-1` (auto) | XMRig sizes the thread count to L3 cache (~2 MB/thread) from detected topology β€” correct on EPYC, Ryzen, Intel hybrid, and X3D (incl. dual-CCD parts) alike. | +| `cpu.rx` | `-1` (auto) | XMRig sizes the thread count to L3 cache (~2 MB/thread) from detected topology; correct on EPYC, Ryzen, Intel hybrid, and X3D (incl. dual-CCD parts) alike. | | `cpu.asm` | `auto` | XMRig picks the Ryzen / Intel / Bulldozer assembly path for the detected CPU. | | `randomx.wrmsr` | `true` | Auto-applies the correct per-family MSR preset (needs root + the `msr` module). | | `randomx.numa` | `true` | A no-op on single-NUMA machines; on multi-NUMA CPUs it gives each node its own dataset copy. Note a single-socket EPYC can still expose several NUMA nodes, so RigForge sizes the 1 GB HugePage reservation per NUMA node rather than per socket. | @@ -63,13 +62,13 @@ auto-detection and layers on a few defaults that make sense because the box is a | `cpu.priority` | `2` | Wins scheduling vs. background daemons (XMRig warns >2 can hang a desktop). | | `cpu.huge-pages` / `randomx.1gb-pages` | `true` (Linux) | The single biggest lever; see below. | -> **Why not a per-model lookup table?** XMRig's auto-config is cache-aware and updated every release, -> so it gets thread placement right for CPUs a static table would miss or mishandle. Take **dual-CCD -> X3D** parts (7950X3D/7900X3D), where only one CCD has the V-cache and blindly using all cores -> would push threads onto the slow CCD. Letting XMRig decide is both simpler and more correct. +> Why not a per-model lookup table? XMRig's auto-config is cache-aware and updated every release, so it +> gets thread placement right for CPUs a static table would miss or mishandle. Take dual-CCD X3D parts +> (7950X3D/7900X3D), where only one CCD has the V-cache and using all cores would push threads onto the +> slow CCD. Letting XMRig decide is both simpler and more correct. -The only branch that remains is **OS-level**: macOS has no HugePages or MSRs, so those are disabled and -the API binds IPv6 `::` instead of `0.0.0.0`. +The only branch that remains is OS-level: macOS has no HugePages or MSRs, so those are disabled and the +API binds IPv6 `::` instead of `0.0.0.0`. The resulting XMRig config (pools, donate level, API, CPU section) lives under your worker root; see [Configuration](configuration.md#how-the-generated-xmrig-config-is-built) for how it's generated. diff --git a/docs/how-it-works.md b/docs/how-it-works.md index 6695c72..130043e 100644 --- a/docs/how-it-works.md +++ b/docs/how-it-works.md @@ -1,8 +1,8 @@ # How It Works -RigForge is **not a custom miner**. It compiles stock, upstream [XMRig](https://github.com/xmrig/xmrig) -and wraps it in the setup, hardware tuning, and service management that are otherwise fiddly to get -right by hand. This page explains what the script does, step by step. +RigForge compiles stock, upstream [XMRig](https://github.com/xmrig/xmrig) and wraps it in the setup, +hardware tuning, and service management that are otherwise fiddly to get right by hand. It is not a +custom miner. This page describes what the script does, step by step. --- @@ -11,25 +11,25 @@ right by hand. This page explains what the script does, step by step. A `setup` run executes these stages in order. Each is idempotent, so re-running skips work that's already done. -1. **Prerequisites** β€” detects the OS (Linux vs. macOS) and installs `jq` if it's missing. Privileged +1. Prerequisites: detect the OS (Linux vs. macOS) and install `jq` if it's missing. Privileged steps use `sudo` as needed, so run the script with `sudo` (or as root). -2. **Config** β€” creates a minimal `config.json` interactively if none exists, then parses and validates +2. Config: create a minimal `config.json` interactively if none exists, then parse and validate it (see [Configuration](configuration.md)). -3. **Rebuild decision** β€” figures out whether XMRig actually needs (re)building, based on the pinned +3. Rebuild decision: decide whether XMRig needs (re)building, based on the pinned version/commit vs. what's already compiled. -4. **Workspace** β€” prepares the worker root. Any prior install is archived rather than clobbered, and +4. Workspace: prepare the worker root. Any prior install is archived rather than clobbered, and old archives are pruned so re-runs don't leak disk. -5. **Dependencies** β€” installs the build toolchain and runtime libraries for the OS (`cmake`, `libuv`, +5. Dependencies: install the build toolchain and runtime libraries for the OS (`cmake`, `libuv`, `hwloc`, OpenSSL, …). -6. **Compile** β€” clones XMRig at the pinned commit, patches the donate level, and builds it. Output is +6. Compile: clone XMRig at the pinned commit, patch the donate level, and build it. Output is captured to a logfile; `make -j` is capped by available RAM to avoid OOM on low-memory hosts. -7. **Generate config** β€” detects the CPU and writes the tuned XMRig `config.json` (pools, donate level, +7. Generate config: detect the CPU and write the tuned XMRig `config.json` (pools, donate level, HTTP API, and the per-CPU `cpu`/`randomx` sections). -8. **Kernel tuning (Linux)** β€” HugePages, MSR, and module loading. -9. **Limits (Linux)** β€” `hugetlbfs` mounts, `fstab`, and memlock limits. -10. **Service (Linux)** β€” installs and enables the `xmrig` systemd unit with a performance governor and +8. Kernel tuning (Linux): HugePages, MSR, and module loading. +9. Limits (Linux): `hugetlbfs` mounts, `fstab`, and memlock limits. +10. Service (Linux): install and enable the `xmrig` systemd unit with a performance governor and log rotation. -11. **Finish** β€” prints next steps (and, if the kernel was tuned, the reboot prompt). +11. Finish: print next steps (and, if the kernel was tuned, the reboot prompt). --- @@ -37,67 +37,67 @@ already done. RigForge builds XMRig from source rather than shipping a binary: -- **Pinned** to a known `XMRIG_VERSION` / `XMRIG_COMMIT`. The checkout is verified against the pinned +- Pinned to a known `XMRIG_VERSION` / `XMRIG_COMMIT`. The checkout is verified against the pinned commit (`git rev-parse HEAD` must match `XMRIG_COMMIT`, or the build aborts), so every worker runs the same audited source and supply-chain risk is bounded. -- **Donate level patched at build time.** The configured `DONATION` is `sed`'d into `donate.h` so the +- Donate level patched at build time. The configured `DONATION` is `sed`'d into `donate.h` so the compiled binary honors it (XMRig's floor is otherwise 1%). It's also written into the runtime config. Because this patch happens during the compile, changing `DONATION` after XMRig is already built only updates the runtime config; re-patching the binary requires a rebuild (see [Configuration](configuration.md#changing-settings-later)). -- **Memory-guarded parallelism.** `make -j` is capped based on available RAM, so the build doesn't OOM +- Memory-guarded parallelism. `make -j` is capped based on available RAM, so the build doesn't OOM on small machines. -- **Idempotent.** If the pinned build already exists, setup skips the slow recompile entirely. The +- Idempotent. If the pinned build already exists, setup skips the recompile. The [`upgrade`](operations.md#upgrading-xmrig-redeploy-after-a-git-pull) command rebuilds only when the pin changes. --- ## Hardware tuning -The hashrate win comes mostly from configuration rather than the silicon. RigForge leans on XMRig's own +Most of the hashrate comes from configuration, not the silicon. RigForge uses XMRig's own auto-detection and adds dedicated-miner defaults: -- **Auto-detected thread count, ASM path, MSR preset and NUMA** β€” XMRig reads the CPU topology and +- Auto-detected thread count, ASM path, MSR preset and NUMA. XMRig reads the CPU topology and sizes everything to it (`cpu.rx: -1`, `cpu.asm: auto`, `randomx.wrmsr: true`, `randomx.numa: true`), which stays correct for CPUs a model-name table would miss. See [Hardware β€Ί How RigForge tunes](hardware.md#how-rigforge-tunes). -- **Dedicated-miner defaults**: `cpu.yield: false` (busy-wait for max hashrate) and `cpu.priority: 2`. -- **RandomX fast mode**: the full 2 GB dataset in memory for maximum hashrate. -- **Thread layout sized to L3.** RandomX wants ~2 MB of L3 per thread, so XMRig sizes threads to the +- Dedicated-miner defaults: `cpu.yield: false` (busy-wait for max hashrate) and `cpu.priority: 2`. +- RandomX fast mode: the full 2 GB dataset in memory for maximum hashrate. +- Thread layout sized to L3. RandomX wants ~2 MB of L3 per thread, so XMRig sizes threads to the detected L3 rather than using every core. --- ## Measured tuning: the `tune` search -The defaults above are good, but a handful of knobs have a best value that genuinely varies per CPU: the -RandomX prefetch mode, `cpu.yield`, the thread count and placement (`cpu.rx`), `1gb-pages`, and, opt-in, +The defaults above are good, but a handful of knobs have a best value that varies per CPU: the RandomX +prefetch mode, `cpu.yield`, the thread count and placement (`cpu.rx`), `1gb-pages`, and, opt-in, `cpu.huge-pages-jit` and `randomx.cache_qos`. The `tune` command measures the best value instead of -guessing. By default it's an iterative, noise-aware **coordinate hill-climb**: +guessing. By default it's an iterative, noise-aware coordinate hill-climb: -1. **Seed.** Start from two candidate configurations, XMRig's auto baseline and an educated guess, so +1. Seed. Start from two candidate configurations, XMRig's auto baseline and an educated guess, so the search can escape a local optimum one seed happens to land in. -2. **Climb.** Sweep one knob at a time. For each, benchmark its candidate values (holding the others +2. Climb. Sweep one knob at a time. For each, benchmark its candidate values (holding the others fixed) and adopt the best, but only if it beats the current best by a minimum relative margin, so benchmark noise can't masquerade as a win. -3. **Repeat until plateau.** Run passes over all knobs until a full pass yields no improvement, or a +3. Repeat until plateau. Run passes over all knobs until a full pass yields no improvement, or a round cap is hit. For a small knob space where you'd rather not risk a local optimum at all, `TUNE_SEARCH=grid` switches to -an **exhaustive** search of every combination: slower, but guaranteed to find the global best. +an exhaustive search of every combination: slower, but guaranteed to find the global best. -The thread search is **SMT-aware**. Rather than only nudging Β±1 around the L3 Γ· 2 MB estimate, it also +The thread search is SMT-aware. Rather than only nudging Β±1 around the L3 Γ· 2 MB estimate, it also tries XMRig's own auto value and the physical- and logical-core counts, because RandomX often peaks at one thread per physical core (SMT siblings share the L2/L3 each thread needs). A few design choices keep it reliable and cheap on jittery RandomX hardware: -- **Median over max.** Each candidate is measured as the median of several `xmrig --bench` runs, so one +- Median over max. Each candidate is measured as the median of several `xmrig --bench` runs, so one lucky spike doesn't crown a worse config. -- **Contention-free.** In `--bench` mode `tune` stops the miner service for the run (restarting it +- Contention-free. In `--bench` mode `tune` stops the miner service for the run (restarting it after, even if interrupted), so the benchmark isn't fighting a live miner for cores and huge pages. That contention is the biggest source of bogus readings. -- **Memoized.** Because a coordinate climb keeps revisiting the current point, every measured +- Memoized. Because a coordinate climb keeps revisiting the current point, every measured combination is cached, so a combo is never benchmarked twice. Reboot-bound knobs are handled explicitly. `1gb-pages` only matters once 1G HugePages are reserved (a @@ -111,7 +111,7 @@ Every part of the search is overridable; the defaults favour a thorough one-time | Env var | Default | Meaning | |---|---|---| -| `TUNE_SEARCH` | `climb` | `climb` (hill-climb, fast) or `grid` (exhaustive over all knob combos, robust but slower). | +| `TUNE_SEARCH` | `climb` | `climb` (hill-climb, fast) or `grid` (exhaustive over all knob combos, thorough but slower). | | `TUNE_ITERS` | `5` | Benchmark runs per candidate; the median is used. | | `TUNE_BENCH` | `10M` | `xmrig --bench` size. Longer = steadier and closer to sustained load; set `1M` for a quick pass. | | `TUNE_MIN_DELTA` | `0.01` | Minimum *relative* gain (1%) needed to adopt a change. | @@ -138,19 +138,19 @@ rather than the raw-fastest, useful for a power-cost or heat/PSU-constrained rig it warns and falls back to `perf`. To measure whole-system wall power instead of the CPU package alone, point `TUNE_POWER_CMD` at a source that echoes instantaneous watts. -The **periodic `autotune`** takes the same target: set `"autotune": "efficiency"` in `config.json` and the +The periodic `autotune` takes the same target: set `"autotune": "efficiency"` in `config.json` and the scheduled run ranks prefetch modes by hashrate-per-watt (sampling watts over the same live window), instead of `"performance"`'s raw H/s. The target is baked into the systemd unit at setup; the same RAPL/`TUNE_POWER_CMD` sources and the same fall-back-to-`perf`-with-a-warning behavior apply. See [Operations β†’ Live auto-tuning](operations.md#live-auto-tuning-opt-in). -> **`hs_per_watt` is relative, not absolute.** It only compares candidates measured by the same method on +> NOTE: `hs_per_watt` is relative, not absolute. It only compares candidates measured by the same method on > the same machine. Built-in RAPL counts the CPU package only (not RAM, board, PSU loss); a smart plug > counts whole-wall AC. Don't compare the number across methods or across rigs. ### Reservation-aware thread tuning -RandomX wants its scratchpads backed by **HugePages**. `setup` reserves a pool sized for an estimated thread +RandomX wants its scratchpads backed by HugePages. `setup` reserves a pool sized for an estimated thread count; `tune` then benchmarks thread counts within that reservation. A thread count that needs more 2 MB pages than are reserved still runs, but the extra threads fall back to normal pages, so its benchmark is a floor rather than a fair reading. `tune` flags each such candidate `hugepages_capped: true` in @@ -173,17 +173,17 @@ see [Operations β€Ί Tuning](operations.md#tuning). ## Kernel & system tuning (Linux only) -These are why a **reboot** is needed on Linux: +These are why a reboot is needed on Linux: -- **HugePages (1 GB + 2 MB).** Backs the RandomX dataset with huge pages to cut TLB misses, the biggest +- HugePages (1 GB + 2 MB). Backs the RandomX dataset with huge pages to cut TLB misses, the biggest performance lever. Sizing is topology-aware (see `util/proposed-grub.sh`). Making it persistent edits - **GRUB**, which takes effect on reboot. RigForge merges its parameters into the existing + GRUB, which takes effect on reboot. RigForge merges its parameters into the existing `GRUB_CMDLINE_LINUX_DEFAULT` instead of overwriting it, so other kernel params are preserved (a boot-safety fix). -- **MSR access.** Loads the `msr` module and sets the hardware-prefetcher / cache model-specific +- MSR access. Loads the `msr` module and sets the hardware-prefetcher / cache model-specific registers XMRig recommends for the CPU. (Blocked by Secure Boot; see [troubleshooting](operations.md#troubleshooting).) -- **`hugetlbfs` mounts + memlock limits.** Mounts the 1 GB HugePage filesystem and raises `memlock` in +- `hugetlbfs` mounts + memlock limits. Mounts the 1 GB HugePage filesystem and raises `memlock` in `fstab` and `limits.conf` so XMRig can pin memory. These edits are applied once (append-only, deduplicated) so re-runs don't accumulate duplicate lines. @@ -195,16 +195,16 @@ XMRig accordingly (and there's no systemd service, so you run the miner yourself ## Service management (Linux) -- **systemd unit.** XMRig runs as the `xmrig` service, enabled at boot, restarting on failure. -- **`cpupower` performance governor.** Pins the CPU to its performance frequency so it isn't throttled +- systemd unit. XMRig runs as the `xmrig` service, enabled at boot, restarting on failure. +- `cpupower` performance governor. Pins the CPU to its performance frequency so it isn't throttled down mid-hash. -- **Log rotation.** A `logrotate` policy compresses and archives `xmrig.log`. -- **Hardened unit.** The service runs as root (required for the MSR mod and HugePages) but with +- Log rotation. A `logrotate` policy compresses and archives `xmrig.log`. +- Hardened unit. The service runs as root (required for the MSR mod and HugePages) but with defense-in-depth sandboxing: `NoNewPrivileges`, `ProtectSystem=full` (read-only `/usr`,`/etc`,…), `PrivateTmp`, `ProtectControlGroups`, `LockPersonality`, and `ReadWritePaths` limited to the worker root. Directives that would break RandomX are deliberately not set: `PrivateDevices` (hides `/dev/cpu/*/msr`), `MemoryDenyWriteExecute` (blocks the JIT), and `ProtectKernelModules`. -- **Scoped `memlock`.** Unlimited `memlock` is granted to the service (`LimitMEMLOCK=infinity`) and, +- Scoped `memlock`. Unlimited `memlock` is granted to the service (`LimitMEMLOCK=infinity`) and, for manual runs, to the mining user only in `limits.conf`, not to every account via `*`. --- @@ -213,12 +213,12 @@ XMRig accordingly (and there's no systemd service, so you run the miner yourself RigForge is built to be re-run: -- **Idempotent edits.** System-file changes (`fstab`, `limits.conf`, `/etc/modules`) are append-only +- Idempotent edits. System-file changes (`fstab`, `limits.conf`, `/etc/modules`) are append-only and deduplicated, so running setup twice never doubles a line. -- **Non-destructive workspace.** A prior install is archived, not overwritten. -- **Fail-fast with context.** An `ERR` trap names the step that failed; config input is validated - before the slow build starts. -- **Tested.** A dependency-free suite fakes all hardware and privileged commands so every supported +- Non-destructive workspace. A prior install is archived, not overwritten. +- Fail-fast with context. An `ERR` trap names the step that failed; config input is validated + before the build starts. +- Tested. A dependency-free suite fakes all hardware and privileged commands so every supported platform's config generation and a full deployment (run twice for idempotency) are asserted on any machine; a Docker end-to-end run validates the real Linux path. See the project README's testing section. diff --git a/docs/operations.md b/docs/operations.md index 13222dc..9f53f23 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -7,23 +7,23 @@ upgrading, and troubleshooting. ## Common tasks -Most days you'll only touch a handful of these. Each is a single command. The full [command +Most days you touch a handful of these. Each is a single command. The full [command reference](#commands) is below. | I want to… | Command | What happens | |---|---|---| -| **Change a setting** β€” pool, rig name, TLS, failover | edit `config.json`, then `sudo ./rigforge.sh apply` | Regenerates the live config and restarts. No rebuild. | -| **Redeploy after a `git pull`** | `git pull && sudo ./rigforge.sh upgrade` | Rebuilds + restarts (and re-tunes) **if** the XMRig pin moved; otherwise a no-op. See [the note below](#upgrading-xmrig-redeploy-after-a-git-pull). | -| **Run a live tune now** | `sudo ./rigforge.sh tune --now` | One live pass against the running miner; keeps the best prefetch mode if it wins. Linux only. | -| **Check the worker is healthy** | `sudo ./rigforge.sh doctor` | HugePages, MSR, governor, service, with a fix hint for anything off. | -| **Watch it mining** | `sudo ./rigforge.sh logs` | Live logs; `Ctrl-C` stops following (the miner keeps running). | -| **Stop / start / restart** | `sudo ./rigforge.sh stop` Β· `start` Β· `restart` | Control the miner service. | -| **Quick speed check** | `sudo ./rigforge.sh bench` | One-off offline benchmark; reports H/s. | -| **Save config + tuning** | `sudo ./rigforge.sh backup` | Snapshots the only hard-to-recreate state to `./backups`. | - -> On **macOS**, drop the `sudo` (the privileged steps are Linux-only) and run `./rigforge.sh restart` -> after `apply` to pick up changes. `doctor` and the live re-tunes (`tune --now`, `tune --live`) are -> Linux-only. See [Running on macOS](#running-on-macos). +| Change a setting (pool, rig name, TLS, failover) | edit `config.json`, then `sudo ./rigforge.sh apply` | Regenerates the live config and restarts. No rebuild. | +| Redeploy after a `git pull` | `git pull && sudo ./rigforge.sh upgrade` | Rebuilds + restarts (and re-tunes) if the XMRig pin moved; otherwise a no-op. See [the note below](#upgrading-xmrig-redeploy-after-a-git-pull). | +| Run a live tune now | `sudo ./rigforge.sh tune --now` | One live pass against the running miner; keeps the best prefetch mode if it wins. Linux only. | +| Check the worker is healthy | `sudo ./rigforge.sh doctor` | HugePages, MSR, governor, service, with a fix hint for anything off. | +| Watch it mining | `sudo ./rigforge.sh logs` | Live logs; `Ctrl-C` stops following (the miner keeps running). | +| Stop / start / restart | `sudo ./rigforge.sh stop` Β· `start` Β· `restart` | Control the miner service. | +| Quick speed check | `sudo ./rigforge.sh bench` | One-off offline benchmark; reports H/s. | +| Save config + tuning | `sudo ./rigforge.sh backup` | Snapshots the only hard-to-recreate state to `./backups`. | + +> On macOS, drop the `sudo` (the privileged steps are Linux-only) and run `./rigforge.sh restart` after +> `apply` to pick up changes. `doctor` and the live re-tunes (`tune --now`, `tune --live`) are Linux-only. +> See [Running on macOS](#running-on-macos). --- @@ -32,20 +32,20 @@ reference](#commands) is below. The complete surface. Most days you only need the handful in [Common tasks](#common-tasks) above; the rest are here for completeness. -RigForge is a single script. Run it as `sudo ./rigforge.sh [command]`. *(Optional: set +RigForge is a single script. Run it as `sudo ./rigforge.sh [command]`. Optional: set `"add_to_path": true` in `config.json` and setup installs a `rigforge` command on your PATH, so you can -run `sudo rigforge [command]` from any directory; `uninstall` removes it.)* +run `sudo rigforge [command]` from any directory; `uninstall` removes it. | Command | What it does | |---|---| | `setup` *(default)* | Provision the worker: dependencies, build, hardware + kernel tuning, and the service. Idempotent and safe to re-run; skips the recompile when the pinned XMRig is already built. | -| `upgrade` | Rebuild **and** restart **only if** the pinned XMRig version/commit changed. A no-op when you're already on the pinned build. If periodic autotune is enabled, it also **re-tunes the new build** (the fastest knobs can shift between versions). | -| `apply` | Re-read `config.json`, regenerate the live XMRig config, and restart, **without** recompiling. 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). | -| `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, HugePages are reserved, 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. | +| `upgrade` | Rebuild and restart only if the pinned XMRig version/commit changed. A no-op when you're already on the pinned build. If periodic autotune is enabled, it also re-tunes the new build (the fastest knobs can shift between versions). | +| `apply` | Re-read `config.json`, regenerate the live XMRig config, and restart, without recompiling. 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). | +| `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, HugePages are reserved, 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. | | `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). | -| `autotune` | The **scheduled** live tuner. You normally don't type it; `tune --now` is the friendlier spelling for an on-demand run, and the periodic schedule is what this verb is really for: set `"autotune": "performance"` (raw H/s) or `"autotune": "efficiency"` (hashrate-per-watt) in `config.json` and setup installs a systemd timer (also re-tuned on `upgrade`). Conservative: it keeps a change only if it beats the baseline by a margin, else rolls back. Linux-only. See [Live auto-tuning](#live-auto-tuning-opt-in). | +| `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). | +| `autotune` | The scheduled live tuner. You normally don't type it; `tune --now` is the friendlier spelling for an on-demand run, and the periodic schedule is what this verb is really for: set `"autotune": "performance"` (raw H/s) or `"autotune": "efficiency"` (hashrate-per-watt) in `config.json` and setup installs a systemd timer (also re-tuned on `upgrade`). Conservative: it keeps a change only if it beats the baseline by a margin, else rolls back. Linux-only. See [Live auto-tuning](#live-auto-tuning-opt-in). | | `backup` | Snapshot `config.json` + the tuning files into a timestamped `tar.gz` under `./backups`. See [Backup & restore](#backup--restore). | | `restore` | Restore `config.json` + tuning from a backup archive: `restore [-y] `. Prompts before overwriting. | | `status` | Show the systemd service status. | @@ -68,17 +68,17 @@ After setup (and the reboot), confirm everything took effect: sudo ./rigforge.sh doctor ``` -It's the quickest way to catch the common silent failures: HugePages not reserved (needs a reboot) or -the MSR mod blocked by Secure Boot. See [Troubleshooting](#troubleshooting). +This catches the common silent failures: HugePages not reserved (needs a reboot) or the MSR mod blocked +by Secure Boot. See [Troubleshooting](#troubleshooting). -> On a fresh install `setup` **enables** the service but doesn't start it until you reboot (HugePages -> aren't reserved before then), so a `doctor` run between `setup` and the reboot will report "service is -> not active". That's expected; it starts automatically after you reboot. +> On a fresh install `setup` enables the service but doesn't start it until you reboot (HugePages aren't +> reserved before then), so a `doctor` run between `setup` and the reboot will report "service is not +> active". That's expected; it starts automatically after you reboot. ### Tuning RigForge auto-configures the hashrate-critical settings, so a freshly-deployed worker already runs well. -`tune` is an **optional, one-time** step that measures the handful of knobs whose best value is genuinely +`tune` is an optional, one-time step that measures the handful of knobs whose best value is genuinely CPU-specific (the RandomX prefetch mode, `cpu.yield`, the thread count) and keeps the fastest: ```bash @@ -86,15 +86,15 @@ sudo ./rigforge.sh tune # measure the fastest knobs for this CPU β€” thoro sudo ./rigforge.sh apply # regenerate the config with them and restart ``` -> **Tune once, run for months.** The result is saved to a separate overlay (`tune-overrides.json`), so your +> Tune once, run for months. The result is saved to a separate overlay (`tune-overrides.json`), so your > `config.json` is never touched, and it's kept for the life of the rig. After an `upgrade` bumps XMRig, > RigForge reminds you to re-tune (the fastest knobs can shift between versions). -`tune` **optimizes for whatever your [`autotune`](configuration.md#configuration-reference) config is set to**, -so if `autotune` is `"efficiency"`, a plain `tune` measures hashrate-per-watt, matching what the scheduled -run does. Override per-run with `--perf` or `--efficiency`. It announces the target at the start, e.g. -`Optimization target: efficiency (hashrate-per-watt)`. Run it without `sudo` and it re-runs itself with -`sudo` for you. +`tune` optimizes for whatever your [`autotune`](configuration.md#configuration-reference) config is set +to, so if `autotune` is `"efficiency"`, a plain `tune` measures hashrate-per-watt, matching what the +scheduled run does. Override per-run with `--perf` or `--efficiency`. It announces the target at the +start, e.g. `Optimization target: efficiency (hashrate-per-watt)`. Run it without `sudo` and it re-runs +itself with `sudo` for you. See what's tuned, and what the periodic auto-tuner has been doing, at any time: @@ -102,13 +102,13 @@ See what's tuned, and what the periodic auto-tuner has been doing, at any time: ./rigforge.sh tune --history # applied knobs + the last full run + recent auto-tune decisions ``` -**Useful variants** (all optional): +Useful variants (all optional): | Command | What it does | |---|---| -| `tune --now` *(or `--short`)* | **Run a live tune now**: a quick convergent pass against the running miner that keeps the best prefetch mode if it wins. The everyday live re-tune; Linux only. | -| `tune --now --long` | A **full** live sweep of every knob (prefetch, `cpu.yield`, thread count, 1G-pages) against the running miner, not just the prefetch mode. Thorough but slower; measures your running pool's real conditions/algorithm. Alias: `tune --live`. Linux only. | -| `tune --efficiency` / `--perf` | Force the optimization target, **hashrate-per-watt** vs **raw speed**, overriding the `autotune` config default for this run (efficiency needs a power source). | +| `tune --now` *(or `--short`)* | Run a live tune now: a quick convergent pass against the running miner that keeps the best prefetch mode if it wins. The everyday live re-tune; Linux only. | +| `tune --now --long` | A full live sweep of every knob (prefetch, `cpu.yield`, thread count, 1G-pages) against the running miner, not just the prefetch mode. Thorough but slower; measures your running pool's real conditions/algorithm. Alias: `tune --live`. Linux only. | +| `tune --efficiency` / `--perf` | Force the optimization target, hashrate-per-watt vs raw speed, overriding the `autotune` config default for this run (efficiency needs a power source). | | `tune --confirm` | A/B-check the winner on the live miner and keep it only if it genuinely beats the previous config. Linux only. | | `tune --history` | Show the current tuning, the last full run, and recent auto-tune decisions. | | `tune --clear` | Discard all tuning and return to the auto defaults. | @@ -119,68 +119,67 @@ power/efficiency and reservation-aware details are all in ### Live auto-tuning (opt-in) -**Run one pass on demand** any time with `sudo ./rigforge.sh tune --now`. It sweeps the prefetch modes -against your running miner and keeps the best if it beats the current setting by a margin. Want a -thorough pass that sweeps **every** knob live (threads, yield, 1G-pages, not just prefetch)? Use -`tune --now --long` (the live equivalent of a bare `tune`). No scheduling needed; either is a quick way -to re-tune live after a BIOS, RAM, or cooling change. (`tune --now` is the friendly name for the -`autotune` engine; the standalone `autotune` verb still works and is what the scheduled timer below -runs.) +Run one pass on demand any time with `sudo ./rigforge.sh tune --now`. It sweeps the prefetch modes +against your running miner and keeps the best if it beats the current setting by a margin. For a thorough +pass that sweeps every knob live (threads, yield, 1G-pages, not just prefetch), use `tune --now --long` +(the live equivalent of a bare `tune`). No scheduling needed; either is a quick way to re-tune live after +a BIOS, RAM, or cooling change. `tune --now` is the friendly name for the `autotune` engine; the +standalone `autotune` verb still works and is what the scheduled timer below runs. -Prefer it hands-off? Set `autotune` in `config.json` to a target and re-run `setup`. RigForge installs a -**systemd timer** that periodically optimizes the prefetch mode against your live miner: +For a hands-off schedule, set `autotune` in `config.json` to a target and re-run `setup`. RigForge +installs a systemd timer that periodically optimizes the prefetch mode against your live miner: | `autotune` | What the scheduled run optimizes for | | --- | --- | -| `"disabled"` *(default)* | Nothing β€” no timer is installed. | -| `"performance"` | **Raw hashrate** (H/s). | -| `"efficiency"` | **Hashrate-per-watt** (H/s/W) β€” for power-cost-, heat-, or PSU-limited rigs. Needs a power source (built-in RAPL, or a `TUNE_POWER_CMD` for a smart plug / IPMI); without one it falls back to `performance` with a warning. | +| `"disabled"` *(default)* | Nothing. No timer is installed. | +| `"performance"` | Raw hashrate (H/s). | +| `"efficiency"` | Hashrate-per-watt (H/s/W), for power-cost-, heat-, or PSU-limited rigs. Needs a power source (built-in RAPL, or a `TUNE_POWER_CMD` for a smart plug / IPMI); without one it falls back to `performance` with a warning. | -(Legacy booleans still work: `true` β†’ `performance`, `false` β†’ `disabled`.) The chosen target is baked +Legacy booleans still work: `true` β†’ `performance`, `false` β†’ `disabled`. The chosen target is baked into the systemd unit at setup, so the scheduled run optimizes for what you picked, and `tune --history` shows it. -**Each run converges in one pass (~minutes).** It reads the current hashrate from the miner's API -(median of a few samples, plus average watts when the target is `efficiency`), then sweeps every prefetch -mode (applying each, restarting, and re-measuring over a warmup window) and adopts the best by the -target's metric, but **only if it beats the baseline by a margin** (else it keeps the current mode). So a -single run settles on the best prefetch mode; you don't wait days. The change is merged on top of any -offline `tune` result, so your tuned thread count and `cpu.yield` are preserved. +Each run converges in one pass (~minutes). It reads the current hashrate from the miner's API (median of +a few samples, plus average watts when the target is `efficiency`), then sweeps every prefetch mode +(applying each, restarting, and re-measuring over a warmup window) and adopts the best by the target's +metric, but only if it beats the baseline by a margin (else it keeps the current mode). A single run +settles on the best prefetch mode; you don't wait days. The change is merged on top of any offline `tune` +result, so your tuned thread count and `cpu.yield` are preserved. -**When it re-tunes.** Once the prefetch mode converges it's stable, so re-tuning is **event-driven**: not -a blind daily loop that churns the miner to re-confirm a result that rarely changes. +When it re-tunes: once the prefetch mode converges it's stable, so re-tuning is event-driven, not a blind +daily loop that churns the miner to re-confirm a result that rarely changes. -- **After an `upgrade`** β€” the real trigger. The fastest knobs can shift between XMRig versions, so once a +- After an `upgrade`, the real trigger. The fastest knobs can shift between XMRig versions, so once a rebuild finishes (and the new build is live) RigForge re-tunes it automatically. -- **A monthly safety-net timer.** The default cadence is **monthly**, to catch slow drift (thermal, - ambient temperature, fan/dust). Change it with `AUTOTUNE_ONCALENDAR` (any [systemd calendar](https://www.freedesktop.org/software/systemd/man/systemd.time.html) +- A monthly safety-net timer. The default cadence is monthly, to catch slow drift (thermal, ambient + temperature, fan/dust). Change it with `AUTOTUNE_ONCALENDAR` (any [systemd calendar](https://www.freedesktop.org/software/systemd/man/systemd.time.html) spec) before `setup`, e.g. `AUTOTUNE_ONCALENDAR=weekly sudo ./rigforge.sh setup`. -Review the schedule, the next run, and recent decisions any time with **`rigforge tune --history`** (or +Review the schedule, the next run, and recent decisions any time with `rigforge tune --history` (or `journalctl -u rigforge-autotune`). -Auto-tune only touches the prefetch mode, the knob most worth re-checking live. For a **definitive, -one-time sweep of every knob**, run the offline [`tune`](#tuning) above. Linux only. +Auto-tune only touches the prefetch mode, the knob most worth re-checking live. For a definitive, +one-time sweep of every knob, run the offline [`tune`](#tuning) above. Linux only. ### MSR mod verification The MSR "RandomX boost" (writing the CPU's prefetcher MSRs) is one of the biggest levers, worth ~10–15%, so `doctor` verifies it actually took effect, not just that the `msr` module loaded: -- **From XMRig's log** (always): the `msr register values for "" preset have been set - successfully` line confirms XMRig wrote the per-family preset (e.g. `ryzen_19h_zen4`). A `FAILED` line - is flagged, usually Secure Boot or a missing `msr.allow_writes=on`. -- **Register read-back via `rdmsr`** (run `doctor` as root, with `msr-tools` installed; `setup` - installs it): `doctor` reads the prefetcher registers back and checks they hold the preset's values, - catching a write a hypervisor or kernel lockdown silently dropped even though XMRig reported success. - Run without root, without `rdmsr`, or with the `msr` module unloaded, this step is skipped with an - advisory, never a false alarm; the log check above still confirms the write. - -You almost never need to **tune** the MSR preset. XMRig auto-selects the right per-family preset, and -that's optimal on the vast majority of CPUs. The knob exists for the rare case where a non-default preset -(or disabling the mod) wins on unusual silicon: set `TUNE_WRMSR="true false"` (or a preset number) to -sweep `randomx.wrmsr` alongside the other knobs. It's applied per-bench (no reboot) and pinned only if -it actually wins. +- From XMRig's log (always): the `msr register values for "" preset have been set successfully` + line confirms XMRig wrote the per-family preset (e.g. `ryzen_19h_zen4`). A `FAILED` line is flagged, + usually Secure Boot or a missing `msr.allow_writes=on`. +- Register read-back via `rdmsr` (run `doctor` as root, with `msr-tools` installed; `setup` installs it): + `doctor` reads the prefetcher registers back and checks they hold the preset's values, catching a write + a hypervisor or kernel lockdown silently dropped even though XMRig reported success. Run without root, + without `rdmsr`, or with the `msr` module unloaded, this step is skipped with an advisory, never a false + alarm; the log check above still confirms the write. + +You almost never need to tune the MSR preset. XMRig auto-selects the right per-family preset, and that's +optimal on the vast majority of CPUs. The knob exists for the rare case where a non-default preset (or +disabling the mod) wins on unusual silicon: set `TUNE_WRMSR="true false"` (or a preset number) to sweep +`randomx.wrmsr` alongside the other knobs. It's applied per-bench (no reboot) and pinned only if it +actually wins. --- @@ -201,26 +200,25 @@ RigForge also wraps these so you don't have to remember the unit name: The service is enabled at install, so it starts automatically on boot (and after the post-setup reboot). -> On **macOS** there is no systemd service. RigForge builds and configures XMRig but you run it -> yourself. See [Running on macOS](#running-on-macos) below. +> On macOS there is no systemd service. RigForge builds and configures XMRig but you run it yourself. See +> [Running on macOS](#running-on-macos) below. --- ## Running on macOS -macOS is a **development / light-use** target; Ubuntu is the supported deployment platform. On macOS, -`sudo ./rigforge.sh` still does the core work: it installs dependencies (via **Homebrew**), compiles -XMRig from source, and writes a tuned `config.json`. What it doesn't do is the Linux-only system -integration: +macOS is a development / light-use target; Ubuntu is the supported deployment platform. On macOS, +`sudo ./rigforge.sh` still does the core work: it installs dependencies (via Homebrew), compiles XMRig +from source, and writes a tuned `config.json`. What it doesn't do is the Linux-only system integration: -- **No kernel tuning, and no reboot.** macOS doesn't expose HugePages or MSRs, so the HugePages, MSR, +- No kernel tuning, and no reboot. macOS doesn't expose HugePages or MSRs, so the HugePages, MSR, `hugetlbfs`, and GRUB steps are skipped. The generated config turns those knobs off accordingly (`huge-pages`, `1gb-pages`, `wrmsr`/`rdmsr` are `false`) and binds the API to IPv6 `::`. Because the - biggest RandomX levers (HugePages + MSR) are Linux-only, **expect a lower hashrate than a tuned Linux - box**: fine for development, not for a production rig. -- **No systemd service / no auto-start on boot.** There's no service to install, and the miner doesn't - start at boot. But `setup` doesn't leave you to hand-roll a launch command. The same `start` / `stop` - / `restart` / `status` / `logs` verbs work on macOS too (see below); on macOS they manage XMRig as a + biggest RandomX levers (HugePages + MSR) are Linux-only, expect a lower hashrate than a tuned Linux box: + fine for development, not for a production rig. +- No systemd service / no auto-start on boot. There's no service to install, and the miner doesn't start + at boot. `setup` doesn't leave you to hand-roll a launch command, though. The same `start` / `stop` / + `restart` / `status` / `logs` verbs work on macOS too (see below); on macOS they manage XMRig as a background process tracked by a PID file under the worker root, instead of via systemd. ### Run the miner @@ -242,8 +240,8 @@ binary from the worker build dir with the generated config; the log is at `/xmrig.log` (e.g. `data/worker/xmrig.log`). -- **Rotation:** a `logrotate` policy is installed automatically to compress and archive logs. -- **Build log:** the XMRig compile output is captured to `/build.log` (e.g. +- Log file: `/xmrig.log` (e.g. `data/worker/xmrig.log`). +- Rotation: a `logrotate` policy is installed automatically to compress and archive logs. +- Build log: the XMRig compile output is captured to `/build.log` (e.g. `data/worker/build.log`) during setup, so a failed build is diagnosable after the fact. On any unexpected failure the script also names the step that failed and prints the last lines of the build log. @@ -304,7 +302,7 @@ the exception: it's compiled into the binary and needs a rebuild. See [Configuration β€Ί Changing settings later](configuration.md#changing-settings-later). A full `setup` re-run also regenerates the config, but it's meant for re-provisioning, and so that it -won't interrupt a running miner, does **not** restart an already-built worker on its own. When you want +won't interrupt a running miner, does not restart an already-built worker on its own. When you want an edit to take effect, use `apply`. (On macOS, `apply` regenerates the config but you restart the miner yourself; see [Running on macOS](#running-on-macos).) @@ -321,11 +319,11 @@ sudo ./rigforge.sh upgrade # rebuild + restart only if the pin changed `upgrade` is a no-op when the pinned XMRig is already built, so it's cheap to run. A plain `sudo ./rigforge.sh` (setup) also picks up a changed pin, but `upgrade` is the explicit, restart-aware -path. If you've enabled periodic [autotune](#live-auto-tuning-opt-in), `upgrade` **re-tunes the new build -automatically** once it's live. The optimal prefetch mode can change between XMRig versions, so the -upgrade is the moment that actually warrants a re-tune (the monthly timer is just a slow safety net). +path. If you've enabled periodic [autotune](#live-auto-tuning-opt-in), `upgrade` re-tunes the new build +automatically once it's live. The optimal prefetch mode can change between XMRig versions, so the upgrade +is the moment that actually warrants a re-tune (the monthly timer is just a slow safety net). -> **Pulled RigForge changes but not a new XMRig pin?** Then `upgrade` is a no-op, since the build is +> Pulled RigForge changes but not a new XMRig pin? Then `upgrade` is a no-op, since the build is > unchanged. To pick up RigForge's own changes, run `sudo ./rigforge.sh apply` (regenerate the live > config + restart); if the pull also changed kernel tuning or the service unit, run a full > `sudo ./rigforge.sh setup`, then `restart`. When unsure, `upgrade` followed by `apply` covers the @@ -337,10 +335,10 @@ upgrade is the moment that actually warrants a re-tune (the monthly timer is jus ## Backup & restore -A worker's **expensive, hard-to-recreate state** is small: your `config.json` and the **tuning** result +A worker's expensive, hard-to-recreate state is small: your `config.json` and the tuning result (`tune-overrides.json`, which can take hours to produce). The XMRig build and the system tuning are -regenerated by `setup`, so they're not worth saving. `backup` snapshots that valuable state into a -portable archive: +regenerated by `setup`, so they're not worth saving. `backup` snapshots that state into a portable +archive: ```bash sudo ./rigforge.sh backup # -> ./backups/rigforge-backup-YYYYMMDD-HHMMSS.tar.gz @@ -349,7 +347,7 @@ sudo ./rigforge.sh backup # -> ./backups/rigforge-backup-YYYYMMDD-HHMM The archive is owner-only (`chmod 600`) and includes `config.json`, the tuning files, and a small manifest (RigForge version + source host). Back up after first-run setup and again after each `tune`. -**Restore** puts it back. Point it at an archive: +`restore` puts it back. Point it at an archive: ```bash sudo ./rigforge.sh restore ./backups/rigforge-backup-20260101-120000.tar.gz # prompts; -y to skip @@ -361,23 +359,23 @@ you to run `setup`/`apply` to put the restored config into effect. ### Two reasons to use it -- **Recover from data loss.** A wiped disk would otherwise mean re-doing setup and re-tuning. With a - backup it's `restore` + `setup`. -- **Roll a tune across a fleet.** Tune one machine, `backup`, then `restore` on each identical machine; - they all get the same config and the same tuning without re-running the slow search. +- Recover from data loss. A wiped disk would otherwise mean re-doing setup and re-tuning. With a backup + it's `restore` + `setup`. +- Roll a tune across a fleet. Tune one machine, `backup`, then `restore` on each identical machine; they + all get the same config and the same tuning without re-running the slow search. -> ⚠️ **Tuning is CPU-specific.** Only reuse `tune-overrides.json` between **identical** CPUs. On -> different hardware, restore the config but re-run `tune` (or `tune --clear` to drop the inherited -> tuning). Backups made with the default `HOME_DIR` (`DYNAMIC_HOME`) are fully portable; an absolute -> `HOME_DIR` carries that machine's path. +> NOTE: Tuning is CPU-specific. Only reuse `tune-overrides.json` between identical CPUs. On different +> hardware, restore the config but re-run `tune` (or `tune --clear` to drop the inherited tuning). +> Backups made with the default `HOME_DIR` (`DYNAMIC_HOME`) are fully portable; an absolute `HOME_DIR` +> carries that machine's path. --- ## Verification -After setup (and the reboot, on Linux), confirm the optimizations applied: +After setup (and the reboot, on Linux), confirm the optimizations applied. -**HugePages** +HugePages: ```bash grep Huge /proc/meminfo @@ -386,7 +384,7 @@ grep Huge /proc/meminfo `HugePages_Total`, `HugePages_Free`, and `Hugepagesize` should be non-zero and match what setup configured. -**MSR (Model-Specific Registers)** +MSR (Model-Specific Registers): ```bash grep -i msr /xmrig.log @@ -400,16 +398,16 @@ If you see MSR errors, see Troubleshooting below. | Symptom | Likely cause & fix | |---|---| -| **Setup fails during the build** | The script names the step that failed and tails the build log. Read the full error in `/build.log` (e.g. `data/worker/build.log`). Common causes: a build dependency you declined to install (re-run and accept), or too little RAM during compilation (the build already caps parallelism by RAM; add swap on very low-memory hosts). Re-run `sudo ./rigforge.sh` once resolved; it resumes without redoing finished work. | -| **MSR errors in the log** | Secure Boot is blocking the `msr` kernel module. **Disable Secure Boot** in your BIOS/UEFI, then reboot. | -| **`doctor`: "MSR registers don't match the preset"** | XMRig's log says the write succeeded but the read-back disagrees: the kernel or hypervisor silently dropped it. Common on VMs/cloud instances and under kernel lockdown. Run RigForge on **bare metal**, and ensure `msr.allow_writes=on` (RigForge sets this) and that lockdown isn't enforced. | -| **`doctor`: "couldn't read the MSRs via rdmsr"** | The `msr` module isn't loaded (or `doctor` wasn't run as root). Run `sudo ./rigforge.sh doctor`; if it persists, `sudo modprobe msr` (Secure Boot can block it). This is advisory; XMRig's log already confirms the write. | -| **`HugePages_Total` is 0** | The kernel tuning needs a **reboot** to take effect (GRUB change). Reboot, then re-check `grep Huge /proc/meminfo`. | -| **HugePages still 0 after reboot** | Not enough contiguous memory was reservable, or another tool changed GRUB. Re-run `sudo ./rigforge.sh`; RigForge **merges** its kernel parameters into `GRUB_CMDLINE_LINUX_DEFAULT` rather than overwriting, so other params are preserved. | -| **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 HTTP API token must equal the rig name (or be unset), the API must be on `:8080`, and the worker must be reachable from the stack host. 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. | +| Setup fails during the build | The script names the step that failed and tails the build log. Read the full error in `/build.log` (e.g. `data/worker/build.log`). Common causes: a build dependency you declined to install (re-run and accept), or too little RAM during compilation (the build already caps parallelism by RAM; add swap on very low-memory hosts). Re-run `sudo ./rigforge.sh` once resolved; it resumes without redoing finished work. | +| MSR errors in the log | Secure Boot is blocking the `msr` kernel module. Disable Secure Boot in your BIOS/UEFI, then reboot. | +| `doctor`: "MSR registers don't match the preset" | XMRig's log says the write succeeded but the read-back disagrees: the kernel or hypervisor silently dropped it. Common on VMs/cloud instances and under kernel lockdown. Run RigForge on bare metal, and ensure `msr.allow_writes=on` (RigForge sets this) and that lockdown isn't enforced. | +| `doctor`: "couldn't read the MSRs via rdmsr" | The `msr` module isn't loaded (or `doctor` wasn't run as root). Run `sudo ./rigforge.sh doctor`; if it persists, `sudo modprobe msr` (Secure Boot can block it). This is advisory; XMRig's log already confirms the write. | +| `HugePages_Total` is 0 | The kernel tuning needs a reboot to take effect (GRUB change). Reboot, then re-check `grep Huge /proc/meminfo`. | +| HugePages still 0 after reboot | Not enough contiguous memory was reservable, or another tool changed GRUB. Re-run `sudo ./rigforge.sh`; RigForge merges its kernel parameters into `GRUB_CMDLINE_LINUX_DEFAULT` rather than overwriting, so other params are preserved. | +| 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 HTTP API token must equal the rig name (or be unset), the API must be on `:8080`, and the worker must be reachable from the stack host. 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. | --- diff --git a/docs/pithead-integration.md b/docs/pithead-integration.md index caa4666..d2d18ba 100644 --- a/docs/pithead-integration.md +++ b/docs/pithead-integration.md @@ -1,14 +1,14 @@ # Pithead Integration -RigForge works against any RandomX pool, but it's built as the companion miner for -**[Pithead](https://github.com/p2pool-starter-stack/pithead)**, a self-hosted Monero + P2Pool + Tari -mining stack. This page describes the contract between a RigForge worker and the Pithead dashboard, so -the two read as one product. +The contract between a RigForge worker and the Pithead dashboard. RigForge works against any RandomX +pool, but it's built as the companion miner for +[Pithead](https://github.com/p2pool-starter-stack/pithead), a self-hosted Monero + P2Pool + Tari mining +stack. There are two connections between a worker and the stack: -1. **Mining** β€” the worker β†’ the stack's stratum proxy on `:3333`. -2. **Stats** β€” the dashboard β†’ the worker's XMRig HTTP API on `:8080`. +1. Mining: the worker β†’ the stack's stratum proxy on `:3333`. +2. Stats: the dashboard β†’ the worker's XMRig HTTP API on `:8080`. --- @@ -16,22 +16,22 @@ There are two connections between a worker and the stack: Point a pool at the stack: `{ "pools": [{ "url": "your-stack:3333" }] }`, the stack's `xmrig-proxy` endpoint (its proxy listens on `3333`). The stack handles pool selection, payouts, and the P2Pool/XvB -split centrally, so the worker config stays minimal and you **never put a wallet address in it**. +split centrally, so the worker config stays minimal and you never put a wallet address in it. -- 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. +- 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. -- The endpoint must be reachable from the worker; if the stack host has a firewall, allow the Stratum - port (3333) on the LAN. +- Workers talk to the pool over plain Stratum on your local network; they do not need Tor. +- The endpoint must be reachable from the worker; if the stack host has a firewall, allow the Stratum port + (3333) on the LAN. ### Stratum authentication (optional) -By default the stack's `:3333` is **open**: any rig that can reach it may mine, and the pool `pass` -is ignored (RigForge defaults it to `"x"`). If the operator turns authentication **on** by setting +By default the stack's `:3333` is open: any rig that can reach it may mine, and the pool `pass` is +ignored (RigForge defaults it to `"x"`). If the operator turns authentication on by setting [`p2pool.stratum_password`](https://github.com/p2pool-starter-stack/pithead/blob/main/docs/workers.md#authentication) -on the stack, the proxy then **rejects any rig whose `pass` doesn't match**: XMRig logs -`Permission denied` and the rig won't mine. Put that same secret in the rig's pool `pass`: +on the stack, the proxy then rejects any rig whose `pass` doesn't match: XMRig logs `Permission denied` +and the rig won't mine. Put that same secret in the rig's pool `pass`: ```jsonc // config.json β€” set "pass" to the stack's p2pool.stratum_password @@ -44,50 +44,49 @@ on the stack, the proxy then **rejects any rig whose `pass` doesn't match**: XMR Then `./rigforge.sh apply` (or `setup`) regenerates the worker config with the new password. -- It's the **same secret on every rig**. The operator finds it on the stack side: it's printed after +- It's the same secret on every rig. The operator finds it on the stack side: it's printed after `pithead apply`/`setup`, stored in the stack's `.env` as `PROXY_STRATUM_PASSWORD`, and shown by `pithead status`. -- The password travels **cleartext** over your LAN's plain Stratum, so this is access control (who may - mine), **not** encryption. Keep `:3333` on a trusted LAN (the stack's `p2pool.stratum_bind` - / a firewall do the rest). -- This is unrelated to the `DONATION` knob (that's this rig's dev-fee donation) and to the optional - API `ACCESS_TOKEN` below (that gates the read-only stats API on `:8080`, which is open by default). +- The password travels cleartext over your LAN's plain Stratum, so this is access control (who may mine), + not encryption. Keep `:3333` on a trusted LAN (the stack's `p2pool.stratum_bind` / a firewall do the + rest). +- This is unrelated to the `DONATION` knob (that's this rig's dev-fee donation) and to the optional API + `ACCESS_TOKEN` below (that gates the read-only stats API on `:8080`, which is open by default). --- ## 2. Stats connection β€” the Worker API (`:8080`) Each worker exposes XMRig's HTTP API so Pithead's dashboard can show per-rig stats (hashrate, shares, -uptime). RigForge configures the API to match Pithead's contract **exactly**, so there's nothing to -set up stack-side: +uptime). RigForge configures the API to match Pithead's contract exactly, so there's nothing to set up +stack-side: | Setting | Value | Why | |---|---|---| -| **Port** | `8080` | Pithead reads `GET http://:8080/1/summary`; the port is fixed dashboard-side. | -| **Bind** | `0.0.0.0` (all interfaces) | The dashboard polls each worker from the stack host over the LAN. | -| **Mode** | `restricted: true` (read-only) | The API can be **read** but not used to **control** the miner remotely. | -| **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 out of the box. Setting `ACCESS_TOKEN` turns auth on β€” see below. | +| Port | `8080` | Pithead reads `GET http://:8080/1/summary`; the port is fixed dashboard-side. | +| Bind | `0.0.0.0` (all interfaces) | The dashboard polls each worker from the stack host over the LAN. | +| Mode | `restricted: true` (read-only) | The API can be read but not used to control the miner remotely. | +| 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. | 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 run on a trusted LAN and need no Tor. --- ## The token rule (important) -> βœ… **By default the worker API is open** (read-only, no token), which matches Pithead's default -> probe (`workers.api_auth: none`) β€” nothing to coordinate. Leave `ACCESS_TOKEN` unset and it just -> works. +> NOTE: By default the worker API is open (read-only, no token), which matches Pithead's default probe +> (`workers.api_auth: none`). Nothing to coordinate. Leave `ACCESS_TOKEN` unset and it works. -If you **do** want a token (e.g. you don't fully trust the LAN), set `ACCESS_TOKEN` here **and** match -it on the dashboard side: +If you do want a token (e.g. you don't fully trust the LAN), set `ACCESS_TOKEN` here and match it on the +dashboard side: - a single shared token β†’ Pithead `workers.api_auth: token` + `workers.api_token: `; - the rig name as the token (`ACCESS_TOKEN` = the first pool's `user`) β†’ Pithead `workers.api_auth: name`. -Likewise, **don't** bind the API to localhost only and **don't** change the port without matching it -on the stack side (`workers.api_port`): a non-`8080` port, or a worker reachable at a different host -than the one it connects from, also need matching configuration on **both** sides +Likewise, don't bind the API to localhost only and don't change the port without matching it on the stack +side (`workers.api_port`): a non-`8080` port, or a worker reachable at a different host than the one it +connects from, also need matching configuration on both sides (Pithead [#171](https://github.com/p2pool-starter-stack/pithead/issues/171) / [#172](https://github.com/p2pool-starter-stack/pithead/issues/172)). @@ -97,10 +96,10 @@ than the one it connects from, also need matching configuration on **both** side | Symptom | Fix | |---|---| -| **Rig won't mine / XMRig logs `Permission denied` at login** | The stack has stratum authentication on (`p2pool.stratum_password`) β€” set the pool `pass` to that secret. See [Stratum authentication](#stratum-authentication-optional). | -| **Worker missing from the dashboard** | The dashboard discovers rigs from their stratum `user` label β€” confirm the worker is actually connected to the pool and mining. | -| **Rig shows as connected but no stats** | By default the API is open and the dashboard reads it with no token. If you set an `ACCESS_TOKEN` here, the dashboard must match it (`workers.api_auth: token` + `workers.api_token`, or `name` if the token is the rig name) β€” otherwise clear `ACCESS_TOKEN` and re-run setup. | -| **Stats unreachable from the stack host** | Confirm the worker's `:8080` is reachable from the stack host over the LAN (firewall, correct IP). RigForge binds `0.0.0.0` by default. | +| Rig won't mine / XMRig logs `Permission denied` at login | The stack has stratum authentication on (`p2pool.stratum_password`); set the pool `pass` to that secret. See [Stratum authentication](#stratum-authentication-optional). | +| Worker missing from the dashboard | The dashboard discovers rigs from their stratum `user` label; confirm the worker is actually connected to the pool and mining. | +| Rig shows as connected but no stats | By default the API is open and the dashboard reads it with no token. If you set an `ACCESS_TOKEN` here, the dashboard must match it (`workers.api_auth: token` + `workers.api_token`, or `name` if the token is the rig name); otherwise clear `ACCESS_TOKEN` and re-run setup. | +| Stats unreachable from the stack host | Confirm the worker's `:8080` is reachable from the stack host over the LAN (firewall, correct IP). RigForge binds `0.0.0.0` by default. | --- diff --git a/tests/README.md b/tests/README.md index 37424c8..b7c11d9 100644 --- a/tests/README.md +++ b/tests/README.md @@ -1,7 +1,7 @@ # RigForge tests -RigForge is one self-contained script, so its tests are layered by **how much they exercise for -real**, from a dependency-free suite that runs anywhere up to a real-hardware gate that actually +RigForge is one self-contained script, so its tests are layered by how much they exercise for +real, from a dependency-free suite that runs anywhere up to a real-hardware gate that actually compiles XMRig and mines. Each layer covers what the one below it has to stub. > The split exists so CI proves everything it can on a GitHub runner, while the things a runner @@ -20,29 +20,29 @@ compiles XMRig and mines. Each layer covers what the one below it has to stub. | **Release e2e (full)** | [`e2e-real.sh`](e2e-real.sh) | Real Linux rig, **manual, root**. Not in CI. | The real thing end to end: build + tune + kernel tuning + service + a real hash, then a clean uninstall. **The release gate.** | `make e2e-real` (see [`RELEASING.md`](../RELEASING.md)) | The first four run automatically on every push/PR (see [`.github/workflows/ci.yml`](../.github/workflows/ci.yml)). -The last two are deliberately kept **out of CI**, because a real build, HugePages, and live mining are -flaky-by-nature and against GitHub Actions' ToS. They're a manual pre-tag gate the releaser runs. +The last two are deliberately kept out of CI, because a real build, HugePages, and live mining are +flaky by nature and against GitHub Actions' ToS. They're a manual pre-tag gate the releaser runs. ## Where does my test go? -- **New logic, config-gen behaviour, a CPU/OS profile, or command behaviour** β†’ [`run.sh`](run.sh). +- New logic, config-gen behaviour, a CPU/OS profile, or command behaviour β†’ [`run.sh`](run.sh). It's the default home for almost everything; hardware and OS are simulated with PATH stubs, so it stays hardware-independent and runs the same on any machine. -- **A new real-`/etc` system effect** (fstab, memlock limits, an MSR/modules edit, a mount) β†’ assert it +- A new real-`/etc` system effect (fstab, memlock limits, an MSR/modules edit, a mount) β†’ assert it in [`e2e/in-container.sh`](e2e/in-container.sh), which runs against a throwaway filesystem. -- **macOS-specific behaviour** (BSD tools, launchd, the mac process control) β†’ [`e2e/macos.sh`](e2e/macos.sh). -- **Something only provable on real hardware** (it actually hashes, MSRs really applied, HugePages +- macOS-specific behaviour (BSD tools, launchd, the mac process control) β†’ [`e2e/macos.sh`](e2e/macos.sh). +- Something only provable on real hardware (it actually hashes, MSRs really applied, HugePages really reserved) β†’ [`e2e-real.sh`](e2e-real.sh). ## Conventions -- `run.sh` is **dependency-free**: no bats, no frameworks. Tiny `assert_*` helpers, and every external +- `run.sh` is dependency-free: no bats, no frameworks. Tiny `assert_*` helpers, and every external or privileged command is faked in a stub dir placed first on `PATH`. Keep it that way: a contributor must be able to run `bash tests/run.sh` on a stock machine. -- It's **hardware-independent on purpose**: all hardware-probe env hooks are pointed at non-existent +- It's hardware-independent on purpose: all hardware-probe env hooks are pointed at non-existent paths up top, so the same run exercises EPYC / Ryzen-X3D / macOS inputs back to back and gives the same result on any host. Don't read real `/sys` or `/proc`; drive behaviour through the stubs. -- The suite must pass under both modern bash and **Apple's bash 3.2** (CI runs `/bin/bash tests/run.sh` +- The suite must pass under both modern bash and Apple's bash 3.2 (CI runs `/bin/bash tests/run.sh` on macOS); avoid bash-4-only syntax. - Lint everything: `make lint` (shellcheck + shfmt). The file list lives in the Makefile's `SHELL_FILES` so CI and local stay in sync β€” add new `tests/*.sh` there.