Skip to content

Compatibility with DMOJ

MOJ is a rewrite of DMOJ, not a fork. What faces outward is kept, so a site, its problem repositories, its scripts and its bookmarks move across unchanged.

The same

SurfaceDetail
URLsEvery path DMOJ serves is served here, with the trailing slash: /problems/, /problem/aplusb, /submissions/user/alice/, /submission/12345, /contest/spring26/ranking/, /user/alice/solved, /organization/3-example, /accounts/login/, /feed/blog/atom/, /api/v2/problems.
Problem datainit.yml, test layout, batches, dependencies, checkers, graders, generators, interactors, pretests. The grader is the DMOJ judge-server.
StatementsDMOJ's markdown, including ~x^2~ inline maths, [user:name] links, and raw HTML in the contexts DMOJ allowed it.
API v2The same paths, envelope, filter names and capitalised True/False booleans. 48-character bearer tokens.
Contest formatsdefault, ioi, ioi16, atcoder, icpc, ecoo, with DMOJ's configuration keys, defaults and validation messages.
RatingsElo-MMR, so imported ratings continue rather than restart.
PermissionsDMOJ's permission codes verbatim, such as judge.edit_all_problem and judge.rejudge_submission.
Problem labelsOnly icpc letters its problems; every other format numbers them.

What imports

Importing from DMOJ reads a mysqldump of the old database. Every imported row keeps its old primary key, so ids that appeared in URLs stay stable.

Carried acrossLeft behind
Users, with their password hashes verbatimSessions: everyone signs in again
Two-factor secrets and scratch codes, and passkeysRegistration keys: unactivated users get a new mail
API tokens, while LEGACY_SECRET_KEY is setSocial logins
Profiles, points, ratings, organisations, classesCustom user JavaScript
Problems, statements, translations, editorials, limitsAnything computed: aggregates, sitemap, rendered PDFs
Submissions with their sources and per-case resultsDjango bookkeeping: admin log, migrations, redirects
Contests, participations, rating history
Comments, blog posts, tickets, navigation, flat pages

The first sign-in with a legacy password hash rewrites it in the new format, so nobody is asked to reset anything.

What differs

BehaviourDMOJMOJ
Judge connectionA bridge daemon pushes work down a held socket on TCP 9999The judge polls over HTTPS, so it needs no inbound port
Live pagesA websocket event daemonPages subscribe; there is no second service to run
Problem metadataTyped into the Django adminconfig.json in the problem repository
Test dataDistributed to each judge out of bandPublished to the site, which hands it to judges by hash
PDF statementspdfoid, mathoid and texoidOne binary, with no browser in the render path
Contest labelsA per-contest Lua scriptA label scheme: letters, numbers, or an explicit list
Tilde maths~...~ may span a line endingIt may not, so an unbalanced tilde spoils one line
Inline $...$ mathsNot offeredOn by default
Comment repliesNewest first at every levelNewest first at top level, oldest first inside a thread
Hidden commentsFiltered out for everyoneReturned to holders of judge.change_comment, flagged
Judge keysStored in the clearStored as a SHA-256; shown once
Staff two-factorOnly blocks removing the last factorRequired on every page
PasskeysA second factorA sign-in path of their own
Scoreboard revealRuns in one browserShared state: every screen turns the same cell over at once
Object idsAlways integersAn integer where one was imported, a string otherwise; both are accepted

A contest imported with a Lua label script is given the custom scheme with an empty label list and is named in the import report.

Released under the AGPL-3.0-only licence.