Dokumentacja AlgoJudge0.1

The REST API

The shape of the surface, what authenticates a request, the error contract, and the admin surface that answers 404 to everything it turns away.

Ta strona jest po angielsku

Polskie tłumaczenie obejmuje sekcje Instalacja i utrzymanie i Korzystanie z AlgoJudge. Pozostałe sekcje są materiałem technicznym i pozostają po angielsku.

The endpoint-by-endpoint reference is REST reference, which is generated from the Server's own openapi.json and is never written by hand. This page is the shape around it.

Everything lives under /api/v1

The prefix is applied to the whole application — controllers, the Identity endpoints and the WebSocket alike — rather than repeated on each route.

A request without the prefix is a 404, deliberately

Left alone, the framework would answer at both /health and /api/v1/health. A Client asking a correct host for the wrong path would then be answered instead of corrected, which is the misconfiguration the fixed prefix exists to prevent. The guard is explicit and runs before the prefix is stripped.

What authenticates a request

CallerCredential
A person, through the Clientthe session cookie minted by ASP.NET Core Identity, on sign-in or after an OIDC callback
The WebSocket at /api/v1/wsthe same cookie. No token in the query string — it would end up in proxy logs
A Runnerits own bearer token, from a challenge it signed with its immutable key
The admin surfacethe loopback interface and a token header. See below

A Runner is not a user and holds no cookie, which is why its controller is anonymous at the class level and authenticates each call itself. It is then authorized against the job it holds, not against being a Runner — without that, any approved Runner could fetch every test package in the installation.

Authorisation for everything else is the permission model, resolved per request and cached for that request's lifetime only. A longer-lived cache would keep answering with rights that had been revoked.

Parts of the Identity surface are closed

MapIdentityApi maps its whole surface unconditionally and offers no way to omit any of it, so the Server refuses several of them in middleware — in front of the endpoint, before a body is bound.

  • Self-service registration is off unless the installation turns it on. POST /identity/register answers 403 registration.closed while it is, and the switch is the manager panel's accept local sign-ups. With it off, accounts are created by an organiser or arrive through a provider.
  • There is no password reset and no confirmation to resend, because there is no mail sender in v1. forgotPassword, resetPassword and resendConfirmationEmail answer 403 mail.unavailable. An endpoint that exists and cannot work is worse than one that refuses: it invites a screen to promise something nothing will deliver.
  • /identity/manage/info is refused too, with 403 identity.info.unavailable, and for a different reason than the mail ones: the framework builds that answer with a throw when an account has no address, and this product allows accounts without one — the seeded administrator is one. GET /account is the product's own answer, and it works for every account this product allows.
  • Two-factor authentication is not wired up. The endpoints the framework brings are mapped and unused rather than half-built, by decision.

The match is on the whole remaining segment, trailing slash normalised: a suffix test would let POST /identity/register/ through the guard and refuse /identity/manage/2fa/register, which this rule has nothing to say about.

Errors

Every failure is application/problem+json (RFC 9457), including the ones nobody raised — a 404 from routing is not an exception, so a status-code page supplies the body.

MemberMeaning
statusthe HTTP status
title
detail
codestable across releases
errorsfield name to messages, on a validation failure

Switch on `code`, not on the status

A 503 may equally come from an edge proxy that has never heard of this product. The code is the Server's own word for what happened.

A page past the end is not an error

A list endpoint asked for a page beyond what it holds answers an empty page, not a failure. That includes an absurd one: ?page=2147483647 would overflow the offset it is multiplied into and answer 500 on a negative number, so the offset is clamped and the answer is the empty page.

The admin surface

/api/v1/admin/** is the operator's surface: the maintenance switch, the key ring, the storage tools, the pre-configuration endpoint and the administrator password.

It requires two things, and both of them:

  1. the caller is on the Server container's own loopback interface;
  2. the request carries the configured token in X-AlgoJudge-Admin-Token.

Loopback alone is not enough. Anything that gets a foothold inside the container — including the Server's own process — is already on loopback; the token is what a stolen foothold does not come with.

The address is captured before forwarded headers are applied. Left to the rewritten value, a request from the far side of the internet carrying X-Forwarded-For: 127.0.0.1 would look local, and the maintenance switch would be a switch anybody could throw.

It is deliberately not a permission

This is an operator's act rather than a role somebody can be granted. The moment it is a permission, it is also something a stolen administrator session can do.

Everything it turns away gets a 404

Wrong machine, no token configured, no header, wrong value — one answer, and it is 404, never 403.

A 403 would confirm that the endpoint is there and that the caller got one of the two halves right, which is exactly the feedback somebody probing for it wants. To anything not entitled to be here, this surface does not exist.

An absent, empty or whitespace token setting closes the whole group. There is no "no token means no check" reading: the failure shuts the door rather than opening it.

Because the surface is unreachable from the network, the way an operator uses it is docker exec into the Server's container — which is what aj-admin does.

The maintenance gate exempts /admin, so an installation that has closed itself can still be reopened.

The generated reference

REST reference is built from the openapi.json of a pinned Server release — v0.1.0 — fetched from that tag and checked against a recorded SHA-256, never from a local build, which would document whatever happened to be on the machine that ran it.

One page per tag rather than one per operation: that document declares 162 paths and 199 operations over 44 tags, and a page per operation would bury the eight a reader actually wants.

Its own info.version reads 1.0. That is the framework's default for a document nobody versioned; the Server release this reference describes is the one named above.

The interactive playground is switched off. Turning it on later is a CORS decision, not a framework one.

Na tej stronie