# SciMesh Coordinator — API requests # # Runnable from any editor with a REST client (VSCodium/VS Code "REST Client", # JetBrains HTTP Client). Click "Send Request" above each block, top to bottom: # later requests reuse ids captured from earlier responses. # # Start the stack first: docker compose up -d @host = http://localhost:8080 @token = change-me @worker = worker-1 ### Readiness — the only unauthenticated endpoint (probes the database) GET {{host}}/health ### Auth check — no token must be rejected with 401 POST {{host}}/tasks/claim Content-Type: application/json { "worker_id": "{{worker}}" } ### 0. Register a worker (201) # @name register POST {{host}}/workers/register Authorization: Bearer {{token}} Content-Type: application/json { "name": "lab-worker-01", "capabilities": ["similarity_search"], "cpu_count": 8, "memory_mb": 16384 } @workerId = {{register.response.body.worker_id}} ### 0b. Upload a dataset — the coordinator splits it into shard tasks (201) # Text fields first, the file part last (it is streamed, not buffered). # @name uploadJob POST {{host}}/jobs/upload Authorization: Bearer {{token}} Content-Type: multipart/form-data; boundary=----scimesh ------scimesh Content-Disposition: form-data; name="workload" similarity_search ------scimesh Content-Disposition: form-data; name="parameters" {"top_k":10} ------scimesh Content-Disposition: form-data; name="chunk_rows" 2 ------scimesh Content-Disposition: form-data; name="file"; filename="chembl.tsv" Content-Type: text/tab-separated-values id smiles A CC B CCC C CCCC D CCCCC ------scimesh-- ### Download a task's input shard (200) — taskId must be a shard task from an ### uploaded job (claim one first; its input.uri is /tasks/{id}/input). GET {{host}}/tasks/{{taskId}}/input Authorization: Bearer {{token}} ### 1. Create a job and its chunks (201) # The coordinator splits the submission into one task per chunk, transactionally. # @name createJob POST {{host}}/jobs Authorization: Bearer {{token}} Content-Type: application/json { "workload": "similarity_search", "input_uri": "s3://chembl/full.sdf", "parameters": { "top_k": 10 }, "chunks": [ { "chunk_index": 0, "input_uri": "s3://chembl/shard-0.sdf", "input_sha256": "aaa", "max_attempts": 3 }, { "chunk_index": 1, "input_uri": "s3://chembl/shard-1.sdf", "input_sha256": "bbb", "max_attempts": 3 }, { "chunk_index": 2, "input_uri": "s3://chembl/shard-2.sdf", "input_sha256": "ccc", "max_attempts": 3 } ] } @jobId = {{createJob.response.body.id}} ### 2. Claim a task (200, or 204 when the queue is empty) # Each call leases a different task; run it repeatedly to see chunk_index advance. # @name claim POST {{host}}/tasks/claim Authorization: Bearer {{token}} Content-Type: application/json { "worker_id": "{{worker}}", "capabilities": ["similarity_search"], "max_concurrency": 1 } @taskId = {{claim.response.body.task_id}} @attempt = {{claim.response.body.attempt}} ### 3. Heartbeat — renew the lease while the task is still running (200) POST {{host}}/tasks/{{taskId}}/heartbeat Authorization: Bearer {{token}} Content-Type: application/json { "worker_id": "{{worker}}", "attempt": {{attempt}} } ### 3a. Upload a partial-result artifact (200) — while the task is leased # Identity travels in headers per the contract; the body is streamed as-is. # @name uploadArtifact PUT {{host}}/tasks/{{taskId}}/artifacts/result.csv Authorization: Bearer {{token}} Content-Type: text/csv X-Worker-ID: {{worker}} X-Task-Attempt: {{attempt}} query,match,score CHEMBL25,CHEMBL139,0.87 @artifactId = {{uploadArtifact.response.body.artifact_id}} ### 3b. Download the artifact by id (200) GET {{host}}/artifacts/{{artifactId}}/download Authorization: Bearer {{token}} ### 3c. Upload a second artifact — used by the conflict check below (200) # @name uploadArtifact2 PUT {{host}}/tasks/{{taskId}}/artifacts/secondary.csv Authorization: Bearer {{token}} Content-Type: text/csv X-Worker-ID: {{worker}} X-Task-Attempt: {{attempt}} query,match,score CHEMBL25,CHEMBL521,0.42 @artifactId2 = {{uploadArtifact2.response.body.artifact_id}} ### 4. Submit the result, referencing the uploaded artifact (200) POST {{host}}/tasks/{{taskId}}/result Authorization: Bearer {{token}} Content-Type: application/json { "worker_id": "{{worker}}", "attempt": {{attempt}}, "result": { "artifact_id": "{{artifactId}}", "content_type": "text/csv" }, "metrics": { "elapsed_ms": 1234, "candidates": 50000 } } ### 4a. Replay the same result — must be idempotent (200, not 409) POST {{host}}/tasks/{{taskId}}/result Authorization: Bearer {{token}} Content-Type: application/json { "worker_id": "{{worker}}", "attempt": {{attempt}}, "result": { "artifact_id": "{{artifactId}}" } } ### 4b. A different artifact for the same task — conflict (409) POST {{host}}/tasks/{{taskId}}/result Authorization: Bearer {{token}} Content-Type: application/json { "worker_id": "{{worker}}", "attempt": {{attempt}}, "result": { "artifact_id": "{{artifactId2}}" } } ### 4c. Another worker submitting for this task — conflict (409) POST {{host}}/tasks/{{taskId}}/result Authorization: Bearer {{token}} Content-Type: application/json { "worker_id": "impostor", "attempt": {{attempt}}, "result": { "artifact_id": "{{artifactId}}" } } ### 5. Report a failure instead (200) # retryable=true returns the task to the queue while attempts remain; # retryable=false fails it terminally. POST {{host}}/tasks/{{taskId}}/failure Authorization: Bearer {{token}} Content-Type: application/json { "worker_id": "{{worker}}", "attempt": {{attempt}}, "error_code": "download_failed", "error_message": "checksum mismatch on shard", "retryable": true } ### 6. Job progress (200) GET {{host}}/jobs/{{jobId}} Authorization: Bearer {{token}} ### --- error cases ------------------------------------------------------- ### Malformed UUID in the path (400) POST {{host}}/tasks/not-a-uuid/result Authorization: Bearer {{token}} Content-Type: application/json { "worker_id": "{{worker}}", "attempt": 1, "result_uri": "s3://x", "result_sha256": "x" } ### Unknown field in the body (400) — a misspelled key must not pass silently POST {{host}}/tasks/claim Authorization: Bearer {{token}} Content-Type: application/json { "worker_ID": "{{worker}}" } ### Unknown job (404) GET {{host}}/jobs/00000000-0000-0000-0000-000000000000 Authorization: Bearer {{token}} ### Stitching is not implemented yet (501) # Any endpoint whose use case is still a stub answers 501.