Template verification, Noise NX encryption, fail-closed prevhash switching, and a two-event share lifecycle. Every decision carries a machine-readable reason code.
From miner handshake to upstream share submission, every hop is observable.
Shadow evaluates policy without miners. Observe monitors real traffic without gating. Both lead to inline enforcement. One binary, one config key.
mode = "shadow" | "observe" | "inline" See modesEvery share produces share_accepted at ACK and share_forward_result after upstream POST. Joined by share_id with a 1:1 invariant.
When a new block arrives, miners hold on stale work until the verifier responds. 50ms verdict timeout, 5s stale hold, deterministic disconnect on expiry.
prevhash_timeout_ms = 50 See timingScroll to continue
| Capability | Traditional pool | SV2 without ReserveGrid | SV2 + ReserveGrid |
|---|---|---|---|
| Miner transport encryption | ✗ Plaintext | ✓ Noise NX | ✓ Noise NX |
| Template verification | ○ Pool-side only | ✗ None | ✓ Policy-driven, 73 reason codes |
| Prevhash safety | ○ Implicit | ✗ No verification gate | ✓ Fail-closed, 50ms + 5s hold |
| Share audit trail | ✗ Pool internal | ✗ None at gateway | ✓ Two-event lifecycle with HMAC |
| Reject traceability | ✗ Opaque | ✗ SV2 wire codes only | ✓ reason_code + policy context |
| Operator config surface | ○ Varies | ✗ Minimal | ✓ 51 TOML keys + env overrides |
| Observability | ○ Custom | ✗ Not built in | ✓ Prometheus + event stream + Grafana |
Every verdict is structured, machine-readable, and carries the policy context that caused it.
{
"accepted": false,
"reason_code": "avg_fee_below_minimum",
"reason_detail": "avg_fee=141 < min_avg_fee_used=200",
"policy_context": {
"fee_tier": "mid",
"min_avg_fee_used": 200
}
}
Start with shadow or observe. Enforce with inline when ready.