Skip to content

Rate-limit the validator axon behind an edge proxy + proof-parsing fix#445

Merged
LandynDev merged 4 commits into
testfrom
axon-rate-limit-config
Jun 2, 2026
Merged

Rate-limit the validator axon behind an edge proxy + proof-parsing fix#445
LandynDev merged 4 commits into
testfrom
axon-rate-limit-config

Conversation

@anderdc
Copy link
Copy Markdown
Collaborator

@anderdc anderdc commented Jun 2, 2026

Summary

Hardens validator-axon ingress and the swap CLI's handling of edge-proxy rate limiting, plus an unrelated one-line proof-parsing fix.

  • Bind the axon loopback-only behind a proxy (AXON_PUBLISH) — lets validators run the axon behind an edge proxy that does the rate limiting, instead of exposing it directly.
  • Surface edge-proxy 429s in the swap CLI — a 429 is rejected at the edge proxy and never reaches the validator, so the CLI now recognizes it as transient, reads it as "rate limited" (not a generic timeout), backs off, and auto-retries with a fresh signed timestamp.
  • Accept 0x-prefixed signatures in TAO verify_from_proofbytes.fromhex() rejects a leading 0x, so a 0x-prefixed sr25519 proof (e.g. from a JS/web client) threw and the proof check returned False, rejecting the reserve as "Invalid source address proof". CLI proofs are bare hex so they were unaffected. Strips the prefix like the rest of the file already does. Independent of the rate-limiting work.

Changes

  • .env.example, docker-compose.vali.ymlAXON_PUBLISH wiring
  • allways/cli/swap_commands/{swap.py,miner_commands.py}, allways/cli/validator_rejections.py — 429 detection + backoff/retry
  • allways/chain_providers/subtensor.py0x strip in verify_from_proof
  • tests/test_validator_rejections.py — coverage for the 429 path

Notes

The dest-tx replay false-rejects seen on testnet4 (swaps #39/#42) are not in scope here — that's a testnet4 block-cadence artifact, not a mainnet issue and not caused by the proxy.

anderdc and others added 4 commits June 1, 2026 18:01
Lets an operator front the axon with a local reverse proxy (e.g. nginx for
rate limiting) by setting AXON_PUBLISH=127.0.0.1:<port>, so docker publishes
the axon to host loopback instead of 0.0.0.0. Unset keeps the raw
internet-facing publish, so existing validators are unchanged.
A reverse proxy (nginx) in front of the validator axon returns 429 on
rate-limit. The dendrite couldn't parse nginx's HTML error body, so a 429
showed up as a generic parse failure and the CLI rendered it as 'no response
— timeout or validator down' — misleading, and only manually retryable.

- render_and_aggregate: detect dendrite.status_code == 429 as a distinct
  'rate_limited' category (transient), printed as 'rate limited' instead of
  'no response'. Shared by reserve, confirm, and activate.
- reserve / confirm / activate: auto-backoff 6s and retry on rate_limited
  instead of prompting. Only this category is auto-retried, and a 429 was
  rejected at the edge (the validator never ran it), so there's no
  double-submit risk. Activate re-signs a fresh timestamp per attempt.

Requires the proxy to return a JSON 429 body so the status survives
dendrite parsing.
bytes.fromhex() rejects a leading 0x, so a 0x-prefixed sr25519 proof
(e.g. from a JS/web client) threw and verify_from_proof returned False,
rejecting the swap reserve as 'Invalid source address proof'. CLI proofs
are bare hex so they were unaffected. Strip the prefix like the rest of
the file (subtensor.py:75, commitments.py:121) so both formats verify.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants