The dashboard JSON was deployed but its provider config never was, so
Grafana had no reason to read the directory -- the dashboard would have
been a file on disk and an empty folder in the UI.
Tracked separately from the dashboard because Grafana reads providers
only at container startup while dashboards are re-read on the
provisioning interval: a provider change restarts grafana, a dashboard
change does not.
validate now queries Grafana's search API for the dashboard uid rather
than testing that a file exists, since a provisioned dashboard that
fails to parse leaves the JSON looking perfectly fine on disk.
Adds a deploy-metrics job shipping a Prometheus scrape drop-in and a
provisioned Grafana dashboard to the fleet metrics host, plus the
exposure work the exporters needed to be reachable at all.
The node's exporter was loopback-only, so --prometheus-external. The
miner's already bound 0.0.0.0 unconditionally (crates/metrics builds the
listener from [0,0,0,0], no bind option) -- I had recorded it as loopback
in the port registry, which was wrong and is corrected. Both exporters
are unauthenticated, so each gets a firewalld service opened by a rich
rule scoped to the scrape host; that rule, not the bind, is the access
boundary.
Dashboard panels are built on metric names read off the running node and
the miner's source, not guessed. It leads with the states that look
healthy but are not: peers at zero, hash rate at zero while the unit is
active, GPU devices at zero, jobs never arriving.
Two shared-infra changes are in infra-setup.sh rather than the workflow,
since other projects depend on both files: prometheus.yml gains
scrape_config_files so projects stop sharing one file, and
grafana.container gains a dashboards mount it did not have.
The node embeds the reward preimage verbatim in the PreRuntime digest of
every block it authors (client/consensus/qpow/src/lib.rs:726), and the
mining-rewards pallet reads that digest back to derive the payout
address on-chain. It is public from the first mined block.
So the Gitea secret, the 0640 handling and the ps-visibility caveat were
all theatre over a value published on a blockchain. It moves to the
workflow env: block with the rest of the infra truth, and
infra-setup.sh loses --mint-reward-key entirely along with its pass and
Gitea-API dependencies.
This also answers why the node takes a preimage and not an address: the
runtime does the derivation, which is what proves the payout target is a
canonical wormhole address rather than an arbitrary account. An address
would give it nothing to verify.
Neither the node nor the miner needs a mnemonic. The node needs only the
32-byte preimage, from which it derives the reward address; the mining
protocol carries no reward address at all, so the miner cannot know it.
Deriving an inner_hash from a wallet is a one-time offline step and has
no business happening on the deploy workstation. infra-setup.sh now
accepts only the inner_hash, validates it is 0x + 64 hex, and refuses a
mnemonic outright. quantus-node itself refuses --words/--seed as argv
for the same reason: argv is world-readable and recorded in shell
history and audit logs.
This drops the seed phrase, the temporary binary download and the local
key derivation from the provisioning path entirely.
Node on bob, external GPU miner on benjy, split because
--miner-listen-port disables the node's built-in CPU mining.
The PoW is Poseidon2 over Goldilocks: register-resident, no memory
hardness, dominated by 64-bit modular multiplication. Measured on the
fleet, GPU beats CPU by ~200x (RTX 5090 368 MH/s @ 600W, RTX 4090
183 MH/s @ 450W, Ryzen 9 9950X3D 4.15 MH/s), so CPU mining is disabled
everywhere including on the GPU hosts.
The workflow deploys or validates. Validate mode asserts the hash
counter is advancing and the GPU is drawing power, because a miner that
enumerated no adapter and a node authoring nothing both look healthy to
systemctl is-active.
infra-setup.sh provisions the scoped gitea_ci accounts, generates the
node's libp2p key (the node refuses to start without one and it must
never be rotated by a deploy), and derives the reward preimage from a
seed phrase on stdin. It deliberately will not mint a fresh wallet: that
phrase is escalation-grade and fails agent-credentials.md §1.