802-line playbook becomes 68 lines (three plays: the role, the Caddy dashboard, the Stratum socket proxy) plus a 345-line role. datum_gateway_vars.yml is deleted; its content is the role's defaults. Verified after a real run with zero miners connected: datum-gateway restarted cleanly onto the reformatted config, deployed config.json semantically identical to what was there (pool_address bc1qvrj3g84..., pool_pass_* false, ports unchanged), health check timer firing, and the Knots side untouched - bitcoind still up since 2026-08-19 with blocknotify intact. TWO PIECES OF DRIFT WHERE THE NODE WAS RIGHT, both confirmed with the operator: - datum_mining_address: the vault held bc1qdse9dsg... while the node had been mining to bc1qvrj3g... since 2026-08-08. This is WHERE BLOCK REWARDS ARE PAID. And unlike fulcrum and bitcoin-knots, the `Restart datum-gateway` handler here was never gated, so the stale value would have applied immediately rather than sitting inert on disk. - pool_pass_workers / pool_pass_full_users: false on the node, true in the vars file. Both corrected in the vault and role defaults with notes recording why. Comparing this config needs semantics, not text: the live file is single-line JSON and the template renders pretty-printed, so a textual diff is pure noise. Rendering it and comparing parsed JSON is what surfaced both differences. config.json carries bitcoind.rpcpassword and api.admin_password, and --diff prints rendered content - so `--check --diff` put them on the terminal. The task now sets diff: false by default (-e datum_reveal_config=true to opt in). Those two should be rotated. I also mis-reported pool_pass_workers/pool_pass_full_users as exposed credentials because my masking matched "pass" in the key name. They are BOOLEANS, and mining.pool_address is a Bitcoin address, public by nature. Only the two real passwords above were exposed. `Configure cmake build` and `Compile datum_gateway` are bare command: tasks with no changed_when, so they recompile on every run. The build is reproducible - Install datum_gateway binary sees identical content and leaves the installed binary's timestamp alone - but it is wasted work each time. Documented as the idempotent floor. Ownership parity checked mechanically against `git show HEAD:` keyed by task name: 7/7 match, 9 Kuma tasks dropped. This completes Plan 6 Stage 2: all six services in the list are roles. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
3.1 KiB
datum_gateway
Builds and runs DATUM Gateway, the
solo/pooled mining gateway, on knots-box. The calling playbook adds two more
plays on the edge host: the dashboard via caddy_site, and the public Stratum
port via socket_proxy.
Converted from deploy_datum_gateway_playbook.yml (802 lines) under Plan 6. The
playbook is now 68 lines and keeps all three plays.
⚠ This is half of a system
The Bitcoin Knots node on the same host feeds this gateway through
blocknotify=killall -USR1 datum_gateway in bitcoin.conf — see
roles/bitcoin_knots/README.md, where that line was found to be missing from the
template entirely. Changing either config means thinking about both.
Interrupting Stratum costs mining shares. Check before any run that restarts it:
ss -tn state established '( sport = :23334 )'
Two pieces of drift where the node was right
The repo and the node had diverged on values that matter, and the deployment would have applied the repo's:
| node (correct) | repo said | |
|---|---|---|
datum_mining_address |
bc1qvrj3g84… |
bc1qdse9dsg… |
pool_pass_workers / _full_users |
false |
true |
The address is the one that would have hurt: it is where block rewards are
paid, and unlike fulcrum and bitcoin-knots the Restart datum-gateway handler
here was never gated, so the change would have applied immediately rather than
sitting inert. Both corrected in the vault and defaults, with notes.
Verify semantics rather than text when touching config.json — render it and
compare parsed JSON, because the live file is single-line and the template is
pretty-printed, so a textual diff is all noise:
json.load(open('live.json')) == json.load(open('rendered.json'))
config.json holds real secrets — diff is suppressed
The file carries bitcoind.rpcpassword and api.admin_password. --diff
prints rendered content, so the task sets diff: false by default; pass
-e datum_reveal_config=true to opt in.
Note pool_pass_workers / pool_pass_full_users are booleans, not
passwords, despite the names — they control DATUM's pool-password passthrough.
mining.pool_address is a Bitcoin address and public by nature.
Expect changed on the compile every run
Configure cmake build and Compile datum_gateway are bare command: tasks
with no changed_when, so they always report changed and always re-run. The
build is reproducible — Install datum_gateway binary sees identical content and
does not replace it, so the installed binary keeps its original timestamp — but
the compile itself is wasted work on every run. That is the idempotent floor, not
drift.
Monitoring: one variable, no product knowledge
The check tests the gateway API and records the answer in its exit code, which
systemd keeps: systemctl is-failed datum-gateway-healthcheck.service. Set
healthcheck_push_url to report anywhere accepting an HTTP ping.
Unlike the other services here, only the health-check timer handler was gated
by uptime_kuma_enabled; the main deployment restart worked throughout.