AlgoJudge documentation0.1

The Server–Runner contract

What a Runner is on the wire, where the authority for this section lies, and the shape of every call.

This section is for somebody implementing a Runner of their own: a program that registers with an AlgoJudge Server, is approved, claims work, and reports results. It is not a description of the Runner that ships — that is Runner.

The accepted specification prevails

This section describes version 1.1 of the contract. It is written here for a reader, and the accepted specification is what a Server is built to: on any divergence, the specification is right and this section is wrong.

It holds for every page in this section, which is why none of them repeats it.

What a Runner is, on the wire

Three properties, and everything else follows from them.

  • The Runner initiates every connection. The Server never calls a Runner. There is no callback URL, no inbound port, and nothing to register but a key.
  • The Runner is stateless apart from a cache. One that dies mid-evaluation resumes nothing and nobody comes back for that work. The Server's lease is the whole recovery story, which is why it is a correctness mechanism rather than a safety net.
  • The Server never parses a problem type. It is matched by string equality and nothing else. Adding a problem type is not a Server change, and a Runner may implement a type the Server has never heard of.

A Runner holds an Ed25519 key pair it generates once. The Server knows it by the fingerprint of that key.

The lifecycle

        register            approve             challenge + token
anonymous ────────> pendingApproval ────────> approved ──────────────> holding a token
                            │                     │                          │
                            │                     │ revoke                   │ claim
                            ▼                     ▼                          ▼
                        (refused a token)      revoked ──> forget        holding a job

A Runner may register at any time and evaluates nothing until an administrator approves it — runner:approve is a global permission, and no manager template carries it. A re-registration — which is what every restart is — asks for a challenge first and signs the nonce; the challenge endpoint does not itself require approval, so a Runner still waiting to be approved can restart like any other. Revocation is permanent: there is no rotation, so a leaked key means a new configuration, a new key and a new registration.

The whole conversation runs in one order, and two parts of it are requirements rather than habits: the lease is renewed on a timer while work is in progress, and anything the evaluation produced is uploaded and attached before the report, which ends the job.

Every call

Everything is JSON over HTTP, under /api/v1.

PathAuth
HealthGET /api/v1/healthanonymous
RegisterPOST /api/v1/runner/registeranonymous; signed for a fingerprint the Server already knows — see Registration
ChallengePOST /api/v1/runner/auth/challengeanonymous
TokenPOST /api/v1/runner/auth/tokenanonymous
Claim a jobPOST /api/v1/runner/jobs/claimbearer
Renew a leasePOST /api/v1/runner/jobs/{jobId}/leasebearer
Report progressPOST /api/v1/runner/jobs/{jobId}/progressbearer
Report a resultPOST /api/v1/runner/jobs/{jobId}/reportbearer
Give a job backPOST /api/v1/runner/jobs/{jobId}/releasebearer
Read a fileGET /api/v1/runner/files/{id}bearer
Upload a filePOST /api/v1/runner/filesbearer
Attach to an attemptPOST /api/v1/runner/jobs/{jobId}/filesbearer
Attach to the RunnerPOST /api/v1/runner/files/attachbearer
HeartbeatPOST /api/v1/runner/heartbeatbearer

The token goes in Authorization: Bearer. It has a lifetime, and it authorizes this Runner's own surface and nothing on the panel a person signs in to.

Two conventions that govern every document

Both come from the Server's serializer rather than from taste, and the implementation is the clearest statement of them — crates/aj-protocol/src/wire.rs in AlgoJudge-Runner is the reference implementation of every shape in this section.

  • camelCase, everywhere.
  • Absent, never null. An optional value the Server does not have is simply not in the document. A client must treat a missing member as absent rather than expecting an explicit null, and must not send one either.

Refusals carry a code

An error answer carries a machine-readable code, and the code is what to switch on. The HTTP status alone is not enough: a 503 may equally come from an edge proxy that has never heard of this product.

The codes are listed on the page for the call that produces them.

The rest of this section

Registration and authenticationthe key, approval, and the challenge-response handshake
Claiming and the leasehow work is taken, and what makes a late report refusable
Reportingresults, idempotency, and the failure that is not a verdict
Filestransfer, sha256, and attachments
Availabilitythe heartbeat, and a Server that is up and declining to serve
What is not specifiedincluding two documents that are history, not standards

Conformance

The Server carries a conformance suite that exercises the contract against a real database on every pull request. A second implementation should pass the same sequence: register, be refused a token before approval, be approved, authenticate, be refused a replayed nonce, claim, read only its own package, report, resend the report and receive the same result, and lose a job to lease expiry.

Four cases are worth writing even though nothing obliges an implementation to pass them, because each is a way of being wrong that looks like working: a released job is queued again at once with its delivery count where it was; a Runner waiting on an empty queue is handed a submission made afterwards in a fraction of that wait, rather than when it runs out; a 204 arrives at the deadline and not before; and a job reported as an infrastructure failure is queued again rather than failed, until the deliveries run out.

This project is licensed under MIT. See LICENSE. This documentation is CC BY 4.0.

On this page