Skip to content

Commit d187022

Browse files
committed
deprecate: redirect submissions API to term-challenge
Phase 8 of migration complete: - submit_agent now returns 410 GONE with redirect to term-challenge - get_submission_source now returns 410 GONE with auth instructions - list_submissions and get_submission still work for backward compat - Removed signature verification code (now in term-challenge) - Removed rate limiting code (now in term-challenge) - Removed job creation code (now in term-challenge) Users should update their CLI to point to term-challenge: POST /api/v1/submit (submissions) POST /api/v1/my/agents/:hash/source (owner source access) POST /api/v1/validator/claim_job (validator source access)
1 parent 6fb3820 commit d187022

File tree

1 file changed

+53
-162
lines changed

1 file changed

+53
-162
lines changed
Lines changed: 53 additions & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
//! Submissions API handlers
1+
//! Submissions API handlers - DEPRECATED
2+
//!
3+
//! Submissions have been migrated to term-challenge.
4+
//! These endpoints now return deprecation notices directing users
5+
//! to use the term-challenge API instead.
26
37
use crate::db::queries;
48
use crate::models::*;
@@ -9,13 +13,11 @@ use axum::{
913
Json,
1014
};
1115
use serde::Deserialize;
12-
use sha2::Digest;
13-
use sp_core::crypto::Ss58Codec;
1416
use std::sync::Arc;
1517

