POST /v1/challenges/{challenge_name}/submissions
Uploads a signed submission for the named challenge (app_proxy.py:510-512,
app_proxy.py:424-484). The challenge must be registered and active; otherwise
the master returns 404 (app_proxy.py:425, app_proxy.py:243-254).
Required signature headers
Every upload MUST carry these four headers; a missing header is rejected with401 (miner_auth.py:159-162, miner_auth.py:230-234):
| Header | Meaning | Source |
|---|---|---|
X-Hotkey | The miner’s SS58 hotkey address | miner_auth.py:159 |
X-Signature | Signature over the canonical message | miner_auth.py:160 |
X-Nonce | Unique per-request nonce | miner_auth.py:161 |
X-Timestamp | Unix timestamp (integer seconds) | miner_auth.py:162 |
X-Submission-Filename header is forwarded to the challenge when
present (app_proxy.py:463-465).
Canonical message
The signature is computed over this exact byte string (canonical_upload_message,
miner_auth.py:96-111):
miner_auth.py:107-111, miner_auth.py:170-180):
netuid— the subnet id,100for BASE (config/settings.py:12).challenge_slug— the active challenge’s slug (app_proxy.py:438).METHOD— the HTTP method, upper-cased (miner_auth.py:109).path— the request path being signed, i.e. the public submissions path (app_proxy.py:435-436).hotkey,nonce,timestamp— the values from the matching headers (miner_auth.py:171-178).body_hash—sha256(body).hexdigest()of the raw request body (miner_auth.py:170).
0x-prefixed hex signature is hex-decoded before verification
(verify_substrate_signature, miner_auth.py:114-121; _decode_signature,
miner_auth.py:222-227).
Verification rules
The master enforces, in order:- Body size — bodies larger than the configured limit (default
2_000_000bytes) return413(app_proxy.py:274,app_proxy.py:427-431). - Timestamp freshness — if
abs(now - timestamp)exceeds the TTL (default300seconds) the signature is rejected as stale (app_proxy.py:272,miner_auth.py:163-169). - Signature — an invalid signature is rejected (
miner_auth.py:181-182). - Hotkey registration — by default the hotkey must be registered in the
metagraph; an unknown hotkey is rejected and UID
0is blocked (app_proxy.py:275,miner_auth.py:200-219). - Nonce replay — the nonce is reserved once; a repeat returns
409(miner_auth.py:184-191,app_proxy.py:440-441).
Response and error codes
| Status | When | Source |
|---|---|---|
401 | Missing/invalid signature, stale timestamp, or unknown hotkey | app_proxy.py:442-443 |
409 | Nonce already used (replay) | app_proxy.py:440-441 |
413 | Submission larger than the body limit | app_proxy.py:427-431 |
404 | Challenge not registered or not active | app_proxy.py:243-254 |
502 | Challenge unreachable, or challenge token unavailable | app_proxy.py:446-449, app_proxy.py:475-478 |
app_proxy.py:479-484). The response body shape is owned by the challenge and
is not defined in the master.
What the master forwards to the challenge
After verification, the master POSTs the body to the challenge’s internal bridge route/internal/v1/bridge/submissions (app_proxy.py:470) with these headers
(app_proxy.py:450-465):
| Header | Value |
|---|---|
Authorization | Bearer <challenge token> (app_proxy.py:451) |
X-Platform-Challenge-Slug | the challenge slug (app_proxy.py:452) |
X-Platform-Verified-Hotkey | the verified hotkey (app_proxy.py:453) |
X-Platform-Verified-Nonce | the verified nonce (app_proxy.py:454) |
X-Platform-Request-Hash | the body hash (app_proxy.py:455) |
X-Platform-Verified-Uid | the resolved UID, when available (app_proxy.py:461-462) |
Example
Related
Security model
How signed uploads are verified at the proxy.
Submit to a challenge
The miner-side guide to building and signing a submission.
Proxy API
The proxy routes that sit alongside this upload.