Quick start reminder: the main README gives a concise walk-through; this document expands each section into the operational context needed to run goPool day-to-day.
goPool ships as a self-contained pool daemon that connects directly to Bitcoin Core (JSON-RPC + ZMQ), hosts a Stratum v1 endpoint, and exposes a status UI with JSON APIs. This documentation covers the operational steps teams repeat in production; refer to sibling documents (especially documentation/TESTING.md) for testing recipes.
Operational Stratum notes:
- Stratum is gated (at startup and during runtime) only when the job feed reports errors or the node is in a non-usable syncing/indexing state. While gated, new miner connections are refused and existing miners are disconnected to avoid idling on stale/no work during node/bootstrap issues.
- When the node/job feed is stale, the main status page (
/) displays a dedicated "node unavailable" page instead of the normal overview. - A background heartbeat (
stratumHeartbeatInterval) performs periodic non-longpoll template refreshes so "quiet mempool / no template churn" does not look like a dead node. - When updates are degraded but basic node RPC calls still work, the node-unavailable page will also show common sync/indexing indicators (IBD flag and blocks/headers) to help diagnose "node indexing" situations.
goPool provides a Dockerfile and docker-compose.yml for containerized deployments. This is the recommended way to run in production or for easy local testing.
Build the image:
docker build -t gopool:local .Run the container:
docker run --rm -it \
-e BUILD_TIME="$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
-e BUILD_VERSION="v0.0.0-dev" \
-p 3333:3333 -p 80:80 -p 443:443 \
-v "$PWD/data:/app/data" \
gopool:local -stdout+Edit .env or env.example to set environment variables (ports, build args, runtime flags). Then:
docker compose up -d --buildThis will build and start the container, mapping ports and mounting ./data for persistent config/state.
BUILD_TIMEandBUILD_VERSIONare passed at build time (set automatically by the Makefile and CI).- Runtime environment variables (see
env.example) control ports, network mode, and extra flags.
The data directory is mounted into the container at /app/data to persist configuration, logs, and state. Always back up this directory.
Requirements:
- Go 1.26.0+ — install from https://go.dev/dl/ for matching ABI guarantees.
- ZeroMQ headers (
libzmq3-dev,zeromq, etc.) to satisfygithub.com/pebbe/zmq4. On Debian/Ubuntu runsudo apt install -y libzmq3-dev; other distros follow their package manager.
Clone and build:
git clone https://github.com/Distortions81/M45-Core-goPool.git
cd M45-Core-goPool
go build -o goPoolUse GOOS/GOARCH for cross-compilation and avoid go install unless populating GOBIN intentionally. Hardware acceleration flags (noavx, nojsonsimd) remain the only build tags you usually need; runtime logging/tracing is controlled by [logging].debug and [logging].net_debug (or -debug / -net-debug).
Release builds embed two fields via -ldflags:
main.buildTime: the UTC timestamp recorded when the binary was compiled. The status UI exposes it asbuild_time.main.buildVersion: the version label (e.g.,v1.2.3) and shows up underbuild_version.
GitHub Actions sets both automatically per run. If you build manually and want consistent metadata, pass the same flags yourself:
go build -ldflags="-X main.buildTime=$(date -u +%Y-%m-%dT%H:%M:%SZ) -X main.buildVersion=vX.Y.Z" ./...
Both values appear on the status page and JSON endpoints so you can verify the exact build at runtime.
- Run
./goPoolonce; it generatesdata/config/examples/and exits. - Copy
data/config/examples/config.toml.exampletodata/config/config.toml. - Set required values in
config.toml(especiallynode.payout_address,node.rpc_url, and optionalnode.zmq_hashblock_addr/node.zmq_rawblock_addr). - Optional: copy split files from
data/config/examples/intodata/config/(secrets.toml,services.toml,policy.toml,tuning.toml,version_bits.toml) when you need overrides. - Start goPool again with
./goPool.
| Flag | Description |
|---|---|
| `-network <mainnet | testnet |
-bind <ip> |
Replace the bind IP of every listener (Stratum, status HTTP/HTTPS). |
-listen <addr> |
Override Stratum TCP listen address for this run (for example :3333). |
-status <addr> |
Override status HTTP listen address for this run (for example :80). |
-status-tls <addr> |
Override status HTTPS listen address for this run (for example :443). |
-stratum-tls <addr> |
Override Stratum TLS listen address for this run (for example :24333). |
-rpc-url <url> |
Override node.rpc_url for this run—useful for temporary test nodes. |
-rpc-cookie <path> |
Override node.rpc_cookie_path when testing alternate cookie locations. |
-data-dir <path> |
Override the data directory (logs/state/config/examples) for this run. |
-log-dir <path> |
Override directory for pool/debug/net-debug logs only. |
-pool-log <path> |
Override pool log file path. |
-debug-log <path> |
Override debug log file path. |
-net-debug-log <path> |
Override net-debug log file path. |
-max-conns <n> |
Override max concurrent miner connections (-1 keeps configured value). |
| `-safe-mode <true | false>` |
| `-ckpool-emulate <true | false>` |
-stratum-tcp-read-buffer <bytes> |
Override Stratum TCP read buffer bytes (0 uses OS default). |
-stratum-tcp-write-buffer <bytes> |
Override Stratum TCP write buffer bytes (0 uses OS default). |
-secrets <path> |
Point to an alternate secrets.toml; the file is not rewritten. |
-rewrite-config |
Persist derived values like pool_entropy back into config.toml. |
-stdout |
Mirror every structured log entry to stdout (nice when running under systemd/journal). |
-profile |
Write a CPU profile to default.pgo for offline pprof analysis. |
-flood |
Force both min_difficulty and max_difficulty to a low value for stress testing. |
-debug / -net-debug |
Force debug logging and raw network tracing at startup. |
-no-json |
Disable the JSON status endpoints while keeping the HTML UI active. |
-allow-public-rpc |
Allow connecting to an unauthenticated RPC endpoint (testing only). |
-allow-rpc-creds |
Force username/password auth from secrets.toml; logs a warning and is deprecated. |
-backup-on-boot |
Run one forced database backup pass at startup (best-effort). |
-miner-profile-json <path> |
Write aggregated miner profile JSON to a file for offline tuning. |
-saved-workers-local-noauth |
Allow saved-worker pages without Clerk auth (local single-user mode). |
Flags only override values for the running instance; nothing is written back to config.toml (except node.rpc_cookie_path when auto-detected). Use configuration files for durable behavior.
The required data/config/config.toml is the primary interface for pool behavior. Key sections include:
[server]:pool_listen,status_listen,status_tls_listen, andstatus_public_url. Setstatus_tls_listen = ""to disable HTTPS and rely onstatus_listenonly. Leavingstatus_listenempty disables HTTP entirely (e.g., TLS-only deployments).status_public_urlfeeds redirects and Clerk cookie domains. When both HTTP and HTTPS are enabled, the HTTP listener now issues a temporary (307) redirect to the HTTPS endpoint so the public UI and JSON APIs stay behind TLS.[branding]: Styling and branding options shown in the status UI (tagline, pool donation link, location string).[stratum]:stratum_tls_listenfor TLS-enabled Stratum (leave blank to disable secure Stratum), plusstratum_password_enabled/stratum_passwordto require a shared password onmining.authorize, andstratum_password_publicto show the password on the public connect panel.policy.toml [stratum]:ckpool_emulatecontrols CKPool-style subscribe response compatibility.tuning.toml [stratum]:tcp_read_buffer_bytesandtcp_write_buffer_bytescontrol Stratum socket buffer tuning.- Optional runtime overrides (temporary):
-ckpool-emulate,-stratum-tcp-read-buffer, and-stratum-tcp-write-buffer. [node]:rpc_url,rpc_cookie_path, and ZMQ addresses (zmq_hashblock_addr/zmq_rawblock_addr).[mining]: Pool fee, donation settings, andpooltag_prefix.[logging]:debugenables verbose runtime logging, andnet_debugenables raw network tracing (net-debug.log) when debug logging is active.
Set numeric values explicitly (do not rely on automation), and trim whitespace (goPool trims internally but a clean config is easier to audit). After editing, restart goPool or send SIGUSR2 (see below).
Optional split override files can layer advanced settings without touching the main config:
services.toml: service/integration settings:auth(Clerk URLs/session cookie),backblaze_backup(backup service settings),discord(Discord URLs/channels + worker notify threshold),status(mempool_address_url,github_urllinks).[rate_limits]:max_conns, burst windows, steady-state rates,stratum_messages_per_minute(messages/min before disconnect + 1h ban), and whether to auto-calculate throttles frommax_conns.[timeouts]:connection_timeout_seconds.[mining]inpolicy.toml: share-validation policy toggles (share_*settings) plussubmit_process_inline.[difficulty]:default_difficultyfallback when no suggestion arrives,max_difficulty/min_difficultyclamps (0 disables a clamp), whether to lock miner-suggested difficulty, and whether to enforce min/max on suggested difficulty (ban/disconnect when outside limits). The firstmining.suggest_*is honored once per connection, triggers a clean notify, and subsequent suggests are ignored.[mining]:extranonce2_size,template_extra_nonce2_size,job_entropy,coinbase_scriptsig_max_bytes, anddisable_pool_job_entropyto remove the<pool_entropy>-<job_entropy>suffix. Assigned Stratum difficulty is rounded to whole numbers at difficulty>= 1for broad miner compatibility; fractional difficulty is only used below1.[hashrate]:hashrate_ema_tau_seconds,share_ntime_max_forward_seconds.[peer_cleaning]: Enable/disable peer cleanup and tune thresholds.[bans]: Ban thresholds/durations,banned_miner_types(disconnect miners by client ID on subscribe), andclean_expired_on_startup(defaults totrue). Preferdata/config/miner_blacklist.jsonfor client ID blacklist management; it overridesbanned_miner_typeswhen present. Setclean_expired_on_startup = falseif you want to keep expired bans for inspection.[version]inpolicy.toml:min_version_bits(advertised/negotiated bit count, not a per-share changed-bit requirement),share_allow_out_of_mask_version_bits(allows submits outside negotiated mask, useful for BIP-110 bit 4 signaling),share_allow_degraded_version_bits(retained compatibility flag), andbip110_enabled(sets bit 4 on newly generated templates).version_bits.toml: explicit[[bits]]overrides for block header version bits (bit=<0..31>,enabled=true|false). This file is read-only from goPool's perspective and is never rewritten. Overrides are applied afterbip110_enabled, soversion_bits.tomlhas final authority per bit.
Keep these files absent to use built-in defaults. The first run creates examples under data/config/examples/.
Keep sensitive data out of config.toml:
rpc_user/rpc_pass: Only used when-allow-rpc-credsis supplied (deprecated). The preferred path isnode.rpc_cookie_path.discord_token,clerk_secret_key,clerk_publishable_key,backblaze_account_id,backblaze_application_key.
secrets.toml is gitignored and should live under data/config. The example is re-generated on each restart for reference.
goPool expects a Bitcoin Core node with RPC enabled. Configure:
node.rpc_url: The RPC endpoint forgetblocktemplateandsubmitblock.node.rpc_cookie_path: Point this to~/.bitcoin/.cookie(or equivalent). When empty, goPool auto-detects common locations and, when successful, writes the discovered path back intoconfig.toml.-allow-public-rpc: Allow connecting to an unauthenticated RPC endpoint (testing only).-rpc-cookie/-rpc-url: Use these overrides for temporary testing (e.g., a local regtest instance).-allow-rpc-creds: Forcesrpc_user/rpc_passfromsecrets.toml. goPool logs a warning every run and you lose the security of the cookie file workflow.
To change network defaults, use the -network flag:
mainnet,testnet,signet,regtest— only one may be set per run. goPool applies RPC and ZMQ port defaults, RPC URL overrides, and setscfg.mainnet/testnet/...booleans used for validation.
goPool can use Bitcoin Core's ZMQ publisher to learn about new blocks quickly, but it still uses RPC (including longpoll) to fetch the actual getblocktemplate payload and keep templates current.
node.zmq_hashblock_addr and node.zmq_rawblock_addr control the ZMQ subscriber connections. When both are empty goPool disables ZMQ and logs a warning that you are running RPC/longpoll-only; this lets regtest or longpoll-only pools skip configuring a publisher. When a network flag (-network) is set and both are blank, goPool auto-fills the default tcp://127.0.0.1:28332 for that network.
If you publish hashblock and rawblock on different ports, configure:
node.zmq_hashblock_addrforhashblocknode.zmq_rawblock_addrforrawblock
If both are set to the same tcp://IP:port, goPool will share a single ZMQ connection.
goPool subscribes to these Bitcoin Core ZMQ topics:
hashblock: triggers an immediate template refresh (new block).rawblock: records block-tip telemetry (height/time/difficulty + payload size) and triggers an immediate template refresh (new block).
Only hashblock and rawblock affect job freshness.
To avoid losing anything that affects mining/job freshness:
- Publish/subscribe at least one of
hashblockorrawblockso goPool refreshes immediately on new blocks.
Common choices:
- Lowest bandwidth: enable only
hashblock. - More block-tip telemetry without extra RPC: enable
rawblock(and optionally alsohashblock).
Even with ZMQ enabled, goPool still uses RPC longpoll to keep templates current when the mempool/tx set changes. ZMQ tx topics are not used to refresh templates today, so if you disable longpoll you may stop picking up transaction-only template updates (fees/txs) between blocks.
The status UI uses two listeners:
server.status_listen(default:80) — serves HTTP, static files, and JSON endpoints.server.status_tls_listen(default:443) — serves HTTPS with auto-generated certificates (stored indata/tls_cert.pemanddata/tls_key.pem).
Set status_tls_listen = "" to disable HTTPS and keep only the HTTP listener. Set status_listen = "" to disable HTTP entirely and rely solely on TLS. The CLI no longer provides an -http-only toggle.
goPool also auto-creates /stats/ and /api/* handlers plus optional TLS/cert reloading. Run systemctl kill -s SIGUSR1 <service> to reload the templates (the previous template set is kept when parsing fails) and SIGUSR2 to reload the configuration files without stopping the daemon.
data/config/admin.toml is created automatically the first time goPool runs. The generated file documents the panel, defaults to enabled = false, and ships with username = "admin" plus a random password (check the file to copy the generated secret). Update the file to enable the UI, pick a unique username/password, and keep it out of version control. The session_expiration_seconds value controls how long the admin session remains valid (default 900 seconds).
goPool now stores a password_sha256 alongside the plaintext password. On startup, if password is set, goPool verifies/refreshes password_sha256 to match it. After the first successful admin login, the plaintext password is cleared from admin.toml and only the hash remains; subsequent logins use the hash.
When enabled, visit /admin (deliberately absent from the main navigation) and log in with the credentials stored in admin.toml. The panel exposes:
- Live settings – a field-based UI that updates goPool's in-memory configuration immediately. Some settings still require a reboot to fully apply across all subsystems.
- Save to disk – optionally force-write the current in-memory settings to
config.toml,services.toml,policy.toml, andtuning.toml. - Reboot – a button that sends SIGTERM to goPool. It requires re-entering the admin password and typing
REBOOTto confirm the action so your pool does not restart accidentally.
Because the admin login is intentionally simple, bind this UI to trusted networks only (e.g., keep server.status_listen local-domain, use firewall rules, or run behind an authenticated proxy) and rotate credentials whenever you rotate administrators.
mining.pool_fee_percent,operator_donation_percent, andoperator_donation_addressdetermine how rewards are split.pooltag_prefixcustomizes the/goPool/coinbase tag (only letters/digits).job_entropyandpool_entropyhelp make each template unique; disable the suffix withtuning.toml[mining] disable_pool_job_entropy = true.- Share validation checks are explicit toggles in
policy.toml[mining]:share_require_authorized_connectiondefaults totrue.share_job_freshness_modedefaults to1(options:0=off,1=job_id,2=job_id+prevhash).share_check_param_formatdefaults totrue.share_check_ntime_windowandshare_check_version_rollingdefault totrue.
share_check_duplicatedefaults totrueand enables duplicate-share detection (same job/extranonce2/ntime/nonce/version on one connection).share_require_worker_matchdefaults tofalse; enable it if you want strict submit/authorize worker-name matching.submit_process_inlinedefaults tofalse. Enabling it can reduce submit latency by processingmining.submitinline instead of queueing work.vardiff_enableddefaults totrue; set it tofalseto keep connection difficulty static unless explicitly changed.
Log files live under data/logs/:
pool.log– structured log of pool events.errors.log– capturesERRORevents for quick troubleshooting.net-debug.log– recorded when debug logging + network tracing are enabled ([logging].debug=trueand[logging].net_debug=true, or-debug -net-debug); contains raw requests/responses and raw RPC/ZMQ traffic.
Use -stdout to mirror every entry to stdout. Pair that with journalctl or container logs for live debugging.
The internal simpleLogger writes a daily rolling file per log type, rotating after three days (configurable via const logRetentionDays).
goPool maintains its state in data/state/workers.db. For Backblaze uploads, it takes a consistent SQLite snapshot first (using SQLite's backup API). If you enable a local snapshot (keep_local_copy = true or set snapshot_path), goPool also writes a persistent snapshot you can back up safely (for example data/state/workers.db.bak).
Configure services.toml [backblaze_backup]:
[backblaze_backup]
enabled = true
bucket = "my-bucket"
prefix = "gopool/"
interval_seconds = 43200
keep_local_copy = true
snapshot_path = ""Store credentials in secrets.toml and keep them secure.
If Backblaze is temporarily unavailable at startup (network outage, transient auth failure), goPool keeps writing local snapshots (when enabled) and will retry connecting to B2 on later backup runs without requiring a restart.
Expired bans are rewritten on every startup by default. Control this via policy.toml [bans].clean_expired_on_startup (defaults to true). Set it to false to inspect expired entries without clearing them.
Clean bans happen inside NewAccountStore as it opens the shared state DB; when disabled, you still get bans loaded from disk, but expired entries remain visible via the status UI.
If you need a “safe to copy while goPool is running” database file, enable a local snapshot via [backblaze_backup].keep_local_copy (defaults the snapshot to data/state/workers.db.bak) or [backblaze_backup].snapshot_path. That snapshot is written atomically during each backup run.
When [backblaze_backup].enabled = true, goPool always writes a local snapshot (defaulting to data/state/workers.db.bak when snapshot_path is empty) so you still have a reliable local backup even if B2 is temporarily unavailable.
If you do not have a snapshot configured, stop the pool before copying data/state/workers.db. Avoid opening the live DB with external tools while goPool is running.
The data/state/ directory also holds ban metadata, saved workers snapshots, and any auto-generated JSON caches—keep it alongside your main data/ backup strategy.
Auto-configured accept rate limits calculate max_accept_burst/max_accepts_per_second based on max_conns unless tuning.toml overrides them. Recent defaults aim to allow all miners to reconnect within accept_reconnect_window seconds.
Key runtime knobs:
accept_burst_window/accept_reconnect_window/accept_steady_state_*– windows that shape burst vs sustained behavior.hashrate_ema_tau_seconds– tune EMA smoothing for per-worker hashrate.share_ntime_max_forward_seconds– tolerated future timestamps on shares (default 7000 seconds).peer_cleaning– enable/disable and tune thresholds for cleaning stalled miners.difficulty– clamp advertised difficulty, optionally enforce min/max on miner-suggested difficulty, and optionally lock miner suggestions.
Each override value logs when set, so goPool operators can audit what changed via pool.log.
- SIGUSR1 re-parses the embedded HTML templates and refreshes the embedded static cache. Errors are logged but the previous template set remains active so the site keeps serving—check
pool.logif pages look odd after a reload. - SIGUSR2 reloads
config.toml,secrets.toml,services.toml,policy.toml,tuning.toml, andversion_bits.toml, reapplies overrides, and updates the status server with the new config. - Shutdown occurs on
SIGINT/SIGTERM. goPool stops the status servers, Stratum listener, and pending replayers gracefully. - TLS cert reloading uses
certReloaderto monitordata/tls_cert.pem/tls_key.pemhourly. Certificate renewals (e.g., via certbot) are picked up without restarts.
/api/overview,/api/pool-page,/api/server,/api/node,/api/pool-hashrate, and/api/blocksprovide the public JSON snapshots consumed by the UI. Disable all JSON APIs with-no-json./user/<wallet>and/stats/<wallet>are standard wallet lookup routes./users/<wallet_sha256>is the privacy variant of wallet lookup (keeps raw wallet values out of links/bookmarks)./stats/serves the saved-worker dashboards, including per-worker graphing data.- Authenticated JSON routes (
/api/saved-workers*,/api/discord/notify-enabled,/api/auth/session-refresh) back saved-worker and Clerk/Discord flows when enabled.
-profilewritesdefault.pgo; usego tool pprofor./scripts/profile-graph.sh default.pgo profile.svgto inspect the profile and generate SVGs.- Watch
/api/pool-pageand/api/serverfor RPC/share error counters and feed-health drift. net-debug.logrecords RPC/ZMQ traffic when debug logging + network tracing are enabled ([logging].debug=trueand[logging].net_debug=true, or-debug -net-debug).
documentation/TESTING.md– How to run and extend the test suite, including fuzz targets and benchmarks.
Refer back to the concise main README for quick start instructions, and keep this document nearby while you tune your deployment.