16-
/// Validate that a string is a valid SS58 hotkey address
17-
fn is_valid_ss58_hotkey(hotkey: &str) -> bool {
18-
sp_core::crypto::AccountId32::from_ss58check(hotkey).is_ok()
18+
/// Term-challenge URL for submissions (configured via TERM_CHALLENGE_URL env)
19+
fn get_term_challenge_url() -> String {
20+
std::env::var("TERM_CHALLENGE_URL").unwrap_or_else(|_| "http://localhost:8081".to_string())
1921
}
2022

2123
#[derive(Debug, Deserialize)]
@@ -24,10 +26,13 @@ pub struct ListSubmissionsQuery {
2426
pub status: Option<String>,
2527
}
2628

29+
/// DEPRECATED: List submissions
30+
/// Submissions are now managed by term-challenge.
2731
pub async fn list_submissions(
2832
State(state): State<Arc<AppState>>,
2933
Query(query): Query<ListSubmissionsQuery>,
3034
) -> Result<Json<Vec<Submission>>, StatusCode> {
35+
// Still return data from local DB for backward compatibility during migration
3136
let submissions = if query.status.as_deref() == Some("pending") {
3237
queries::get_pending_submissions(&state.db).await
3338
} else {
@@ -40,6 +45,8 @@ pub async fn list_submissions(
4045
Ok(Json(limited))
4146
}
4247

48+
/// DEPRECATED: Get submission by ID
49+
/// Submissions are now managed by term-challenge.
4350
pub async fn get_submission(
4451
State(state): State<Arc<AppState>>,
4552
Path(id): Path<String>,
@@ -51,165 +58,49 @@ pub async fn get_submission(
5158
Ok(Json(submission))
5259
}
5360

61+
/// DEPRECATED: Get submission source code
62+
/// Source code access is now managed by term-challenge with proper authentication.
5463
pub async fn get_submission_source(
55-
State(state): State<Arc<AppState>>,
56-
Path(id): Path<String>,
57-
) -> Result<Json<serde_json::Value>, StatusCode> {
58-
let submission = queries::get_submission(&state.db, &id)
59-
.await
60-
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
61-
.ok_or(StatusCode::NOT_FOUND)?;
62-
63-
Ok(Json(serde_json::json!({
64-
"agent_hash": submission.agent_hash,
65-
"source_code": submission.source_code,
66-
})))
64+
State(_state): State<Arc<AppState>>,
65+
Path(_id): Path<String>,
66+
) -> Result<Json<serde_json::Value>, (StatusCode, Json<serde_json::Value>)> {
67+
// No longer expose source code from platform-server
68+
// Users must use term-challenge API with proper authentication
69+
Err((
70+
StatusCode::GONE,
71+
Json(serde_json::json!({
72+
"error": "Source code access has been migrated to term-challenge",
73+
"message": "Use the term-challenge API to access source code with proper authentication",
74+
"term_challenge_url": get_term_challenge_url(),
75+
"endpoints": {
76+
"owner_source": "POST /api/v1/my/agents/:hash/source",
77+
"validator_claim": "POST /api/v1/validator/claim_job"
78+
}
79+
})),
80+
))
6781
}
6882

83+
/// DEPRECATED: Submit agent endpoint
84+
/// Agent submissions have been migrated to term-challenge.
85+
/// This endpoint returns a redirect notice.
6986
pub async fn submit_agent(
70-
State(state): State<Arc<AppState>>,
71-
Json(req): Json<SubmitAgentRequest>,
87+
State(_state): State<Arc<AppState>>,
88+
Json(_req): Json<SubmitAgentRequest>,
7289
) -> Result<Json<SubmitAgentResponse>, (StatusCode, Json<SubmitAgentResponse>)> {
73-
// Validate miner_hotkey is a valid SS58 address
74-
if !is_valid_ss58_hotkey(&req.miner_hotkey) {
75-
tracing::warn!(
76-
"Invalid miner_hotkey format: {} (expected SS58 address starting with '5')",
77-
&req.miner_hotkey[..32.min(req.miner_hotkey.len())]
78-
);
79-
return Err((
80-
StatusCode::BAD_REQUEST,
81-
Json(SubmitAgentResponse {
82-
success: false,
83-
submission_id: None,
84-
agent_hash: None,
85-
error: Some(format!(
86-
"Invalid miner_hotkey: must be a valid SS58 address (e.g., '5GrwvaEF...'). Received: {}",
87-
&req.miner_hotkey[..32.min(req.miner_hotkey.len())]
88-
)),
89-
}),
90-
));
91-
}
92-
93-
// Verify signature: miner must sign their source code hash to prove ownership
94-
// Message format: "submit_agent:<sha256_of_source_code>"
95-
let source_hash = hex::encode(sha2::Sha256::digest(req.source_code.as_bytes()));
96-
let message = format!("submit_agent:{}", source_hash);
97-
98-
if !crate::api::auth::verify_signature(&req.miner_hotkey, &message, &req.signature) {
99-
tracing::warn!(
100-
"Invalid signature for submission from {}",
101-
&req.miner_hotkey[..16.min(req.miner_hotkey.len())]
102-
);
103-
return Err((
104-
StatusCode::UNAUTHORIZED,
105-
Json(SubmitAgentResponse {
106-
success: false,
107-
submission_id: None,
108-
agent_hash: None,
109-
error: Some(format!(
110-
"Invalid signature. Message to sign: '{}'. Use sr25519 signature from your hotkey.",
111-
message
112-
)),
113-
}),
114-
));
115-
}
116-
117-
let epoch = queries::get_current_epoch(&state.db).await.unwrap_or(0);
118-
119-
// Rate limiting: 0.33 submissions per epoch (1 every 3 epochs)
120-
let can_submit = match queries::can_miner_submit(&state.db, &req.miner_hotkey, epoch).await {
121-
Ok(can) => can,
122-
Err(e) => {
123-
tracing::error!("Rate limit check failed for {}: {}", req.miner_hotkey, e);
124-
true // Allow submission if rate limit check fails
125-
}
126-
};
127-
128-
tracing::debug!(
129-
"Submission check: miner={}, epoch={}, can_submit={}",
130-
&req.miner_hotkey[..16.min(req.miner_hotkey.len())],
131-
epoch,
132-
can_submit
133-
);
134-
135-
if !can_submit {
136-
return Err((
137-
StatusCode::TOO_MANY_REQUESTS,
138-
Json(SubmitAgentResponse {
139-
success: false,
140-
submission_id: None,
141-
agent_hash: None,
142-
error: Some("Rate limit: 1 submission per 3 epochs".to_string()),
143-
}),
144-
));
145-
}
146-
147-
// Create submission with API key for centralized LLM inference
148-
let submission = queries::create_submission(
149-
&state.db,
150-
&req.miner_hotkey,
151-
&req.source_code,
152-
req.name.as_deref(),
153-
req.api_key.as_deref(),
154-
req.api_provider.as_deref(),
155-
req.api_keys_encrypted.as_deref(),
156-
epoch,
157-
)
158-
.await
159-
.map_err(|e| {
160-
(
161-
StatusCode::INTERNAL_SERVER_ERROR,
162-
Json(SubmitAgentResponse {
163-
success: false,
164-
submission_id: None,
165-
agent_hash: None,
166-
error: Some(e.to_string()),
167-
}),
168-
)
169-
})?;
170-
171-
tracing::info!(
172-
"Agent submitted: {} (hash: {}) from {} with provider: {:?}",
173-
submission.name.as_deref().unwrap_or("unnamed"),
174-
&submission.agent_hash[..16],
175-
&req.miner_hotkey,
176-
req.api_provider
177-
);
178-
179-
// Create evaluation job for each active challenge
180-
let challenges = queries::get_challenges(&state.db).await.unwrap_or_default();
181-
182-
for challenge in challenges.iter().filter(|c| c.status == "active") {
183-
match queries::create_job(&state.db, &submission.id, &challenge.id).await {
184-
Ok(job) => {
185-
tracing::info!(
186-
"Created job {} for submission {} on challenge {}",
187-
job.id,
188-
&submission.id[..8],
189-
challenge.id
190-
);
191-
}
192-
Err(e) => {
193-
tracing::warn!("Failed to create job for challenge {}: {}", challenge.id, e);
194-
}
195-
}
196-
}
197-
198-
// Broadcast submission event
199-
state
200-
.broadcast_event(WsEvent::SubmissionReceived(SubmissionEvent {
201-
submission_id: submission.id.clone(),
202-
agent_hash: submission.agent_hash.clone(),
203-
miner_hotkey: submission.miner_hotkey.clone(),
204-
name: submission.name.clone(),
205-
epoch,
206-
}))
207-
.await;
208-
209-
Ok(Json(SubmitAgentResponse {
210-
success: true,
211-
submission_id: Some(submission.id),
212-
agent_hash: Some(submission.agent_hash),
213-
error: None,
214-
}))
90+
// Submissions are now handled by term-challenge
91+
tracing::warn!("Deprecated submit_agent endpoint called - redirecting to term-challenge");
92+
93+
Err((
94+
StatusCode::GONE,
95+
Json(SubmitAgentResponse {
96+
success: false,
97+
submission_id: None,
98+
agent_hash: None,
99+
error: Some(format!(
100+
"Agent submissions have been migrated to term-challenge. \
101+
Please use: POST {}/api/v1/submit",
102+
get_term_challenge_url()
103+
)),
104+
}),
105+
))
215106
}

0 commit comments

Comments
 (0)