Overview
What the broker is, how a play session flows, and where it sits next to RomM and the webstation container.
romm-broker manages play sessions for the RomM webstation container, and it also runs the shared room each session happens in. The container plays one game at a time. RomM starts a game by sending an activate request; the broker launches the emulator (restoring your save data if you gave it any), and sends back a link to the room. That room shows the selkies video stream, the tool that turns the emulator's screen into video you can watch in a browser, right inside the page. Exiting a session saves your progress, stops the emulator, and packages up whatever save data changed.
The pieces
RomM (parent) ──REST──▶ romm-broker ──spawns──▶ emulator
▲ │ │
│ iframe │ └──tokens──▶ selkies (stream + input)
└──────── browser ◀───────┘ room UI (chat, webcam, input assignment)- RomM is your game library. It decides what to launch, stores every save state and save archive, and calls the broker to start, stop, and manage sessions, using a shared secret both sides know (like a password between them).
- romm-broker is a Python web app (built with FastAPI) that runs inside the container. It manages the one active session, starts and stops the emulator, moves save data in and out, and serves the room.
- selkies turns the emulator's screen into a video stream and decides who
is allowed to send input. The broker tells it which tokens are active with
a token map,
{token: {role, slot, mk_control}}, and selkies uses that to decide, for each person watching or playing, whether their input reaches the mouse and keyboard, a specific virtual gamepad slot, or nowhere. - The emulator is whichever launcher the activate request asked for (the
broker's term for an emulator integration, like PCSX2). Each one is its own
module in the code, a subclass of the broker's
Emulatorbase class. See Emulators.
A session, end to end
Activate
RomM tells the broker to start a game by calling /api/session/activate,
sending the user, which emulator to launch, and the rom's file path. It can
also include a save archive to restore and a state slot to resume from. The
broker figures out the right file to boot, clears out the emulator's working
state slot, restores the save archive if one was given, launches the
emulator, and answers with a token URL, a private link for reaching the
room. See Launch a game.
Play
Opening the token URL takes you to the room: the video stream sits right
inside the page, alongside chat, webcam video from everyone connected, and a
drag-and-drop panel for assigning gamepads and mouse/keyboard control. Other
people join by calling /api/session/join or by using an invite link, and
each one gets their own token. RomM can save and load a state mid-session
and pull the state file out into its library. See
Save states.
Key ideas
- Everything lives under
SUBFOLDER(default/streaming/). The same setting drives three things: the nginx templating that configures the container's web server, the FastAPI mount that places the broker's own app, and the vite base that sets the room UI's build path. So your whole app only has to follow one reverse-proxy rule. See Reverse proxy. - RomM keeps the permanent copy of your save data. Each emulator only uses one state slot at a time; no matter what slot number a request asks for, it resolves to that same slot. States and save archives get pulled out of the container every time you save or exit, and pushed back in before you load a state or resume a session. That's what lets a brand new container, with nothing saved on its own disk, pick up right where a session left off.
- Save data moves around as zip files, covering only the folders each
emulator actually uses for saves (for example
memcards/andsstates/for PCSX2). Activate restores a zip archive without overwriting anything newer that's already there; exit zips up everything that's changed since launch. Your save data survives even when the upload fails: outside of dev mode, the broker only writes the zip to disk if the upload to the callback doesn't go through, and the export routes let you get it back. - By default, the save archive uploads back to RomM itself. The broker
runs under RomM's own
SUBFOLDER, so as far as the browser is concerned they're the same website, which lets activate work out where to send the finished archive just from the incoming request's headers (X-Forwarded-Proto/X-Forwarded-Host, or plainHostif neither is set). If RomM and the broker run on separate domains or ports, override this withcallback.base_url.
Where to go next
Quickstart
A step-by-step walkthrough from an empty container to a working session.
Run the container
The image, its volumes and what happens at boot.
Configuration
Every environment variable the broker and the launchers read.
REST API
The endpoints RomM and the room UI call.
Developer guide
Dev mode, tests, adding an emulator and the Python reference.
Troubleshooting
Common setup problems, by symptom, with fixes.