diff --git a/.gitignore b/.gitignore index 4e837b2..170ebb8 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,6 @@ # and a `git add -A` sweeps them into a public repo. .DS_Store **/.DS_Store +# graphify knowledge-graph output (graph.json, graph.html, report, cache). +# Built locally per machine; not part of the project. +/graphify-out/ diff --git a/CHANGELOG.md b/CHANGELOG.md index db4d835..5cfad6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,43 @@ Anything that changes what a miner is paid, or what an operator has to tell their miners, is called out explicitly — those are the changes that cost somebody money if they go unread. +## Unreleased + +### Operators: the dashboard now listens on loopback by default + +`dashboard/server.js` binds `DASHBOARD_BIND`, default `127.0.0.1`, where it +used to bind every interface. Behind nginx nothing changes. **If you reach the +dashboard directly on `:8081`, add `Environment=DASHBOARD_BIND=0.0.0.0` to its +systemd drop-in before upgrading**, or it stops answering there. `install.sh` +sets it for you: loopback with nginx, all interfaces with `--no-nginx`. The +Docker image sets `0.0.0.0` inside the container. + +### Miners on pplns-thunder / pplns-btc: small blocks are no longer short-changed + +When a block's operator fee came to less than the 546-sat dust limit, the +coinbase (correctly) paid no fee output and the pool wallet received the whole +reward — but the distributor still took `fee_bps` off before crediting miners, +so the difference sat in the pool wallet credited to nobody. The distributor +now applies the coinbase's dust rule. Only blocks worth less than roughly +`546 × 10000 / fee_bps` sats are affected (54,600 sats at 1%). + +### Config: an eighth `listener` line is refused + +The server has room for `listen_port` plus seven extra ports. An eighth +`listener` used to load cleanly and then silently not be bound; it is now a +config error naming the limit. + +### Smaller fixes + +- `install.sh --help` no longer claims `--pps-sats-per-diff` defaults to 1000 + — it defaults to unset (derived per template) — and the installer warns if + you pass it. +- `schema.sql` documents the unique index on `blocks_found(hash)` that the + proxy creates at startup, and why it is not created there. +- `docs/simplepool.html` is now a full reference: every config key and + environment variable, the stratum protocol, the coinbase layout, every API, + the schema and the hard limits. + ## 0.4.0 — three PPLNS modes, and coinbase-direct payouts The headline is that a pool no longer has to hold miners' money to run PPLNS. diff --git a/INSTALL.md b/INSTALL.md index cc6ef35..e1cb1c3 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -656,8 +656,11 @@ scripted one-liner in [OPERATOR_GUIDE.md](OPERATOR_GUIDE.md#rotating-the-admin-p ### Reverse proxy (recommended) -The dashboard binds `0.0.0.0:8081` — reachable directly. In -production you probably want nginx / caddy in front of it. Solo +The dashboard binds `127.0.0.1:8081` by default (`DASHBOARD_BIND`), so it +is reachable only through a reverse proxy on the same host. To serve it +directly, set `Environment=DASHBOARD_BIND=0.0.0.0` in its drop-in — and then +put TLS in front of `/admin` some other way. In production you want nginx / +caddy in front of it. Solo `deploy/nginx/simplepool.conf` has a working template. Do NOT expose `/admin` on plain HTTP over the internet without at diff --git a/OPERATOR_GUIDE.md b/OPERATOR_GUIDE.md index f6501fe..bf151d7 100644 --- a/OPERATOR_GUIDE.md +++ b/OPERATOR_GUIDE.md @@ -45,6 +45,12 @@ tracked by git. | SSH | `root@` | `` | | Everything from the shell | `simplepoolctl status` / `doctor` / `logs -f` | root for `restart`, `upgrade`, `uninstall` | +The `:8081` URLs assume the dashboard listens publicly. Since it defaults to +loopback (`DASHBOARD_BIND=127.0.0.1`), that needs +`Environment=DASHBOARD_BIND=0.0.0.0` in +`/etc/systemd/system/simplepool-dashboard.service.d/local.conf`; behind nginx, +use `https:///` instead and leave it on loopback. + The admin password is stashed at `/root/simplepool-admin-cred.txt` on the box (root-only). To rotate, edit `/etc/systemd/system/simplepool-dashboard.service.d/pps-thunder.conf` diff --git a/dashboard/README.md b/dashboard/README.md index 8ba28fe..bdccd64 100644 --- a/dashboard/README.md +++ b/dashboard/README.md @@ -43,7 +43,8 @@ npm start # production npm run dev # auto-restart on file change ``` -Defaults: `PORT=8081`, `PROXY_DB_PATH=../data/shares.snapshot.db`. +Defaults: `PORT=8081`, `DASHBOARD_BIND=127.0.0.1` (set `0.0.0.0` to serve it +without a reverse proxy), `PROXY_DB_PATH=../data/shares.snapshot.db`. If the snapshot file doesn't exist yet, the dashboard starts anyway and displays "no data yet" until the first `.backup` produces it. You can also diff --git a/dashboard/server.js b/dashboard/server.js index f216cb1..dfc1d63 100644 --- a/dashboard/server.js +++ b/dashboard/server.js @@ -22,6 +22,11 @@ import { createAdminRouter } from './lib/admin-router.js'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const PORT = parseInt(process.env.PORT || '8081', 10); +// Loopback by default, like the payout and slipstream services: the +// dashboard is meant to be published through nginx, and /admin speaks HTTP +// Basic auth, which must not be reachable in the clear on a public +// interface. Set DASHBOARD_BIND=0.0.0.0 (or ::) to serve it directly. +const BIND = process.env.DASHBOARD_BIND || '127.0.0.1'; const DB_PATH = process.env.PROXY_DB_PATH || '../data/shares.db'; const app = express(); @@ -296,6 +301,6 @@ app.use('/admin', app.use((_req, res) => res.status(404).render('404', { what: 'page' })); -app.listen(PORT, () => { - console.log(`simplepool dashboard on :${PORT} (db: ${db.path})`); +app.listen(PORT, BIND, () => { + console.log(`simplepool dashboard on ${BIND}:${PORT} (db: ${db.path})`); }); diff --git a/deploy/docker/Dockerfile.dashboard b/deploy/docker/Dockerfile.dashboard index f3ac658..d94e793 100644 --- a/deploy/docker/Dockerfile.dashboard +++ b/deploy/docker/Dockerfile.dashboard @@ -23,5 +23,7 @@ WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY dashboard/ ./ USER node +# Inside a container loopback is unreachable from the published port. +ENV DASHBOARD_BIND=0.0.0.0 EXPOSE 8081 ENTRYPOINT ["/usr/bin/tini", "--", "node", "server.js"] diff --git a/deploy/docker/docker-compose.yml b/deploy/docker/docker-compose.yml index 7c2fb47..478f18c 100644 --- a/deploy/docker/docker-compose.yml +++ b/deploy/docker/docker-compose.yml @@ -50,6 +50,7 @@ services: - "${DASHBOARD_PORT:-8081}:8081" environment: PORT: "8081" + DASHBOARD_BIND: 0.0.0.0 PROXY_DB_PATH: /data/shares.db PUBLIC_STRATUM_URL: ${PUBLIC_STRATUM_URL:-stratum+tcp://:3334} THUNDER_RPC_URL: ${THUNDER_RPC_URL:-http://host.docker.internal:6009} diff --git a/deploy/systemd/simplepool-dashboard.service b/deploy/systemd/simplepool-dashboard.service index aa5da18..3a6876c 100644 --- a/deploy/systemd/simplepool-dashboard.service +++ b/deploy/systemd/simplepool-dashboard.service @@ -10,6 +10,9 @@ User=@USER@ Group=@USER@ WorkingDirectory=@ROOT@/dashboard Environment=PORT=8081 +# Loopback: nginx publishes the dashboard. Set 0.0.0.0 only to serve it +# directly, and then only behind a firewall or TLS -- /admin uses Basic auth. +Environment=DASHBOARD_BIND=127.0.0.1 Environment=PROXY_DB_PATH=@ROOT@/data/shares.db # Rendered on the public "Connect a miner" card. Set to the actual # host miners should point their ASIC at. Leave unset to show diff --git a/docs/simplepool.html b/docs/simplepool.html index 0dd9cae..f17a332 100644 --- a/docs/simplepool.html +++ b/docs/simplepool.html @@ -4,7 +4,7 @@ simplepool — how it works - +