Skip to main content

Backend overview

mistwarp-api is the OSL service behind the MistWarp community platform. It handles Rotur sign-in, projects and their history, Cloudflare R2 storage, comments and other community features, MistWarp Classroom, game data, and pull requests between MistWarp forks. Rotur Git can be connected as an optional external Git provider. The source is at MistWarp/api.

Community data lives in SQLite at data/community.db. Sessions, indexes and queues are JSON files under data/. Project files go to R2, or to data/blobs/ when no bucket is configured.

Running it locally​

Copy .env.example to .env, fill in the values, then run:

osl run main.osl

See Configuration for every setting and the order they are read in.

The API needs OSL 3.0. opal.json lists the usual commands:

CommandDoes
osl testRuns every *.test.osl
osl lsp check main.oslType checks the whole program
osl compile main.osl -o mainBuilds the server binary
python3 scripts/route_dump.py .Prints every registered route with its middleware
python3 scripts/e2e_smoke.py <binary> [<other binary>]Runs an offline end to end smoke test, and diffs the responses of two builds

Source layout​

main.osl only composes the app: it imports each domain directory, runs startup and migrations, builds the router and starts it. Everything else lives in src/<domain>/:

DirectoryOwns
configReading .env and the environment, data paths, size limits, the R2 client
coreShared helpers and the community database
httpRequest body helpers, request size limits, auth and admin middleware, cache headers
authSessions, user identity, editor sessions, the agreement, blocks and mutes, settings, privacy
storageBlob storage on R2 or disk, the upload queue, thumbnails, quotas, the Rotur storage ledger, the backpack
projectsProjects, sharing, remixes, releases, extensions, live collaboration, monetisation
historyProject history, deltas, history backfill and commit metadata
gitGit inspection, pull requests and Rotur Git
gamesGame saves, inventories and marketplace products
realtimeThe multiplayer and site WebSockets
communitySpaces, roadmap, comments, bounties, profiles, news, notifications, milestones, standing, reports, groups
adminAdmin tools, bans and shadow bans, moderation, error reports, funnel events
classroomMistWarp Classroom
roturThe Rotur App, plans and perks, payments, signals, badges and webhooks

Each directory registers its routes in its own routes.osl (registerProjectRoutes(api) and so on), and main.osl calls them in order under /v1 and /api. Each file lists what other directories may use in an export {...} block at the top; anything not exported is private to its directory. Global caches live next to the code that uses them. Tests sit beside the file they test as <name>.test.osl.

Where it runs​

The site and editor are one scratch-gui build (community pages live in scratch-gui/src/community), served on the frontend domain:

PathServes
/community app (index.html)
/editorscratch-gui editor
/embed.htmlscratch-gui embed player (project pages iframe this)
/project/, /explore, /users/, /settingscommunity app (client routing)

This API runs at api.mistwarp.org, with mwapi.mistium.com retained as a backwards-compatible hostname. The frontend calls it at https://api.mistwarp.org/v1 directly. The scratch-gui dev server talks to the live API too (see Building and Running). Auth is Bearer-token based (the session token returned by /v1/auth), so a cross-domain API works without shared cookies. CORS echoes the request Origin with credentials, so any frontend origin is accepted.

/v1 is the canonical route group. Every route is also registered under /api for backwards compatibility, including uploads and their larger body limits. New clients and generated URLs must use /v1.

R2 bucket only needs public GET through R2_PUBLIC_BASE, and the public domain must expose the bucket's assets/ prefix and allow CORS GET requests from the MistWarp frontend. Project metadata keeps the API /blobs/assets base. That endpoint serves a local asset when its file exists under data/blobs/assets/; otherwise it redirects to R2_PUBLIC_BASE. All writes go through this server. With no R2 configured, the server falls back to local disk (data/blobs/, served at /blobs) so it runs locally with zero setup.