webstation-broker
REST API

Exit

Ending a session with POST /api/session/exit: the final state, the save delta, and the upload to the callback origin.

From the room UI's exit button (controller token) or directly:

curl -k -X POST 'https://localhost:3001/streaming/api/session/exit?slot=10' \
  -H 'X-Broker-Secret: <shared secret>'

Saves state (into the working slot, whatever slot says), stops the emulator, dumps the save delta, and uploads it to the callback origin as multipart form data: POST {base_url}{BROKER_SAVE_UPLOAD_PATH} with an archive file part (zip, {session_id}-{timestamp}.zip) plus session_id, emulator, and when the rom carried them rom_id / rom_name form fields. If activate supplied callback.token, it is sent as Authorization: Bearer <token>.

Query parameters:

ParameterDefaultPurpose
tokennoneThe controller token; lets the room UI exit without the secret.
slot0The state slot to save into. Resolved to the emulator's working slot like everywhere else; the report echoes the one used.
savetruefalse (or 0) stops without writing a state. The game's own save data is still flushed and shipped: not writing a state is the whole of "exit without saving", and discarding an in-game save the player made at a save point would be losing real progress.

Slot 0 is a real slot on this broker, which is why save is a separate flag: there is no slot number left to spend on meaning "do not save".

The callback origin

The callback base URL is derived from the activate request (the parent origin) unless the activate payload overrides it:

"callback": { "base_url": "https://romm.example.com", "token": "<upload token>" }

Same-origin deployments do not need to send one: the broker sits under the parent's SUBFOLDER, so the origin that served the activate request is the parent. The derivation reads X-Forwarded-Proto and X-Forwarded-Host when a proxy sets them, else Host.

Report-only and failed uploads

In dev mode (BROKER_DEV_MODE=true) the upload is skipped: the archive is written to BROKER_EXPORT_DIR and the exit report's upload object is mode: "report-only" with what would have been sent. Outside dev mode a failed upload also writes the archive there so nothing is lost, and the report's upload object carries mode: "failed" with the error. Either way the export routes hand the archive back. The summary is posted to the room chat before the room is torn down.

The exit report

The response carries everything the parent needs to file the session:

FieldMeaning
statusexited
session_id, emulator, romEchoed from activate.
state_saved, state_slot, state_fileWhether a final state was written, into which slot, and its filename. state_saved is false with save=0; the file is then fetchable through the state-file GET until the next activate.
save_dumpThe delta that was zipped: the archive name and size and the list of files with their paths relative to the save root.
uploadmode of uploaded, failed or report-only, with the target url, the HTTP status, and on failure the error and the path the archive was written to.

After the report the room websocket is closed, the token map pushed to selkies is emptied so every stream disconnects, and the session is retired. The emulator object outlives it just long enough for the state-file and screenshot GETs to answer.

On this page