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.
quantus
Deployment for a Quantus Network Planck node and its external GPU miner.
Two components, two hosts:
| Component | Host | Role |
|---|---|---|
quantus-node |
bob.hanzalova.internal |
consensus, P2P, reward address. Disk + network bound. |
quantus-miner |
benjy.hanzalova.internal |
Poseidon2-over-Goldilocks search on 1× RTX 4090. Pure GPU compute. |
They are split deliberately. Setting --miner-listen-port on the node
disables its built-in CPU mining, so the node authors nothing unless an
external miner is connected — the split is not optional once that flag is set.
The miner connects out to the node over QUIC/9833 and listens on nothing but a
loopback metrics port.
Conventions: ~/git/architecture — generic.md for the on-host shape,
deployment-gitea-actions.md for the deploy mechanism. Deviations are listed
below.
Why GPU, and why only these hosts
The proof-of-work is Poseidon2 over the Goldilocks field (p = 2⁶⁴ − 2³² + 1),
width 12, 4 external + 22 internal rounds — an arithmetic hash, not a bit-mixing
one. Cost is dominated by 64-bit modular multiplication, the state lives entirely
in registers, and there is no memory hardness at all. Measured on this fleet:
| Hardware | Hashrate | Power | Efficiency |
|---|---|---|---|
RTX 5090 (beast, ×2) |
368 MH/s each | 600 W | 0.60 MH/s/W |
| RTX 5090 @ 400 W floor | 281 MH/s | 400 W | 0.70 MH/s/W |
RTX 4090 (benjy) |
183 MH/s | 450 W | 0.41 MH/s/W |
| Ryzen 9 9950X3D, 32t | 4.15 MH/s | — | — |
Xeon E5-2620 v3, 12t (bob) |
0.50 MH/s | — | — |
A single 5090 at its minimum power limit out-hashes every CPU in the fleet
combined by roughly 24×. CPU mining is therefore disabled everywhere
(--cpu-workers 0), including on the GPU hosts, where it would add ~1% hashrate
for a couple hundred watts. The old Xeons are node-capable and mining-worthless.
GPU/CPU parity is verified upstream (engine-gpu's gpu_cpu_parity example,
25/25 jobs) — the GPU is doing real work, not advancing a lying counter.
Ports
Registered in ~/git/architecture/port-allocations.md §5.
| Port | Proto | Component | Exposure |
|---|---|---|---|
30333 |
tcp | node P2P (libp2p) | public — named firewalld service |
9833 |
udp | node ← miner control channel | mesh, firewalld rich rule scoped to the miner host only |
9615 |
tcp | node Prometheus | loopback |
9900 |
tcp | miner Prometheus | loopback |
Deviation from port-allocations.md §3: these are upstream protocol
defaults, not numbers derived from the service name. 30333 is the chain's
well-known P2P port and changing it buys nothing; 9833 is the miner protocol's
default and both ends must agree. They are recorded in the registry, which is
what the doc says actually prevents collisions. Neither lands in the crowded
alt-HTTP cluster.
The miner link gets a rich rule rather than a plain --add-service because the
fleet uses a single default zone (generic.md §9): adding the service outright
would publish 9833/udp on every address the host carries, and upstream is
explicit that this port must never face the internet.
Secrets
| Name | What | Set by |
|---|---|---|
RSYNC_SSH_KEY |
the shared fleet runner key | already exists — do not rotate |
QUANTUS_INNER_HASH |
32-byte wormhole preimage, 0x-prefixed |
script/infra-setup.sh --mint-reward-key |
Mining rewards accrue to a wormhole address derived from QUANTUS_INNER_HASH.
The script derives it from a wallet seed phrase read on stdin, so rewards
land in a wallet you already control and appear with no claiming step.
It will not generate a fresh wallet for you. A newly minted wormhole key's
24-word phrase is the only way to ever spend what it accumulates, and it fails
agent-credentials.md §1 outright — a seed controlling real funds is
escalation-grade and must not enter ~/.agents. Create the wallet in the
Quantus wallet app, back the phrase up there, then feed it in.
The miner's own credentials — miner-auth-token and miner-tls-cert-sha256 —
are not secrets in this repo. The node generates them on first start and
regenerates them if its base-path is ever wiped, so the deploy copies them from
the node to the miner on every run. That makes the pairing self-healing instead
of a silent auth failure six months later.
Usage
One-time, from a workstation with sudo ssh to the targets:
./script/infra-setup.sh --pubkey ~/.ssh/id_gitea_ci.pub
./script/infra-setup.sh --mint-reward-key # reads a seed phrase on stdin
Then push to main, or run the deploy workflow from the Actions UI. It has
two modes:
- deploy (default, and what a push does) — apply, then validate.
- validate — check only, change nothing. Asserts each unit is active, the
installed binary matches the pinned version, the node's P2P and miner ports
are listening, peers are connected, the GPU is enumerated and drawing power,
and
miner_hashes_totalis advancing.
systemctl is-active alone is not evidence this stack works: a miner that found
no GPU adapter, and a node that is authoring nothing because no miner is
attached, both look perfectly healthy to systemd. Hence the counter check.
Upgrades are deliberate — bump NODE_VERSION / MINER_VERSION in the workflow.
There is no auto-update on a validator on purpose.
Known conflict: helexa on benjy ⚠️
benjy already runs helexa neuron on :13131 (architecture/generic.md
§11, inference.md), serving inference from the same RTX 4090 this miner will
saturate. They will contend directly: the miner drives the GPU to ~95%
utilisation and ~450 W continuously, which will make inference latency on benjy
by turns awful and unpredictable.
Nothing here resolves that, and the deploy does not try to arbitrate. Options, in rough order of preference:
- Move mining to
beast(2× 5090, and 4× the hashrate) and leave benjy to inference. Requires checking what beast's neuron is doing first. - Make the miner yield —
--gpu-throttle-ms, or anExecConditionthat refuses to start while a model is loaded. - Accept the contention on benjy and treat inference there as best-effort.
Decide before enabling quantus-miner.service on benjy, not after.
Deviations from house convention
runs-on: infra, notfedora-43.gitea-runners.md§4 says a deploy needs only ssh + rsync and so fitsfedora-43. That is about tooling, not routing — these targets are mesh-only.internalnames, andlair/mail's two working deploys both useinfrafor that reason. Revisit if the fedora runners gain mesh reachability.- Upstream protocol ports rather than derived ones — see Ports above.
MemoryDenyWriteExecute=falseon the node (Substrate JITs its wasm runtime through wasmtime) andPrivateDevices=falseon the miner (needs/dev/nvidia*). Both are annotated in the unit files; neither is tidy-up-able.Type=simple, notnotify—quantus-nodedoes notsd_notify(READY=1).- No Rust workspace. This repo deploys upstream release binaries; there is
nothing to build, so
generic.md§1–§2 do not apply.
Upstream
- node — https://github.com/Quantus-Network/chain (
MINING.mdis the real doc) - miner — https://github.com/Quantus-Network/quantus-miner
- guide — https://docs.quantus.com/guides/mining
Note that Planck is a testnet: "tokens have no monetary value". Mainnet difficulty and reward schedule are unpublished, so no profitability claim here is possible yet. Also worth tracking: this PoW has no memory hardness and a small fixed state, which makes it an unusually clean FPGA/ASIC target — relevant to any decision to buy hardware for it.